j2me二维码编解码
This commit is contained in:
commit
bb61a6680f
83
build.xml
Normal file
83
build.xml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- You may freely edit this file. See commented blocks below for -->
|
||||||
|
<!-- some examples of how to customize the build. -->
|
||||||
|
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||||
|
<project name="QRreader" default="jar" basedir=".">
|
||||||
|
<description>Builds, tests, and runs the project .</description>
|
||||||
|
<import file="nbproject/build-impl.xml"/>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
There exist several targets which are by default empty and which can be
|
||||||
|
used for execution of your tasks. These targets are usually executed
|
||||||
|
before and after some main targets. They are:
|
||||||
|
|
||||||
|
pre-init: called before initialization of project properties
|
||||||
|
post-init: called after initialization of project properties
|
||||||
|
pre-preprocess: called before text preprocessing of sources
|
||||||
|
post-preprocess: called after text preprocessing of sources
|
||||||
|
pre-compile: called before source compilation
|
||||||
|
post-compile: called after source compilation
|
||||||
|
pre-obfuscate: called before obfuscation
|
||||||
|
post-obfuscate: called after obfuscation
|
||||||
|
pre-preverify: called before preverification
|
||||||
|
post-preverify: called after preverification
|
||||||
|
pre-jar: called before jar building
|
||||||
|
post-jar: called after jar building
|
||||||
|
pre-build: called before final distribution building
|
||||||
|
post-build: called after final distribution building
|
||||||
|
pre-clean: called before cleaning build products
|
||||||
|
post-clean: called after cleaning build products
|
||||||
|
|
||||||
|
Example of pluging a my-special-task after the compilation could look like
|
||||||
|
|
||||||
|
<target name="post-compile">
|
||||||
|
<my-special-task>
|
||||||
|
<fileset dir="${build.classes.dir}"/>
|
||||||
|
</my-special-task>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
For list of available properties check the imported
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
Other way how to customize the build is by overriding existing main targets.
|
||||||
|
The target of interest are:
|
||||||
|
|
||||||
|
preprocess: preprocessing
|
||||||
|
extract-libs: extraction of libraries and resources
|
||||||
|
compile: compilation
|
||||||
|
create-jad: construction of jad and jar manifest source
|
||||||
|
obfuscate: obfuscation
|
||||||
|
preverify: preverification
|
||||||
|
jar: jar archive building
|
||||||
|
run: execution
|
||||||
|
debug: execution in debug mode
|
||||||
|
build: building of the final distribution
|
||||||
|
javadoc: javadoc generation
|
||||||
|
|
||||||
|
Example of overriding the target for project execution could look like
|
||||||
|
|
||||||
|
<target name="run" depends="init,jar">
|
||||||
|
<my-special-exec jadfile="${dist.dir}/${dist.jad}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
Be careful about correct dependencies when overriding original target.
|
||||||
|
Again, for list of available properties which you can use check the target
|
||||||
|
you are overriding in nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
A special target for-all-configs can be used to run some specific targets for
|
||||||
|
all project configurations in a sequence. File nbproject/build-impl.xml
|
||||||
|
already contains some "for-all" targets:
|
||||||
|
|
||||||
|
jar-all
|
||||||
|
javadoc-all
|
||||||
|
clean-all
|
||||||
|
|
||||||
|
Example of definition of target iterating over all project configurations:
|
||||||
|
|
||||||
|
<target name="jar-all">
|
||||||
|
<property name="target.to.call" value="jar"/>
|
||||||
|
<antcall target="for-all-configs"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
-->
|
||||||
|
</project>
|
BIN
jsr75_1.0.jar
Normal file
BIN
jsr75_1.0.jar
Normal file
Binary file not shown.
1376
nbproject/build-impl.xml
Normal file
1376
nbproject/build-impl.xml
Normal file
File diff suppressed because it is too large
Load Diff
8
nbproject/genfiles.properties
Normal file
8
nbproject/genfiles.properties
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||||
|
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||||
|
build.xml.data.CRC32=9f5f57dd
|
||||||
|
build.xml.script.CRC32=51aac932
|
||||||
|
build.xml.stylesheet.CRC32=9c6a911d
|
||||||
|
nbproject/build-impl.xml.data.CRC32=9f5f57dd
|
||||||
|
nbproject/build-impl.xml.script.CRC32=3413fbf5
|
||||||
|
nbproject/build-impl.xml.stylesheet.CRC32=296da595
|
7
nbproject/private/private.properties
Normal file
7
nbproject/private/private.properties
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#Mon, 16 Dec 2024 16:04:01 +0800
|
||||||
|
app-version.autoincrement=true
|
||||||
|
config.active=
|
||||||
|
deployment.counter=11
|
||||||
|
deployment.number=0.0.10
|
||||||
|
javadoc.preview=true
|
||||||
|
netbeans.user=C\:\\Users\\enhua\\.netbeans\\6.9
|
4
nbproject/private/private.xml
Normal file
4
nbproject/private/private.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||||
|
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
|
||||||
|
</project-private>
|
146
nbproject/project.properties
Normal file
146
nbproject/project.properties
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
abilities=MMAPI=1.1,JSR82=1.1,JSR280=1.0,JSR226=1.0,MIDP=2.1,JSR229=1.1,SATSA=1.0,CLDC=1.1,JSR177=1.0,JSR179=1.0,J2MEWS=1.0,WMA=2.0,JSR172=1.0,JSR256=1.0,OBEX=1.0,ColorScreen,JSR238=1.0,JSR239=1.0,JSR211=1.0,JSR234=1.0,ScreenWidth=240,JSR75=1.0,JSR184=1.1,ScreenHeight=320,ScreenColorDepth=16,JSR180=1.1.0,
|
||||||
|
all.configurations=\
|
||||||
|
application.args=
|
||||||
|
application.description=
|
||||||
|
application.description.detail=
|
||||||
|
application.name=
|
||||||
|
application.vendor=Vendor
|
||||||
|
build.classes.dir=${build.dir}/compiled
|
||||||
|
build.classes.excludes=**/*.java,**/*.form,**/*.class,**/.nbintdb,**/*.mvd,**/*.wsclient,**/*.vmd
|
||||||
|
build.dir=build/${config.active}
|
||||||
|
build.root.dir=build
|
||||||
|
debug.level=debug
|
||||||
|
debugger.timeout=
|
||||||
|
deployment.copy.target=${file.reference.deploy-1}
|
||||||
|
deployment.instance=default
|
||||||
|
deployment.jarurl=${dist.jar}
|
||||||
|
deployment.method=NONE
|
||||||
|
deployment.override.jarurl=false
|
||||||
|
dist.dir=dist/${config.active}
|
||||||
|
dist.jad=QRreader.jad
|
||||||
|
dist.jar=QRreader.jar
|
||||||
|
dist.javadoc.dir=${dist.dir}/doc
|
||||||
|
dist.root.dir=dist
|
||||||
|
extra.classpath=
|
||||||
|
file.reference.builtin.ks=${netbeans.user}/config/j2me/builtin.ks
|
||||||
|
file.reference.deploy-1=deploy
|
||||||
|
file.reference.jsr75_1.0.jar=jsr75_1.0.jar
|
||||||
|
file.reference.netbeans_midp_components_basic.jar=../../../../../Program Files/NetBeans 6.9.1/mobility/modules/ext/netbeans_midp_components_basic.jar
|
||||||
|
filter.exclude.tests=false
|
||||||
|
filter.excludes=
|
||||||
|
filter.more.excludes=**/overview.html,**/package.html
|
||||||
|
filter.use.standard=true
|
||||||
|
jar.compress=true
|
||||||
|
javac.debug=true
|
||||||
|
javac.deprecation=false
|
||||||
|
javac.encoding=UTF-8
|
||||||
|
javac.optimize=false
|
||||||
|
javac.source=1.3
|
||||||
|
javac.target=1.3
|
||||||
|
javadoc.author=false
|
||||||
|
javadoc.encoding=
|
||||||
|
javadoc.noindex=false
|
||||||
|
javadoc.nonavbar=false
|
||||||
|
javadoc.notree=false
|
||||||
|
javadoc.private=false
|
||||||
|
javadoc.splitindex=true
|
||||||
|
javadoc.use=true
|
||||||
|
javadoc.version=false
|
||||||
|
javadoc.windowtitle=
|
||||||
|
libs.classpath=${libs.NetBeans MIDP Components.classpath};${libs.NetBeans MIDP Components PDA.classpath};${file.reference.jsr75_1.0.jar};${file.reference.netbeans_midp_components_basic.jar}
|
||||||
|
main.class=
|
||||||
|
main.class.class=applet
|
||||||
|
manifest.apipermissions=
|
||||||
|
manifest.file=manifest.mf
|
||||||
|
manifest.is.liblet=false
|
||||||
|
manifest.jad=
|
||||||
|
manifest.manifest=
|
||||||
|
manifest.midlets=MIDlet-1: \u4e8c\u7ef4\u7801\u52a9\u624b,/zxing-icon.png,com.google.zxing.client.j2me.ZXMIDlet\n
|
||||||
|
manifest.others=MIDlet-Vendor: Vendor\nMIDlet-Name: \u4e8c\u7ef4\u7801\u52a9\u624b\nMIDlet-Version: 1.0\n
|
||||||
|
manifest.pushregistry=
|
||||||
|
name=QRreader
|
||||||
|
no.dependencies=false
|
||||||
|
nokiaS80.application.icon=
|
||||||
|
nsicom.application.monitorhost=
|
||||||
|
nsicom.application.runremote=
|
||||||
|
nsicom.application.runverbose=
|
||||||
|
nsicom.remoteapp.location=\\My Documents\\NetBeans Applications
|
||||||
|
nsicom.remotevm.location=\\Windows\\creme\\bin\\CrEme.exe
|
||||||
|
obfuscated.classes.dir=${build.dir}/obfuscated
|
||||||
|
obfuscation.custom=
|
||||||
|
obfuscation.level=0
|
||||||
|
obfuscator.destjar=${build.dir}/obfuscated.jar
|
||||||
|
obfuscator.srcjar=${build.dir}/before-obfuscation.jar
|
||||||
|
platform.active=Java_TM__Platform_Micro_Edition_SDK_3_0
|
||||||
|
platform.active.description=Java(TM) Platform Micro Edition SDK 3.0
|
||||||
|
platform.apis=MMAPI-1.1,SATSA-1.0,WMA-1.1
|
||||||
|
platform.bootclasspath=${platform.home}/lib/jsr135_1.1.jar:${platform.home}/lib/jsr177_1.0.jar:${platform.home}/lib/jsr120_1.1.jar:${platform.home}/lib/midp_2.1.jar:${platform.home}/lib/cldc_1.1.jar
|
||||||
|
platform.configuration=CLDC-1.1
|
||||||
|
platform.device=ClamshellCldcPhone1
|
||||||
|
platform.fat.jar=true
|
||||||
|
platform.profile=MIDP-2.1
|
||||||
|
platform.trigger=CLDC
|
||||||
|
platform.type=UEI-1.0.1
|
||||||
|
preprocessed.dir=${build.dir}/preprocessed
|
||||||
|
preverify.classes.dir=${build.dir}/preverified
|
||||||
|
preverify.sources.dir=${build.dir}/preverifysrc
|
||||||
|
resources.dir=resources
|
||||||
|
ricoh.application.email=
|
||||||
|
ricoh.application.fax=
|
||||||
|
ricoh.application.icon=
|
||||||
|
ricoh.application.target-jar=
|
||||||
|
ricoh.application.telephone=
|
||||||
|
ricoh.application.uid=12248964
|
||||||
|
ricoh.application.version=
|
||||||
|
ricoh.dalp.application-desc.auto-run=false
|
||||||
|
ricoh.dalp.application-desc.energy-save=
|
||||||
|
ricoh.dalp.application-desc.exec-auth=
|
||||||
|
ricoh.dalp.application-desc.visible=true
|
||||||
|
ricoh.dalp.argument=
|
||||||
|
ricoh.dalp.codebase=
|
||||||
|
ricoh.dalp.display-mode.color=true
|
||||||
|
ricoh.dalp.display-mode.is-4line-support=false
|
||||||
|
ricoh.dalp.display-mode.is-hvga-support=true
|
||||||
|
ricoh.dalp.display-mode.is-vga-support=false
|
||||||
|
ricoh.dalp.display-mode.is-wvga-support=false
|
||||||
|
ricoh.dalp.information.abbreviation=
|
||||||
|
ricoh.dalp.information.icon.basepath=
|
||||||
|
ricoh.dalp.information.icon.location=
|
||||||
|
ricoh.dalp.information.is-icon-used=true
|
||||||
|
ricoh.dalp.install.destination=hdd
|
||||||
|
ricoh.dalp.install.mode.auto=true
|
||||||
|
ricoh.dalp.install.work-dir=hdd
|
||||||
|
ricoh.dalp.is-managed=true
|
||||||
|
ricoh.dalp.resources.dsdk.version=2.0
|
||||||
|
ricoh.dalp.resources.jar.basepath=
|
||||||
|
ricoh.dalp.resources.jar.version=
|
||||||
|
ricoh.dalp.version=
|
||||||
|
ricoh.icon.invert=false
|
||||||
|
ricoh.platform.target.version=
|
||||||
|
run.cmd.options=
|
||||||
|
run.jvmargs=
|
||||||
|
run.method=STANDARD
|
||||||
|
run.security.domain=minimum
|
||||||
|
run.use.security.domain=false
|
||||||
|
savaje.application.icon=
|
||||||
|
savaje.application.icon.focused=
|
||||||
|
savaje.application.icon.small=
|
||||||
|
savaje.application.uid=TBD
|
||||||
|
savaje.bundle.base=
|
||||||
|
savaje.bundle.debug=false
|
||||||
|
savaje.bundle.debug.port=
|
||||||
|
semc.application.caps=
|
||||||
|
semc.application.icon=
|
||||||
|
semc.application.icon.count=
|
||||||
|
semc.application.icon.splash=
|
||||||
|
semc.application.icon.splash.installonly=false
|
||||||
|
semc.application.uid=E5047815
|
||||||
|
semc.certificate.path=
|
||||||
|
semc.private.key.password=
|
||||||
|
semc.private.key.path=
|
||||||
|
sign.alias=trusted
|
||||||
|
sign.enabled=false
|
||||||
|
sign.keystore=${file.reference.builtin.ks}
|
||||||
|
src.dir=src
|
||||||
|
use.emptyapis=true
|
||||||
|
use.preprocessor=true
|
10
nbproject/project.xml
Normal file
10
nbproject/project.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||||
|
<type>org.netbeans.modules.kjava.j2meproject</type>
|
||||||
|
<configuration>
|
||||||
|
<data xmlns="http://www.netbeans.org/ns/j2me-project">
|
||||||
|
<name>QRreader</name>
|
||||||
|
<minimum-ant-version>1.6</minimum-ant-version>
|
||||||
|
</data>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
BIN
res/zxing-icon.png
Normal file
BIN
res/zxing-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
103
src/com/google/zxing/BarcodeFormat.java
Normal file
103
src/com/google/zxing/BarcodeFormat.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerates barcode formats known to this package.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class BarcodeFormat {
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
private static final Hashtable VALUES = new Hashtable();
|
||||||
|
|
||||||
|
/** QR Code 2D barcode format. */
|
||||||
|
public static final BarcodeFormat QR_CODE = new BarcodeFormat("QR_CODE");
|
||||||
|
|
||||||
|
/** Data Matrix 2D barcode format. */
|
||||||
|
public static final BarcodeFormat DATA_MATRIX = new BarcodeFormat("DATA_MATRIX");
|
||||||
|
|
||||||
|
/** UPC-E 1D format. */
|
||||||
|
public static final BarcodeFormat UPC_E = new BarcodeFormat("UPC_E");
|
||||||
|
|
||||||
|
/** UPC-A 1D format. */
|
||||||
|
public static final BarcodeFormat UPC_A = new BarcodeFormat("UPC_A");
|
||||||
|
|
||||||
|
/** EAN-8 1D format. */
|
||||||
|
public static final BarcodeFormat EAN_8 = new BarcodeFormat("EAN_8");
|
||||||
|
|
||||||
|
/** EAN-13 1D format. */
|
||||||
|
public static final BarcodeFormat EAN_13 = new BarcodeFormat("EAN_13");
|
||||||
|
|
||||||
|
/** UPC/EAN extension format. Not a stand-alone format. */
|
||||||
|
public static final BarcodeFormat UPC_EAN_EXTENSION = new BarcodeFormat("UPC_EAN_EXTENSION");
|
||||||
|
|
||||||
|
/** Code 128 1D format. */
|
||||||
|
public static final BarcodeFormat CODE_128 = new BarcodeFormat("CODE_128");
|
||||||
|
|
||||||
|
/** Code 39 1D format. */
|
||||||
|
public static final BarcodeFormat CODE_39 = new BarcodeFormat("CODE_39");
|
||||||
|
|
||||||
|
/** Code 93 1D format. */
|
||||||
|
public static final BarcodeFormat CODE_93 = new BarcodeFormat("CODE_93");
|
||||||
|
|
||||||
|
/** CODABAR 1D format. */
|
||||||
|
public static final BarcodeFormat CODABAR = new BarcodeFormat("CODABAR");
|
||||||
|
|
||||||
|
/** ITF (Interleaved Two of Five) 1D format. */
|
||||||
|
public static final BarcodeFormat ITF = new BarcodeFormat("ITF");
|
||||||
|
|
||||||
|
/** RSS 14 */
|
||||||
|
public static final BarcodeFormat RSS14 = new BarcodeFormat("RSS14");
|
||||||
|
|
||||||
|
/** PDF417 format. */
|
||||||
|
public static final BarcodeFormat PDF417 = new BarcodeFormat("PDF417");
|
||||||
|
|
||||||
|
/** RSS EXPANDED */
|
||||||
|
public static final BarcodeFormat RSS_EXPANDED = new BarcodeFormat("RSS_EXPANDED");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private BarcodeFormat(String name) {
|
||||||
|
this.name = name;
|
||||||
|
VALUES.put(name, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BarcodeFormat valueOf(String name) {
|
||||||
|
if (name == null || name.length() == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
BarcodeFormat format = (BarcodeFormat) VALUES.get(name);
|
||||||
|
if (format == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
80
src/com/google/zxing/Binarizer.java
Normal file
80
src/com/google/zxing/Binarizer.java
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import com.google.zxing.common.BitArray;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
|
||||||
|
* It allows the algorithm to vary polymorphically, for example allowing a very expensive
|
||||||
|
* thresholding technique for servers and a fast one for mobile. It also permits the implementation
|
||||||
|
* to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public abstract class Binarizer {
|
||||||
|
|
||||||
|
private final LuminanceSource source;
|
||||||
|
|
||||||
|
protected Binarizer(LuminanceSource source) {
|
||||||
|
if (source == null) {
|
||||||
|
throw new IllegalArgumentException("Source must be non-null.");
|
||||||
|
}
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuminanceSource getLuminanceSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||||
|
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||||
|
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||||
|
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
|
||||||
|
* and passed in with each call for performance. However it is legal to keep more than one row
|
||||||
|
* at a time if needed.
|
||||||
|
*
|
||||||
|
* @param y The row to fetch, 0 <= y < bitmap height.
|
||||||
|
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||||
|
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||||
|
* @return The array of bits for this row (true means black).
|
||||||
|
*/
|
||||||
|
public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
|
||||||
|
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||||
|
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||||
|
* fetched using getBlackRow(), so don't mix and match between them.
|
||||||
|
*
|
||||||
|
* @return The 2D array of bits for the image (true means black).
|
||||||
|
*/
|
||||||
|
public abstract BitMatrix getBlackMatrix() throws NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object with the same type as this Binarizer implementation, but with pristine
|
||||||
|
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
|
||||||
|
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
|
||||||
|
*
|
||||||
|
* @param source The LuminanceSource this Binarizer will operate on.
|
||||||
|
* @return A new concrete Binarizer implementation object.
|
||||||
|
*/
|
||||||
|
public abstract Binarizer createBinarizer(LuminanceSource source);
|
||||||
|
|
||||||
|
}
|
128
src/com/google/zxing/BinaryBitmap.java
Normal file
128
src/com/google/zxing/BinaryBitmap.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import com.google.zxing.common.BitArray;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
|
||||||
|
* accept a BinaryBitmap and attempt to decode it.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class BinaryBitmap {
|
||||||
|
|
||||||
|
private final Binarizer binarizer;
|
||||||
|
private BitMatrix matrix;
|
||||||
|
|
||||||
|
public BinaryBitmap(Binarizer binarizer) {
|
||||||
|
if (binarizer == null) {
|
||||||
|
throw new IllegalArgumentException("Binarizer must be non-null.");
|
||||||
|
}
|
||||||
|
this.binarizer = binarizer;
|
||||||
|
matrix = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The width of the bitmap.
|
||||||
|
*/
|
||||||
|
public int getWidth() {
|
||||||
|
return binarizer.getLuminanceSource().getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The height of the bitmap.
|
||||||
|
*/
|
||||||
|
public int getHeight() {
|
||||||
|
return binarizer.getLuminanceSource().getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||||
|
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||||
|
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||||
|
*
|
||||||
|
* @param y The row to fetch, 0 <= y < bitmap height.
|
||||||
|
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||||
|
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||||
|
* @return The array of bits for this row (true means black).
|
||||||
|
*/
|
||||||
|
public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
|
||||||
|
return binarizer.getBlackRow(y, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
|
||||||
|
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||||
|
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||||
|
* fetched using getBlackRow(), so don't mix and match between them.
|
||||||
|
*
|
||||||
|
* @return The 2D array of bits for the image (true means black).
|
||||||
|
*/
|
||||||
|
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||||
|
// The matrix is created on demand the first time it is requested, then cached. There are two
|
||||||
|
// reasons for this:
|
||||||
|
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
|
||||||
|
// 1D Reader finds a barcode before the 2D Readers run.
|
||||||
|
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
|
||||||
|
if (matrix == null) {
|
||||||
|
matrix = binarizer.getBlackMatrix();
|
||||||
|
}
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this bitmap can be cropped.
|
||||||
|
*/
|
||||||
|
public boolean isCropSupported() {
|
||||||
|
return binarizer.getLuminanceSource().isCropSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||||
|
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||||
|
*
|
||||||
|
* @param left The left coordinate, 0 <= left < getWidth().
|
||||||
|
* @param top The top coordinate, 0 <= top <= getHeight().
|
||||||
|
* @param width The width of the rectangle to crop.
|
||||||
|
* @param height The height of the rectangle to crop.
|
||||||
|
* @return A cropped version of this object.
|
||||||
|
*/
|
||||||
|
public BinaryBitmap crop(int left, int top, int width, int height) {
|
||||||
|
LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height);
|
||||||
|
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this bitmap supports counter-clockwise rotation.
|
||||||
|
*/
|
||||||
|
public boolean isRotateSupported() {
|
||||||
|
return binarizer.getLuminanceSource().isRotateSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
|
||||||
|
*
|
||||||
|
* @return A rotated version of this object.
|
||||||
|
*/
|
||||||
|
public BinaryBitmap rotateCounterClockwise() {
|
||||||
|
LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise();
|
||||||
|
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/com/google/zxing/ChecksumException.java
Normal file
37
src/com/google/zxing/ChecksumException.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a barcode was successfully detected and decoded, but
|
||||||
|
* was not returned because its checksum feature failed.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class ChecksumException extends ReaderException {
|
||||||
|
|
||||||
|
private static final ChecksumException instance = new ChecksumException();
|
||||||
|
|
||||||
|
private ChecksumException() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChecksumException getChecksumInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
79
src/com/google/zxing/DecodeHintType.java
Normal file
79
src/com/google/zxing/DecodeHintType.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates a type of hint that a caller may pass to a barcode reader to help it
|
||||||
|
* more quickly or accurately decode it. It is up to implementations to decide what,
|
||||||
|
* if anything, to do with the information that is supplied.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
* @see Reader#decode(BinaryBitmap,java.util.Hashtable)
|
||||||
|
*/
|
||||||
|
public final class DecodeHintType {
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType OTHER = new DecodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
|
||||||
|
* use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType PURE_BARCODE = new DecodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is known to be of one of a few possible formats.
|
||||||
|
* Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType POSSIBLE_FORMATS = new DecodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spend more time to try to find a barcode; optimize for accuracy, not speed.
|
||||||
|
* Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType TRY_HARDER = new DecodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what character encoding to use when decoding, where applicable (type String)
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType CHARACTER_SET = new DecodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed lengths of encoded data -- reject anything else. Maps to an int[].
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType ALLOWED_LENGTHS = new DecodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assume Code 39 codes employ a check digit. Maps to {@link Boolean}.
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new DecodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The caller needs to be notified via callback when a possible {@link ResultPoint}
|
||||||
|
* is found. Maps to a {@link ResultPointCallback}.
|
||||||
|
*/
|
||||||
|
public static final DecodeHintType NEED_RESULT_POINT_CALLBACK = new DecodeHintType();
|
||||||
|
|
||||||
|
private DecodeHintType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/com/google/zxing/EncodeHintType.java
Normal file
39
src/com/google/zxing/EncodeHintType.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are a set of hints that you may pass to Writers to specify their behavior.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class EncodeHintType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what degree of error correction to use, for example in QR Codes (type Integer).
|
||||||
|
*/
|
||||||
|
public static final EncodeHintType ERROR_CORRECTION = new EncodeHintType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what character encoding to use where applicable (type String)
|
||||||
|
*/
|
||||||
|
public static final EncodeHintType CHARACTER_SET = new EncodeHintType();
|
||||||
|
|
||||||
|
private EncodeHintType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
src/com/google/zxing/FormatException.java
Normal file
38
src/com/google/zxing/FormatException.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a barcode was successfully detected, but some aspect of
|
||||||
|
* the content did not conform to the barcode's format rules. This could have
|
||||||
|
* been due to a mis-detection.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class FormatException extends ReaderException {
|
||||||
|
|
||||||
|
private static final FormatException instance = new FormatException();
|
||||||
|
|
||||||
|
private FormatException() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatException getFormatInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
113
src/com/google/zxing/LuminanceSource.java
Normal file
113
src/com/google/zxing/LuminanceSource.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The purpose of this class hierarchy is to abstract different bitmap implementations across
|
||||||
|
* platforms into a standard interface for requesting greyscale luminance values. The interface
|
||||||
|
* only provides immutable methods; therefore crop and rotation create copies. This is to ensure
|
||||||
|
* that one Reader does not modify the original luminance source and leave it in an unknown state
|
||||||
|
* for other Readers in the chain.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public abstract class LuminanceSource {
|
||||||
|
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
|
||||||
|
protected LuminanceSource(int width, int height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches one row of luminance data from the underlying platform's bitmap. Values range from
|
||||||
|
* 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
|
||||||
|
* to bitwise and with 0xff for each value. It is preferable for implementations of this method
|
||||||
|
* to only fetch this row rather than the whole image, since no 2D Readers may be installed and
|
||||||
|
* getMatrix() may never be called.
|
||||||
|
*
|
||||||
|
* @param y The row to fetch, 0 <= y < getHeight().
|
||||||
|
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||||
|
* Always use the returned object, and ignore the .length of the array.
|
||||||
|
* @return An array containing the luminance data.
|
||||||
|
*/
|
||||||
|
public abstract byte[] getRow(int y, byte[] row);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches luminance data for the underlying bitmap. Values should be fetched using:
|
||||||
|
* int luminance = array[y * width + x] & 0xff;
|
||||||
|
*
|
||||||
|
* @return A row-major 2D array of luminance values. Do not use result.length as it may be
|
||||||
|
* larger than width * height bytes on some platforms. Do not modify the contents
|
||||||
|
* of the result.
|
||||||
|
*/
|
||||||
|
public abstract byte[] getMatrix();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The width of the bitmap.
|
||||||
|
*/
|
||||||
|
public final int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The height of the bitmap.
|
||||||
|
*/
|
||||||
|
public final int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this subclass supports cropping.
|
||||||
|
*/
|
||||||
|
public boolean isCropSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||||
|
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||||
|
*
|
||||||
|
* @param left The left coordinate, 0 <= left < getWidth().
|
||||||
|
* @param top The top coordinate, 0 <= top <= getHeight().
|
||||||
|
* @param width The width of the rectangle to crop.
|
||||||
|
* @param height The height of the rectangle to crop.
|
||||||
|
* @return A cropped version of this object.
|
||||||
|
*/
|
||||||
|
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||||
|
throw new RuntimeException("This luminance source does not support cropping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this subclass supports counter-clockwise rotation.
|
||||||
|
*/
|
||||||
|
public boolean isRotateSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new object with rotated image data. Only callable if isRotateSupported() is true.
|
||||||
|
*
|
||||||
|
* @return A rotated version of this object.
|
||||||
|
*/
|
||||||
|
public LuminanceSource rotateCounterClockwise() {
|
||||||
|
throw new RuntimeException("This luminance source does not support rotation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
166
src/com/google/zxing/MultiFormatReader.java
Normal file
166
src/com/google/zxing/MultiFormatReader.java
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import com.google.zxing.datamatrix.DataMatrixReader;
|
||||||
|
import com.google.zxing.oned.MultiFormatOneDReader;
|
||||||
|
import com.google.zxing.pdf417.PDF417Reader;
|
||||||
|
import com.google.zxing.qrcode.QRCodeReader;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MultiFormatReader is a convenience class and the main entry point into the library for most uses.
|
||||||
|
* By default it attempts to decode all barcode formats that the library supports. Optionally, you
|
||||||
|
* can provide a hints object to request different behavior, for example only decoding QR codes.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class MultiFormatReader implements Reader {
|
||||||
|
|
||||||
|
private Hashtable hints;
|
||||||
|
private Vector readers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it
|
||||||
|
* passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly.
|
||||||
|
* Use setHints() followed by decodeWithState() for continuous scan applications.
|
||||||
|
*
|
||||||
|
* @param image The pixel data to decode
|
||||||
|
* @return The contents of the image
|
||||||
|
* @throws NotFoundException Any errors which occurred
|
||||||
|
*/
|
||||||
|
public Result decode(BinaryBitmap image) throws NotFoundException {
|
||||||
|
setHints(null);
|
||||||
|
return decodeInternal(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode an image using the hints provided. Does not honor existing state.
|
||||||
|
*
|
||||||
|
* @param image The pixel data to decode
|
||||||
|
* @param hints The hints to use, clearing the previous state.
|
||||||
|
* @return The contents of the image
|
||||||
|
* @throws NotFoundException Any errors which occurred
|
||||||
|
*/
|
||||||
|
public Result decode(BinaryBitmap image, Hashtable hints) throws NotFoundException {
|
||||||
|
setHints(hints);
|
||||||
|
return decodeInternal(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode an image using the state set up by calling setHints() previously. Continuous scan
|
||||||
|
* clients will get a <b>large</b> speed increase by using this instead of decode().
|
||||||
|
*
|
||||||
|
* @param image The pixel data to decode
|
||||||
|
* @return The contents of the image
|
||||||
|
* @throws NotFoundException Any errors which occurred
|
||||||
|
*/
|
||||||
|
public Result decodeWithState(BinaryBitmap image) throws NotFoundException {
|
||||||
|
// Make sure to set up the default state so we don't crash
|
||||||
|
if (readers == null) {
|
||||||
|
setHints(null);
|
||||||
|
}
|
||||||
|
return decodeInternal(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls
|
||||||
|
* to decodeWithState(image) can reuse the same set of readers without reallocating memory. This
|
||||||
|
* is important for performance in continuous scan clients.
|
||||||
|
*
|
||||||
|
* @param hints The set of hints to use for subsequent calls to decode(image)
|
||||||
|
*/
|
||||||
|
public void setHints(Hashtable hints) {
|
||||||
|
this.hints = hints;
|
||||||
|
|
||||||
|
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
|
||||||
|
Vector formats = hints == null ? null : (Vector) hints.get(DecodeHintType.POSSIBLE_FORMATS);
|
||||||
|
readers = new Vector();
|
||||||
|
if (formats != null) {
|
||||||
|
boolean addOneDReader =
|
||||||
|
formats.contains(BarcodeFormat.UPC_A) ||
|
||||||
|
formats.contains(BarcodeFormat.UPC_E) ||
|
||||||
|
formats.contains(BarcodeFormat.EAN_13) ||
|
||||||
|
formats.contains(BarcodeFormat.EAN_8) ||
|
||||||
|
//formats.contains(BarcodeFormat.CODABAR) ||
|
||||||
|
formats.contains(BarcodeFormat.CODE_39) ||
|
||||||
|
formats.contains(BarcodeFormat.CODE_93) ||
|
||||||
|
formats.contains(BarcodeFormat.CODE_128) ||
|
||||||
|
formats.contains(BarcodeFormat.ITF) ||
|
||||||
|
formats.contains(BarcodeFormat.RSS14) ||
|
||||||
|
formats.contains(BarcodeFormat.RSS_EXPANDED);
|
||||||
|
// Put 1D readers upfront in "normal" mode
|
||||||
|
if (addOneDReader && !tryHarder) {
|
||||||
|
readers.addElement(new MultiFormatOneDReader(hints));
|
||||||
|
}
|
||||||
|
if (formats.contains(BarcodeFormat.QR_CODE)) {
|
||||||
|
readers.addElement(new QRCodeReader());
|
||||||
|
}
|
||||||
|
if (formats.contains(BarcodeFormat.DATA_MATRIX)) {
|
||||||
|
readers.addElement(new DataMatrixReader());
|
||||||
|
}
|
||||||
|
if (formats.contains(BarcodeFormat.PDF417)) {
|
||||||
|
readers.addElement(new PDF417Reader());
|
||||||
|
}
|
||||||
|
// At end in "try harder" mode
|
||||||
|
if (addOneDReader && tryHarder) {
|
||||||
|
readers.addElement(new MultiFormatOneDReader(hints));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (readers.isEmpty()) {
|
||||||
|
if (!tryHarder) {
|
||||||
|
readers.addElement(new MultiFormatOneDReader(hints));
|
||||||
|
}
|
||||||
|
readers.addElement(new QRCodeReader());
|
||||||
|
|
||||||
|
readers.addElement(new DataMatrixReader());
|
||||||
|
|
||||||
|
// TODO: Enable once PDF417 has passed QA
|
||||||
|
//readers.addElement(new PDF417Reader());
|
||||||
|
|
||||||
|
if (tryHarder) {
|
||||||
|
readers.addElement(new MultiFormatOneDReader(hints));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
int size = readers.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
Reader reader = (Reader) readers.elementAt(i);
|
||||||
|
reader.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result decodeInternal(BinaryBitmap image) throws NotFoundException {
|
||||||
|
int size = readers.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
Reader reader = (Reader) readers.elementAt(i);
|
||||||
|
try {
|
||||||
|
return reader.decode(image, hints);
|
||||||
|
} catch (ReaderException re) {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
65
src/com/google/zxing/MultiFormatWriter.java
Normal file
65
src/com/google/zxing/MultiFormatWriter.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.oned.Code128Writer;
|
||||||
|
import com.google.zxing.oned.Code39Writer;
|
||||||
|
import com.google.zxing.oned.EAN13Writer;
|
||||||
|
import com.google.zxing.oned.EAN8Writer;
|
||||||
|
import com.google.zxing.oned.ITFWriter;
|
||||||
|
import com.google.zxing.qrcode.QRCodeWriter;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a factory class which finds the appropriate Writer subclass for the BarcodeFormat
|
||||||
|
* requested and encodes the barcode with the supplied contents.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class MultiFormatWriter implements Writer {
|
||||||
|
|
||||||
|
public BitMatrix encode(String contents, BarcodeFormat format, int width,
|
||||||
|
int height) throws WriterException {
|
||||||
|
|
||||||
|
return encode(contents, format, width, height, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height,
|
||||||
|
Hashtable hints) throws WriterException {
|
||||||
|
|
||||||
|
Writer writer;
|
||||||
|
if (format == BarcodeFormat.EAN_8) {
|
||||||
|
writer = new EAN8Writer();
|
||||||
|
} else if (format == BarcodeFormat.EAN_13) {
|
||||||
|
writer = new EAN13Writer();
|
||||||
|
} else if (format == BarcodeFormat.QR_CODE) {
|
||||||
|
writer = new QRCodeWriter();
|
||||||
|
} else if (format == BarcodeFormat.CODE_39) {
|
||||||
|
writer = new Code39Writer();
|
||||||
|
} else if (format == BarcodeFormat.CODE_128) {
|
||||||
|
writer = new Code128Writer();
|
||||||
|
} else if (format == BarcodeFormat.ITF) {
|
||||||
|
writer = new ITFWriter();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("No encoder available for format " + format);
|
||||||
|
}
|
||||||
|
return writer.encode(contents, format, width, height, hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/com/google/zxing/NotFoundException.java
Normal file
37
src/com/google/zxing/NotFoundException.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a barcode was not found in the image. It might have been
|
||||||
|
* partially detected but could not be confirmed.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class NotFoundException extends ReaderException {
|
||||||
|
|
||||||
|
private static final NotFoundException instance = new NotFoundException();
|
||||||
|
|
||||||
|
private NotFoundException() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NotFoundException getNotFoundInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
src/com/google/zxing/Reader.java
Normal file
64
src/com/google/zxing/Reader.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of this interface can decode an image of a barcode in some format into
|
||||||
|
* the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
|
||||||
|
* decode a QR code. The decoder may optionally receive hints from the caller which may help
|
||||||
|
* it decode more quickly or accurately.
|
||||||
|
*
|
||||||
|
* See {@link com.google.zxing.MultiFormatReader}, which attempts to determine what barcode
|
||||||
|
* format is present within the image as well, and then decodes it accordingly.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public interface Reader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates and decodes a barcode in some format within an image.
|
||||||
|
*
|
||||||
|
* @param image image of barcode to decode
|
||||||
|
* @return String which the barcode encodes
|
||||||
|
* @throws NotFoundException if the barcode cannot be located or decoded for any reason
|
||||||
|
*/
|
||||||
|
Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates and decodes a barcode in some format within an image. This method also accepts
|
||||||
|
* hints, each possibly associated to some data, which may help the implementation decode.
|
||||||
|
*
|
||||||
|
* @param image image of barcode to decode
|
||||||
|
* @param hints passed as a {@link java.util.Hashtable} from {@link com.google.zxing.DecodeHintType}
|
||||||
|
* to arbitrary data. The
|
||||||
|
* meaning of the data depends upon the hint type. The implementation may or may not do
|
||||||
|
* anything with these hints.
|
||||||
|
* @return String which the barcode encodes
|
||||||
|
* @throws NotFoundException if the barcode cannot be located or decoded for any reason
|
||||||
|
*/
|
||||||
|
Result decode(BinaryBitmap image, Hashtable hints) throws NotFoundException, ChecksumException, FormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets any internal state the implementation has after a decode, to prepare it
|
||||||
|
* for reuse.
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
}
|
98
src/com/google/zxing/ReaderException.java
Normal file
98
src/com/google/zxing/ReaderException.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The general exception class throw when something goes wrong during decoding of a barcode.
|
||||||
|
* This includes, but is not limited to, failing checksums / error correction algorithms, being
|
||||||
|
* unable to locate finder timing patterns, and so on.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public abstract class ReaderException extends Exception {
|
||||||
|
|
||||||
|
// TODO: Currently we throw up to 400 ReaderExceptions while scanning a single 240x240 image before
|
||||||
|
// rejecting it. This involves a lot of overhead and memory allocation, and affects both performance
|
||||||
|
// and latency on continuous scan clients. In the future, we should change all the decoders not to
|
||||||
|
// throw exceptions for routine events, like not finding a barcode on a given row. Instead, we
|
||||||
|
// should return error codes back to the callers, and simply delete this class. In the mean time, I
|
||||||
|
// have altered this class to be as lightweight as possible, by ignoring the exception string, and
|
||||||
|
// by disabling the generation of stack traces, which is especially time consuming. These are just
|
||||||
|
// temporary measures, pending the big cleanup.
|
||||||
|
|
||||||
|
//private static final ReaderException instance = new ReaderException();
|
||||||
|
|
||||||
|
// EXCEPTION TRACKING SUPPORT
|
||||||
|
// Identifies who is throwing exceptions and how often. To use:
|
||||||
|
//
|
||||||
|
// 1. Uncomment these lines and the code below which uses them.
|
||||||
|
// 2. Uncomment the two corresponding lines in j2se/CommandLineRunner.decode()
|
||||||
|
// 3. Change core to build as Java 1.5 temporarily
|
||||||
|
// private static int exceptionCount = 0;
|
||||||
|
// private static Map<String,Integer> throwers = new HashMap<String,Integer>(32);
|
||||||
|
|
||||||
|
ReaderException() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
//public static ReaderException getInstance() {
|
||||||
|
// Exception e = new Exception();
|
||||||
|
// // Take the stack frame before this one.
|
||||||
|
// StackTraceElement stack = e.getStackTrace()[1];
|
||||||
|
// String key = stack.getClassName() + "." + stack.getMethodName() + "(), line " +
|
||||||
|
// stack.getLineNumber();
|
||||||
|
// if (throwers.containsKey(key)) {
|
||||||
|
// Integer value = throwers.get(key);
|
||||||
|
// value++;
|
||||||
|
// throwers.put(key, value);
|
||||||
|
// } else {
|
||||||
|
// throwers.put(key, 1);
|
||||||
|
// }
|
||||||
|
// exceptionCount++;
|
||||||
|
|
||||||
|
//return instance;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// public static int getExceptionCountAndReset() {
|
||||||
|
// int temp = exceptionCount;
|
||||||
|
// exceptionCount = 0;
|
||||||
|
// return temp;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static String getThrowersAndReset() {
|
||||||
|
// StringBuilder builder = new StringBuilder(1024);
|
||||||
|
// Object[] keys = throwers.keySet().toArray();
|
||||||
|
// for (int x = 0; x < keys.length; x++) {
|
||||||
|
// String key = (String) keys[x];
|
||||||
|
// Integer value = throwers.get(key);
|
||||||
|
// builder.append(key);
|
||||||
|
// builder.append(": ");
|
||||||
|
// builder.append(value);
|
||||||
|
// builder.append("\n");
|
||||||
|
// }
|
||||||
|
// throwers.clear();
|
||||||
|
// return builder.toString();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Prevent stack traces from being taken
|
||||||
|
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
|
||||||
|
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
|
||||||
|
public final Throwable fillInStackTrace() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
143
src/com/google/zxing/Result.java
Normal file
143
src/com/google/zxing/Result.java
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates the result of decoding a barcode within an image.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class Result {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
private final byte[] rawBytes;
|
||||||
|
private ResultPoint[] resultPoints;
|
||||||
|
private final BarcodeFormat format;
|
||||||
|
private Hashtable resultMetadata;
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
public Result(String text,
|
||||||
|
byte[] rawBytes,
|
||||||
|
ResultPoint[] resultPoints,
|
||||||
|
BarcodeFormat format) {
|
||||||
|
this(text, rawBytes, resultPoints, format, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result(String text,
|
||||||
|
byte[] rawBytes,
|
||||||
|
ResultPoint[] resultPoints,
|
||||||
|
BarcodeFormat format,
|
||||||
|
long timestamp) {
|
||||||
|
if (text == null && rawBytes == null) {
|
||||||
|
throw new IllegalArgumentException("Text and bytes are null");
|
||||||
|
}
|
||||||
|
this.text = text;
|
||||||
|
this.rawBytes = rawBytes;
|
||||||
|
this.resultPoints = resultPoints;
|
||||||
|
this.format = format;
|
||||||
|
this.resultMetadata = null;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return raw text encoded by the barcode, if applicable, otherwise <code>null</code>
|
||||||
|
*/
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return raw bytes encoded by the barcode, if applicable, otherwise <code>null</code>
|
||||||
|
*/
|
||||||
|
public byte[] getRawBytes() {
|
||||||
|
return rawBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return points related to the barcode in the image. These are typically points
|
||||||
|
* identifying finder patterns or the corners of the barcode. The exact meaning is
|
||||||
|
* specific to the type of barcode that was decoded.
|
||||||
|
*/
|
||||||
|
public ResultPoint[] getResultPoints() {
|
||||||
|
return resultPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link BarcodeFormat} representing the format of the barcode that was decoded
|
||||||
|
*/
|
||||||
|
public BarcodeFormat getBarcodeFormat() {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link Hashtable} mapping {@link ResultMetadataType} keys to values. May be
|
||||||
|
* <code>null</code>. This contains optional metadata about what was detected about the barcode,
|
||||||
|
* like orientation.
|
||||||
|
*/
|
||||||
|
public Hashtable getResultMetadata() {
|
||||||
|
return resultMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putMetadata(ResultMetadataType type, Object value) {
|
||||||
|
if (resultMetadata == null) {
|
||||||
|
resultMetadata = new Hashtable(3);
|
||||||
|
}
|
||||||
|
resultMetadata.put(type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putAllMetadata(Hashtable metadata) {
|
||||||
|
if (metadata != null) {
|
||||||
|
if (resultMetadata == null) {
|
||||||
|
resultMetadata = metadata;
|
||||||
|
} else {
|
||||||
|
Enumeration e = metadata.keys();
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
ResultMetadataType key = (ResultMetadataType) e.nextElement();
|
||||||
|
Object value = metadata.get(key);
|
||||||
|
resultMetadata.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addResultPoints(ResultPoint[] newPoints) {
|
||||||
|
if (resultPoints == null) {
|
||||||
|
resultPoints = newPoints;
|
||||||
|
} else if (newPoints != null && newPoints.length > 0) {
|
||||||
|
ResultPoint[] allPoints = new ResultPoint[resultPoints.length + newPoints.length];
|
||||||
|
System.arraycopy(resultPoints, 0, allPoints, 0, resultPoints.length);
|
||||||
|
System.arraycopy(newPoints, 0, allPoints, resultPoints.length, newPoints.length);
|
||||||
|
resultPoints = allPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (text == null) {
|
||||||
|
return "[" + rawBytes.length + " bytes]";
|
||||||
|
} else {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
109
src/com/google/zxing/ResultMetadataType.java
Normal file
109
src/com/google/zxing/ResultMetadataType.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents some type of metadata about the result of the decoding that the decoder
|
||||||
|
* wishes to communicate back to the caller.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class ResultMetadataType {
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
private static final Hashtable VALUES = new Hashtable();
|
||||||
|
|
||||||
|
// No, we can't use an enum here. J2ME doesn't support it.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
|
||||||
|
*/
|
||||||
|
public static final ResultMetadataType OTHER = new ResultMetadataType("OTHER");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes the likely approximate orientation of the barcode in the image. This value
|
||||||
|
* is given as degrees rotated clockwise from the normal, upright orientation.
|
||||||
|
* For example a 1D barcode which was found by reading top-to-bottom would be
|
||||||
|
* said to have orientation "90". This key maps to an {@link Integer} whose
|
||||||
|
* value is in the range [0,360).
|
||||||
|
*/
|
||||||
|
public static final ResultMetadataType ORIENTATION = new ResultMetadataType("ORIENTATION");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
|
||||||
|
* which is sometimes used to encode binary data. While {@link Result} makes available
|
||||||
|
* the complete raw bytes in the barcode for these formats, it does not offer the bytes
|
||||||
|
* from the byte segments alone.</p>
|
||||||
|
*
|
||||||
|
* <p>This maps to a {@link java.util.Vector} of byte arrays corresponding to the
|
||||||
|
* raw bytes in the byte segments in the barcode, in order.</p>
|
||||||
|
*/
|
||||||
|
public static final ResultMetadataType BYTE_SEGMENTS = new ResultMetadataType("BYTE_SEGMENTS");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error correction level used, if applicable. The value type depends on the
|
||||||
|
* format, but is typically a String.
|
||||||
|
*/
|
||||||
|
public static final ResultMetadataType ERROR_CORRECTION_LEVEL = new ResultMetadataType("ERROR_CORRECTION_LEVEL");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some periodicals, indicates the issue number as an {@link Integer}.
|
||||||
|
*/
|
||||||
|
public static final ResultMetadataType ISSUE_NUMBER = new ResultMetadataType("ISSUE_NUMBER");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some products, indicates the suggested retail price in the barcode as a
|
||||||
|
* formatted {@link String}.
|
||||||
|
*/
|
||||||
|
public static final ResultMetadataType SUGGESTED_PRICE = new ResultMetadataType("SUGGESTED_PRICE");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some products, the possible country of manufacture as a {@link String} denoting the
|
||||||
|
* ISO country code. Some map to multiple possible countries, like "US/CA".
|
||||||
|
*/
|
||||||
|
public static final ResultMetadataType POSSIBLE_COUNTRY = new ResultMetadataType("POSSIBLE_COUNTRY");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private ResultMetadataType(String name) {
|
||||||
|
this.name = name;
|
||||||
|
VALUES.put(name, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResultMetadataType valueOf(String name) {
|
||||||
|
if (name == null || name.length() == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
ResultMetadataType format = (ResultMetadataType) VALUES.get(name);
|
||||||
|
if (format == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
127
src/com/google/zxing/ResultPoint.java
Normal file
127
src/com/google/zxing/ResultPoint.java
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
|
||||||
|
* would be the location of a finder pattern or the corner of the barcode, for example.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public class ResultPoint {
|
||||||
|
|
||||||
|
private final float x;
|
||||||
|
private final float y;
|
||||||
|
|
||||||
|
public ResultPoint(float x, float y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final float getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final float getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other instanceof ResultPoint) {
|
||||||
|
ResultPoint otherPoint = (ResultPoint) other;
|
||||||
|
return x == otherPoint.x && y == otherPoint.y;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer result = new StringBuffer(25);
|
||||||
|
result.append('(');
|
||||||
|
result.append(x);
|
||||||
|
result.append(',');
|
||||||
|
result.append(y);
|
||||||
|
result.append(')');
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and
|
||||||
|
* BC < AC and the angle between BC and BA is less than 180 degrees.
|
||||||
|
*/
|
||||||
|
public static void orderBestPatterns(ResultPoint[] patterns) {
|
||||||
|
|
||||||
|
// Find distances between pattern centers
|
||||||
|
float zeroOneDistance = distance(patterns[0], patterns[1]);
|
||||||
|
float oneTwoDistance = distance(patterns[1], patterns[2]);
|
||||||
|
float zeroTwoDistance = distance(patterns[0], patterns[2]);
|
||||||
|
|
||||||
|
ResultPoint pointA, pointB, pointC;
|
||||||
|
// Assume one closest to other two is B; A and C will just be guesses at first
|
||||||
|
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
|
||||||
|
pointB = patterns[0];
|
||||||
|
pointA = patterns[1];
|
||||||
|
pointC = patterns[2];
|
||||||
|
} else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
|
||||||
|
pointB = patterns[1];
|
||||||
|
pointA = patterns[0];
|
||||||
|
pointC = patterns[2];
|
||||||
|
} else {
|
||||||
|
pointB = patterns[2];
|
||||||
|
pointA = patterns[0];
|
||||||
|
pointC = patterns[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use cross product to figure out whether A and C are correct or flipped.
|
||||||
|
// This asks whether BC x BA has a positive z component, which is the arrangement
|
||||||
|
// we want for A, B, C. If it's negative, then we've got it flipped around and
|
||||||
|
// should swap A and C.
|
||||||
|
if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
|
||||||
|
ResultPoint temp = pointA;
|
||||||
|
pointA = pointC;
|
||||||
|
pointC = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns[0] = pointA;
|
||||||
|
patterns[1] = pointB;
|
||||||
|
patterns[2] = pointC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return distance between two points
|
||||||
|
*/
|
||||||
|
public static float distance(ResultPoint pattern1, ResultPoint pattern2) {
|
||||||
|
float xDiff = pattern1.getX() - pattern2.getX();
|
||||||
|
float yDiff = pattern1.getY() - pattern2.getY();
|
||||||
|
return (float) Math.sqrt((double) (xDiff * xDiff + yDiff * yDiff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the z component of the cross product between vectors BC and BA.
|
||||||
|
*/
|
||||||
|
private static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) {
|
||||||
|
float bX = pointB.x;
|
||||||
|
float bY = pointB.y;
|
||||||
|
return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
29
src/com/google/zxing/ResultPointCallback.java
Normal file
29
src/com/google/zxing/ResultPointCallback.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback which is invoked when a possible result point (significant
|
||||||
|
* point in the barcode image such as a corner) is found.
|
||||||
|
*
|
||||||
|
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
|
||||||
|
*/
|
||||||
|
public interface ResultPointCallback {
|
||||||
|
|
||||||
|
void foundPossibleResultPoint(ResultPoint point);
|
||||||
|
|
||||||
|
}
|
54
src/com/google/zxing/Writer.java
Normal file
54
src/com/google/zxing/Writer.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for all objects which encode/generate a barcode image.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public interface Writer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a barcode using the default settings.
|
||||||
|
*
|
||||||
|
* @param contents The contents to encode in the barcode
|
||||||
|
* @param format The barcode format to generate
|
||||||
|
* @param width The preferred width in pixels
|
||||||
|
* @param height The preferred height in pixels
|
||||||
|
* @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
|
||||||
|
*/
|
||||||
|
BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
|
||||||
|
throws WriterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param contents The contents to encode in the barcode
|
||||||
|
* @param format The barcode format to generate
|
||||||
|
* @param width The preferred width in pixels
|
||||||
|
* @param height The preferred height in pixels
|
||||||
|
* @param hints Additional parameters to supply to the encoder
|
||||||
|
* @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
|
||||||
|
*/
|
||||||
|
BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints)
|
||||||
|
throws WriterException;
|
||||||
|
|
||||||
|
}
|
35
src/com/google/zxing/WriterException.java
Normal file
35
src/com/google/zxing/WriterException.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class which covers the range of exceptions which may occur when encoding a barcode using
|
||||||
|
* the Writer framework.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class WriterException extends Exception {
|
||||||
|
|
||||||
|
public WriterException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WriterException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.j2me;
|
||||||
|
|
||||||
|
import com.google.zxing.LuminanceSource;
|
||||||
|
|
||||||
|
import javax.microedition.lcdui.Image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A LuminanceSource based on Java ME's Image class. It does not support cropping or rotation.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class LCDUIImageLuminanceSource extends LuminanceSource {
|
||||||
|
|
||||||
|
private final Image image;
|
||||||
|
private int[] rgbData;
|
||||||
|
|
||||||
|
public LCDUIImageLuminanceSource(Image image) {
|
||||||
|
super(image.getWidth(), image.getHeight());
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instead of multiplying by 306, 601, 117, we multiply by 256, 512, 256, so that
|
||||||
|
// the multiplies can be implemented as shifts.
|
||||||
|
//
|
||||||
|
// Really, it's:
|
||||||
|
//
|
||||||
|
// return ((((pixel >> 16) & 0xFF) << 8) +
|
||||||
|
// (((pixel >> 8) & 0xFF) << 9) +
|
||||||
|
// (( pixel & 0xFF) << 8)) >> 10;
|
||||||
|
//
|
||||||
|
// That is, we're replacing the coefficients in the original with powers of two,
|
||||||
|
// which can be implemented as shifts, even though changing the coefficients slightly
|
||||||
|
// alters the conversion. The difference is not significant for our purposes.
|
||||||
|
public byte[] getRow(int y, byte[] row) {
|
||||||
|
if (y < 0 || y >= getHeight()) {
|
||||||
|
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||||
|
}
|
||||||
|
int width = getWidth();
|
||||||
|
if (row == null || row.length < width) {
|
||||||
|
row = new byte[width];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rgbData == null || rgbData.length < width) {
|
||||||
|
rgbData = new int[width];
|
||||||
|
}
|
||||||
|
image.getRGB(rgbData, 0, width, 0, y, width, 1);
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int pixel = rgbData[x];
|
||||||
|
int luminance = (((pixel & 0x00FF0000) >> 16) +
|
||||||
|
((pixel & 0x0000FF00) >> 7) +
|
||||||
|
(pixel & 0x000000FF )) >> 2;
|
||||||
|
row[x] = (byte) luminance;
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getMatrix() {
|
||||||
|
int width = getWidth();
|
||||||
|
int height = getHeight();
|
||||||
|
int area = width * height;
|
||||||
|
byte[] matrix = new byte[area];
|
||||||
|
|
||||||
|
int[] rgb = new int[area];
|
||||||
|
image.getRGB(rgb, 0, width, 0, 0, width, height);
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
int offset = y * width;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int pixel = rgb[offset + x];
|
||||||
|
int luminance = (((pixel & 0x00FF0000) >> 16) +
|
||||||
|
((pixel & 0x0000FF00) >> 7) +
|
||||||
|
(pixel & 0x000000FF )) >> 2;
|
||||||
|
matrix[offset + x] = (byte) luminance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
262
src/com/google/zxing/client/j2me/PNGEncoder.java
Normal file
262
src/com/google/zxing/client/j2me/PNGEncoder.java
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
package com.google.zxing.client.j2me;
|
||||||
|
/*
|
||||||
|
* Minimal PNG encoder to create PNG streams (and MIDP images) from RGBA arrays.
|
||||||
|
*
|
||||||
|
* Copyright 2006-2009 Christian Fröschlin
|
||||||
|
*
|
||||||
|
* www.chrfr.de
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Changelog:
|
||||||
|
*
|
||||||
|
* 09/22/08: Fixed Adler checksum calculation and byte order for storing length
|
||||||
|
* of zlib deflate block. Thanks to Miloslav Ruzicka for noting this.
|
||||||
|
*
|
||||||
|
* 05/12/09: Split PNG and ZLIB functionality into separate classes. Added
|
||||||
|
* support for images > 64K by splitting the data into multiple uncompressed
|
||||||
|
* deflate blocks.
|
||||||
|
*
|
||||||
|
* 03/19/10: Re-packaged, and modified interface to be more MIDP-2 friendly,
|
||||||
|
* using int[] rather than byte[] data. Alpha channel is now optional, to
|
||||||
|
* allow a smaller output size. New toPNG() method works directly from an
|
||||||
|
* Image object. (Graham Hughes, for Forum Nokia)
|
||||||
|
*
|
||||||
|
* Terms of Use:
|
||||||
|
*
|
||||||
|
* You may use the PNG encoder free of charge for any purpose you desire, as
|
||||||
|
* long as you do not claim credit for the original sources and agree not to
|
||||||
|
* hold me responsible for any damage arising out of its use.
|
||||||
|
*
|
||||||
|
* If you have a suitable location in GUI or documentation for giving credit,
|
||||||
|
* I'd appreciate a mention of
|
||||||
|
*
|
||||||
|
* PNG encoder (C) 2006-2009 by Christian Fröschlin, www.chrfr.de
|
||||||
|
*
|
||||||
|
* but that's not mandatory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.microedition.lcdui.Image;
|
||||||
|
|
||||||
|
public class PNGEncoder {
|
||||||
|
private static final byte[] SIGNATURE = new byte[] { (byte) 137, (byte) 80,
|
||||||
|
(byte) 78, (byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a PNG data stream from a pixel array.
|
||||||
|
* <p>
|
||||||
|
* Setting processAlpha to false will result in a PNG file that contains no
|
||||||
|
* transparency information, but may be up to 25% smaller.
|
||||||
|
* <p>
|
||||||
|
* The pixel array must contain (width * height) pixels.
|
||||||
|
*
|
||||||
|
* @param width
|
||||||
|
* width of image, in pixels
|
||||||
|
* @param height
|
||||||
|
* height of image, in pixels
|
||||||
|
* @param argb
|
||||||
|
* pixel array, as populated from Image.getRGB()
|
||||||
|
* @param processAlpha
|
||||||
|
* true if you want to keep alpha channel data
|
||||||
|
* @return PNG data in a byte[]
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the size of the pixel array does not match the specified
|
||||||
|
* width and height
|
||||||
|
*/
|
||||||
|
public static byte[] toPNG(int width, int height, int[] argb,
|
||||||
|
boolean processAlpha) throws IllegalArgumentException {
|
||||||
|
ByteArrayOutputStream png;
|
||||||
|
try {
|
||||||
|
byte[] header = createHeaderChunk(width, height, processAlpha);
|
||||||
|
byte[] data = createDataChunk(width, height, argb, processAlpha);
|
||||||
|
byte[] trailer = createTrailerChunk();
|
||||||
|
|
||||||
|
png = new ByteArrayOutputStream(SIGNATURE.length + header.length
|
||||||
|
+ data.length + trailer.length);
|
||||||
|
png.write(SIGNATURE);
|
||||||
|
png.write(header);
|
||||||
|
png.write(data);
|
||||||
|
png.write(trailer);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// none of the code should ever throw an IOException
|
||||||
|
throw new IllegalStateException("Unexpected " + ioe);
|
||||||
|
}
|
||||||
|
return png.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a PNG data stream from an Image object.
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* source Image
|
||||||
|
* @param processAlpha
|
||||||
|
* true if you want to keep the alpha channel data
|
||||||
|
* @return PNG data in a byte[]
|
||||||
|
*/
|
||||||
|
public static byte[] toPNG(Image img, boolean processAlpha) {
|
||||||
|
int width = img.getWidth();
|
||||||
|
int height = img.getHeight();
|
||||||
|
int[] argb = new int[width * height];
|
||||||
|
img.getRGB(argb, 0, width, 0, 0, width, height);
|
||||||
|
// allow garbage collection, if this is the only reference
|
||||||
|
img = null;
|
||||||
|
return toPNG(width, height, argb, processAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] createHeaderChunk(int width, int height,
|
||||||
|
boolean processAlpha) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(13);
|
||||||
|
DataOutputStream chunk = new DataOutputStream(baos);
|
||||||
|
chunk.writeInt(width);
|
||||||
|
chunk.writeInt(height);
|
||||||
|
chunk.writeByte(8); // Bitdepth
|
||||||
|
chunk.writeByte(processAlpha ? 6 : 2); // Colortype ARGB or RGB
|
||||||
|
chunk.writeByte(0); // Compression
|
||||||
|
chunk.writeByte(0); // Filter
|
||||||
|
chunk.writeByte(0); // Interlace
|
||||||
|
return toChunk("IHDR", baos.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] createDataChunk(int width, int height, int[] argb,
|
||||||
|
boolean processAlpha) throws IOException, IllegalArgumentException {
|
||||||
|
if (argb.length != (width * height)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"array size does not match image dimensions");
|
||||||
|
}
|
||||||
|
int source = 0;
|
||||||
|
int dest = 0;
|
||||||
|
byte[] raw = new byte[(processAlpha ? 4 : 3) * (width * height)
|
||||||
|
+ height];
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
raw[dest++] = 0; // No filter
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int pixel = argb[source++];
|
||||||
|
raw[dest++] = (byte) (pixel >> 16); // red
|
||||||
|
raw[dest++] = (byte) (pixel >> 8); // green
|
||||||
|
raw[dest++] = (byte) (pixel); // blue
|
||||||
|
if (processAlpha) {
|
||||||
|
raw[dest++] = (byte) (pixel >> 24); // alpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toChunk("IDAT", toZLIB(raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] createTrailerChunk() throws IOException {
|
||||||
|
return toChunk("IEND", new byte[] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] toChunk(String id, byte[] raw) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(raw.length + 12);
|
||||||
|
DataOutputStream chunk = new DataOutputStream(baos);
|
||||||
|
|
||||||
|
chunk.writeInt(raw.length);
|
||||||
|
|
||||||
|
byte[] bid = new byte[4];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
bid[i] = (byte) id.charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.write(bid);
|
||||||
|
|
||||||
|
chunk.write(raw);
|
||||||
|
|
||||||
|
int crc = 0xFFFFFFFF;
|
||||||
|
crc = updateCRC(crc, bid);
|
||||||
|
crc = updateCRC(crc, raw);
|
||||||
|
chunk.writeInt(~crc);
|
||||||
|
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] crcTable = null;
|
||||||
|
|
||||||
|
private static void createCRCTable() {
|
||||||
|
crcTable = new int[256];
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
int c = i;
|
||||||
|
for (int k = 0; k < 8; k++) {
|
||||||
|
c = ((c & 1) > 0) ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
||||||
|
}
|
||||||
|
crcTable[i] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int updateCRC(int crc, byte[] raw) {
|
||||||
|
if (crcTable == null) {
|
||||||
|
createCRCTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < raw.length; i++) {
|
||||||
|
crc = crcTable[(crc ^ raw[i]) & 0xFF] ^ (crc >>> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method is called to encode the image data as a zlib block as
|
||||||
|
* required by the PNG specification. This file comes with a minimal ZLIB
|
||||||
|
* encoder which uses uncompressed deflate blocks (fast, short, easy, but no
|
||||||
|
* compression). If you want compression, call another encoder (such as
|
||||||
|
* JZLib?) here.
|
||||||
|
*/
|
||||||
|
private static byte[] toZLIB(byte[] raw) throws IOException {
|
||||||
|
return ZLIB.toZLIB(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZLIB {
|
||||||
|
private static final int BLOCK_SIZE = 32000;
|
||||||
|
|
||||||
|
public static byte[] toZLIB(byte[] raw) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(raw.length + 6
|
||||||
|
+ (raw.length / BLOCK_SIZE) * 5);
|
||||||
|
DataOutputStream zlib = new DataOutputStream(baos);
|
||||||
|
|
||||||
|
byte tmp = (byte) 8;
|
||||||
|
zlib.writeByte(tmp); // CM = 8, CMINFO = 0
|
||||||
|
zlib.writeByte((31 - ((tmp << 8) % 31)) % 31); // FCHECK(FDICT/FLEVEL=0)
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
while (raw.length - pos > BLOCK_SIZE) {
|
||||||
|
writeUncompressedDeflateBlock(zlib, false, raw, pos,
|
||||||
|
(char) BLOCK_SIZE);
|
||||||
|
pos += BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUncompressedDeflateBlock(zlib, true, raw, pos,
|
||||||
|
(char) (raw.length - pos));
|
||||||
|
|
||||||
|
// zlib check sum of uncompressed data
|
||||||
|
zlib.writeInt(calcADLER32(raw));
|
||||||
|
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeUncompressedDeflateBlock(DataOutputStream zlib,
|
||||||
|
boolean last, byte[] raw, int off, char len) throws IOException {
|
||||||
|
zlib.writeByte((byte) (last ? 1 : 0)); // Final flag, Compression type 0
|
||||||
|
zlib.writeByte((byte) (len & 0xFF)); // Length LSB
|
||||||
|
zlib.writeByte((byte) ((len & 0xFF00) >> 8)); // Length MSB
|
||||||
|
zlib.writeByte((byte) (~len & 0xFF)); // Length 1st complement LSB
|
||||||
|
zlib.writeByte((byte) ((~len & 0xFF00) >> 8)); // Length 1st complement
|
||||||
|
// MSB
|
||||||
|
zlib.write(raw, off, len); // Data
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int calcADLER32(byte[] raw) {
|
||||||
|
int s1 = 1;
|
||||||
|
int s2 = 0;
|
||||||
|
for (int i = 0; i < raw.length; i++) {
|
||||||
|
int abs = raw[i] >= 0 ? raw[i] : (raw[i] + 256);
|
||||||
|
s1 = (s1 + abs) % 65521;
|
||||||
|
s2 = (s2 + s1) % 65521;
|
||||||
|
}
|
||||||
|
return (s2 << 16) + s1;
|
||||||
|
}
|
||||||
|
}
|
670
src/com/google/zxing/client/j2me/ZXMIDlet.java
Normal file
670
src/com/google/zxing/client/j2me/ZXMIDlet.java
Normal file
@ -0,0 +1,670 @@
|
|||||||
|
/*
|
||||||
|
* To change this template, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package com.google.zxing.client.j2me;
|
||||||
|
|
||||||
|
import org.netbeans.microedition.lcdui.SplashScreen;
|
||||||
|
import org.netbeans.microedition.lcdui.pda.FileBrowser;
|
||||||
|
import java.io.*;
|
||||||
|
import javax.microedition.io.file.*;
|
||||||
|
import javax.microedition.midlet.*;
|
||||||
|
import javax.microedition.lcdui.*;
|
||||||
|
import com.google.zxing.*;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.common.HybridBinarizer;
|
||||||
|
import com.google.zxing.qrcode.QRCodeWriter;
|
||||||
|
import java.util.Date;
|
||||||
|
import org.netbeans.microedition.lcdui.SplashScreen;
|
||||||
|
import org.netbeans.microedition.lcdui.pda.FileBrowser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author URSER
|
||||||
|
*/
|
||||||
|
public class ZXMIDlet extends MIDlet implements CommandListener {
|
||||||
|
|
||||||
|
private boolean midletPaused = false;
|
||||||
|
private Image img;
|
||||||
|
private Image qrCodeImage;
|
||||||
|
private String filename;
|
||||||
|
private static final int BACK = 0xFF000000;
|
||||||
|
private static final int WHTIE = 0xFFFFFFFF;
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Fields ">//GEN-BEGIN:|fields|0|
|
||||||
|
private FileBrowser fileBrowser;
|
||||||
|
private TextBox textBox;
|
||||||
|
private SplashScreen splashScreen;
|
||||||
|
private TextBox textBox1;
|
||||||
|
private TextBox textBox2;
|
||||||
|
private Command backCommand;
|
||||||
|
private Command exitCommand;
|
||||||
|
private Command backCommand1;
|
||||||
|
private Command itemCommand1;
|
||||||
|
private Command itemCommand;
|
||||||
|
private Command okCommand;
|
||||||
|
private Command cancelCommand;
|
||||||
|
private Command exitCommand1;
|
||||||
|
private Command cancelCommand1;
|
||||||
|
private Command exitCommand2;
|
||||||
|
private Command backCommand2;
|
||||||
|
private Command backCommand3;
|
||||||
|
private Command helpCommand;
|
||||||
|
private Command okCommand1;
|
||||||
|
private Command itemCommand2;
|
||||||
|
private Image image;
|
||||||
|
//</editor-fold>//GEN-END:|fields|0|
|
||||||
|
/**
|
||||||
|
* The ZXMIDlet constructor.
|
||||||
|
*/
|
||||||
|
public ZXMIDlet() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Methods ">//GEN-BEGIN:|methods|0|
|
||||||
|
//</editor-fold>//GEN-END:|methods|0|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Method: initialize ">//GEN-BEGIN:|0-initialize|0|0-preInitialize
|
||||||
|
/**
|
||||||
|
* Initilizes the application.
|
||||||
|
* It is called only once when the MIDlet is started. The method is called before the <code>startMIDlet</code> method.
|
||||||
|
*/
|
||||||
|
private void initialize() {//GEN-END:|0-initialize|0|0-preInitialize
|
||||||
|
// write pre-initialize user code here
|
||||||
|
//GEN-LINE:|0-initialize|1|0-postInitialize
|
||||||
|
// write post-initialize user code here
|
||||||
|
}//GEN-BEGIN:|0-initialize|2|
|
||||||
|
//</editor-fold>//GEN-END:|0-initialize|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Method: startMIDlet ">//GEN-BEGIN:|3-startMIDlet|0|3-preAction
|
||||||
|
/**
|
||||||
|
* Performs an action assigned to the Mobile Device - MIDlet Started point.
|
||||||
|
*/
|
||||||
|
public void startMIDlet() {//GEN-END:|3-startMIDlet|0|3-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getSplashScreen());//GEN-LINE:|3-startMIDlet|1|3-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|3-startMIDlet|2|
|
||||||
|
//</editor-fold>//GEN-END:|3-startMIDlet|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Method: resumeMIDlet ">//GEN-BEGIN:|4-resumeMIDlet|0|4-preAction
|
||||||
|
/**
|
||||||
|
* Performs an action assigned to the Mobile Device - MIDlet Resumed point.
|
||||||
|
*/
|
||||||
|
public void resumeMIDlet() {//GEN-END:|4-resumeMIDlet|0|4-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
//GEN-LINE:|4-resumeMIDlet|1|4-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|4-resumeMIDlet|2|
|
||||||
|
//</editor-fold>//GEN-END:|4-resumeMIDlet|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Method: switchDisplayable ">//GEN-BEGIN:|5-switchDisplayable|0|5-preSwitch
|
||||||
|
/**
|
||||||
|
* Switches a current displayable in a display. The <code>display</code> instance is taken from <code>getDisplay</code> method. This method is used by all actions in the design for switching displayable.
|
||||||
|
* @param alert the Alert which is temporarily set to the display; if <code>null</code>, then <code>nextDisplayable</code> is set immediately
|
||||||
|
* @param nextDisplayable the Displayable to be set
|
||||||
|
*/
|
||||||
|
public void switchDisplayable(Alert alert, Displayable nextDisplayable) {//GEN-END:|5-switchDisplayable|0|5-preSwitch
|
||||||
|
// write pre-switch user code here
|
||||||
|
Display display = getDisplay();//GEN-BEGIN:|5-switchDisplayable|1|5-postSwitch
|
||||||
|
if (alert == null) {
|
||||||
|
display.setCurrent(nextDisplayable);
|
||||||
|
} else {
|
||||||
|
display.setCurrent(alert, nextDisplayable);
|
||||||
|
}//GEN-END:|5-switchDisplayable|1|5-postSwitch
|
||||||
|
// write post-switch user code here
|
||||||
|
}//GEN-BEGIN:|5-switchDisplayable|2|
|
||||||
|
//</editor-fold>//GEN-END:|5-switchDisplayable|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Method: commandAction for Displayables ">//GEN-BEGIN:|7-commandAction|0|7-preCommandAction
|
||||||
|
/**
|
||||||
|
* Called by a system to indicated that a command has been invoked on a particular displayable.
|
||||||
|
* @param command the Command that was invoked
|
||||||
|
* @param displayable the Displayable where the command was invoked
|
||||||
|
*/
|
||||||
|
public void commandAction(Command command, Displayable displayable) {//GEN-END:|7-commandAction|0|7-preCommandAction
|
||||||
|
// write pre-action user code here
|
||||||
|
if (displayable == fileBrowser) {//GEN-BEGIN:|7-commandAction|1|16-preAction
|
||||||
|
if (command == FileBrowser.SELECT_FILE_COMMAND) {//GEN-END:|7-commandAction|1|16-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getTextBox());//GEN-LINE:|7-commandAction|2|16-postAction
|
||||||
|
readFile();// write post-action user code here
|
||||||
|
} else if (command == cancelCommand1) {//GEN-LINE:|7-commandAction|3|54-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getSplashScreen());//GEN-LINE:|7-commandAction|4|54-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == exitCommand) {//GEN-LINE:|7-commandAction|5|30-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
exitMIDlet();//GEN-LINE:|7-commandAction|6|30-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|7-commandAction|7|23-preAction
|
||||||
|
} else if (displayable == splashScreen) {
|
||||||
|
if (command == SplashScreen.DISMISS_COMMAND) {//GEN-END:|7-commandAction|7|23-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
//GEN-LINE:|7-commandAction|8|23-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == exitCommand2) {//GEN-LINE:|7-commandAction|9|60-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
exitMIDlet();//GEN-LINE:|7-commandAction|10|60-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == helpCommand) {//GEN-LINE:|7-commandAction|11|70-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getTextBox2());//GEN-LINE:|7-commandAction|12|70-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == itemCommand) {//GEN-LINE:|7-commandAction|13|38-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getFileBrowser());//GEN-LINE:|7-commandAction|14|38-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == itemCommand1) {//GEN-LINE:|7-commandAction|15|40-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getTextBox1());//GEN-LINE:|7-commandAction|16|40-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|7-commandAction|17|28-preAction
|
||||||
|
} else if (displayable == textBox) {
|
||||||
|
if (command == backCommand) {//GEN-END:|7-commandAction|17|28-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getFileBrowser());//GEN-LINE:|7-commandAction|18|28-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == backCommand2) {//GEN-LINE:|7-commandAction|19|57-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getSplashScreen());//GEN-LINE:|7-commandAction|20|57-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|7-commandAction|21|45-preAction
|
||||||
|
} else if (displayable == textBox1) {
|
||||||
|
if (command == cancelCommand) {//GEN-END:|7-commandAction|21|45-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getSplashScreen());//GEN-LINE:|7-commandAction|22|45-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == exitCommand1) {//GEN-LINE:|7-commandAction|23|51-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
exitMIDlet();//GEN-LINE:|7-commandAction|24|51-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
} else if (command == okCommand) {//GEN-LINE:|7-commandAction|25|47-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
//GEN-LINE:|7-commandAction|26|47-postAction
|
||||||
|
//
|
||||||
|
DoEncode();
|
||||||
|
switchDisplayable(null, getSplashScreen());// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|7-commandAction|27|73-preAction
|
||||||
|
} else if (displayable == textBox2) {
|
||||||
|
if (command == okCommand1) {//GEN-END:|7-commandAction|27|73-preAction
|
||||||
|
// write pre-action user code here
|
||||||
|
switchDisplayable(null, getSplashScreen());//GEN-LINE:|7-commandAction|28|73-postAction
|
||||||
|
// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|7-commandAction|29|7-postCommandAction
|
||||||
|
}//GEN-END:|7-commandAction|29|7-postCommandAction
|
||||||
|
// write post-action user code here
|
||||||
|
}//GEN-BEGIN:|7-commandAction|30|
|
||||||
|
//</editor-fold>//GEN-END:|7-commandAction|30|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: fileBrowser ">//GEN-BEGIN:|14-getter|0|14-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of fileBrowser component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public FileBrowser getFileBrowser() {
|
||||||
|
if (fileBrowser == null) {//GEN-END:|14-getter|0|14-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
fileBrowser = new FileBrowser(getDisplay());//GEN-BEGIN:|14-getter|1|14-postInit
|
||||||
|
fileBrowser.setTitle("\u9009\u62E9\u56FE\u7247");
|
||||||
|
fileBrowser.setCommandListener(this);
|
||||||
|
fileBrowser.addCommand(FileBrowser.SELECT_FILE_COMMAND);
|
||||||
|
fileBrowser.addCommand(getExitCommand());
|
||||||
|
fileBrowser.addCommand(getCancelCommand1());//GEN-END:|14-getter|1|14-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|14-getter|2|
|
||||||
|
return fileBrowser;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|14-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: textBox ">//GEN-BEGIN:|17-getter|0|17-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of textBox component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public TextBox getTextBox() {
|
||||||
|
if (textBox == null) {//GEN-END:|17-getter|0|17-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
textBox = new TextBox("\u89E3\u7801\u6587\u5B57", "", 3000, TextField.ANY);//GEN-BEGIN:|17-getter|1|17-postInit
|
||||||
|
textBox.addCommand(getBackCommand());
|
||||||
|
textBox.addCommand(getBackCommand2());
|
||||||
|
textBox.setCommandListener(this);//GEN-END:|17-getter|1|17-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|17-getter|2|
|
||||||
|
return textBox;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|17-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: splashScreen ">//GEN-BEGIN:|22-getter|0|22-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of splashScreen component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public SplashScreen getSplashScreen() {
|
||||||
|
if (splashScreen == null) {//GEN-END:|22-getter|0|22-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
splashScreen = new SplashScreen(getDisplay());//GEN-BEGIN:|22-getter|1|22-postInit
|
||||||
|
splashScreen.setTitle("\u4E8C\u7EF4\u7801\u89E3\u7801\u7A0B\u5E8F");
|
||||||
|
splashScreen.addCommand(getItemCommand());
|
||||||
|
splashScreen.addCommand(getItemCommand1());
|
||||||
|
splashScreen.addCommand(getExitCommand2());
|
||||||
|
splashScreen.addCommand(getHelpCommand());
|
||||||
|
splashScreen.setCommandListener(this);
|
||||||
|
splashScreen.setImage(getImage());
|
||||||
|
splashScreen.setText("\u89E3\u7801\u56FE\u7247\u6216\u7F16\u7801\u6587\u5B57");//GEN-END:|22-getter|1|22-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|22-getter|2|
|
||||||
|
return splashScreen;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|22-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: backCommand ">//GEN-BEGIN:|27-getter|0|27-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of backCommand component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getBackCommand() {
|
||||||
|
if (backCommand == null) {//GEN-END:|27-getter|0|27-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
backCommand = new Command("\u540E\u9000", Command.BACK, 0);//GEN-LINE:|27-getter|1|27-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|27-getter|2|
|
||||||
|
return backCommand;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|27-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: exitCommand ">//GEN-BEGIN:|29-getter|0|29-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of exitCommand component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getExitCommand() {
|
||||||
|
if (exitCommand == null) {//GEN-END:|29-getter|0|29-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
exitCommand = new Command("\u9000\u51FA", Command.EXIT, 0);//GEN-LINE:|29-getter|1|29-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|29-getter|2|
|
||||||
|
return exitCommand;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|29-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: image ">//GEN-BEGIN:|33-getter|0|33-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of image component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Image getImage() {
|
||||||
|
if (image == null) {//GEN-END:|33-getter|0|33-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
try {//GEN-BEGIN:|33-getter|1|33-@java.io.IOException
|
||||||
|
image = Image.createImage("/zxing-icon.png");
|
||||||
|
} catch (java.io.IOException e) {//GEN-END:|33-getter|1|33-@java.io.IOException
|
||||||
|
e.printStackTrace();
|
||||||
|
}//GEN-LINE:|33-getter|2|33-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|33-getter|3|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|33-getter|3|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: backCommand1 ">//GEN-BEGIN:|34-getter|0|34-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of backCommand1 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getBackCommand1() {
|
||||||
|
if (backCommand1 == null) {//GEN-END:|34-getter|0|34-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
backCommand1 = new Command("\u540E\u9000", Command.BACK, 0);//GEN-LINE:|34-getter|1|34-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|34-getter|2|
|
||||||
|
return backCommand1;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|34-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: itemCommand ">//GEN-BEGIN:|37-getter|0|37-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of itemCommand component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getItemCommand() {
|
||||||
|
if (itemCommand == null) {//GEN-END:|37-getter|0|37-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
itemCommand = new Command("\u89E3\u7801", Command.ITEM, 0);//GEN-LINE:|37-getter|1|37-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|37-getter|2|
|
||||||
|
return itemCommand;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|37-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: itemCommand1 ">//GEN-BEGIN:|39-getter|0|39-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of itemCommand1 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getItemCommand1() {
|
||||||
|
if (itemCommand1 == null) {//GEN-END:|39-getter|0|39-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
itemCommand1 = new Command("\u7F16\u7801", Command.ITEM, 0);//GEN-LINE:|39-getter|1|39-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|39-getter|2|
|
||||||
|
return itemCommand1;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|39-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: textBox1 ">//GEN-BEGIN:|42-getter|0|42-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of textBox1 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public TextBox getTextBox1() {
|
||||||
|
if (textBox1 == null) {//GEN-END:|42-getter|0|42-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
textBox1 = new TextBox("\u7F16\u7801\u6587\u5B57", "", 3000, TextField.ANY);//GEN-BEGIN:|42-getter|1|42-postInit
|
||||||
|
textBox1.addCommand(getOkCommand());
|
||||||
|
textBox1.addCommand(getCancelCommand());
|
||||||
|
textBox1.addCommand(getExitCommand1());
|
||||||
|
textBox1.setCommandListener(this);//GEN-END:|42-getter|1|42-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|42-getter|2|
|
||||||
|
return textBox1;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|42-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: cancelCommand ">//GEN-BEGIN:|44-getter|0|44-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of cancelCommand component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getCancelCommand() {
|
||||||
|
if (cancelCommand == null) {//GEN-END:|44-getter|0|44-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
cancelCommand = new Command("\u53D6\u6D88", Command.CANCEL, 0);//GEN-LINE:|44-getter|1|44-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|44-getter|2|
|
||||||
|
return cancelCommand;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|44-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: okCommand ">//GEN-BEGIN:|46-getter|0|46-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of okCommand component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getOkCommand() {
|
||||||
|
if (okCommand == null) {//GEN-END:|46-getter|0|46-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
okCommand = new Command("\u786E\u5B9A", Command.OK, 0);//GEN-LINE:|46-getter|1|46-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|46-getter|2|
|
||||||
|
return okCommand;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|46-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: exitCommand1 ">//GEN-BEGIN:|50-getter|0|50-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of exitCommand1 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getExitCommand1() {
|
||||||
|
if (exitCommand1 == null) {//GEN-END:|50-getter|0|50-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
exitCommand1 = new Command("\u9000\u51FA", Command.EXIT, 0);//GEN-LINE:|50-getter|1|50-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|50-getter|2|
|
||||||
|
return exitCommand1;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|50-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: cancelCommand1 ">//GEN-BEGIN:|53-getter|0|53-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of cancelCommand1 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getCancelCommand1() {
|
||||||
|
if (cancelCommand1 == null) {//GEN-END:|53-getter|0|53-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
cancelCommand1 = new Command("\u53D6\u6D88", Command.CANCEL, 0);//GEN-LINE:|53-getter|1|53-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|53-getter|2|
|
||||||
|
return cancelCommand1;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|53-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: backCommand2 ">//GEN-BEGIN:|56-getter|0|56-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of backCommand2 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getBackCommand2() {
|
||||||
|
if (backCommand2 == null) {//GEN-END:|56-getter|0|56-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
backCommand2 = new Command("\u4E3B\u754C\u9762", Command.BACK, 0);//GEN-LINE:|56-getter|1|56-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|56-getter|2|
|
||||||
|
return backCommand2;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|56-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: exitCommand2 ">//GEN-BEGIN:|59-getter|0|59-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of exitCommand2 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getExitCommand2() {
|
||||||
|
if (exitCommand2 == null) {//GEN-END:|59-getter|0|59-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
exitCommand2 = new Command("\u9000\u51FA", Command.EXIT, 0);//GEN-LINE:|59-getter|1|59-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|59-getter|2|
|
||||||
|
return exitCommand2;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|59-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: backCommand3 ">//GEN-BEGIN:|62-getter|0|62-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of backCommand3 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getBackCommand3() {
|
||||||
|
if (backCommand3 == null) {//GEN-END:|62-getter|0|62-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
backCommand3 = new Command("\u540E\u9000", Command.BACK, 0);//GEN-LINE:|62-getter|1|62-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|62-getter|2|
|
||||||
|
return backCommand3;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|62-getter|2|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: textBox2 ">//GEN-BEGIN:|68-getter|0|68-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of textBox2 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public TextBox getTextBox2() {
|
||||||
|
if (textBox2 == null) {//GEN-END:|68-getter|0|68-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
textBox2 = new TextBox("\u5173\u4E8E", "\u4E8C\u7EF4\u7801\u52A9\u624B\nPowered by Zxing\nVersion 3.1\n\u7248\u6743(C) 2011-2012 xuenhua\nhttp://weibo.com/xuenhua\nhttp://code.google.com/p/zxing", 172, TextField.ANY | TextField.UNEDITABLE);//GEN-BEGIN:|68-getter|1|68-postInit
|
||||||
|
textBox2.addCommand(getOkCommand1());
|
||||||
|
textBox2.setCommandListener(this);//GEN-END:|68-getter|1|68-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|68-getter|2|
|
||||||
|
return textBox2;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|68-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: helpCommand ">//GEN-BEGIN:|69-getter|0|69-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of helpCommand component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getHelpCommand() {
|
||||||
|
if (helpCommand == null) {//GEN-END:|69-getter|0|69-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
helpCommand = new Command("\u5173\u4E8E", Command.HELP, 0);//GEN-LINE:|69-getter|1|69-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|69-getter|2|
|
||||||
|
return helpCommand;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|69-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: okCommand1 ">//GEN-BEGIN:|72-getter|0|72-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of okCommand1 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getOkCommand1() {
|
||||||
|
if (okCommand1 == null) {//GEN-END:|72-getter|0|72-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
okCommand1 = new Command("\u786E\u5B9A", Command.OK, 0);//GEN-LINE:|72-getter|1|72-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|72-getter|2|
|
||||||
|
return okCommand1;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|72-getter|2|
|
||||||
|
|
||||||
|
//<editor-fold defaultstate="collapsed" desc=" Generated Getter: itemCommand2 ">//GEN-BEGIN:|75-getter|0|75-preInit
|
||||||
|
/**
|
||||||
|
* Returns an initiliazed instance of itemCommand2 component.
|
||||||
|
* @return the initialized component instance
|
||||||
|
*/
|
||||||
|
public Command getItemCommand2() {
|
||||||
|
if (itemCommand2 == null) {//GEN-END:|75-getter|0|75-preInit
|
||||||
|
// write pre-init user code here
|
||||||
|
itemCommand2 = new Command("\u8BBE\u7F6E", Command.ITEM, 0);//GEN-LINE:|75-getter|1|75-postInit
|
||||||
|
// write post-init user code here
|
||||||
|
}//GEN-BEGIN:|75-getter|2|
|
||||||
|
return itemCommand2;
|
||||||
|
}
|
||||||
|
//</editor-fold>//GEN-END:|75-getter|2|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a display instance.
|
||||||
|
* @return the display instance.
|
||||||
|
*/
|
||||||
|
public Display getDisplay() {
|
||||||
|
return Display.getDisplay(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exits MIDlet.
|
||||||
|
*/
|
||||||
|
public void exitMIDlet() {
|
||||||
|
switchDisplayable(null, null);
|
||||||
|
destroyApp(true);
|
||||||
|
notifyDestroyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when MIDlet is started.
|
||||||
|
* Checks whether the MIDlet have been already started and initialize/starts or resumes the MIDlet.
|
||||||
|
*/
|
||||||
|
public void startApp() {
|
||||||
|
if (midletPaused) {
|
||||||
|
resumeMIDlet();
|
||||||
|
} else {
|
||||||
|
initialize();
|
||||||
|
startMIDlet();
|
||||||
|
}
|
||||||
|
midletPaused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when MIDlet is paused.
|
||||||
|
*/
|
||||||
|
public void pauseApp() {
|
||||||
|
midletPaused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to signal the MIDlet to terminate.
|
||||||
|
* @param unconditional if true, then the MIDlet has to be unconditionally terminated and all resources has to be released.
|
||||||
|
*/
|
||||||
|
public void destroyApp(boolean unconditional) {
|
||||||
|
}
|
||||||
|
private void readFile() {
|
||||||
|
try {
|
||||||
|
FileConnection textFile = fileBrowser.getSelectedFile();
|
||||||
|
getTextBox().setString("正在解码……");
|
||||||
|
InputStream fis = textFile.openInputStream();
|
||||||
|
|
||||||
|
//read image SO Slowly!
|
||||||
|
// byte[] imagearray = null;
|
||||||
|
// ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
|
||||||
|
// int ch;
|
||||||
|
// while( ( ch = fis.read() ) != -1 )
|
||||||
|
// {
|
||||||
|
// bytestream.write( ch );
|
||||||
|
// }
|
||||||
|
// imagearray = bytestream.toByteArray();
|
||||||
|
// img = Image.createImage( imagearray, 0, imagearray.length );
|
||||||
|
img = Image.createImage(fis);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LuminanceSource source = new LCDUIImageLuminanceSource(img);
|
||||||
|
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||||
|
Result result;
|
||||||
|
try {
|
||||||
|
result = new MultiFormatReader().decode(bitmap);
|
||||||
|
textBox.setString(result.getText());
|
||||||
|
} catch (ReaderException re) {
|
||||||
|
textBox.setString(re.toString());
|
||||||
|
}
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Image encode(String content) {
|
||||||
|
try {
|
||||||
|
QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
||||||
|
int qrWidth = 200;
|
||||||
|
int qrHeigth = 200;
|
||||||
|
BitMatrix qrBitMatrix = qrCodeWriter.encode(content,
|
||||||
|
BarcodeFormat.QR_CODE, qrWidth, qrHeigth);
|
||||||
|
int[] rgb = new int[qrWidth * qrHeigth];
|
||||||
|
for (int y = 0; y < qrBitMatrix.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < qrWidth; x++) {
|
||||||
|
int offset = y * qrHeigth;
|
||||||
|
rgb[offset + x] = qrBitMatrix.get(x, y) ? BACK : WHTIE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Image.createRGBImage(rgb, qrWidth, qrHeigth, false);
|
||||||
|
} catch (WriterException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveQRCodeImage() {
|
||||||
|
byte[] pngData = PNGEncoder.toPNG(qrCodeImage, false);
|
||||||
|
javax.microedition.io.Connection c = null;
|
||||||
|
// java.io.OutputStream os = null;
|
||||||
|
filename = Long.toString((new Date()).getTime()) + ".png";
|
||||||
|
try {
|
||||||
|
String path = "E:/" + filename;
|
||||||
|
c = javax.microedition.io.Connector.open("file:///" + path, javax.microedition.io.Connector.READ_WRITE);// (FileConnection) Connector.open(url, Connector.READ_WRITE);
|
||||||
|
javax.microedition.io.file.FileConnection fc =
|
||||||
|
(javax.microedition.io.file.FileConnection) c;
|
||||||
|
if (!fc.exists()) {
|
||||||
|
fc.create();
|
||||||
|
}
|
||||||
|
OutputStream outputStream = fc.openOutputStream();
|
||||||
|
|
||||||
|
outputStream.write(pngData);
|
||||||
|
outputStream.close();
|
||||||
|
fc.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
System.out.println("IOException: " + ioe.getMessage());
|
||||||
|
} catch (SecurityException se) {
|
||||||
|
System.out.println("Security exception:" + se.getMessage());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoEncode() {
|
||||||
|
qrCodeImage = encode(textBox1.getString());
|
||||||
|
saveQRCodeImage();
|
||||||
|
}
|
||||||
|
}
|
310
src/com/google/zxing/client/j2me/ZXMIDlet.vmd
Normal file
310
src/com/google/zxing/client/j2me/ZXMIDlet.vmd
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<VisualDesign projectType="vmd-midp" version="1">
|
||||||
|
<Document version="2">
|
||||||
|
<Component componentID="0" typeID="C#Root">
|
||||||
|
<Property name="version" typeID="Pjava.lang.String" value="VMIDP-2.0"/>
|
||||||
|
<Component componentID="1" typeID="C#PointsCategory">
|
||||||
|
<Component componentID="2" typeID="C#MobileDevice">
|
||||||
|
<Property name="start" typeID="C#MobileDeviceStartEventSource" value="R3"/>
|
||||||
|
<Property name="resume" typeID="C#MobileDeviceResumeEventSource" value="R4"/>
|
||||||
|
<Component componentID="3" typeID="C#MobileDeviceStartEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R24"/>
|
||||||
|
<Component componentID="24" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R3"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="4" typeID="C#MobileDeviceResumeEventSource"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="5" typeID="C#Display"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="6" typeID="C#ControllersCategory">
|
||||||
|
<Component componentID="7" typeID="C#CommandListener"/>
|
||||||
|
<Component componentID="8" typeID="C#ItemCommandListener"/>
|
||||||
|
<Component componentID="9" typeID="C#ItemStateListener"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="10" typeID="C#DisplayablesCategory">
|
||||||
|
<Component componentID="14" typeID="Corg.netbeans.microedition.lcdui.pda.FileBrowser">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VfileBrowser"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="commands" typeID="1C#CommandEventSource" value="A3:3_R163_R303_R54"/>
|
||||||
|
<Property name="title" typeID="Pjava.lang.String" value="V选择图片"/>
|
||||||
|
<Property name="commandListener" typeID="C#CommandListener" value="R7"/>
|
||||||
|
<Component componentID="16" typeID="C#FileBrowserOpenCommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R26"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R14"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R15"/>
|
||||||
|
<Component componentID="26" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R16"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R17"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="30" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R31"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R14"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R29"/>
|
||||||
|
<Component componentID="31" typeID="C#ExitMidletEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R30"/>
|
||||||
|
<Property name="mobileDevice" typeID="C#MobileDevice" value="R2"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="54" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R55"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R14"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R53"/>
|
||||||
|
<Component componentID="55" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R54"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="17" typeID="Cjavax.microedition.lcdui.TextBox">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VtextBox"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="commands" typeID="1C#CommandEventSource" value="A2:3_R283_R57"/>
|
||||||
|
<Property name="title" typeID="Pjava.lang.String" value="V解码文字"/>
|
||||||
|
<Property name="commandListener" typeID="C#CommandListener" value="R7"/>
|
||||||
|
<Property name="string" typeID="Pjava.lang.String" value="V"/>
|
||||||
|
<Property name="maxSize" typeID="Pint" value="V3000"/>
|
||||||
|
<Component componentID="28" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R32"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R17"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R27"/>
|
||||||
|
<Component componentID="32" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R28"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R14"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="57" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R58"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R17"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R56"/>
|
||||||
|
<Component componentID="58" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R57"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="22" typeID="Corg.netbeans.microedition.lcdui.SplashScreen">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VsplashScreen"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="commands" typeID="1C#CommandEventSource" value="A5:3_R233_R383_R403_R603_R70"/>
|
||||||
|
<Property name="title" typeID="Pjava.lang.String" value="V二维码解码程序"/>
|
||||||
|
<Property name="commandListener" typeID="C#CommandListener" value="R7"/>
|
||||||
|
<Property name="image" typeID="Cjavax.microedition.lcdui.Image" value="R33"/>
|
||||||
|
<Property name="text" typeID="Pjava.lang.String" value="V解码图片或编码文字"/>
|
||||||
|
<Component componentID="23" typeID="C#SplashScreenDismissCommandEventSource">
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R20"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="38" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R41"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R37"/>
|
||||||
|
<Component componentID="41" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R38"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R14"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="40" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R43"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R39"/>
|
||||||
|
<Component componentID="43" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R40"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R42"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="60" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R61"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R59"/>
|
||||||
|
<Component componentID="61" typeID="C#ExitMidletEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R60"/>
|
||||||
|
<Property name="mobileDevice" typeID="C#MobileDevice" value="R2"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="70" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R71"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R69"/>
|
||||||
|
<Component componentID="71" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R70"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R68"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="42" typeID="Cjavax.microedition.lcdui.TextBox">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VtextBox1"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="commands" typeID="1C#CommandEventSource" value="A3:3_R473_R453_R51"/>
|
||||||
|
<Property name="title" typeID="Pjava.lang.String" value="V编码文字"/>
|
||||||
|
<Property name="commandListener" typeID="C#CommandListener" value="R7"/>
|
||||||
|
<Property name="string" typeID="Pjava.lang.String" value="V"/>
|
||||||
|
<Property name="maxSize" typeID="Pint" value="V3000"/>
|
||||||
|
<Component componentID="47" typeID="C#CommandEventSource">
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R42"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R46"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="45" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R48"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R42"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R44"/>
|
||||||
|
<Component componentID="48" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R45"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="51" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R52"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R42"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R50"/>
|
||||||
|
<Component componentID="52" typeID="C#ExitMidletEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R51"/>
|
||||||
|
<Property name="mobileDevice" typeID="C#MobileDevice" value="R2"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="68" typeID="Cjavax.microedition.lcdui.TextBox">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VtextBox2"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="commands" typeID="1C#CommandEventSource" value="A1:3_R73"/>
|
||||||
|
<Property name="title" typeID="Pjava.lang.String" value="V关于"/>
|
||||||
|
<Property name="commandListener" typeID="C#CommandListener" value="R7"/>
|
||||||
|
<Property name="string" typeID="Pjava.lang.String" value="V二维码助手 Powered by Zxing Version 3.1 版权(C) 2011-2012 xuenhua http://weibo.com/xuenhua http://code.google.com/p/zxing"/>
|
||||||
|
<Property name="maxSize" typeID="Pint" value="V172"/>
|
||||||
|
<Property name="constraints" typeID="Pint" value="V131072"/>
|
||||||
|
<Component componentID="73" typeID="C#CommandEventSource">
|
||||||
|
<Property name="eventHandler" typeID="C#EventHandler" value="R74"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R68"/>
|
||||||
|
<Property name="command" typeID="Cjavax.microedition.lcdui.Command" value="R72"/>
|
||||||
|
<Component componentID="74" typeID="C#SwitchDisplayableEventHandler">
|
||||||
|
<Property name="eventSource" typeID="C#EventSource" value="R73"/>
|
||||||
|
<Property name="displayable" typeID="Cjavax.microedition.lcdui.Displayable" value="R22"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="11" typeID="C#CommandsCategory">
|
||||||
|
<Component componentID="15" typeID="C#FileBrowserOpenCommand">
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="VOpen File"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V4"/>
|
||||||
|
<Property name="priority" typeID="Pint" value="V0"/>
|
||||||
|
<Property name="ordinary" typeID="Pboolean" value="Vfalse"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="20" typeID="C#SplashScreenDismissCommand">
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="VDismiss"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V4"/>
|
||||||
|
<Property name="priority" typeID="Pint" value="V0"/>
|
||||||
|
<Property name="ordinary" typeID="Pboolean" value="Vfalse"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="27" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VbackCommand"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V后退"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V2"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="29" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VexitCommand"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V退出"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V7"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="34" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VbackCommand1"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V后退"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V2"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="39" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VitemCommand1"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V编码"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V8"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="37" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VitemCommand"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V解码"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V8"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="46" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VokCommand"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V确定"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V4"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="44" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VcancelCommand"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V取消"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V3"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="50" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VexitCommand1"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V退出"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V7"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="53" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VcancelCommand1"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V取消"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V3"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="59" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VexitCommand2"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V退出"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V7"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="56" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VbackCommand2"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V主界面"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V2"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="62" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VbackCommand3"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V后退"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V2"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="69" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VhelpCommand"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V关于"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V5"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="72" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VokCommand1"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V确定"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V4"/>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="75" typeID="Cjavax.microedition.lcdui.Command">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="VitemCommand2"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="label" typeID="Pjava.lang.String" value="V设置"/>
|
||||||
|
<Property name="type" typeID="Pint" value="V8"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="12" typeID="C#ResourcesCategory">
|
||||||
|
<Component componentID="33" typeID="Cjavax.microedition.lcdui.Image">
|
||||||
|
<Property name="instanceName" typeID="Pjava.lang.String" value="Vimage"/>
|
||||||
|
<Property name="codeGenerated" typeID="Pboolean" value="Vtrue"/>
|
||||||
|
<Property name="resourcePath" typeID="Pjava.lang.String" value="V/zxing-icon.png"/>
|
||||||
|
</Component>
|
||||||
|
</Component>
|
||||||
|
<Component componentID="13" typeID="C#DatabindingCategory"/>
|
||||||
|
</Component>
|
||||||
|
</Document>
|
||||||
|
<FlowScene version="1">
|
||||||
|
<Node componentID="22" descriptorID="componentNode22" x="241" y="207"/>
|
||||||
|
<Node componentID="42" descriptorID="componentNode42" x="34" y="426"/>
|
||||||
|
<Node componentID="14" descriptorID="componentNode14" x="476" y="32"/>
|
||||||
|
<Node componentID="17" descriptorID="componentNode17" x="34" y="32"/>
|
||||||
|
<Node componentID="68" descriptorID="componentNode68" x="476" y="426"/>
|
||||||
|
<Node componentID="2" descriptorID="componentNode2" x="715" y="207"/>
|
||||||
|
</FlowScene>
|
||||||
|
</VisualDesign>
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>See
|
||||||
|
* <a href="http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html">
|
||||||
|
* DoCoMo's documentation</a> about the result types represented by subclasses of this class.</p>
|
||||||
|
*
|
||||||
|
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
|
||||||
|
* on exception-based mechanisms during parsing.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
abstract class AbstractDoCoMoResultParser extends ResultParser {
|
||||||
|
|
||||||
|
static String[] matchDoCoMoPrefixedField(String prefix, String rawText, boolean trim) {
|
||||||
|
return matchPrefixedField(prefix, rawText, ';', trim);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String matchSingleDoCoMoPrefixedField(String prefix, String rawText, boolean trim) {
|
||||||
|
return matchSinglePrefixedField(prefix, rawText, ';', trim);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements KDDI AU's address book format. See
|
||||||
|
* <a href="http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html">
|
||||||
|
* http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html</a>.
|
||||||
|
* (Thanks to Yuzo for translating!)
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class AddressBookAUResultParser extends ResultParser {
|
||||||
|
|
||||||
|
public static AddressBookParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
// MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF
|
||||||
|
if (rawText == null || rawText.indexOf("MEMORY") < 0 || rawText.indexOf("\r\n") < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NAME1 and NAME2 have specific uses, namely written name and pronunciation, respectively.
|
||||||
|
// Therefore we treat them specially instead of as an array of names.
|
||||||
|
String name = matchSinglePrefixedField("NAME1:", rawText, '\r', true);
|
||||||
|
String pronunciation = matchSinglePrefixedField("NAME2:", rawText, '\r', true);
|
||||||
|
|
||||||
|
String[] phoneNumbers = matchMultipleValuePrefix("TEL", 3, rawText, true);
|
||||||
|
String[] emails = matchMultipleValuePrefix("MAIL", 3, rawText, true);
|
||||||
|
String note = matchSinglePrefixedField("MEMORY:", rawText, '\r', false);
|
||||||
|
String address = matchSinglePrefixedField("ADD:", rawText, '\r', true);
|
||||||
|
String[] addresses = address == null ? null : new String[] {address};
|
||||||
|
return new AddressBookParsedResult(maybeWrap(name), pronunciation, phoneNumbers, emails, note,
|
||||||
|
addresses, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] matchMultipleValuePrefix(String prefix, int max, String rawText,
|
||||||
|
boolean trim) {
|
||||||
|
Vector values = null;
|
||||||
|
for (int i = 1; i <= max; i++) {
|
||||||
|
String value = matchSinglePrefixedField(prefix + i + ':', rawText, '\r', trim);
|
||||||
|
if (value == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (values == null) {
|
||||||
|
values = new Vector(max); // lazy init
|
||||||
|
}
|
||||||
|
values.addElement(value);
|
||||||
|
}
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return toStringArray(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the "MECARD" address book entry format.
|
||||||
|
*
|
||||||
|
* Supported keys: N, SOUND, TEL, EMAIL, NOTE, ADR, BDAY, URL, plus ORG
|
||||||
|
* Unsupported keys: TEL-AV, NICKNAME
|
||||||
|
*
|
||||||
|
* Except for TEL, multiple values for keys are also not supported;
|
||||||
|
* the first one found takes precedence.
|
||||||
|
*
|
||||||
|
* Our understanding of the MECARD format is based on this document:
|
||||||
|
*
|
||||||
|
* http://www.mobicode.org.tw/files/OMIA%20Mobile%20Bar%20Code%20Standard%20v3.2.1.doc
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class AddressBookDoCoMoResultParser extends AbstractDoCoMoResultParser {
|
||||||
|
|
||||||
|
public static AddressBookParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || !rawText.startsWith("MECARD:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] rawName = matchDoCoMoPrefixedField("N:", rawText, true);
|
||||||
|
if (rawName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String name = parseName(rawName[0]);
|
||||||
|
String pronunciation = matchSingleDoCoMoPrefixedField("SOUND:", rawText, true);
|
||||||
|
String[] phoneNumbers = matchDoCoMoPrefixedField("TEL:", rawText, true);
|
||||||
|
String[] emails = matchDoCoMoPrefixedField("EMAIL:", rawText, true);
|
||||||
|
String note = matchSingleDoCoMoPrefixedField("NOTE:", rawText, false);
|
||||||
|
String[] addresses = matchDoCoMoPrefixedField("ADR:", rawText, true);
|
||||||
|
String birthday = matchSingleDoCoMoPrefixedField("BDAY:", rawText, true);
|
||||||
|
if (birthday != null && !isStringOfDigits(birthday, 8)) {
|
||||||
|
// No reason to throw out the whole card because the birthday is formatted wrong.
|
||||||
|
birthday = null;
|
||||||
|
}
|
||||||
|
String url = matchSingleDoCoMoPrefixedField("URL:", rawText, true);
|
||||||
|
|
||||||
|
// Although ORG may not be strictly legal in MECARD, it does exist in VCARD and we might as well
|
||||||
|
// honor it when found in the wild.
|
||||||
|
String org = matchSingleDoCoMoPrefixedField("ORG:", rawText, true);
|
||||||
|
|
||||||
|
return new AddressBookParsedResult(maybeWrap(name),
|
||||||
|
pronunciation,
|
||||||
|
phoneNumbers,
|
||||||
|
emails,
|
||||||
|
note,
|
||||||
|
addresses,
|
||||||
|
org,
|
||||||
|
birthday,
|
||||||
|
null,
|
||||||
|
url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseName(String name) {
|
||||||
|
int comma = name.indexOf((int) ',');
|
||||||
|
if (comma >= 0) {
|
||||||
|
// Format may be last,first; switch it around
|
||||||
|
return name.substring(comma + 1) + ' ' + name.substring(0, comma);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
122
src/com/google/zxing/client/result/AddressBookParsedResult.java
Normal file
122
src/com/google/zxing/client/result/AddressBookParsedResult.java
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class AddressBookParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String[] names;
|
||||||
|
private final String pronunciation;
|
||||||
|
private final String[] phoneNumbers;
|
||||||
|
private final String[] emails;
|
||||||
|
private final String note;
|
||||||
|
private final String[] addresses;
|
||||||
|
private final String org;
|
||||||
|
private final String birthday;
|
||||||
|
private final String title;
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
public AddressBookParsedResult(String[] names,
|
||||||
|
String pronunciation,
|
||||||
|
String[] phoneNumbers,
|
||||||
|
String[] emails,
|
||||||
|
String note,
|
||||||
|
String[] addresses,
|
||||||
|
String org,
|
||||||
|
String birthday,
|
||||||
|
String title,
|
||||||
|
String url) {
|
||||||
|
super(ParsedResultType.ADDRESSBOOK);
|
||||||
|
this.names = names;
|
||||||
|
this.pronunciation = pronunciation;
|
||||||
|
this.phoneNumbers = phoneNumbers;
|
||||||
|
this.emails = emails;
|
||||||
|
this.note = note;
|
||||||
|
this.addresses = addresses;
|
||||||
|
this.org = org;
|
||||||
|
this.birthday = birthday;
|
||||||
|
this.title = title;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getNames() {
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In Japanese, the name is written in kanji, which can have multiple readings. Therefore a hint
|
||||||
|
* is often provided, called furigana, which spells the name phonetically.
|
||||||
|
*
|
||||||
|
* @return The pronunciation of the getNames() field, often in hiragana or katakana.
|
||||||
|
*/
|
||||||
|
public String getPronunciation() {
|
||||||
|
return pronunciation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getPhoneNumbers() {
|
||||||
|
return phoneNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getEmails() {
|
||||||
|
return emails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNote() {
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getAddresses() {
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrg() {
|
||||||
|
return org;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getURL() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return birthday formatted as yyyyMMdd (e.g. 19780917)
|
||||||
|
*/
|
||||||
|
public String getBirthday() {
|
||||||
|
return birthday;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(100);
|
||||||
|
maybeAppend(names, result);
|
||||||
|
maybeAppend(pronunciation, result);
|
||||||
|
maybeAppend(title, result);
|
||||||
|
maybeAppend(org, result);
|
||||||
|
maybeAppend(addresses, result);
|
||||||
|
maybeAppend(phoneNumbers, result);
|
||||||
|
maybeAppend(emails, result);
|
||||||
|
maybeAppend(url, result);
|
||||||
|
maybeAppend(birthday, result);
|
||||||
|
maybeAppend(note, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
94
src/com/google/zxing/client/result/BizcardResultParser.java
Normal file
94
src/com/google/zxing/client/result/BizcardResultParser.java
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the "BIZCARD" address book entry format, though this has been
|
||||||
|
* largely reverse-engineered from examples observed in the wild -- still
|
||||||
|
* looking for a definitive reference.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class BizcardResultParser extends AbstractDoCoMoResultParser {
|
||||||
|
|
||||||
|
// Yes, we extend AbstractDoCoMoResultParser since the format is very much
|
||||||
|
// like the DoCoMo MECARD format, but this is not technically one of
|
||||||
|
// DoCoMo's proposed formats
|
||||||
|
|
||||||
|
public static AddressBookParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || !rawText.startsWith("BIZCARD:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String firstName = matchSingleDoCoMoPrefixedField("N:", rawText, true);
|
||||||
|
String lastName = matchSingleDoCoMoPrefixedField("X:", rawText, true);
|
||||||
|
String fullName = buildName(firstName, lastName);
|
||||||
|
String title = matchSingleDoCoMoPrefixedField("T:", rawText, true);
|
||||||
|
String org = matchSingleDoCoMoPrefixedField("C:", rawText, true);
|
||||||
|
String[] addresses = matchDoCoMoPrefixedField("A:", rawText, true);
|
||||||
|
String phoneNumber1 = matchSingleDoCoMoPrefixedField("B:", rawText, true);
|
||||||
|
String phoneNumber2 = matchSingleDoCoMoPrefixedField("M:", rawText, true);
|
||||||
|
String phoneNumber3 = matchSingleDoCoMoPrefixedField("F:", rawText, true);
|
||||||
|
String email = matchSingleDoCoMoPrefixedField("E:", rawText, true);
|
||||||
|
|
||||||
|
return new AddressBookParsedResult(maybeWrap(fullName),
|
||||||
|
null,
|
||||||
|
buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3),
|
||||||
|
maybeWrap(email),
|
||||||
|
null,
|
||||||
|
addresses,
|
||||||
|
org,
|
||||||
|
null,
|
||||||
|
title,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] buildPhoneNumbers(String number1, String number2, String number3) {
|
||||||
|
Vector numbers = new Vector(3);
|
||||||
|
if (number1 != null) {
|
||||||
|
numbers.addElement(number1);
|
||||||
|
}
|
||||||
|
if (number2 != null) {
|
||||||
|
numbers.addElement(number2);
|
||||||
|
}
|
||||||
|
if (number3 != null) {
|
||||||
|
numbers.addElement(number3);
|
||||||
|
}
|
||||||
|
int size = numbers.size();
|
||||||
|
if (size == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] result = new String[size];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
result[i] = (String) numbers.elementAt(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildName(String firstName, String lastName) {
|
||||||
|
if (firstName == null) {
|
||||||
|
return lastName;
|
||||||
|
} else {
|
||||||
|
return lastName == null ? firstName : firstName + ' ' + lastName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class BookmarkDoCoMoResultParser extends AbstractDoCoMoResultParser {
|
||||||
|
|
||||||
|
private BookmarkDoCoMoResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URIParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || !rawText.startsWith("MEBKM:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String title = matchSingleDoCoMoPrefixedField("TITLE:", rawText, true);
|
||||||
|
String[] rawUri = matchDoCoMoPrefixedField("URL:", rawText, true);
|
||||||
|
if (rawUri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String uri = rawUri[0];
|
||||||
|
if (!URIResultParser.isBasicallyValidURI(uri)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new URIParsedResult(uri, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
134
src/com/google/zxing/client/result/CalendarParsedResult.java
Normal file
134
src/com/google/zxing/client/result/CalendarParsedResult.java
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class CalendarParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String summary;
|
||||||
|
private final String start;
|
||||||
|
private final String end;
|
||||||
|
private final String location;
|
||||||
|
private final String attendee;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
public CalendarParsedResult(String summary,
|
||||||
|
String start,
|
||||||
|
String end,
|
||||||
|
String location,
|
||||||
|
String attendee,
|
||||||
|
String description) {
|
||||||
|
super(ParsedResultType.CALENDAR);
|
||||||
|
// Start is required, end is not
|
||||||
|
if (start == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
validateDate(start);
|
||||||
|
if (end == null) {
|
||||||
|
end = start;
|
||||||
|
} else {
|
||||||
|
validateDate(end);
|
||||||
|
}
|
||||||
|
this.summary = summary;
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
this.location = location;
|
||||||
|
this.attendee = attendee;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSummary() {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>We would return the start and end date as a {@link java.util.Date} except that this code
|
||||||
|
* needs to work under JavaME / MIDP and there is no date parsing library available there, such
|
||||||
|
* as <code>java.text.SimpleDateFormat</code>.</p> See validateDate() for the return format.
|
||||||
|
*
|
||||||
|
* @return start time formatted as a RFC 2445 DATE or DATE-TIME.</p>
|
||||||
|
*/
|
||||||
|
public String getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getStart(). May return null if the event has no duration.
|
||||||
|
*/
|
||||||
|
public String getEnd() {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocation() {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttendee() {
|
||||||
|
return attendee;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(100);
|
||||||
|
maybeAppend(summary, result);
|
||||||
|
maybeAppend(start, result);
|
||||||
|
maybeAppend(end, result);
|
||||||
|
maybeAppend(location, result);
|
||||||
|
maybeAppend(attendee, result);
|
||||||
|
maybeAppend(description, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021) or DATE-TIME
|
||||||
|
* (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC).
|
||||||
|
*
|
||||||
|
* @param date The string to validate
|
||||||
|
*/
|
||||||
|
private static void validateDate(String date) {
|
||||||
|
if (date != null) {
|
||||||
|
int length = date.length();
|
||||||
|
if (length != 8 && length != 15 && length != 16) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if (!Character.isDigit(date.charAt(i))) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (length > 8) {
|
||||||
|
if (date.charAt(8) != 'T') {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
for (int i = 9; i < 15; i++) {
|
||||||
|
if (!Character.isDigit(date.charAt(i))) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (length == 16 && date.charAt(15) != 'Z') {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class EmailAddressParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String emailAddress;
|
||||||
|
private final String subject;
|
||||||
|
private final String body;
|
||||||
|
private final String mailtoURI;
|
||||||
|
|
||||||
|
EmailAddressParsedResult(String emailAddress, String subject, String body, String mailtoURI) {
|
||||||
|
super(ParsedResultType.EMAIL_ADDRESS);
|
||||||
|
this.emailAddress = emailAddress;
|
||||||
|
this.subject = subject;
|
||||||
|
this.body = body;
|
||||||
|
this.mailtoURI = mailtoURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmailAddress() {
|
||||||
|
return emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMailtoURI() {
|
||||||
|
return mailtoURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(30);
|
||||||
|
maybeAppend(emailAddress, result);
|
||||||
|
maybeAppend(subject, result);
|
||||||
|
maybeAppend(body, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a result that encodes an e-mail address, either as a plain address
|
||||||
|
* like "joe@example.org" or a mailto: URL like "mailto:joe@example.org".
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class EmailAddressResultParser extends ResultParser {
|
||||||
|
|
||||||
|
public static EmailAddressParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String emailAddress;
|
||||||
|
if (rawText.startsWith("mailto:") || rawText.startsWith("MAILTO:")) {
|
||||||
|
// If it starts with mailto:, assume it is definitely trying to be an email address
|
||||||
|
emailAddress = rawText.substring(7);
|
||||||
|
int queryStart = emailAddress.indexOf('?');
|
||||||
|
if (queryStart >= 0) {
|
||||||
|
emailAddress = emailAddress.substring(0, queryStart);
|
||||||
|
}
|
||||||
|
Hashtable nameValues = parseNameValuePairs(rawText);
|
||||||
|
String subject = null;
|
||||||
|
String body = null;
|
||||||
|
if (nameValues != null) {
|
||||||
|
if (emailAddress.length() == 0) {
|
||||||
|
emailAddress = (String) nameValues.get("to");
|
||||||
|
}
|
||||||
|
subject = (String) nameValues.get("subject");
|
||||||
|
body = (String) nameValues.get("body");
|
||||||
|
}
|
||||||
|
return new EmailAddressParsedResult(emailAddress, subject, body, rawText);
|
||||||
|
} else {
|
||||||
|
if (!EmailDoCoMoResultParser.isBasicallyValidEmailAddress(rawText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
emailAddress = rawText;
|
||||||
|
return new EmailAddressParsedResult(emailAddress, null, null, "mailto:" + emailAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the "MATMSG" email message entry format.
|
||||||
|
*
|
||||||
|
* Supported keys: TO, SUB, BODY
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class EmailDoCoMoResultParser extends AbstractDoCoMoResultParser {
|
||||||
|
|
||||||
|
private static final char[] ATEXT_SYMBOLS =
|
||||||
|
{'@','.','!','#','$','%','&','\'','*','+','-','/','=','?','^','_','`','{','|','}','~'};
|
||||||
|
|
||||||
|
public static EmailAddressParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || !rawText.startsWith("MATMSG:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] rawTo = matchDoCoMoPrefixedField("TO:", rawText, true);
|
||||||
|
if (rawTo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String to = rawTo[0];
|
||||||
|
if (!isBasicallyValidEmailAddress(to)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String subject = matchSingleDoCoMoPrefixedField("SUB:", rawText, false);
|
||||||
|
String body = matchSingleDoCoMoPrefixedField("BODY:", rawText, false);
|
||||||
|
return new EmailAddressParsedResult(to, subject, body, "mailto:" + to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implements only the most basic checking for an email address's validity -- that it contains
|
||||||
|
* an '@' contains no characters disallowed by RFC 2822. This is an overly lenient definition of
|
||||||
|
* validity. We want to generally be lenient here since this class is only intended to encapsulate what's
|
||||||
|
* in a barcode, not "judge" it.
|
||||||
|
*/
|
||||||
|
static boolean isBasicallyValidEmailAddress(String email) {
|
||||||
|
if (email == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean atFound = false;
|
||||||
|
for (int i = 0; i < email.length(); i++) {
|
||||||
|
char c = email.charAt(i);
|
||||||
|
if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') &&
|
||||||
|
!isAtextSymbol(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c == '@') {
|
||||||
|
if (atFound) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
atFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return atFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAtextSymbol(char c) {
|
||||||
|
for (int i = 0; i < ATEXT_SYMBOLS.length; i++) {
|
||||||
|
if (c == ATEXT_SYMBOLS[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These authors would like to acknowledge the Spanish Ministry of Industry,
|
||||||
|
* Tourism and Trade, for the support in the project TSI020301-2008-2
|
||||||
|
* "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled
|
||||||
|
* Mobile Dynamic Environments", led by Treelogic
|
||||||
|
* ( http://www.treelogic.com/ ):
|
||||||
|
*
|
||||||
|
* http://www.piramidepse.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Antonio Manuel Benjumea Conde, Servinform, S.A.
|
||||||
|
* @author Agustín Delgado, Servinform, S.A.
|
||||||
|
*/
|
||||||
|
public class ExpandedProductParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
public static final String KILOGRAM = "KG";
|
||||||
|
public static final String POUND = "LB";
|
||||||
|
|
||||||
|
private final String productID;
|
||||||
|
private final String sscc;
|
||||||
|
private final String lotNumber;
|
||||||
|
private final String productionDate;
|
||||||
|
private final String packagingDate;
|
||||||
|
private final String bestBeforeDate;
|
||||||
|
private final String expirationDate;
|
||||||
|
private final String weight;
|
||||||
|
private final String weightType;
|
||||||
|
private final String weightIncrement;
|
||||||
|
private final String price;
|
||||||
|
private final String priceIncrement;
|
||||||
|
private final String priceCurrency;
|
||||||
|
// For AIS that not exist in this object
|
||||||
|
private final Hashtable uncommonAIs;
|
||||||
|
|
||||||
|
ExpandedProductParsedResult() {
|
||||||
|
super(ParsedResultType.PRODUCT);
|
||||||
|
this.productID = "";
|
||||||
|
this.sscc = "";
|
||||||
|
this.lotNumber = "";
|
||||||
|
this.productionDate = "";
|
||||||
|
this.packagingDate = "";
|
||||||
|
this.bestBeforeDate = "";
|
||||||
|
this.expirationDate = "";
|
||||||
|
this.weight = "";
|
||||||
|
this.weightType = "";
|
||||||
|
this.weightIncrement = "";
|
||||||
|
this.price = "";
|
||||||
|
this.priceIncrement = "";
|
||||||
|
this.priceCurrency = "";
|
||||||
|
this.uncommonAIs = new Hashtable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpandedProductParsedResult(String productID, String sscc,
|
||||||
|
String lotNumber, String productionDate, String packagingDate,
|
||||||
|
String bestBeforeDate, String expirationDate, String weight,
|
||||||
|
String weightType, String weightIncrement, String price,
|
||||||
|
String priceIncrement, String priceCurrency, Hashtable uncommonAIs) {
|
||||||
|
super(ParsedResultType.PRODUCT);
|
||||||
|
this.productID = productID;
|
||||||
|
this.sscc = sscc;
|
||||||
|
this.lotNumber = lotNumber;
|
||||||
|
this.productionDate = productionDate;
|
||||||
|
this.packagingDate = packagingDate;
|
||||||
|
this.bestBeforeDate = bestBeforeDate;
|
||||||
|
this.expirationDate = expirationDate;
|
||||||
|
this.weight = weight;
|
||||||
|
this.weightType = weightType;
|
||||||
|
this.weightIncrement = weightIncrement;
|
||||||
|
this.price = price;
|
||||||
|
this.priceIncrement = priceIncrement;
|
||||||
|
this.priceCurrency = priceCurrency;
|
||||||
|
this.uncommonAIs = uncommonAIs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o){
|
||||||
|
if (!(o instanceof ExpandedProductParsedResult)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpandedProductParsedResult other = (ExpandedProductParsedResult)o;
|
||||||
|
|
||||||
|
return this.productID.equals( other.productID)
|
||||||
|
&& this.sscc.equals( other.sscc)
|
||||||
|
&& this.lotNumber.equals( other.lotNumber)
|
||||||
|
&& this.productionDate.equals( other.productionDate)
|
||||||
|
&& this.bestBeforeDate.equals( other.bestBeforeDate)
|
||||||
|
&& this.expirationDate.equals( other.expirationDate)
|
||||||
|
&& this.weight.equals( other.weight)
|
||||||
|
&& this.weightType.equals( other.weightType)
|
||||||
|
&& this.weightIncrement.equals( other.weightIncrement)
|
||||||
|
&& this.price.equals( other.price)
|
||||||
|
&& this.priceIncrement.equals( other.priceIncrement)
|
||||||
|
&& this.priceCurrency.equals( other.priceCurrency)
|
||||||
|
&& this.uncommonAIs.equals( other.uncommonAIs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode(){
|
||||||
|
int hash1 = this.productID.hashCode();
|
||||||
|
hash1 = 31 * hash1 + this.sscc.hashCode();
|
||||||
|
hash1 = 31 * hash1 + this.lotNumber.hashCode();
|
||||||
|
hash1 = 31 * hash1 + this.productionDate.hashCode();
|
||||||
|
hash1 = 31 * hash1 + this.bestBeforeDate.hashCode();
|
||||||
|
hash1 = 31 * hash1 + this.expirationDate.hashCode();
|
||||||
|
hash1 = 31 * hash1 + this.weight.hashCode();
|
||||||
|
|
||||||
|
int hash2 = this.weightType.hashCode();
|
||||||
|
hash2 = 31 * hash2 + this.weightIncrement.hashCode();
|
||||||
|
hash2 = 31 * hash2 + this.price.hashCode();
|
||||||
|
hash2 = 31 * hash2 + this.priceIncrement.hashCode();
|
||||||
|
hash2 = 31 * hash2 + this.priceCurrency.hashCode();
|
||||||
|
hash2 = 31 * hash2 + this.uncommonAIs.hashCode();
|
||||||
|
return hash1 ^ hash2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProductID() {
|
||||||
|
return productID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSscc() {
|
||||||
|
return sscc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLotNumber() {
|
||||||
|
return lotNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProductionDate() {
|
||||||
|
return productionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPackagingDate() {
|
||||||
|
return packagingDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBestBeforeDate() {
|
||||||
|
return bestBeforeDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpirationDate() {
|
||||||
|
return expirationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWeight() {
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWeightType() {
|
||||||
|
return weightType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWeightIncrement() {
|
||||||
|
return weightIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPriceIncrement() {
|
||||||
|
return priceIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPriceCurrency() {
|
||||||
|
return priceCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hashtable getUncommonAIs() {
|
||||||
|
return uncommonAIs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
return productID;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These authors would like to acknowledge the Spanish Ministry of Industry,
|
||||||
|
* Tourism and Trade, for the support in the project TSI020301-2008-2
|
||||||
|
* "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled
|
||||||
|
* Mobile Dynamic Environments", led by Treelogic
|
||||||
|
* ( http://www.treelogic.com/ ):
|
||||||
|
*
|
||||||
|
* http://www.piramidepse.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses strings of digits that represent a RSS Extended code.
|
||||||
|
*
|
||||||
|
* @author Antonio Manuel Benjumea Conde, Servinform, S.A.
|
||||||
|
* @author Agustín Delgado, Servinform, S.A.
|
||||||
|
*/
|
||||||
|
final class ExpandedProductResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private ExpandedProductResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat all RSS EXPANDED, in the sense that they are all
|
||||||
|
// product barcodes with complementary data.
|
||||||
|
public static ExpandedProductParsedResult parse(Result result) {
|
||||||
|
BarcodeFormat format = result.getBarcodeFormat();
|
||||||
|
if (!(BarcodeFormat.RSS_EXPANDED.equals(format))) {
|
||||||
|
// ExtendedProductParsedResult NOT created. Not a RSS Expanded barcode
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Really neither of these should happen:
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null) {
|
||||||
|
// ExtendedProductParsedResult NOT created. Input text is NULL
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String productID = "-";
|
||||||
|
String sscc = "-";
|
||||||
|
String lotNumber = "-";
|
||||||
|
String productionDate = "-";
|
||||||
|
String packagingDate = "-";
|
||||||
|
String bestBeforeDate = "-";
|
||||||
|
String expirationDate = "-";
|
||||||
|
String weight = "-";
|
||||||
|
String weightType = "-";
|
||||||
|
String weightIncrement = "-";
|
||||||
|
String price = "-";
|
||||||
|
String priceIncrement = "-";
|
||||||
|
String priceCurrency = "-";
|
||||||
|
Hashtable uncommonAIs = new Hashtable();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < rawText.length()) {
|
||||||
|
String ai = findAIvalue(i, rawText);
|
||||||
|
if ("ERROR".equals(ai)) {
|
||||||
|
// Error. Code doesn't match with RSS expanded pattern
|
||||||
|
// ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
i += ai.length() + 2;
|
||||||
|
String value = findValue(i, rawText);
|
||||||
|
i += value.length();
|
||||||
|
|
||||||
|
if ("00".equals(ai)) {
|
||||||
|
sscc = value;
|
||||||
|
} else if ("01".equals(ai)) {
|
||||||
|
productID = value;
|
||||||
|
} else if ("10".equals(ai)) {
|
||||||
|
lotNumber = value;
|
||||||
|
} else if ("11".equals(ai)) {
|
||||||
|
productionDate = value;
|
||||||
|
} else if ("13".equals(ai)) {
|
||||||
|
packagingDate = value;
|
||||||
|
} else if ("15".equals(ai)) {
|
||||||
|
bestBeforeDate = value;
|
||||||
|
} else if ("17".equals(ai)) {
|
||||||
|
expirationDate = value;
|
||||||
|
} else if ("3100".equals(ai) || "3101".equals(ai)
|
||||||
|
|| "3102".equals(ai) || "3103".equals(ai)
|
||||||
|
|| "3104".equals(ai) || "3105".equals(ai)
|
||||||
|
|| "3106".equals(ai) || "3107".equals(ai)
|
||||||
|
|| "3108".equals(ai) || "3109".equals(ai)) {
|
||||||
|
weight = value;
|
||||||
|
weightType = ExpandedProductParsedResult.KILOGRAM;
|
||||||
|
weightIncrement = ai.substring(3);
|
||||||
|
} else if ("3200".equals(ai) || "3201".equals(ai)
|
||||||
|
|| "3202".equals(ai) || "3203".equals(ai)
|
||||||
|
|| "3204".equals(ai) || "3205".equals(ai)
|
||||||
|
|| "3206".equals(ai) || "3207".equals(ai)
|
||||||
|
|| "3208".equals(ai) || "3209".equals(ai)) {
|
||||||
|
weight = value;
|
||||||
|
weightType = ExpandedProductParsedResult.POUND;
|
||||||
|
weightIncrement = ai.substring(3);
|
||||||
|
} else if ("3920".equals(ai) || "3921".equals(ai)
|
||||||
|
|| "3922".equals(ai) || "3923".equals(ai)) {
|
||||||
|
price = value;
|
||||||
|
priceIncrement = ai.substring(3);
|
||||||
|
} else if ("3930".equals(ai) || "3931".equals(ai)
|
||||||
|
|| "3932".equals(ai) || "3933".equals(ai)) {
|
||||||
|
if (value.length() < 4) {
|
||||||
|
// The value must have more of 3 symbols (3 for currency and
|
||||||
|
// 1 at least for the price)
|
||||||
|
// ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
price = value.substring(3);
|
||||||
|
priceCurrency = value.substring(0, 3);
|
||||||
|
priceIncrement = ai.substring(3);
|
||||||
|
} else {
|
||||||
|
// No match with common AIs
|
||||||
|
uncommonAIs.put(ai, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExpandedProductParsedResult(productID, sscc, lotNumber,
|
||||||
|
productionDate, packagingDate, bestBeforeDate, expirationDate,
|
||||||
|
weight, weightType, weightIncrement, price, priceIncrement,
|
||||||
|
priceCurrency, uncommonAIs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String findAIvalue(int i, String rawText) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
char c = rawText.charAt(i);
|
||||||
|
// First character must be a open parenthesis.If not, ERROR
|
||||||
|
if (c != '(') {
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
|
||||||
|
String rawTextAux = rawText.substring(i + 1);
|
||||||
|
|
||||||
|
for (int index = 0; index < rawTextAux.length(); index++) {
|
||||||
|
char currentChar = rawTextAux.charAt(index);
|
||||||
|
switch (currentChar){
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
buf.append(currentChar);
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
return buf.toString();
|
||||||
|
default:
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String findValue(int i, String rawText) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
String rawTextAux = rawText.substring(i);
|
||||||
|
|
||||||
|
for (int index = 0; index < rawTextAux.length(); index++) {
|
||||||
|
char c = rawTextAux.charAt(index);
|
||||||
|
if (c == '(') {
|
||||||
|
// We look for a new AI. If it doesn't exist (ERROR), we coninue
|
||||||
|
// with the iteration
|
||||||
|
if ("ERROR".equals(findAIvalue(index, rawTextAux))) {
|
||||||
|
buf.append('(');
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
132
src/com/google/zxing/client/result/GeoParsedResult.java
Normal file
132
src/com/google/zxing/client/result/GeoParsedResult.java
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class GeoParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final double latitude;
|
||||||
|
private final double longitude;
|
||||||
|
private final double altitude;
|
||||||
|
private final String query;
|
||||||
|
|
||||||
|
GeoParsedResult(double latitude, double longitude, double altitude, String query) {
|
||||||
|
super(ParsedResultType.GEO);
|
||||||
|
this.latitude = latitude;
|
||||||
|
this.longitude = longitude;
|
||||||
|
this.altitude = altitude;
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGeoURI() {
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
result.append("geo:");
|
||||||
|
result.append(latitude);
|
||||||
|
result.append(',');
|
||||||
|
result.append(longitude);
|
||||||
|
if (altitude > 0) {
|
||||||
|
result.append(',');
|
||||||
|
result.append(altitude);
|
||||||
|
}
|
||||||
|
if (query != null) {
|
||||||
|
result.append('?');
|
||||||
|
result.append(query);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return latitude in degrees
|
||||||
|
*/
|
||||||
|
public double getLatitude() {
|
||||||
|
return latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return longitude in degrees
|
||||||
|
*/
|
||||||
|
public double getLongitude() {
|
||||||
|
return longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return altitude in meters. If not specified, in the geo URI, returns 0.0
|
||||||
|
*/
|
||||||
|
public double getAltitude() {
|
||||||
|
return altitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return query string associated with geo URI or null if none exists
|
||||||
|
*/
|
||||||
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(20);
|
||||||
|
result.append(latitude);
|
||||||
|
result.append(", ");
|
||||||
|
result.append(longitude);
|
||||||
|
if (altitude > 0.0) {
|
||||||
|
result.append(", ");
|
||||||
|
result.append(altitude);
|
||||||
|
result.append('m');
|
||||||
|
}
|
||||||
|
if (query != null) {
|
||||||
|
result.append(" (");
|
||||||
|
result.append(query);
|
||||||
|
result.append(')');
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a URI link to Google Maps which display the point on the Earth described
|
||||||
|
* by this instance, and sets the zoom level in a way that roughly reflects the
|
||||||
|
* altitude, if specified
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
public String getGoogleMapsURI() {
|
||||||
|
StringBuffer result = new StringBuffer(50);
|
||||||
|
result.append("http://maps.google.com/?ll=");
|
||||||
|
result.append(latitude);
|
||||||
|
result.append(',');
|
||||||
|
result.append(longitude);
|
||||||
|
if (altitude > 0.0f) {
|
||||||
|
// Map altitude to zoom level, cleverly. Roughly, zoom level 19 is like a
|
||||||
|
// view from 1000ft, 18 is like 2000ft, 17 like 4000ft, and so on.
|
||||||
|
double altitudeInFeet = altitude * 3.28;
|
||||||
|
int altitudeInKFeet = (int) (altitudeInFeet / 1000.0);
|
||||||
|
// No Math.log() available here, so compute log base 2 the old fashioned way
|
||||||
|
// Here logBaseTwo will take on a value between 0 and 18 actually
|
||||||
|
int logBaseTwo = 0;
|
||||||
|
while (altitudeInKFeet > 1 && logBaseTwo < 18) {
|
||||||
|
altitudeInKFeet >>= 1;
|
||||||
|
logBaseTwo++;
|
||||||
|
}
|
||||||
|
int zoom = 19 - logBaseTwo;
|
||||||
|
result.append("&z=");
|
||||||
|
result.append(zoom);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
77
src/com/google/zxing/client/result/GeoResultParser.java
Normal file
77
src/com/google/zxing/client/result/GeoResultParser.java
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a "geo:" URI result, which specifies a location on the surface of
|
||||||
|
* the Earth as well as an optional altitude above the surface. See
|
||||||
|
* <a href="http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00">
|
||||||
|
* http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00</a>.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class GeoResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private GeoResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GeoParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || (!rawText.startsWith("geo:") && !rawText.startsWith("GEO:"))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Drop geo, query portion
|
||||||
|
int queryStart = rawText.indexOf('?', 4);
|
||||||
|
String query;
|
||||||
|
String geoURIWithoutQuery;
|
||||||
|
if (queryStart < 0) {
|
||||||
|
query = null;
|
||||||
|
geoURIWithoutQuery = rawText.substring(4);
|
||||||
|
} else {
|
||||||
|
query = rawText.substring(queryStart + 1);
|
||||||
|
geoURIWithoutQuery = rawText.substring(4, queryStart);
|
||||||
|
}
|
||||||
|
int latitudeEnd = geoURIWithoutQuery.indexOf(',');
|
||||||
|
if (latitudeEnd < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int longitudeEnd = geoURIWithoutQuery.indexOf(',', latitudeEnd + 1);
|
||||||
|
double latitude, longitude, altitude;
|
||||||
|
try {
|
||||||
|
latitude = Double.parseDouble(geoURIWithoutQuery.substring(0, latitudeEnd));
|
||||||
|
if (latitude > 90.0 || latitude < -90.0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (longitudeEnd < 0) {
|
||||||
|
longitude = Double.parseDouble(geoURIWithoutQuery.substring(latitudeEnd + 1));
|
||||||
|
altitude = 0.0;
|
||||||
|
} else {
|
||||||
|
longitude = Double.parseDouble(geoURIWithoutQuery.substring(latitudeEnd + 1, longitudeEnd));
|
||||||
|
altitude = Double.parseDouble(geoURIWithoutQuery.substring(longitudeEnd + 1));
|
||||||
|
}
|
||||||
|
if (longitude > 180.0 || longitude < -180.0 || altitude < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new GeoParsedResult(latitude, longitude, altitude, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/com/google/zxing/client/result/ISBNParsedResult.java
Normal file
39
src/com/google/zxing/client/result/ISBNParsedResult.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author jbreiden@google.com (Jeff Breidenbach)
|
||||||
|
*/
|
||||||
|
public final class ISBNParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String isbn;
|
||||||
|
|
||||||
|
ISBNParsedResult(String isbn) {
|
||||||
|
super(ParsedResultType.ISBN);
|
||||||
|
this.isbn = isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getISBN() {
|
||||||
|
return isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
return isbn;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
src/com/google/zxing/client/result/ISBNResultParser.java
Normal file
54
src/com/google/zxing/client/result/ISBNResultParser.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses strings of digits that represent a ISBN.
|
||||||
|
*
|
||||||
|
* @author jbreiden@google.com (Jeff Breidenbach)
|
||||||
|
*/
|
||||||
|
public class ISBNResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private ISBNResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISBN-13 For Dummies
|
||||||
|
// http://www.bisg.org/isbn-13/for.dummies.html
|
||||||
|
public static ISBNParsedResult parse(Result result) {
|
||||||
|
BarcodeFormat format = result.getBarcodeFormat();
|
||||||
|
if (!BarcodeFormat.EAN_13.equals(format)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int length = rawText.length();
|
||||||
|
if (length != 13) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!rawText.startsWith("978") && !rawText.startsWith("979")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ISBNParsedResult(rawText);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
73
src/com/google/zxing/client/result/ParsedResult.java
Normal file
73
src/com/google/zxing/client/result/ParsedResult.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Abstract class representing the result of decoding a barcode, as more than
|
||||||
|
* a String -- as some type of structured data. This might be a subclass which represents
|
||||||
|
* a URL, or an e-mail address. {@link ResultParser#parseResult(Result)} will turn a raw
|
||||||
|
* decoded string into the most appropriate type of structured representation.</p>
|
||||||
|
*
|
||||||
|
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
|
||||||
|
* on exception-based mechanisms during parsing.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public abstract class ParsedResult {
|
||||||
|
|
||||||
|
private final ParsedResultType type;
|
||||||
|
|
||||||
|
protected ParsedResult(ParsedResultType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParsedResultType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getDisplayResult();
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return getDisplayResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void maybeAppend(String value, StringBuffer result) {
|
||||||
|
if (value != null && value.length() > 0) {
|
||||||
|
// Don't add a newline before the first value
|
||||||
|
if (result.length() > 0) {
|
||||||
|
result.append('\n');
|
||||||
|
}
|
||||||
|
result.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void maybeAppend(String[] value, StringBuffer result) {
|
||||||
|
if (value != null) {
|
||||||
|
for (int i = 0; i < value.length; i++) {
|
||||||
|
if (value[i] != null && value[i].length() > 0) {
|
||||||
|
if (result.length() > 0) {
|
||||||
|
result.append('\n');
|
||||||
|
}
|
||||||
|
result.append(value[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
src/com/google/zxing/client/result/ParsedResultType.java
Normal file
53
src/com/google/zxing/client/result/ParsedResultType.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the type of data encoded by a barcode -- from plain text, to a
|
||||||
|
* URI, to an e-mail address, etc.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class ParsedResultType {
|
||||||
|
|
||||||
|
public static final ParsedResultType ADDRESSBOOK = new ParsedResultType("ADDRESSBOOK");
|
||||||
|
public static final ParsedResultType EMAIL_ADDRESS = new ParsedResultType("EMAIL_ADDRESS");
|
||||||
|
public static final ParsedResultType PRODUCT = new ParsedResultType("PRODUCT");
|
||||||
|
public static final ParsedResultType URI = new ParsedResultType("URI");
|
||||||
|
public static final ParsedResultType TEXT = new ParsedResultType("TEXT");
|
||||||
|
public static final ParsedResultType ANDROID_INTENT = new ParsedResultType("ANDROID_INTENT");
|
||||||
|
public static final ParsedResultType GEO = new ParsedResultType("GEO");
|
||||||
|
public static final ParsedResultType TEL = new ParsedResultType("TEL");
|
||||||
|
public static final ParsedResultType SMS = new ParsedResultType("SMS");
|
||||||
|
public static final ParsedResultType CALENDAR = new ParsedResultType("CALENDAR");
|
||||||
|
public static final ParsedResultType WIFI = new ParsedResultType("WIFI");
|
||||||
|
// "optional" types
|
||||||
|
public static final ParsedResultType NDEF_SMART_POSTER = new ParsedResultType("NDEF_SMART_POSTER");
|
||||||
|
public static final ParsedResultType MOBILETAG_RICH_WEB = new ParsedResultType("MOBILETAG_RICH_WEB");
|
||||||
|
public static final ParsedResultType ISBN = new ParsedResultType("ISBN");
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private ParsedResultType(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
src/com/google/zxing/client/result/ProductParsedResult.java
Normal file
49
src/com/google/zxing/client/result/ProductParsedResult.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class ProductParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String productID;
|
||||||
|
private final String normalizedProductID;
|
||||||
|
|
||||||
|
ProductParsedResult(String productID) {
|
||||||
|
this(productID, productID);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductParsedResult(String productID, String normalizedProductID) {
|
||||||
|
super(ParsedResultType.PRODUCT);
|
||||||
|
this.productID = productID;
|
||||||
|
this.normalizedProductID = normalizedProductID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProductID() {
|
||||||
|
return productID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNormalizedProductID() {
|
||||||
|
return normalizedProductID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
return productID;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
src/com/google/zxing/client/result/ProductResultParser.java
Normal file
66
src/com/google/zxing/client/result/ProductResultParser.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.oned.UPCEReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses strings of digits that represent a UPC code.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
final class ProductResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private ProductResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat all UPC and EAN variants as UPCs, in the sense that they are all product barcodes.
|
||||||
|
public static ProductParsedResult parse(Result result) {
|
||||||
|
BarcodeFormat format = result.getBarcodeFormat();
|
||||||
|
if (!(BarcodeFormat.UPC_A.equals(format) || BarcodeFormat.UPC_E.equals(format) ||
|
||||||
|
BarcodeFormat.EAN_8.equals(format) || BarcodeFormat.EAN_13.equals(format))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Really neither of these should happen:
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = rawText.length();
|
||||||
|
for (int x = 0; x < length; x++) {
|
||||||
|
char c = rawText.charAt(x);
|
||||||
|
if (c < '0' || c > '9') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Not actually checking the checksum again here
|
||||||
|
|
||||||
|
String normalizedProductID;
|
||||||
|
// Expand UPC-E for purposes of searching
|
||||||
|
if (BarcodeFormat.UPC_E.equals(format)) {
|
||||||
|
normalizedProductID = UPCEReader.convertUPCEtoUPCA(rawText);
|
||||||
|
} else {
|
||||||
|
normalizedProductID = rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ProductParsedResult(rawText, normalizedProductID);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
319
src/com/google/zxing/client/result/ResultParser.java
Normal file
319
src/com/google/zxing/client/result/ResultParser.java
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Abstract class representing the result of decoding a barcode, as more than
|
||||||
|
* a String -- as some type of structured data. This might be a subclass which represents
|
||||||
|
* a URL, or an e-mail address. {@link #parseResult(com.google.zxing.Result)} will turn a raw
|
||||||
|
* decoded string into the most appropriate type of structured representation.</p>
|
||||||
|
*
|
||||||
|
* <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
|
||||||
|
* on exception-based mechanisms during parsing.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public abstract class ResultParser {
|
||||||
|
|
||||||
|
public static ParsedResult parseResult(Result theResult) {
|
||||||
|
// This is a bit messy, but given limited options in MIDP / CLDC, this may well be the simplest
|
||||||
|
// way to go about this. For example, we have no reflection available, really.
|
||||||
|
// Order is important here.
|
||||||
|
ParsedResult result;
|
||||||
|
if ((result = BookmarkDoCoMoResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = AddressBookDoCoMoResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = EmailDoCoMoResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = AddressBookAUResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = VCardResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = BizcardResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = VEventResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = EmailAddressResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = TelResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = SMSMMSResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = SMSTOMMSTOResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = GeoResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = WifiResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = URLTOResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = URIResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = ISBNResultParser.parse(theResult)) != null) {
|
||||||
|
// We depend on ISBN parsing coming before UPC, as it is a subset.
|
||||||
|
return result;
|
||||||
|
} else if ((result = ProductResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
} else if ((result = ExpandedProductResultParser.parse(theResult)) != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return new TextParsedResult(theResult.getText(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void maybeAppend(String value, StringBuffer result) {
|
||||||
|
if (value != null) {
|
||||||
|
result.append('\n');
|
||||||
|
result.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void maybeAppend(String[] value, StringBuffer result) {
|
||||||
|
if (value != null) {
|
||||||
|
for (int i = 0; i < value.length; i++) {
|
||||||
|
result.append('\n');
|
||||||
|
result.append(value[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String[] maybeWrap(String value) {
|
||||||
|
return value == null ? null : new String[] { value };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String unescapeBackslash(String escaped) {
|
||||||
|
if (escaped != null) {
|
||||||
|
int backslash = escaped.indexOf((int) '\\');
|
||||||
|
if (backslash >= 0) {
|
||||||
|
int max = escaped.length();
|
||||||
|
StringBuffer unescaped = new StringBuffer(max - 1);
|
||||||
|
unescaped.append(escaped.toCharArray(), 0, backslash);
|
||||||
|
boolean nextIsEscaped = false;
|
||||||
|
for (int i = backslash; i < max; i++) {
|
||||||
|
char c = escaped.charAt(i);
|
||||||
|
if (nextIsEscaped || c != '\\') {
|
||||||
|
unescaped.append(c);
|
||||||
|
nextIsEscaped = false;
|
||||||
|
} else {
|
||||||
|
nextIsEscaped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unescaped.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String urlDecode(String escaped) {
|
||||||
|
|
||||||
|
// No we can't use java.net.URLDecoder here. JavaME doesn't have it.
|
||||||
|
if (escaped == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
char[] escapedArray = escaped.toCharArray();
|
||||||
|
|
||||||
|
int first = findFirstEscape(escapedArray);
|
||||||
|
if (first < 0) {
|
||||||
|
return escaped;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max = escapedArray.length;
|
||||||
|
// final length is at most 2 less than original due to at least 1 unescaping
|
||||||
|
StringBuffer unescaped = new StringBuffer(max - 2);
|
||||||
|
// Can append everything up to first escape character
|
||||||
|
unescaped.append(escapedArray, 0, first);
|
||||||
|
|
||||||
|
for (int i = first; i < max; i++) {
|
||||||
|
char c = escapedArray[i];
|
||||||
|
if (c == '+') {
|
||||||
|
// + is translated directly into a space
|
||||||
|
unescaped.append(' ');
|
||||||
|
} else if (c == '%') {
|
||||||
|
// Are there even two more chars? if not we will just copy the escaped sequence and be done
|
||||||
|
if (i >= max - 2) {
|
||||||
|
unescaped.append('%'); // append that % and move on
|
||||||
|
} else {
|
||||||
|
int firstDigitValue = parseHexDigit(escapedArray[++i]);
|
||||||
|
int secondDigitValue = parseHexDigit(escapedArray[++i]);
|
||||||
|
if (firstDigitValue < 0 || secondDigitValue < 0) {
|
||||||
|
// bad digit, just move on
|
||||||
|
unescaped.append('%');
|
||||||
|
unescaped.append(escapedArray[i-1]);
|
||||||
|
unescaped.append(escapedArray[i]);
|
||||||
|
}
|
||||||
|
unescaped.append((char) ((firstDigitValue << 4) + secondDigitValue));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unescaped.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unescaped.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findFirstEscape(char[] escapedArray) {
|
||||||
|
int max = escapedArray.length;
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
char c = escapedArray[i];
|
||||||
|
if (c == '+' || c == '%') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseHexDigit(char c) {
|
||||||
|
if (c >= 'a') {
|
||||||
|
if (c <= 'f') {
|
||||||
|
return 10 + (c - 'a');
|
||||||
|
}
|
||||||
|
} else if (c >= 'A') {
|
||||||
|
if (c <= 'F') {
|
||||||
|
return 10 + (c - 'A');
|
||||||
|
}
|
||||||
|
} else if (c >= '0') {
|
||||||
|
if (c <= '9') {
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isStringOfDigits(String value, int length) {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int stringLength = value.length();
|
||||||
|
if (length != stringLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
if (c < '0' || c > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isSubstringOfDigits(String value, int offset, int length) {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int stringLength = value.length();
|
||||||
|
int max = offset + length;
|
||||||
|
if (stringLength < max) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = offset; i < max; i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
if (c < '0' || c > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Hashtable parseNameValuePairs(String uri) {
|
||||||
|
int paramStart = uri.indexOf('?');
|
||||||
|
if (paramStart < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Hashtable result = new Hashtable(3);
|
||||||
|
paramStart++;
|
||||||
|
int paramEnd;
|
||||||
|
while ((paramEnd = uri.indexOf('&', paramStart)) >= 0) {
|
||||||
|
appendKeyValue(uri, paramStart, paramEnd, result);
|
||||||
|
paramStart = paramEnd + 1;
|
||||||
|
}
|
||||||
|
appendKeyValue(uri, paramStart, uri.length(), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendKeyValue(String uri, int paramStart, int paramEnd, Hashtable result) {
|
||||||
|
int separator = uri.indexOf('=', paramStart);
|
||||||
|
if (separator >= 0) {
|
||||||
|
// key = value
|
||||||
|
String key = uri.substring(paramStart, separator);
|
||||||
|
String value = uri.substring(separator + 1, paramEnd);
|
||||||
|
value = urlDecode(value);
|
||||||
|
result.put(key, value);
|
||||||
|
}
|
||||||
|
// Can't put key, null into a hashtable
|
||||||
|
}
|
||||||
|
|
||||||
|
static String[] matchPrefixedField(String prefix, String rawText, char endChar, boolean trim) {
|
||||||
|
Vector matches = null;
|
||||||
|
int i = 0;
|
||||||
|
int max = rawText.length();
|
||||||
|
while (i < max) {
|
||||||
|
i = rawText.indexOf(prefix, i);
|
||||||
|
if (i < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += prefix.length(); // Skip past this prefix we found to start
|
||||||
|
int start = i; // Found the start of a match here
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
i = rawText.indexOf((int) endChar, i);
|
||||||
|
if (i < 0) {
|
||||||
|
// No terminating end character? uh, done. Set i such that loop terminates and break
|
||||||
|
i = rawText.length();
|
||||||
|
done = true;
|
||||||
|
} else if (rawText.charAt(i - 1) == '\\') {
|
||||||
|
// semicolon was escaped so continue
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
// found a match
|
||||||
|
if (matches == null) {
|
||||||
|
matches = new Vector(3); // lazy init
|
||||||
|
}
|
||||||
|
String element = unescapeBackslash(rawText.substring(start, i));
|
||||||
|
if (trim) {
|
||||||
|
element = element.trim();
|
||||||
|
}
|
||||||
|
matches.addElement(element);
|
||||||
|
i++;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches == null || matches.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return toStringArray(matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String matchSinglePrefixedField(String prefix, String rawText, char endChar, boolean trim) {
|
||||||
|
String[] matches = matchPrefixedField(prefix, rawText, endChar, trim);
|
||||||
|
return matches == null ? null : matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static String[] toStringArray(Vector strings) {
|
||||||
|
int size = strings.size();
|
||||||
|
String[] result = new String[size];
|
||||||
|
for (int j = 0; j < size; j++) {
|
||||||
|
result[j] = (String) strings.elementAt(j);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
107
src/com/google/zxing/client/result/SMSMMSResultParser.java
Normal file
107
src/com/google/zxing/client/result/SMSMMSResultParser.java
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Parses an "sms:" URI result, which specifies a number to SMS.
|
||||||
|
* See <a href="http://tools.ietf.org/html/rfc5724"> RFC 5724</a> on this.</p>
|
||||||
|
*
|
||||||
|
* <p>This class supports "via" syntax for numbers, which is not part of the spec.
|
||||||
|
* For example "+12125551212;via=+12124440101" may appear as a number.
|
||||||
|
* It also supports a "subject" query parameter, which is not mentioned in the spec.
|
||||||
|
* These are included since they were mentioned in earlier IETF drafts and might be
|
||||||
|
* used.</p>
|
||||||
|
*
|
||||||
|
* <p>This actually also parses URIs starting with "mms:" and treats them all the same way,
|
||||||
|
* and effectively converts them to an "sms:" URI for purposes of forwarding to the platform.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class SMSMMSResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private SMSMMSResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SMSParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!(rawText.startsWith("sms:") || rawText.startsWith("SMS:") ||
|
||||||
|
rawText.startsWith("mms:") || rawText.startsWith("MMS:"))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check up front if this is a URI syntax string with query arguments
|
||||||
|
Hashtable nameValuePairs = parseNameValuePairs(rawText);
|
||||||
|
String subject = null;
|
||||||
|
String body = null;
|
||||||
|
boolean querySyntax = false;
|
||||||
|
if (nameValuePairs != null && !nameValuePairs.isEmpty()) {
|
||||||
|
subject = (String) nameValuePairs.get("subject");
|
||||||
|
body = (String) nameValuePairs.get("body");
|
||||||
|
querySyntax = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop sms, query portion
|
||||||
|
int queryStart = rawText.indexOf('?', 4);
|
||||||
|
String smsURIWithoutQuery;
|
||||||
|
// If it's not query syntax, the question mark is part of the subject or message
|
||||||
|
if (queryStart < 0 || !querySyntax) {
|
||||||
|
smsURIWithoutQuery = rawText.substring(4);
|
||||||
|
} else {
|
||||||
|
smsURIWithoutQuery = rawText.substring(4, queryStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastComma = -1;
|
||||||
|
int comma;
|
||||||
|
Vector numbers = new Vector(1);
|
||||||
|
Vector vias = new Vector(1);
|
||||||
|
while ((comma = smsURIWithoutQuery.indexOf(',', lastComma + 1)) > lastComma) {
|
||||||
|
String numberPart = smsURIWithoutQuery.substring(lastComma + 1, comma);
|
||||||
|
addNumberVia(numbers, vias, numberPart);
|
||||||
|
lastComma = comma;
|
||||||
|
}
|
||||||
|
addNumberVia(numbers, vias, smsURIWithoutQuery.substring(lastComma + 1));
|
||||||
|
|
||||||
|
return new SMSParsedResult(toStringArray(numbers), toStringArray(vias), subject, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addNumberVia(Vector numbers, Vector vias, String numberPart) {
|
||||||
|
int numberEnd = numberPart.indexOf(';');
|
||||||
|
if (numberEnd < 0) {
|
||||||
|
numbers.addElement(numberPart);
|
||||||
|
vias.addElement(null);
|
||||||
|
} else {
|
||||||
|
numbers.addElement(numberPart.substring(0, numberEnd));
|
||||||
|
String maybeVia = numberPart.substring(numberEnd + 1);
|
||||||
|
String via;
|
||||||
|
if (maybeVia.startsWith("via=")) {
|
||||||
|
via = maybeVia.substring(4);
|
||||||
|
} else {
|
||||||
|
via = null;
|
||||||
|
}
|
||||||
|
vias.addElement(via);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
104
src/com/google/zxing/client/result/SMSParsedResult.java
Normal file
104
src/com/google/zxing/client/result/SMSParsedResult.java
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class SMSParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String[] numbers;
|
||||||
|
private final String[] vias;
|
||||||
|
private final String subject;
|
||||||
|
private final String body;
|
||||||
|
|
||||||
|
public SMSParsedResult(String number, String via, String subject, String body) {
|
||||||
|
super(ParsedResultType.SMS);
|
||||||
|
this.numbers = new String[] {number};
|
||||||
|
this.vias = new String[] {via};
|
||||||
|
this.subject = subject;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SMSParsedResult(String[] numbers, String[] vias, String subject, String body) {
|
||||||
|
super(ParsedResultType.SMS);
|
||||||
|
this.numbers = numbers;
|
||||||
|
this.vias = vias;
|
||||||
|
this.subject = subject;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSMSURI() {
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
result.append("sms:");
|
||||||
|
boolean first = true;
|
||||||
|
for (int i = 0; i < numbers.length; i++) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
result.append(',');
|
||||||
|
}
|
||||||
|
result.append(numbers[i]);
|
||||||
|
if (vias[i] != null) {
|
||||||
|
result.append(";via=");
|
||||||
|
result.append(vias[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean hasBody = body != null;
|
||||||
|
boolean hasSubject = subject != null;
|
||||||
|
if (hasBody || hasSubject) {
|
||||||
|
result.append('?');
|
||||||
|
if (hasBody) {
|
||||||
|
result.append("body=");
|
||||||
|
result.append(body);
|
||||||
|
}
|
||||||
|
if (hasSubject) {
|
||||||
|
if (hasBody) {
|
||||||
|
result.append('&');
|
||||||
|
}
|
||||||
|
result.append("subject=");
|
||||||
|
result.append(subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getNumbers() {
|
||||||
|
return numbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getVias() {
|
||||||
|
return vias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubject() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(100);
|
||||||
|
maybeAppend(numbers, result);
|
||||||
|
maybeAppend(subject, result);
|
||||||
|
maybeAppend(body, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Parses an "smsto:" URI result, whose format is not standardized but appears to be like:
|
||||||
|
* <code>smsto:number(:body)</code>.</p>
|
||||||
|
*
|
||||||
|
* <p>This actually also parses URIs starting with "smsto:", "mmsto:", "SMSTO:", and
|
||||||
|
* "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI
|
||||||
|
* for purposes of forwarding to the platform.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class SMSTOMMSTOResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private SMSTOMMSTOResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SMSParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!(rawText.startsWith("smsto:") || rawText.startsWith("SMSTO:") ||
|
||||||
|
rawText.startsWith("mmsto:") || rawText.startsWith("MMSTO:"))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Thanks to dominik.wild for suggesting this enhancement to support
|
||||||
|
// smsto:number:body URIs
|
||||||
|
String number = rawText.substring(6);
|
||||||
|
String body = null;
|
||||||
|
int bodyStart = number.indexOf(':');
|
||||||
|
if (bodyStart >= 0) {
|
||||||
|
body = number.substring(bodyStart + 1);
|
||||||
|
number = number.substring(0, bodyStart);
|
||||||
|
}
|
||||||
|
return new SMSParsedResult(number, null, null, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
src/com/google/zxing/client/result/TelParsedResult.java
Normal file
54
src/com/google/zxing/client/result/TelParsedResult.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class TelParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String number;
|
||||||
|
private final String telURI;
|
||||||
|
private final String title;
|
||||||
|
|
||||||
|
public TelParsedResult(String number, String telURI, String title) {
|
||||||
|
super(ParsedResultType.TEL);
|
||||||
|
this.number = number;
|
||||||
|
this.telURI = telURI;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTelURI() {
|
||||||
|
return telURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(20);
|
||||||
|
maybeAppend(number, result);
|
||||||
|
maybeAppend(title, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
44
src/com/google/zxing/client/result/TelResultParser.java
Normal file
44
src/com/google/zxing/client/result/TelResultParser.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a "tel:" URI result, which specifies a phone number.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class TelResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private TelResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TelParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || (!rawText.startsWith("tel:") && !rawText.startsWith("TEL:"))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Normalize "TEL:" to "tel:"
|
||||||
|
String telURI = rawText.startsWith("TEL:") ? "tel:" + rawText.substring(4) : rawText;
|
||||||
|
// Drop tel, query portion
|
||||||
|
int queryStart = rawText.indexOf('?', 4);
|
||||||
|
String number = queryStart < 0 ? rawText.substring(4) : rawText.substring(4, queryStart);
|
||||||
|
return new TelParsedResult(number, telURI, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
src/com/google/zxing/client/result/TextParsedResult.java
Normal file
48
src/com/google/zxing/client/result/TextParsedResult.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple result type encapsulating a string that has no further
|
||||||
|
* interpretation.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class TextParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
private final String language;
|
||||||
|
|
||||||
|
public TextParsedResult(String text, String language) {
|
||||||
|
super(ParsedResultType.TEXT);
|
||||||
|
this.text = text;
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
113
src/com/google/zxing/client/result/URIParsedResult.java
Normal file
113
src/com/google/zxing/client/result/URIParsedResult.java
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class URIParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
private final String uri;
|
||||||
|
private final String title;
|
||||||
|
|
||||||
|
public URIParsedResult(String uri, String title) {
|
||||||
|
super(ParsedResultType.URI);
|
||||||
|
this.uri = massageURI(uri);
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getURI() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the URI contains suspicious patterns that may suggest it intends to
|
||||||
|
* mislead the user about its true nature. At the moment this looks for the presence
|
||||||
|
* of user/password syntax in the host/authority portion of a URI which may be used
|
||||||
|
* in attempts to make the URI's host appear to be other than it is. Example:
|
||||||
|
* http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
|
||||||
|
* to connect to yourbank.com at first glance.
|
||||||
|
*/
|
||||||
|
public boolean isPossiblyMaliciousURI() {
|
||||||
|
return containsUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsUser() {
|
||||||
|
// This method is likely not 100% RFC compliant yet
|
||||||
|
int hostStart = uri.indexOf(':'); // we should always have scheme at this point
|
||||||
|
hostStart++;
|
||||||
|
// Skip slashes preceding host
|
||||||
|
int uriLength = uri.length();
|
||||||
|
while (hostStart < uriLength && uri.charAt(hostStart) == '/') {
|
||||||
|
hostStart++;
|
||||||
|
}
|
||||||
|
int hostEnd = uri.indexOf('/', hostStart);
|
||||||
|
if (hostEnd < 0) {
|
||||||
|
hostEnd = uriLength;
|
||||||
|
}
|
||||||
|
int at = uri.indexOf('@', hostStart);
|
||||||
|
return at >= hostStart && at < hostEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(30);
|
||||||
|
maybeAppend(title, result);
|
||||||
|
maybeAppend(uri, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a string that represents a URI into something more proper, by adding or canonicalizing
|
||||||
|
* the protocol.
|
||||||
|
*/
|
||||||
|
private static String massageURI(String uri) {
|
||||||
|
int protocolEnd = uri.indexOf(':');
|
||||||
|
if (protocolEnd < 0) {
|
||||||
|
// No protocol, assume http
|
||||||
|
uri = "http://" + uri;
|
||||||
|
} else if (isColonFollowedByPortNumber(uri, protocolEnd)) {
|
||||||
|
// Found a colon, but it looks like it is after the host, so the protocol is still missing
|
||||||
|
uri = "http://" + uri;
|
||||||
|
} else {
|
||||||
|
// Lowercase protocol to avoid problems
|
||||||
|
uri = uri.substring(0, protocolEnd).toLowerCase() + uri.substring(protocolEnd);
|
||||||
|
}
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isColonFollowedByPortNumber(String uri, int protocolEnd) {
|
||||||
|
int nextSlash = uri.indexOf('/', protocolEnd + 1);
|
||||||
|
if (nextSlash < 0) {
|
||||||
|
nextSlash = uri.length();
|
||||||
|
}
|
||||||
|
if (nextSlash <= protocolEnd + 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int x = protocolEnd + 1; x < nextSlash; x++) {
|
||||||
|
if (uri.charAt(x) < '0' || uri.charAt(x) > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
87
src/com/google/zxing/client/result/URIResultParser.java
Normal file
87
src/com/google/zxing/client/result/URIResultParser.java
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to parse results that are a URI of some kind.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class URIResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private URIResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URIParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
// We specifically handle the odd "URL" scheme here for simplicity
|
||||||
|
if (rawText != null && rawText.startsWith("URL:")) {
|
||||||
|
rawText = rawText.substring(4);
|
||||||
|
}
|
||||||
|
if (!isBasicallyValidURI(rawText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new URIParsedResult(rawText, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a string is not obviously not a URI. This implements crude checks; this class does not
|
||||||
|
* intend to strictly check URIs as its only function is to represent what is in a barcode, but, it does
|
||||||
|
* need to know when a string is obviously not a URI.
|
||||||
|
*/
|
||||||
|
static boolean isBasicallyValidURI(String uri) {
|
||||||
|
if (uri == null || uri.indexOf(' ') >= 0 || uri.indexOf('\n') >= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Look for period in a domain but followed by at least a two-char TLD
|
||||||
|
// Forget strings that don't have a valid-looking protocol
|
||||||
|
int period = uri.indexOf('.');
|
||||||
|
if (period >= uri.length() - 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int colon = uri.indexOf(':');
|
||||||
|
if (period < 0 && colon < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (colon >= 0) {
|
||||||
|
if (period < 0 || period > colon) {
|
||||||
|
// colon ends the protocol
|
||||||
|
for (int i = 0; i < colon; i++) {
|
||||||
|
char c = uri.charAt(i);
|
||||||
|
if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// colon starts the port; crudely look for at least two numbers
|
||||||
|
if (colon >= uri.length() - 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = colon + 1; i < colon + 3; i++) {
|
||||||
|
char c = uri.charAt(i);
|
||||||
|
if (c < '0' || c > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
src/com/google/zxing/client/result/URLTOResultParser.java
Normal file
47
src/com/google/zxing/client/result/URLTOResultParser.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the "URLTO" result format, which is of the form "URLTO:[title]:[url]".
|
||||||
|
* This seems to be used sometimes, but I am not able to find documentation
|
||||||
|
* on its origin or official format?
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class URLTOResultParser {
|
||||||
|
|
||||||
|
private URLTOResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static URIParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || (!rawText.startsWith("urlto:") && !rawText.startsWith("URLTO:"))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int titleEnd = rawText.indexOf(':', 6);
|
||||||
|
if (titleEnd < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String title = titleEnd <= 6 ? null : rawText.substring(6, titleEnd);
|
||||||
|
String uri = rawText.substring(titleEnd + 1);
|
||||||
|
return new URIParsedResult(uri, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
346
src/com/google/zxing/client/result/VCardResultParser.java
Normal file
346
src/com/google/zxing/client/result/VCardResultParser.java
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses contact information formatted according to the VCard (2.1) format. This is not a complete
|
||||||
|
* implementation but should parse information as commonly encoded in 2D barcodes.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class VCardResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private VCardResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AddressBookParsedResult parse(Result result) {
|
||||||
|
// Although we should insist on the raw text ending with "END:VCARD", there's no reason
|
||||||
|
// to throw out everything else we parsed just because this was omitted. In fact, Eclair
|
||||||
|
// is doing just that, and we can't parse its contacts without this leniency.
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null || !rawText.startsWith("BEGIN:VCARD")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] names = matchVCardPrefixedField("FN", rawText, true);
|
||||||
|
if (names == null) {
|
||||||
|
// If no display names found, look for regular name fields and format them
|
||||||
|
names = matchVCardPrefixedField("N", rawText, true);
|
||||||
|
formatNames(names);
|
||||||
|
}
|
||||||
|
String[] phoneNumbers = matchVCardPrefixedField("TEL", rawText, true);
|
||||||
|
String[] emails = matchVCardPrefixedField("EMAIL", rawText, true);
|
||||||
|
String note = matchSingleVCardPrefixedField("NOTE", rawText, false);
|
||||||
|
String[] addresses = matchVCardPrefixedField("ADR", rawText, true);
|
||||||
|
if (addresses != null) {
|
||||||
|
for (int i = 0; i < addresses.length; i++) {
|
||||||
|
addresses[i] = formatAddress(addresses[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String org = matchSingleVCardPrefixedField("ORG", rawText, true);
|
||||||
|
String birthday = matchSingleVCardPrefixedField("BDAY", rawText, true);
|
||||||
|
if (!isLikeVCardDate(birthday)) {
|
||||||
|
birthday = null;
|
||||||
|
}
|
||||||
|
String title = matchSingleVCardPrefixedField("TITLE", rawText, true);
|
||||||
|
String url = matchSingleVCardPrefixedField("URL", rawText, true);
|
||||||
|
return new AddressBookParsedResult(names, null, phoneNumbers, emails, note, addresses, org,
|
||||||
|
birthday, title, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] matchVCardPrefixedField(String prefix, String rawText, boolean trim) {
|
||||||
|
Vector matches = null;
|
||||||
|
int i = 0;
|
||||||
|
int max = rawText.length();
|
||||||
|
|
||||||
|
while (i < max) {
|
||||||
|
|
||||||
|
i = rawText.indexOf(prefix, i);
|
||||||
|
if (i < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 0 && rawText.charAt(i - 1) != '\n') {
|
||||||
|
// then this didn't start a new token, we matched in the middle of something
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i += prefix.length(); // Skip past this prefix we found to start
|
||||||
|
if (rawText.charAt(i) != ':' && rawText.charAt(i) != ';') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadataStart = i;
|
||||||
|
while (rawText.charAt(i) != ':') { // Skip until a colon
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean quotedPrintable = false;
|
||||||
|
String quotedPrintableCharset = null;
|
||||||
|
if (i > metadataStart) {
|
||||||
|
// There was something after the tag, before colon
|
||||||
|
int j = metadataStart+1;
|
||||||
|
while (j <= i) {
|
||||||
|
if (rawText.charAt(j) == ';' || rawText.charAt(j) == ':') {
|
||||||
|
String metadata = rawText.substring(metadataStart+1, j);
|
||||||
|
int equals = metadata.indexOf('=');
|
||||||
|
if (equals >= 0) {
|
||||||
|
String key = metadata.substring(0, equals);
|
||||||
|
String value = metadata.substring(equals+1);
|
||||||
|
if (key.equalsIgnoreCase("ENCODING")) {
|
||||||
|
if (value.equalsIgnoreCase("QUOTED-PRINTABLE")) {
|
||||||
|
quotedPrintable = true;
|
||||||
|
}
|
||||||
|
} else if (key.equalsIgnoreCase("CHARSET")) {
|
||||||
|
quotedPrintableCharset = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metadataStart = j;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++; // skip colon
|
||||||
|
|
||||||
|
int matchStart = i; // Found the start of a match here
|
||||||
|
|
||||||
|
while ((i = rawText.indexOf((int) '\n', i)) >= 0) { // Really, end in \r\n
|
||||||
|
if (i < rawText.length() - 1 && // But if followed by tab or space,
|
||||||
|
(rawText.charAt(i+1) == ' ' || // this is only a continuation
|
||||||
|
rawText.charAt(i+1) == '\t')) {
|
||||||
|
i += 2; // Skip \n and continutation whitespace
|
||||||
|
} else if (quotedPrintable && // If preceded by = in quoted printable
|
||||||
|
(rawText.charAt(i-1) == '=' || // this is a continuation
|
||||||
|
rawText.charAt(i-2) == '=')) {
|
||||||
|
i++; // Skip \n
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < 0) {
|
||||||
|
// No terminating end character? uh, done. Set i such that loop terminates and break
|
||||||
|
i = max;
|
||||||
|
} else if (i > matchStart) {
|
||||||
|
// found a match
|
||||||
|
if (matches == null) {
|
||||||
|
matches = new Vector(1); // lazy init
|
||||||
|
}
|
||||||
|
if (rawText.charAt(i-1) == '\r') {
|
||||||
|
i--; // Back up over \r, which really should be there
|
||||||
|
}
|
||||||
|
String element = rawText.substring(matchStart, i);
|
||||||
|
if (trim) {
|
||||||
|
element = element.trim();
|
||||||
|
}
|
||||||
|
if (quotedPrintable) {
|
||||||
|
element = decodeQuotedPrintable(element, quotedPrintableCharset);
|
||||||
|
} else {
|
||||||
|
element = stripContinuationCRLF(element);
|
||||||
|
}
|
||||||
|
matches.addElement(element);
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches == null || matches.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return toStringArray(matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripContinuationCRLF(String value) {
|
||||||
|
int length = value.length();
|
||||||
|
StringBuffer result = new StringBuffer(length);
|
||||||
|
boolean lastWasLF = false;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (lastWasLF) {
|
||||||
|
lastWasLF = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char c = value.charAt(i);
|
||||||
|
lastWasLF = false;
|
||||||
|
switch (c) {
|
||||||
|
case '\n':
|
||||||
|
lastWasLF = true;
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String decodeQuotedPrintable(String value, String charset) {
|
||||||
|
int length = value.length();
|
||||||
|
StringBuffer result = new StringBuffer(length);
|
||||||
|
ByteArrayOutputStream fragmentBuffer = new ByteArrayOutputStream();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
char c = value.charAt(i);
|
||||||
|
switch (c) {
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (i < length - 2) {
|
||||||
|
char nextChar = value.charAt(i+1);
|
||||||
|
if (nextChar == '\r' || nextChar == '\n') {
|
||||||
|
// Ignore, it's just a continuation symbol
|
||||||
|
} else {
|
||||||
|
char nextNextChar = value.charAt(i+2);
|
||||||
|
try {
|
||||||
|
int encodedByte = 16 * toHexValue(nextChar) + toHexValue(nextNextChar);
|
||||||
|
fragmentBuffer.write(encodedByte);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
// continue, assume it was incorrectly encoded
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
maybeAppendFragment(fragmentBuffer, charset, result);
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maybeAppendFragment(fragmentBuffer, charset, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int toHexValue(char c) {
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
return c - '0';
|
||||||
|
} else if (c >= 'A' && c <= 'F') {
|
||||||
|
return c - 'A' + 10;
|
||||||
|
} else if (c >= 'a' && c <= 'f') {
|
||||||
|
return c - 'a' + 10;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void maybeAppendFragment(ByteArrayOutputStream fragmentBuffer,
|
||||||
|
String charset,
|
||||||
|
StringBuffer result) {
|
||||||
|
if (fragmentBuffer.size() > 0) {
|
||||||
|
byte[] fragmentBytes = fragmentBuffer.toByteArray();
|
||||||
|
String fragment;
|
||||||
|
if (charset == null) {
|
||||||
|
fragment = new String(fragmentBytes);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
fragment = new String(fragmentBytes, charset);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
// Yikes, well try anyway:
|
||||||
|
fragment = new String(fragmentBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fragmentBuffer.reset();
|
||||||
|
result.append(fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String matchSingleVCardPrefixedField(String prefix, String rawText, boolean trim) {
|
||||||
|
String[] values = matchVCardPrefixedField(prefix, rawText, trim);
|
||||||
|
return values == null ? null : values[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isLikeVCardDate(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Not really sure this is true but matches practice
|
||||||
|
// Mach YYYYMMDD
|
||||||
|
if (isStringOfDigits(value, 8)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// or YYYY-MM-DD
|
||||||
|
return
|
||||||
|
value.length() == 10 &&
|
||||||
|
value.charAt(4) == '-' &&
|
||||||
|
value.charAt(7) == '-' &&
|
||||||
|
isSubstringOfDigits(value, 0, 4) &&
|
||||||
|
isSubstringOfDigits(value, 5, 2) &&
|
||||||
|
isSubstringOfDigits(value, 8, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatAddress(String address) {
|
||||||
|
if (address == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int length = address.length();
|
||||||
|
StringBuffer newAddress = new StringBuffer(length);
|
||||||
|
for (int j = 0; j < length; j++) {
|
||||||
|
char c = address.charAt(j);
|
||||||
|
if (c == ';') {
|
||||||
|
newAddress.append(' ');
|
||||||
|
} else {
|
||||||
|
newAddress.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newAddress.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like
|
||||||
|
* "Reverend John Q. Public III".
|
||||||
|
*
|
||||||
|
* @param names name values to format, in place
|
||||||
|
*/
|
||||||
|
private static void formatNames(String[] names) {
|
||||||
|
if (names != null) {
|
||||||
|
for (int i = 0; i < names.length; i++) {
|
||||||
|
String name = names[i];
|
||||||
|
String[] components = new String[5];
|
||||||
|
int start = 0;
|
||||||
|
int end;
|
||||||
|
int componentIndex = 0;
|
||||||
|
while ((end = name.indexOf(';', start)) > 0) {
|
||||||
|
components[componentIndex] = name.substring(start, end);
|
||||||
|
componentIndex++;
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
components[componentIndex] = name.substring(start);
|
||||||
|
StringBuffer newName = new StringBuffer(100);
|
||||||
|
maybeAppendComponent(components, 3, newName);
|
||||||
|
maybeAppendComponent(components, 1, newName);
|
||||||
|
maybeAppendComponent(components, 2, newName);
|
||||||
|
maybeAppendComponent(components, 0, newName);
|
||||||
|
maybeAppendComponent(components, 4, newName);
|
||||||
|
names[i] = newName.toString().trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void maybeAppendComponent(String[] components, int i, StringBuffer newName) {
|
||||||
|
if (components[i] != null) {
|
||||||
|
newName.append(' ');
|
||||||
|
newName.append(components[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
src/com/google/zxing/client/result/VEventResultParser.java
Normal file
54
src/com/google/zxing/client/result/VEventResultParser.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partially implements the iCalendar format's "VEVENT" format for specifying a
|
||||||
|
* calendar event. See RFC 2445. This supports SUMMARY, LOCATION, DTSTART and DTEND fields.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class VEventResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private VEventResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CalendarParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
if (rawText == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int vEventStart = rawText.indexOf("BEGIN:VEVENT");
|
||||||
|
if (vEventStart < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String summary = VCardResultParser.matchSingleVCardPrefixedField("SUMMARY", rawText, true);
|
||||||
|
String start = VCardResultParser.matchSingleVCardPrefixedField("DTSTART", rawText, true);
|
||||||
|
String end = VCardResultParser.matchSingleVCardPrefixedField("DTEND", rawText, true);
|
||||||
|
String location = VCardResultParser.matchSingleVCardPrefixedField("LOCATION", rawText, true);
|
||||||
|
String description = VCardResultParser.matchSingleVCardPrefixedField("DESCRIPTION", rawText, true);
|
||||||
|
try {
|
||||||
|
return new CalendarParsedResult(summary, start, end, location, null, description);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
src/com/google/zxing/client/result/WifiParsedResult.java
Normal file
53
src/com/google/zxing/client/result/WifiParsedResult.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vikram Aggarwal
|
||||||
|
*/
|
||||||
|
public final class WifiParsedResult extends ParsedResult {
|
||||||
|
private final String ssid;
|
||||||
|
private final String networkEncryption;
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
public WifiParsedResult(String networkEncryption, String ssid, String password) {
|
||||||
|
super(ParsedResultType.WIFI);
|
||||||
|
this.ssid = ssid;
|
||||||
|
this.networkEncryption = networkEncryption;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSsid() {
|
||||||
|
return ssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNetworkEncryption() {
|
||||||
|
return networkEncryption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
StringBuffer result = new StringBuffer(80);
|
||||||
|
maybeAppend(ssid, result);
|
||||||
|
maybeAppend(networkEncryption, result);
|
||||||
|
maybeAppend(password, result);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
50
src/com/google/zxing/client/result/WifiResultParser.java
Normal file
50
src/com/google/zxing/client/result/WifiResultParser.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a WIFI configuration string. Strings will be of the form:
|
||||||
|
* WIFI:T:WPA;S:mynetwork;P:mypass;;
|
||||||
|
*
|
||||||
|
* The fields can come in any order, and there should be tests to see
|
||||||
|
* if we can parse them all correctly.
|
||||||
|
*
|
||||||
|
* @author Vikram Aggarwal
|
||||||
|
*/
|
||||||
|
final class WifiResultParser extends ResultParser {
|
||||||
|
|
||||||
|
private WifiResultParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WifiParsedResult parse(Result result) {
|
||||||
|
String rawText = result.getText();
|
||||||
|
|
||||||
|
if (rawText == null || !rawText.startsWith("WIFI:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't remove leading or trailing whitespace
|
||||||
|
boolean trim = false;
|
||||||
|
String ssid = matchSinglePrefixedField("S:", rawText, ';', trim);
|
||||||
|
String pass = matchSinglePrefixedField("P:", rawText, ';', trim);
|
||||||
|
String type = matchSinglePrefixedField("T:", rawText, ';', trim);
|
||||||
|
|
||||||
|
return new WifiParsedResult(type, ssid, pass);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result.optional;
|
||||||
|
|
||||||
|
import com.google.zxing.client.result.ResultParser;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Superclass for classes encapsulating results in the NDEF format.
|
||||||
|
* See <a href="http://www.nfc-forum.org/specs/">http://www.nfc-forum.org/specs/</a>.</p>
|
||||||
|
*
|
||||||
|
* <p>This code supports a limited subset of NDEF messages, ones that are plausibly
|
||||||
|
* useful in 2D barcode formats. This generally includes 1-record messages, no chunking,
|
||||||
|
* "short record" syntax, no ID field.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
abstract class AbstractNDEFResultParser extends ResultParser {
|
||||||
|
|
||||||
|
static String bytesToString(byte[] bytes, int offset, int length, String encoding) {
|
||||||
|
try {
|
||||||
|
return new String(bytes, offset, length, encoding);
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
// This should only be used when 'encoding' is an encoding that must necessarily
|
||||||
|
// be supported by the JVM, like UTF-8
|
||||||
|
throw new RuntimeException("Platform does not support required encoding: " + uee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
87
src/com/google/zxing/client/result/optional/NDEFRecord.java
Normal file
87
src/com/google/zxing/client/result/optional/NDEFRecord.java
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result.optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Represents a record in an NDEF message. This class only supports certain types
|
||||||
|
* of records -- namely, non-chunked records, where ID length is omitted, and only
|
||||||
|
* "short records".</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class NDEFRecord {
|
||||||
|
|
||||||
|
private static final int SUPPORTED_HEADER_MASK = 0x3F; // 0 0 1 1 1 111 (the bottom 6 bits matter)
|
||||||
|
private static final int SUPPORTED_HEADER = 0x11; // 0 0 0 1 0 001
|
||||||
|
|
||||||
|
public static final String TEXT_WELL_KNOWN_TYPE = "T";
|
||||||
|
public static final String URI_WELL_KNOWN_TYPE = "U";
|
||||||
|
public static final String SMART_POSTER_WELL_KNOWN_TYPE = "Sp";
|
||||||
|
public static final String ACTION_WELL_KNOWN_TYPE = "act";
|
||||||
|
|
||||||
|
private final int header;
|
||||||
|
private final String type;
|
||||||
|
private final byte[] payload;
|
||||||
|
private final int totalRecordLength;
|
||||||
|
|
||||||
|
private NDEFRecord(int header, String type, byte[] payload, int totalRecordLength) {
|
||||||
|
this.header = header;
|
||||||
|
this.type = type;
|
||||||
|
this.payload = payload;
|
||||||
|
this.totalRecordLength = totalRecordLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NDEFRecord readRecord(byte[] bytes, int offset) {
|
||||||
|
int header = bytes[offset] & 0xFF;
|
||||||
|
// Does header match what we support in the bits we care about?
|
||||||
|
// XOR figures out where we differ, and if any of those are in the mask, fail
|
||||||
|
if (((header ^ SUPPORTED_HEADER) & SUPPORTED_HEADER_MASK) != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int typeLength = bytes[offset + 1] & 0xFF;
|
||||||
|
|
||||||
|
int payloadLength = bytes[offset + 2] & 0xFF;
|
||||||
|
|
||||||
|
String type = AbstractNDEFResultParser.bytesToString(bytes, offset + 3, typeLength, "US-ASCII");
|
||||||
|
|
||||||
|
byte[] payload = new byte[payloadLength];
|
||||||
|
System.arraycopy(bytes, offset + 3 + typeLength, payload, 0, payloadLength);
|
||||||
|
|
||||||
|
return new NDEFRecord(header, type, payload, 3 + typeLength + payloadLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMessageBegin() {
|
||||||
|
return (header & 0x80) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMessageEnd() {
|
||||||
|
return (header & 0x40) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTotalRecordLength() {
|
||||||
|
return totalRecordLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result.optional;
|
||||||
|
|
||||||
|
import com.google.zxing.client.result.ParsedResult;
|
||||||
|
import com.google.zxing.client.result.ParsedResultType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class NDEFSmartPosterParsedResult extends ParsedResult {
|
||||||
|
|
||||||
|
public static final int ACTION_UNSPECIFIED = -1;
|
||||||
|
public static final int ACTION_DO = 0;
|
||||||
|
public static final int ACTION_SAVE = 1;
|
||||||
|
public static final int ACTION_OPEN = 2;
|
||||||
|
|
||||||
|
private final String title;
|
||||||
|
private final String uri;
|
||||||
|
private final int action;
|
||||||
|
|
||||||
|
NDEFSmartPosterParsedResult(int action, String uri, String title) {
|
||||||
|
super(ParsedResultType.NDEF_SMART_POSTER);
|
||||||
|
this.action = action;
|
||||||
|
this.uri = uri;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getURI() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayResult() {
|
||||||
|
if (title == null) {
|
||||||
|
return uri;
|
||||||
|
} else {
|
||||||
|
return title + '\n' + uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result.optional;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Recognizes an NDEF message that encodes information according to the
|
||||||
|
* "Smart Poster Record Type Definition" specification.</p>
|
||||||
|
*
|
||||||
|
* <p>This actually only supports some parts of the Smart Poster format: title,
|
||||||
|
* URI, and action records. Icon records are not supported because the size
|
||||||
|
* of these records are infeasibly large for barcodes. Size and type records
|
||||||
|
* are not supported. Multiple titles are not supported.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class NDEFSmartPosterResultParser extends AbstractNDEFResultParser {
|
||||||
|
|
||||||
|
public static NDEFSmartPosterParsedResult parse(Result result) {
|
||||||
|
byte[] bytes = result.getRawBytes();
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
NDEFRecord headerRecord = NDEFRecord.readRecord(bytes, 0);
|
||||||
|
// Yes, header record starts and ends a message
|
||||||
|
if (headerRecord == null || !headerRecord.isMessageBegin() || !headerRecord.isMessageEnd()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!headerRecord.getType().equals(NDEFRecord.SMART_POSTER_WELL_KNOWN_TYPE)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
int recordNumber = 0;
|
||||||
|
NDEFRecord ndefRecord = null;
|
||||||
|
byte[] payload = headerRecord.getPayload();
|
||||||
|
int action = NDEFSmartPosterParsedResult.ACTION_UNSPECIFIED;
|
||||||
|
String title = null;
|
||||||
|
String uri = null;
|
||||||
|
|
||||||
|
while (offset < payload.length && (ndefRecord = NDEFRecord.readRecord(payload, offset)) != null) {
|
||||||
|
if (recordNumber == 0 && !ndefRecord.isMessageBegin()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String type = ndefRecord.getType();
|
||||||
|
if (NDEFRecord.TEXT_WELL_KNOWN_TYPE.equals(type)) {
|
||||||
|
String[] languageText = NDEFTextResultParser.decodeTextPayload(ndefRecord.getPayload());
|
||||||
|
title = languageText[1];
|
||||||
|
} else if (NDEFRecord.URI_WELL_KNOWN_TYPE.equals(type)) {
|
||||||
|
uri = NDEFURIResultParser.decodeURIPayload(ndefRecord.getPayload());
|
||||||
|
} else if (NDEFRecord.ACTION_WELL_KNOWN_TYPE.equals(type)) {
|
||||||
|
action = ndefRecord.getPayload()[0];
|
||||||
|
}
|
||||||
|
recordNumber++;
|
||||||
|
offset += ndefRecord.getTotalRecordLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordNumber == 0 || (ndefRecord != null && !ndefRecord.isMessageEnd())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NDEFSmartPosterParsedResult(action, uri, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result.optional;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.client.result.TextParsedResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recognizes an NDEF message that encodes text according to the
|
||||||
|
* "Text Record Type Definition" specification.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class NDEFTextResultParser extends AbstractNDEFResultParser {
|
||||||
|
|
||||||
|
public static TextParsedResult parse(Result result) {
|
||||||
|
byte[] bytes = result.getRawBytes();
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
NDEFRecord ndefRecord = NDEFRecord.readRecord(bytes, 0);
|
||||||
|
if (ndefRecord == null || !ndefRecord.isMessageBegin() || !ndefRecord.isMessageEnd()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!ndefRecord.getType().equals(NDEFRecord.TEXT_WELL_KNOWN_TYPE)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] languageText = decodeTextPayload(ndefRecord.getPayload());
|
||||||
|
return new TextParsedResult(languageText[0], languageText[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String[] decodeTextPayload(byte[] payload) {
|
||||||
|
byte statusByte = payload[0];
|
||||||
|
boolean isUTF16 = (statusByte & 0x80) != 0;
|
||||||
|
int languageLength = statusByte & 0x1F;
|
||||||
|
// language is always ASCII-encoded:
|
||||||
|
String language = bytesToString(payload, 1, languageLength, "US-ASCII");
|
||||||
|
String encoding = isUTF16 ? "UTF-16" : "UTF8";
|
||||||
|
String text = bytesToString(payload, 1 + languageLength, payload.length - languageLength - 1, encoding);
|
||||||
|
return new String[] { language, text };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.client.result.optional;
|
||||||
|
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.client.result.URIParsedResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recognizes an NDEF message that encodes a URI according to the
|
||||||
|
* "URI Record Type Definition" specification.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class NDEFURIResultParser extends AbstractNDEFResultParser {
|
||||||
|
|
||||||
|
private static final String[] URI_PREFIXES = {
|
||||||
|
null,
|
||||||
|
"http://www.",
|
||||||
|
"https://www.",
|
||||||
|
"http://",
|
||||||
|
"https://",
|
||||||
|
"tel:",
|
||||||
|
"mailto:",
|
||||||
|
"ftp://anonymous:anonymous@",
|
||||||
|
"ftp://ftp.",
|
||||||
|
"ftps://",
|
||||||
|
"sftp://",
|
||||||
|
"smb://",
|
||||||
|
"nfs://",
|
||||||
|
"ftp://",
|
||||||
|
"dav://",
|
||||||
|
"news:",
|
||||||
|
"telnet://",
|
||||||
|
"imap:",
|
||||||
|
"rtsp://",
|
||||||
|
"urn:",
|
||||||
|
"pop:",
|
||||||
|
"sip:",
|
||||||
|
"sips:",
|
||||||
|
"tftp:",
|
||||||
|
"btspp://",
|
||||||
|
"btl2cap://",
|
||||||
|
"btgoep://",
|
||||||
|
"tcpobex://",
|
||||||
|
"irdaobex://",
|
||||||
|
"file://",
|
||||||
|
"urn:epc:id:",
|
||||||
|
"urn:epc:tag:",
|
||||||
|
"urn:epc:pat:",
|
||||||
|
"urn:epc:raw:",
|
||||||
|
"urn:epc:",
|
||||||
|
"urn:nfc:",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static URIParsedResult parse(Result result) {
|
||||||
|
byte[] bytes = result.getRawBytes();
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
NDEFRecord ndefRecord = NDEFRecord.readRecord(bytes, 0);
|
||||||
|
if (ndefRecord == null || !ndefRecord.isMessageBegin() || !ndefRecord.isMessageEnd()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!ndefRecord.getType().equals(NDEFRecord.URI_WELL_KNOWN_TYPE)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String fullURI = decodeURIPayload(ndefRecord.getPayload());
|
||||||
|
return new URIParsedResult(fullURI, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String decodeURIPayload(byte[] payload) {
|
||||||
|
int identifierCode = payload[0] & 0xFF;
|
||||||
|
String prefix = null;
|
||||||
|
if (identifierCode < URI_PREFIXES.length) {
|
||||||
|
prefix = URI_PREFIXES[identifierCode];
|
||||||
|
}
|
||||||
|
String restOfURI = bytesToString(payload, 1, payload.length - 1, "UTF8");
|
||||||
|
return prefix == null ? restOfURI : prefix + restOfURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
247
src/com/google/zxing/common/BitArray.java
Normal file
247
src/com/google/zxing/common/BitArray.java
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class BitArray {
|
||||||
|
|
||||||
|
// TODO: I have changed these members to be public so ProGuard can inline get() and set(). Ideally
|
||||||
|
// they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the
|
||||||
|
// resulting binary at runtime on Android. If we find a solution to this, these should be changed
|
||||||
|
// back to private.
|
||||||
|
public int[] bits;
|
||||||
|
public int size;
|
||||||
|
|
||||||
|
public BitArray() {
|
||||||
|
this.size = 0;
|
||||||
|
this.bits = new int[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitArray(int size) {
|
||||||
|
this.size = size;
|
||||||
|
this.bits = makeArray(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSizeInBytes() {
|
||||||
|
return (size + 7) >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureCapacity(int size) {
|
||||||
|
if (size > bits.length << 5) {
|
||||||
|
int[] newBits = makeArray(size);
|
||||||
|
System.arraycopy(bits, 0, newBits, 0, bits.length);
|
||||||
|
this.bits = newBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param i bit to get
|
||||||
|
* @return true iff bit i is set
|
||||||
|
*/
|
||||||
|
public boolean get(int i) {
|
||||||
|
return (bits[i >> 5] & (1 << (i & 0x1F))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets bit i.
|
||||||
|
*
|
||||||
|
* @param i bit to set
|
||||||
|
*/
|
||||||
|
public void set(int i) {
|
||||||
|
bits[i >> 5] |= 1 << (i & 0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flips bit i.
|
||||||
|
*
|
||||||
|
* @param i bit to set
|
||||||
|
*/
|
||||||
|
public void flip(int i) {
|
||||||
|
bits[i >> 5] ^= 1 << (i & 0x1F);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a block of 32 bits, starting at bit i.
|
||||||
|
*
|
||||||
|
* @param i first bit to set
|
||||||
|
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
|
||||||
|
* corresponds to bit i, the next-least-significant to i+1, and so on.
|
||||||
|
*/
|
||||||
|
public void setBulk(int i, int newBits) {
|
||||||
|
bits[i >> 5] = newBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all bits (sets to false).
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
int max = bits.length;
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
bits[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Efficient method to check if a range of bits is set, or not set.
|
||||||
|
*
|
||||||
|
* @param start start of range, inclusive.
|
||||||
|
* @param end end of range, exclusive
|
||||||
|
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
|
||||||
|
* @return true iff all bits are set or not set in range, according to value argument
|
||||||
|
* @throws IllegalArgumentException if end is less than or equal to start
|
||||||
|
*/
|
||||||
|
public boolean isRange(int start, int end, boolean value) {
|
||||||
|
if (end < start) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (end == start) {
|
||||||
|
return true; // empty range matches
|
||||||
|
}
|
||||||
|
end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||||
|
int firstInt = start >> 5;
|
||||||
|
int lastInt = end >> 5;
|
||||||
|
for (int i = firstInt; i <= lastInt; i++) {
|
||||||
|
int firstBit = i > firstInt ? 0 : start & 0x1F;
|
||||||
|
int lastBit = i < lastInt ? 31 : end & 0x1F;
|
||||||
|
int mask;
|
||||||
|
if (firstBit == 0 && lastBit == 31) {
|
||||||
|
mask = -1;
|
||||||
|
} else {
|
||||||
|
mask = 0;
|
||||||
|
for (int j = firstBit; j <= lastBit; j++) {
|
||||||
|
mask |= 1 << j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
|
||||||
|
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
|
||||||
|
if ((bits[i] & mask) != (value ? mask : 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendBit(boolean bit) {
|
||||||
|
ensureCapacity(size + 1);
|
||||||
|
if (bit) {
|
||||||
|
bits[size >> 5] |= (1 << (size & 0x1F));
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the least-significant bits, from value, in order from most-significant to
|
||||||
|
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
|
||||||
|
* 0, 1, 1, 1, 1, 0 in that order.
|
||||||
|
*/
|
||||||
|
public void appendBits(int value, int numBits) {
|
||||||
|
if (numBits < 0 || numBits > 32) {
|
||||||
|
throw new IllegalArgumentException("Num bits must be between 0 and 32");
|
||||||
|
}
|
||||||
|
ensureCapacity(size + numBits);
|
||||||
|
for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
|
||||||
|
appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendBitArray(BitArray other) {
|
||||||
|
int otherSize = other.getSize();
|
||||||
|
ensureCapacity(size + otherSize);
|
||||||
|
for (int i = 0; i < otherSize; i++) {
|
||||||
|
appendBit(other.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void xor(BitArray other) {
|
||||||
|
if (bits.length != other.bits.length) {
|
||||||
|
throw new IllegalArgumentException("Sizes don't match");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < bits.length; i++) {
|
||||||
|
// The last byte could be incomplete (i.e. not have 8 bits in
|
||||||
|
// it) but there is no problem since 0 XOR 0 == 0.
|
||||||
|
bits[i] ^= other.bits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param bitOffset first bit to start writing
|
||||||
|
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
|
||||||
|
* of the internal representation, which is exposed by {@link #getBitArray()}
|
||||||
|
* @param offset position in array to start writing
|
||||||
|
* @param numBytes how many bytes to write
|
||||||
|
*/
|
||||||
|
public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {
|
||||||
|
for (int i = 0; i < numBytes; i++) {
|
||||||
|
int theByte = 0;
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
if (get(bitOffset)) {
|
||||||
|
theByte |= 1 << (7 - j);
|
||||||
|
}
|
||||||
|
bitOffset++;
|
||||||
|
}
|
||||||
|
array[offset + i] = (byte) theByte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return underlying array of ints. The first element holds the first 32 bits, and the least
|
||||||
|
* significant bit is bit 0.
|
||||||
|
*/
|
||||||
|
public int[] getBitArray() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverses all bits in the array.
|
||||||
|
*/
|
||||||
|
public void reverse() {
|
||||||
|
int[] newBits = new int[bits.length];
|
||||||
|
int size = this.size;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (get(size - i - 1)) {
|
||||||
|
newBits[i >> 5] |= 1 << (i & 0x1F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bits = newBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] makeArray(int size) {
|
||||||
|
return new int[(size + 31) >> 5];
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer result = new StringBuffer(size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if ((i & 0x07) == 0) {
|
||||||
|
result.append(' ');
|
||||||
|
}
|
||||||
|
result.append(get(i) ? 'X' : '.');
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
226
src/com/google/zxing/common/BitMatrix.java
Normal file
226
src/com/google/zxing/common/BitMatrix.java
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
|
||||||
|
* module, x is the column position, and y is the row position. The ordering is always x, y.
|
||||||
|
* The origin is at the top-left.</p>
|
||||||
|
*
|
||||||
|
* <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
|
||||||
|
* with a new int. This is done intentionally so that we can copy out a row into a BitArray very
|
||||||
|
* efficiently.</p>
|
||||||
|
*
|
||||||
|
* <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
|
||||||
|
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class BitMatrix {
|
||||||
|
|
||||||
|
// TODO: Just like BitArray, these need to be public so ProGuard can inline them.
|
||||||
|
public final int width;
|
||||||
|
public final int height;
|
||||||
|
public final int rowSize;
|
||||||
|
public final int[] bits;
|
||||||
|
|
||||||
|
// A helper to construct a square matrix.
|
||||||
|
public BitMatrix(int dimension) {
|
||||||
|
this(dimension, dimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitMatrix(int width, int height) {
|
||||||
|
if (width < 1 || height < 1) {
|
||||||
|
throw new IllegalArgumentException("Both dimensions must be greater than 0");
|
||||||
|
}
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.rowSize = (width + 31) >> 5;
|
||||||
|
bits = new int[rowSize * height];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Gets the requested bit, where true means black.</p>
|
||||||
|
*
|
||||||
|
* @param x The horizontal component (i.e. which column)
|
||||||
|
* @param y The vertical component (i.e. which row)
|
||||||
|
* @return value of given bit in matrix
|
||||||
|
*/
|
||||||
|
public boolean get(int x, int y) {
|
||||||
|
int offset = y * rowSize + (x >> 5);
|
||||||
|
return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the given bit to true.</p>
|
||||||
|
*
|
||||||
|
* @param x The horizontal component (i.e. which column)
|
||||||
|
* @param y The vertical component (i.e. which row)
|
||||||
|
*/
|
||||||
|
public void set(int x, int y) {
|
||||||
|
int offset = y * rowSize + (x >> 5);
|
||||||
|
bits[offset] |= 1 << (x & 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Flips the given bit.</p>
|
||||||
|
*
|
||||||
|
* @param x The horizontal component (i.e. which column)
|
||||||
|
* @param y The vertical component (i.e. which row)
|
||||||
|
*/
|
||||||
|
public void flip(int x, int y) {
|
||||||
|
int offset = y * rowSize + (x >> 5);
|
||||||
|
bits[offset] ^= 1 << (x & 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all bits (sets to false).
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
int max = bits.length;
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
bits[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets a square region of the bit matrix to true.</p>
|
||||||
|
*
|
||||||
|
* @param left The horizontal position to begin at (inclusive)
|
||||||
|
* @param top The vertical position to begin at (inclusive)
|
||||||
|
* @param width The width of the region
|
||||||
|
* @param height The height of the region
|
||||||
|
*/
|
||||||
|
public void setRegion(int left, int top, int width, int height) {
|
||||||
|
if (top < 0 || left < 0) {
|
||||||
|
throw new IllegalArgumentException("Left and top must be nonnegative");
|
||||||
|
}
|
||||||
|
if (height < 1 || width < 1) {
|
||||||
|
throw new IllegalArgumentException("Height and width must be at least 1");
|
||||||
|
}
|
||||||
|
int right = left + width;
|
||||||
|
int bottom = top + height;
|
||||||
|
if (bottom > this.height || right > this.width) {
|
||||||
|
throw new IllegalArgumentException("The region must fit inside the matrix");
|
||||||
|
}
|
||||||
|
for (int y = top; y < bottom; y++) {
|
||||||
|
int offset = y * rowSize;
|
||||||
|
for (int x = left; x < right; x++) {
|
||||||
|
bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fast method to retrieve one row of data from the matrix as a BitArray.
|
||||||
|
*
|
||||||
|
* @param y The row to retrieve
|
||||||
|
* @param row An optional caller-allocated BitArray, will be allocated if null or too small
|
||||||
|
* @return The resulting BitArray - this reference should always be used even when passing
|
||||||
|
* your own row
|
||||||
|
*/
|
||||||
|
public BitArray getRow(int y, BitArray row) {
|
||||||
|
if (row == null || row.getSize() < width) {
|
||||||
|
row = new BitArray(width);
|
||||||
|
}
|
||||||
|
int offset = y * rowSize;
|
||||||
|
for (int x = 0; x < rowSize; x++) {
|
||||||
|
row.setBulk(x << 5, bits[offset + x]);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is useful in detecting a corner of a 'pure' barcode.
|
||||||
|
*
|
||||||
|
* @return {x,y} coordinate of top-left-most 1 bit, or null if it is all white
|
||||||
|
*/
|
||||||
|
public int[] getTopLeftOnBit() {
|
||||||
|
int bitsOffset = 0;
|
||||||
|
while (bitsOffset < bits.length && bits[bitsOffset] == 0) {
|
||||||
|
bitsOffset++;
|
||||||
|
}
|
||||||
|
if (bitsOffset == bits.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int y = bitsOffset / rowSize;
|
||||||
|
int x = (bitsOffset % rowSize) << 5;
|
||||||
|
|
||||||
|
int theBits = bits[bitsOffset];
|
||||||
|
int bit = 0;
|
||||||
|
while ((theBits << (31-bit)) == 0) {
|
||||||
|
bit++;
|
||||||
|
}
|
||||||
|
x += bit;
|
||||||
|
return new int[] {x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The width of the matrix
|
||||||
|
*/
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The height of the matrix
|
||||||
|
*/
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof BitMatrix)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BitMatrix other = (BitMatrix) o;
|
||||||
|
if (width != other.width || height != other.height ||
|
||||||
|
rowSize != other.rowSize || bits.length != other.bits.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < bits.length; i++) {
|
||||||
|
if (bits[i] != other.bits[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = width;
|
||||||
|
hash = 31 * hash + width;
|
||||||
|
hash = 31 * hash + height;
|
||||||
|
hash = 31 * hash + rowSize;
|
||||||
|
for (int i = 0; i < bits.length; i++) {
|
||||||
|
hash = 31 * hash + bits[i];
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer result = new StringBuffer(height * (width + 1));
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
result.append(get(x, y) ? "X " : " ");
|
||||||
|
}
|
||||||
|
result.append('\n');
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
97
src/com/google/zxing/common/BitSource.java
Normal file
97
src/com/google/zxing/common/BitSource.java
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
|
||||||
|
* number of bits read is not often a multiple of 8.</p>
|
||||||
|
*
|
||||||
|
* <p>This class is thread-safe but not reentrant. Unless the caller modifies the bytes array
|
||||||
|
* it passed in, in which case all bets are off.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class BitSource {
|
||||||
|
|
||||||
|
private final byte[] bytes;
|
||||||
|
private int byteOffset;
|
||||||
|
private int bitOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
|
||||||
|
* Bits are read within a byte from most-significant to least-significant bit.
|
||||||
|
*/
|
||||||
|
public BitSource(byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numBits number of bits to read
|
||||||
|
* @return int representing the bits read. The bits will appear as the least-significant
|
||||||
|
* bits of the int
|
||||||
|
* @throws IllegalArgumentException if numBits isn't in [1,32]
|
||||||
|
*/
|
||||||
|
public int readBits(int numBits) {
|
||||||
|
if (numBits < 1 || numBits > 32) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
// First, read remainder from current byte
|
||||||
|
if (bitOffset > 0) {
|
||||||
|
int bitsLeft = 8 - bitOffset;
|
||||||
|
int toRead = numBits < bitsLeft ? numBits : bitsLeft;
|
||||||
|
int bitsToNotRead = bitsLeft - toRead;
|
||||||
|
int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
|
||||||
|
result = (bytes[byteOffset] & mask) >> bitsToNotRead;
|
||||||
|
numBits -= toRead;
|
||||||
|
bitOffset += toRead;
|
||||||
|
if (bitOffset == 8) {
|
||||||
|
bitOffset = 0;
|
||||||
|
byteOffset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next read whole bytes
|
||||||
|
if (numBits > 0) {
|
||||||
|
while (numBits >= 8) {
|
||||||
|
result = (result << 8) | (bytes[byteOffset] & 0xFF);
|
||||||
|
byteOffset++;
|
||||||
|
numBits -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally read a partial byte
|
||||||
|
if (numBits > 0) {
|
||||||
|
int bitsToNotRead = 8 - numBits;
|
||||||
|
int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
|
||||||
|
result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead);
|
||||||
|
bitOffset += numBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return number of bits that can be read successfully
|
||||||
|
*/
|
||||||
|
public int available() {
|
||||||
|
return 8 * (bytes.length - byteOffset) - bitOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
src/com/google/zxing/common/CharacterSetECI.java
Normal file
110
src/com/google/zxing/common/CharacterSetECI.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
|
||||||
|
* of ISO 18004.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class CharacterSetECI extends ECI {
|
||||||
|
|
||||||
|
private static Hashtable VALUE_TO_ECI;
|
||||||
|
private static Hashtable NAME_TO_ECI;
|
||||||
|
|
||||||
|
private static void initialize() {
|
||||||
|
VALUE_TO_ECI = new Hashtable(29);
|
||||||
|
NAME_TO_ECI = new Hashtable(29);
|
||||||
|
// TODO figure out if these values are even right!
|
||||||
|
addCharacterSet(0, "Cp437");
|
||||||
|
addCharacterSet(1, new String[] {"ISO8859_1", "ISO-8859-1"});
|
||||||
|
addCharacterSet(2, "Cp437");
|
||||||
|
addCharacterSet(3, new String[] {"ISO8859_1", "ISO-8859-1"});
|
||||||
|
addCharacterSet(4, "ISO8859_2");
|
||||||
|
addCharacterSet(5, "ISO8859_3");
|
||||||
|
addCharacterSet(6, "ISO8859_4");
|
||||||
|
addCharacterSet(7, "ISO8859_5");
|
||||||
|
addCharacterSet(8, "ISO8859_6");
|
||||||
|
addCharacterSet(9, "ISO8859_7");
|
||||||
|
addCharacterSet(10, "ISO8859_8");
|
||||||
|
addCharacterSet(11, "ISO8859_9");
|
||||||
|
addCharacterSet(12, "ISO8859_10");
|
||||||
|
addCharacterSet(13, "ISO8859_11");
|
||||||
|
addCharacterSet(15, "ISO8859_13");
|
||||||
|
addCharacterSet(16, "ISO8859_14");
|
||||||
|
addCharacterSet(17, "ISO8859_15");
|
||||||
|
addCharacterSet(18, "ISO8859_16");
|
||||||
|
addCharacterSet(20, new String[] {"SJIS", "Shift_JIS"});
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String encodingName;
|
||||||
|
|
||||||
|
private CharacterSetECI(int value, String encodingName) {
|
||||||
|
super(value);
|
||||||
|
this.encodingName = encodingName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncodingName() {
|
||||||
|
return encodingName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addCharacterSet(int value, String encodingName) {
|
||||||
|
CharacterSetECI eci = new CharacterSetECI(value, encodingName);
|
||||||
|
VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
|
||||||
|
NAME_TO_ECI.put(encodingName, eci);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addCharacterSet(int value, String[] encodingNames) {
|
||||||
|
CharacterSetECI eci = new CharacterSetECI(value, encodingNames[0]);
|
||||||
|
VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
|
||||||
|
for (int i = 0; i < encodingNames.length; i++) {
|
||||||
|
NAME_TO_ECI.put(encodingNames[i], eci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value character set ECI value
|
||||||
|
* @return {@link CharacterSetECI} representing ECI of given value, or null if it is legal but
|
||||||
|
* unsupported
|
||||||
|
* @throws IllegalArgumentException if ECI value is invalid
|
||||||
|
*/
|
||||||
|
public static CharacterSetECI getCharacterSetECIByValue(int value) {
|
||||||
|
if (VALUE_TO_ECI == null) {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
if (value < 0 || value >= 900) {
|
||||||
|
throw new IllegalArgumentException("Bad ECI value: " + value);
|
||||||
|
}
|
||||||
|
return (CharacterSetECI) VALUE_TO_ECI.get(new Integer(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name character set ECI encoding name
|
||||||
|
* @return {@link CharacterSetECI} representing ECI for character encoding, or null if it is legal
|
||||||
|
* but unsupported
|
||||||
|
*/
|
||||||
|
public static CharacterSetECI getCharacterSetECIByName(String name) {
|
||||||
|
if (NAME_TO_ECI == null) {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
return (CharacterSetECI) NAME_TO_ECI.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
src/com/google/zxing/common/Collections.java
Normal file
53
src/com/google/zxing/common/Collections.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This is basically a substitute for <code>java.util.Collections</code>, which is not
|
||||||
|
* present in MIDP 2.0 / CLDC 1.1.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class Collections {
|
||||||
|
|
||||||
|
private Collections() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts its argument (destructively) using insert sort; in the context of this package
|
||||||
|
* insertion sort is simple and efficient given its relatively small inputs.
|
||||||
|
*
|
||||||
|
* @param vector vector to sort
|
||||||
|
* @param comparator comparator to define sort ordering
|
||||||
|
*/
|
||||||
|
public static void insertionSort(Vector vector, Comparator comparator) {
|
||||||
|
int max = vector.size();
|
||||||
|
for (int i = 1; i < max; i++) {
|
||||||
|
Object value = vector.elementAt(i);
|
||||||
|
int j = i - 1;
|
||||||
|
Object valueB;
|
||||||
|
while (j >= 0 && comparator.compare((valueB = vector.elementAt(j)), value) > 0) {
|
||||||
|
vector.setElementAt(valueB, j + 1);
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
vector.setElementAt(value, j + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
src/com/google/zxing/common/Comparator.java
Normal file
27
src/com/google/zxing/common/Comparator.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is merely a clone of <code>Comparator</code> since it is not available in
|
||||||
|
* CLDC 1.1 / MIDP 2.0.
|
||||||
|
*/
|
||||||
|
public interface Comparator {
|
||||||
|
|
||||||
|
int compare(Object o1, Object o2);
|
||||||
|
|
||||||
|
}
|
63
src/com/google/zxing/common/DecoderResult.java
Normal file
63
src/com/google/zxing/common/DecoderResult.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates the result of decoding a matrix of bits. This typically
|
||||||
|
* applies to 2D barcode formats. For now it contains the raw bytes obtained,
|
||||||
|
* as well as a String interpretation of those bytes, if applicable.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class DecoderResult {
|
||||||
|
|
||||||
|
private final byte[] rawBytes;
|
||||||
|
private final String text;
|
||||||
|
private final Vector byteSegments;
|
||||||
|
private final ErrorCorrectionLevel ecLevel;
|
||||||
|
|
||||||
|
public DecoderResult(byte[] rawBytes, String text, Vector byteSegments, ErrorCorrectionLevel ecLevel) {
|
||||||
|
if (rawBytes == null && text == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.rawBytes = rawBytes;
|
||||||
|
this.text = text;
|
||||||
|
this.byteSegments = byteSegments;
|
||||||
|
this.ecLevel = ecLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRawBytes() {
|
||||||
|
return rawBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector getByteSegments() {
|
||||||
|
return byteSegments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ErrorCorrectionLevel getECLevel() {
|
||||||
|
return ecLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
src/com/google/zxing/common/DefaultGridSampler.java
Normal file
81
src/com/google/zxing/common/DefaultGridSampler.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import com.google.zxing.NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class DefaultGridSampler extends GridSampler {
|
||||||
|
|
||||||
|
public BitMatrix sampleGrid(BitMatrix image,
|
||||||
|
int dimension,
|
||||||
|
float p1ToX, float p1ToY,
|
||||||
|
float p2ToX, float p2ToY,
|
||||||
|
float p3ToX, float p3ToY,
|
||||||
|
float p4ToX, float p4ToY,
|
||||||
|
float p1FromX, float p1FromY,
|
||||||
|
float p2FromX, float p2FromY,
|
||||||
|
float p3FromX, float p3FromY,
|
||||||
|
float p4FromX, float p4FromY) throws NotFoundException {
|
||||||
|
|
||||||
|
PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(
|
||||||
|
p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
|
||||||
|
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
|
||||||
|
|
||||||
|
return sampleGrid(image, dimension, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitMatrix sampleGrid(BitMatrix image,
|
||||||
|
int dimension,
|
||||||
|
PerspectiveTransform transform) throws NotFoundException {
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
float[] points = new float[dimension << 1];
|
||||||
|
for (int y = 0; y < dimension; y++) {
|
||||||
|
int max = points.length;
|
||||||
|
float iValue = (float) y + 0.5f;
|
||||||
|
for (int x = 0; x < max; x += 2) {
|
||||||
|
points[x] = (float) (x >> 1) + 0.5f;
|
||||||
|
points[x + 1] = iValue;
|
||||||
|
}
|
||||||
|
transform.transformPoints(points);
|
||||||
|
// Quick check to see if points transformed to something inside the image;
|
||||||
|
// sufficient to check the endpoints
|
||||||
|
checkAndNudgePoints(image, points);
|
||||||
|
try {
|
||||||
|
for (int x = 0; x < max; x += 2) {
|
||||||
|
if (image.get((int) points[x], (int) points[x + 1])) {
|
||||||
|
// Black(-ish) pixel
|
||||||
|
bits.set(x >> 1, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ArrayIndexOutOfBoundsException aioobe) {
|
||||||
|
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
|
||||||
|
// transform gets "twisted" such that it maps a straight line of points to a set of points
|
||||||
|
// whose endpoints are in bounds, but others are not. There is probably some mathematical
|
||||||
|
// way to detect this about the transformation that I don't know yet.
|
||||||
|
// This results in an ugly runtime exception despite our clever checks above -- can't have
|
||||||
|
// that. We could check each point's coordinates but that feels duplicative. We settle for
|
||||||
|
// catching and wrapping ArrayIndexOutOfBoundsException.
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
src/com/google/zxing/common/DetectorResult.java
Normal file
46
src/com/google/zxing/common/DetectorResult.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
|
||||||
|
* matrix of black/white pixels corresponding to the barcode, and possibly points of interest
|
||||||
|
* in the image, like the location of finder patterns or corners of the barcode in the image.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class DetectorResult {
|
||||||
|
|
||||||
|
private final BitMatrix bits;
|
||||||
|
private final ResultPoint[] points;
|
||||||
|
|
||||||
|
public DetectorResult(BitMatrix bits, ResultPoint[] points) {
|
||||||
|
this.bits = bits;
|
||||||
|
this.points = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitMatrix getBits() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultPoint[] getPoints() {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
src/com/google/zxing/common/ECI.java
Normal file
52
src/com/google/zxing/common/ECI.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Superclass of classes encapsulating types ECIs, according to "Extended Channel Interpretations"
|
||||||
|
* 5.3 of ISO 18004.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public abstract class ECI {
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
ECI(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param value ECI value
|
||||||
|
* @return {@link ECI} representing ECI of given value, or null if it is legal but unsupported
|
||||||
|
* @throws IllegalArgumentException if ECI value is invalid
|
||||||
|
*/
|
||||||
|
public static ECI getECIByValue(int value) {
|
||||||
|
if (value < 0 || value > 999999) {
|
||||||
|
throw new IllegalArgumentException("Bad ECI value: " + value);
|
||||||
|
}
|
||||||
|
if (value < 900) { // Character set ECIs use 000000 - 000899
|
||||||
|
return CharacterSetECI.getCharacterSetECIByValue(value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
196
src/com/google/zxing/common/GlobalHistogramBinarizer.java
Normal file
196
src/com/google/zxing/common/GlobalHistogramBinarizer.java
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import com.google.zxing.Binarizer;
|
||||||
|
import com.google.zxing.LuminanceSource;
|
||||||
|
import com.google.zxing.NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
|
||||||
|
* for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
|
||||||
|
* algorithm. However, because it picks a global black point, it cannot handle difficult shadows
|
||||||
|
* and gradients.
|
||||||
|
*
|
||||||
|
* Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public class GlobalHistogramBinarizer extends Binarizer {
|
||||||
|
|
||||||
|
private static final int LUMINANCE_BITS = 5;
|
||||||
|
private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
|
||||||
|
private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
|
||||||
|
|
||||||
|
private byte[] luminances = null;
|
||||||
|
private int[] buckets = null;
|
||||||
|
|
||||||
|
public GlobalHistogramBinarizer(LuminanceSource source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
|
||||||
|
public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
|
||||||
|
LuminanceSource source = getLuminanceSource();
|
||||||
|
int width = source.getWidth();
|
||||||
|
if (row == null || row.getSize() < width) {
|
||||||
|
row = new BitArray(width);
|
||||||
|
} else {
|
||||||
|
row.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
initArrays(width);
|
||||||
|
byte[] localLuminances = source.getRow(y, luminances);
|
||||||
|
int[] localBuckets = buckets;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int pixel = localLuminances[x] & 0xff;
|
||||||
|
localBuckets[pixel >> LUMINANCE_SHIFT]++;
|
||||||
|
}
|
||||||
|
int blackPoint = estimateBlackPoint(localBuckets);
|
||||||
|
|
||||||
|
int left = localLuminances[0] & 0xff;
|
||||||
|
int center = localLuminances[1] & 0xff;
|
||||||
|
for (int x = 1; x < width - 1; x++) {
|
||||||
|
int right = localLuminances[x + 1] & 0xff;
|
||||||
|
// A simple -1 4 -1 box filter with a weight of 2.
|
||||||
|
int luminance = ((center << 2) - left - right) >> 1;
|
||||||
|
if (luminance < blackPoint) {
|
||||||
|
row.set(x);
|
||||||
|
}
|
||||||
|
left = center;
|
||||||
|
center = right;
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
|
||||||
|
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||||
|
LuminanceSource source = getLuminanceSource();
|
||||||
|
int width = source.getWidth();
|
||||||
|
int height = source.getHeight();
|
||||||
|
BitMatrix matrix = new BitMatrix(width, height);
|
||||||
|
|
||||||
|
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
|
||||||
|
// more robust on the blackbox tests than sampling a diagonal as we used to do.
|
||||||
|
initArrays(width);
|
||||||
|
int[] localBuckets = buckets;
|
||||||
|
for (int y = 1; y < 5; y++) {
|
||||||
|
int row = height * y / 5;
|
||||||
|
byte[] localLuminances = source.getRow(row, luminances);
|
||||||
|
int right = (width << 2) / 5;
|
||||||
|
for (int x = width / 5; x < right; x++) {
|
||||||
|
int pixel = localLuminances[x] & 0xff;
|
||||||
|
localBuckets[pixel >> LUMINANCE_SHIFT]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int blackPoint = estimateBlackPoint(localBuckets);
|
||||||
|
|
||||||
|
// We delay reading the entire image luminance until the black point estimation succeeds.
|
||||||
|
// Although we end up reading four rows twice, it is consistent with our motto of
|
||||||
|
// "fail quickly" which is necessary for continuous scanning.
|
||||||
|
byte[] localLuminances = source.getMatrix();
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
int offset = y * width;
|
||||||
|
for (int x = 0; x< width; x++) {
|
||||||
|
int pixel = localLuminances[offset + x] & 0xff;
|
||||||
|
if (pixel < blackPoint) {
|
||||||
|
matrix.set(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Binarizer createBinarizer(LuminanceSource source) {
|
||||||
|
return new GlobalHistogramBinarizer(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initArrays(int luminanceSize) {
|
||||||
|
if (luminances == null || luminances.length < luminanceSize) {
|
||||||
|
luminances = new byte[luminanceSize];
|
||||||
|
}
|
||||||
|
if (buckets == null) {
|
||||||
|
buckets = new int[LUMINANCE_BUCKETS];
|
||||||
|
} else {
|
||||||
|
for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
|
||||||
|
buckets[x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int estimateBlackPoint(int[] buckets) throws NotFoundException {
|
||||||
|
// Find the tallest peak in the histogram.
|
||||||
|
int numBuckets = buckets.length;
|
||||||
|
int maxBucketCount = 0;
|
||||||
|
int firstPeak = 0;
|
||||||
|
int firstPeakSize = 0;
|
||||||
|
for (int x = 0; x < numBuckets; x++) {
|
||||||
|
if (buckets[x] > firstPeakSize) {
|
||||||
|
firstPeak = x;
|
||||||
|
firstPeakSize = buckets[x];
|
||||||
|
}
|
||||||
|
if (buckets[x] > maxBucketCount) {
|
||||||
|
maxBucketCount = buckets[x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the second-tallest peak which is somewhat far from the tallest peak.
|
||||||
|
int secondPeak = 0;
|
||||||
|
int secondPeakScore = 0;
|
||||||
|
for (int x = 0; x < numBuckets; x++) {
|
||||||
|
int distanceToBiggest = x - firstPeak;
|
||||||
|
// Encourage more distant second peaks by multiplying by square of distance.
|
||||||
|
int score = buckets[x] * distanceToBiggest * distanceToBiggest;
|
||||||
|
if (score > secondPeakScore) {
|
||||||
|
secondPeak = x;
|
||||||
|
secondPeakScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure firstPeak corresponds to the black peak.
|
||||||
|
if (firstPeak > secondPeak) {
|
||||||
|
int temp = firstPeak;
|
||||||
|
firstPeak = secondPeak;
|
||||||
|
secondPeak = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is too little contrast in the image to pick a meaningful black point, throw rather
|
||||||
|
// than waste time trying to decode the image, and risk false positives.
|
||||||
|
// TODO: It might be worth comparing the brightest and darkest pixels seen, rather than the
|
||||||
|
// two peaks, to determine the contrast.
|
||||||
|
if (secondPeak - firstPeak <= numBuckets >> 4) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a valley between them that is low and closer to the white peak.
|
||||||
|
int bestValley = secondPeak - 1;
|
||||||
|
int bestValleyScore = -1;
|
||||||
|
for (int x = secondPeak - 1; x > firstPeak; x--) {
|
||||||
|
int fromFirst = x - firstPeak;
|
||||||
|
int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
|
||||||
|
if (score > bestValleyScore) {
|
||||||
|
bestValley = x;
|
||||||
|
bestValleyScore = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestValley << LUMINANCE_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
171
src/com/google/zxing/common/GridSampler.java
Normal file
171
src/com/google/zxing/common/GridSampler.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import com.google.zxing.NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of this class can, given locations of finder patterns for a QR code in an
|
||||||
|
* image, sample the right points in the image to reconstruct the QR code, accounting for
|
||||||
|
* perspective distortion. It is abstracted since it is relatively expensive and should be allowed
|
||||||
|
* to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
|
||||||
|
* Imaging library, but which may not be available in other environments such as J2ME, and vice
|
||||||
|
* versa.
|
||||||
|
*
|
||||||
|
* The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
|
||||||
|
* with an instance of a class which implements this interface.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public abstract class GridSampler {
|
||||||
|
|
||||||
|
private static GridSampler gridSampler = new DefaultGridSampler();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the implementation of {@link GridSampler} used by the library. One global
|
||||||
|
* instance is stored, which may sound problematic. But, the implementation provided
|
||||||
|
* ought to be appropriate for the entire platform, and all uses of this library
|
||||||
|
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
|
||||||
|
* an implementation that takes advantage of native platform libraries.
|
||||||
|
*
|
||||||
|
* @param newGridSampler The platform-specific object to install.
|
||||||
|
*/
|
||||||
|
public static void setGridSampler(GridSampler newGridSampler) {
|
||||||
|
if (newGridSampler == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
gridSampler = newGridSampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current implementation of {@link GridSampler}
|
||||||
|
*/
|
||||||
|
public static GridSampler getInstance() {
|
||||||
|
return gridSampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Samples an image for a square matrix of bits of the given dimension. This is used to extract
|
||||||
|
* the black/white modules of a 2D barcode like a QR Code found in an image. Because this barcode
|
||||||
|
* may be rotated or perspective-distorted, the caller supplies four points in the source image
|
||||||
|
* that define known points in the barcode, so that the image may be sampled appropriately.</p>
|
||||||
|
*
|
||||||
|
* <p>The last eight "from" parameters are four X/Y coordinate pairs of locations of points in
|
||||||
|
* the image that define some significant points in the image to be sample. For example,
|
||||||
|
* these may be the location of finder pattern in a QR Code.</p>
|
||||||
|
*
|
||||||
|
* <p>The first eight "to" parameters are four X/Y coordinate pairs measured in the destination
|
||||||
|
* {@link BitMatrix}, from the top left, where the known points in the image given by the "from"
|
||||||
|
* parameters map to.</p>
|
||||||
|
*
|
||||||
|
* <p>These 16 parameters define the transformation needed to sample the image.</p>
|
||||||
|
*
|
||||||
|
* @param image image to sample
|
||||||
|
* @param dimension width/height of {@link BitMatrix} to sample from image
|
||||||
|
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
|
||||||
|
* defined by the "from" parameters
|
||||||
|
* @throws NotFoundException if image can't be sampled, for example, if the transformation defined
|
||||||
|
* by the given points is invalid or results in sampling outside the image boundaries
|
||||||
|
*/
|
||||||
|
public abstract BitMatrix sampleGrid(BitMatrix image,
|
||||||
|
int dimension,
|
||||||
|
float p1ToX, float p1ToY,
|
||||||
|
float p2ToX, float p2ToY,
|
||||||
|
float p3ToX, float p3ToY,
|
||||||
|
float p4ToX, float p4ToY,
|
||||||
|
float p1FromX, float p1FromY,
|
||||||
|
float p2FromX, float p2FromY,
|
||||||
|
float p3FromX, float p3FromY,
|
||||||
|
float p4FromX, float p4FromY) throws NotFoundException;
|
||||||
|
|
||||||
|
public BitMatrix sampleGrid(BitMatrix image,
|
||||||
|
int dimension,
|
||||||
|
PerspectiveTransform transform) throws NotFoundException {
|
||||||
|
throw new IllegalStateException(); // Can't use UnsupportedOperationException
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks a set of points that have been transformed to sample points on an image against
|
||||||
|
* the image's dimensions to see if the point are even within the image.</p>
|
||||||
|
*
|
||||||
|
* <p>This method will actually "nudge" the endpoints back onto the image if they are found to be
|
||||||
|
* barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
|
||||||
|
* patterns in an image where the QR Code runs all the way to the image border.</p>
|
||||||
|
*
|
||||||
|
* <p>For efficiency, the method will check points from either end of the line until one is found
|
||||||
|
* to be within the image. Because the set of points are assumed to be linear, this is valid.</p>
|
||||||
|
*
|
||||||
|
* @param image image into which the points should map
|
||||||
|
* @param points actual points in x1,y1,...,xn,yn form
|
||||||
|
* @throws NotFoundException if an endpoint is lies outside the image boundaries
|
||||||
|
*/
|
||||||
|
protected static void checkAndNudgePoints(BitMatrix image, float[] points)
|
||||||
|
throws NotFoundException {
|
||||||
|
int width = image.getWidth();
|
||||||
|
int height = image.getHeight();
|
||||||
|
// Check and nudge points from start until we see some that are OK:
|
||||||
|
boolean nudged = true;
|
||||||
|
for (int offset = 0; offset < points.length && nudged; offset += 2) {
|
||||||
|
int x = (int) points[offset];
|
||||||
|
int y = (int) points[offset + 1];
|
||||||
|
if (x < -1 || x > width || y < -1 || y > height) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
nudged = false;
|
||||||
|
if (x == -1) {
|
||||||
|
points[offset] = 0.0f;
|
||||||
|
nudged = true;
|
||||||
|
} else if (x == width) {
|
||||||
|
points[offset] = width - 1;
|
||||||
|
nudged = true;
|
||||||
|
}
|
||||||
|
if (y == -1) {
|
||||||
|
points[offset + 1] = 0.0f;
|
||||||
|
nudged = true;
|
||||||
|
} else if (y == height) {
|
||||||
|
points[offset + 1] = height - 1;
|
||||||
|
nudged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check and nudge points from end:
|
||||||
|
nudged = true;
|
||||||
|
for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {
|
||||||
|
int x = (int) points[offset];
|
||||||
|
int y = (int) points[offset + 1];
|
||||||
|
if (x < -1 || x > width || y < -1 || y > height) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
nudged = false;
|
||||||
|
if (x == -1) {
|
||||||
|
points[offset] = 0.0f;
|
||||||
|
nudged = true;
|
||||||
|
} else if (x == width) {
|
||||||
|
points[offset] = width - 1;
|
||||||
|
nudged = true;
|
||||||
|
}
|
||||||
|
if (y == -1) {
|
||||||
|
points[offset + 1] = 0.0f;
|
||||||
|
nudged = true;
|
||||||
|
} else if (y == height) {
|
||||||
|
points[offset + 1] = height - 1;
|
||||||
|
nudged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
185
src/com/google/zxing/common/HybridBinarizer.java
Normal file
185
src/com/google/zxing/common/HybridBinarizer.java
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import com.google.zxing.Binarizer;
|
||||||
|
import com.google.zxing.LuminanceSource;
|
||||||
|
import com.google.zxing.NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements a local thresholding algorithm, which while slower than the
|
||||||
|
* GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
|
||||||
|
* high frequency images of barcodes with black data on white backgrounds. For this application,
|
||||||
|
* it does a much better job than a global blackpoint with severe shadows and gradients.
|
||||||
|
* However it tends to produce artifacts on lower frequency images and is therefore not
|
||||||
|
* a good general purpose binarizer for uses outside ZXing.
|
||||||
|
*
|
||||||
|
* This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
|
||||||
|
* and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
|
||||||
|
* inherently local, and only fails for horizontal gradients. We can revisit that problem later,
|
||||||
|
* but for now it was not a win to use local blocks for 1D.
|
||||||
|
*
|
||||||
|
* This Binarizer is the default for the unit tests and the recommended class for library users.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class HybridBinarizer extends GlobalHistogramBinarizer {
|
||||||
|
|
||||||
|
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
|
||||||
|
// So this is the smallest dimension in each axis we can accept.
|
||||||
|
private static final int MINIMUM_DIMENSION = 40;
|
||||||
|
|
||||||
|
private BitMatrix matrix = null;
|
||||||
|
|
||||||
|
public HybridBinarizer(LuminanceSource source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||||
|
binarizeEntireImage();
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Binarizer createBinarizer(LuminanceSource source) {
|
||||||
|
return new HybridBinarizer(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates the final BitMatrix once for all requests. This could be called once from the
|
||||||
|
// constructor instead, but there are some advantages to doing it lazily, such as making
|
||||||
|
// profiling easier, and not doing heavy lifting when callers don't expect it.
|
||||||
|
private void binarizeEntireImage() throws NotFoundException {
|
||||||
|
if (matrix == null) {
|
||||||
|
LuminanceSource source = getLuminanceSource();
|
||||||
|
if (source.getWidth() >= MINIMUM_DIMENSION && source.getHeight() >= MINIMUM_DIMENSION) {
|
||||||
|
byte[] luminances = source.getMatrix();
|
||||||
|
int width = source.getWidth();
|
||||||
|
int height = source.getHeight();
|
||||||
|
int subWidth = width >> 3;
|
||||||
|
if ((width & 0x07) != 0) {
|
||||||
|
subWidth++;
|
||||||
|
}
|
||||||
|
int subHeight = height >> 3;
|
||||||
|
if ((height & 0x07) != 0) {
|
||||||
|
subHeight++;
|
||||||
|
}
|
||||||
|
int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height);
|
||||||
|
|
||||||
|
matrix = new BitMatrix(width, height);
|
||||||
|
calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, matrix);
|
||||||
|
} else {
|
||||||
|
// If the image is too small, fall back to the global histogram approach.
|
||||||
|
matrix = super.getBlackMatrix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each 8x8 block in the image, calculate the average black point using a 5x5 grid
|
||||||
|
// of the blocks around it. Also handles the corner cases (fractional blocks are computed based
|
||||||
|
// on the last 8 pixels in the row/column which are also used in the previous block).
|
||||||
|
private static void calculateThresholdForBlock(byte[] luminances, int subWidth, int subHeight,
|
||||||
|
int width, int height, int[][] blackPoints, BitMatrix matrix) {
|
||||||
|
for (int y = 0; y < subHeight; y++) {
|
||||||
|
int yoffset = y << 3;
|
||||||
|
if ((yoffset + 8) >= height) {
|
||||||
|
yoffset = height - 8;
|
||||||
|
}
|
||||||
|
for (int x = 0; x < subWidth; x++) {
|
||||||
|
int xoffset = x << 3;
|
||||||
|
if ((xoffset + 8) >= width) {
|
||||||
|
xoffset = width - 8;
|
||||||
|
}
|
||||||
|
int left = (x > 1) ? x : 2;
|
||||||
|
left = (left < subWidth - 2) ? left : subWidth - 3;
|
||||||
|
int top = (y > 1) ? y : 2;
|
||||||
|
top = (top < subHeight - 2) ? top : subHeight - 3;
|
||||||
|
int sum = 0;
|
||||||
|
for (int z = -2; z <= 2; z++) {
|
||||||
|
int[] blackRow = blackPoints[top + z];
|
||||||
|
sum += blackRow[left - 2];
|
||||||
|
sum += blackRow[left - 1];
|
||||||
|
sum += blackRow[left];
|
||||||
|
sum += blackRow[left + 1];
|
||||||
|
sum += blackRow[left + 2];
|
||||||
|
}
|
||||||
|
int average = sum / 25;
|
||||||
|
threshold8x8Block(luminances, xoffset, yoffset, average, width, matrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies a single threshold to an 8x8 block of pixels.
|
||||||
|
private static void threshold8x8Block(byte[] luminances, int xoffset, int yoffset, int threshold,
|
||||||
|
int stride, BitMatrix matrix) {
|
||||||
|
for (int y = 0; y < 8; y++) {
|
||||||
|
int offset = (yoffset + y) * stride + xoffset;
|
||||||
|
for (int x = 0; x < 8; x++) {
|
||||||
|
int pixel = luminances[offset + x] & 0xff;
|
||||||
|
if (pixel < threshold) {
|
||||||
|
matrix.set(xoffset + x, yoffset + y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates a single black point for each 8x8 block of pixels and saves it away.
|
||||||
|
private static int[][] calculateBlackPoints(byte[] luminances, int subWidth, int subHeight,
|
||||||
|
int width, int height) {
|
||||||
|
int[][] blackPoints = new int[subHeight][subWidth];
|
||||||
|
for (int y = 0; y < subHeight; y++) {
|
||||||
|
int yoffset = y << 3;
|
||||||
|
if ((yoffset + 8) >= height) {
|
||||||
|
yoffset = height - 8;
|
||||||
|
}
|
||||||
|
for (int x = 0; x < subWidth; x++) {
|
||||||
|
int xoffset = x << 3;
|
||||||
|
if ((xoffset + 8) >= width) {
|
||||||
|
xoffset = width - 8;
|
||||||
|
}
|
||||||
|
int sum = 0;
|
||||||
|
int min = 255;
|
||||||
|
int max = 0;
|
||||||
|
for (int yy = 0; yy < 8; yy++) {
|
||||||
|
int offset = (yoffset + yy) * width + xoffset;
|
||||||
|
for (int xx = 0; xx < 8; xx++) {
|
||||||
|
int pixel = luminances[offset + xx] & 0xff;
|
||||||
|
sum += pixel;
|
||||||
|
if (pixel < min) {
|
||||||
|
min = pixel;
|
||||||
|
}
|
||||||
|
if (pixel > max) {
|
||||||
|
max = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the contrast is inadequate, use half the minimum, so that this block will be
|
||||||
|
// treated as part of the white background, but won't drag down neighboring blocks
|
||||||
|
// too much.
|
||||||
|
int average;
|
||||||
|
if (max - min > 24) {
|
||||||
|
average = sum >> 6;
|
||||||
|
} else {
|
||||||
|
// When min == max == 0, let average be 1 so all is black
|
||||||
|
average = max == 0 ? 1 : min >> 1;
|
||||||
|
}
|
||||||
|
blackPoints[y][x] = average;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blackPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
148
src/com/google/zxing/common/PerspectiveTransform.java
Normal file
148
src/com/google/zxing/common/PerspectiveTransform.java
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This class implements a perspective transform in two dimensions. Given four source and four
|
||||||
|
* destination points, it will compute the transformation implied between them. The code is based
|
||||||
|
* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class PerspectiveTransform {
|
||||||
|
|
||||||
|
private final float a11, a12, a13, a21, a22, a23, a31, a32, a33;
|
||||||
|
|
||||||
|
private PerspectiveTransform(float a11, float a21, float a31,
|
||||||
|
float a12, float a22, float a32,
|
||||||
|
float a13, float a23, float a33) {
|
||||||
|
this.a11 = a11;
|
||||||
|
this.a12 = a12;
|
||||||
|
this.a13 = a13;
|
||||||
|
this.a21 = a21;
|
||||||
|
this.a22 = a22;
|
||||||
|
this.a23 = a23;
|
||||||
|
this.a31 = a31;
|
||||||
|
this.a32 = a32;
|
||||||
|
this.a33 = a33;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0,
|
||||||
|
float x1, float y1,
|
||||||
|
float x2, float y2,
|
||||||
|
float x3, float y3,
|
||||||
|
float x0p, float y0p,
|
||||||
|
float x1p, float y1p,
|
||||||
|
float x2p, float y2p,
|
||||||
|
float x3p, float y3p) {
|
||||||
|
|
||||||
|
PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
|
||||||
|
PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
|
||||||
|
return sToQ.times(qToS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transformPoints(float[] points) {
|
||||||
|
int max = points.length;
|
||||||
|
float a11 = this.a11;
|
||||||
|
float a12 = this.a12;
|
||||||
|
float a13 = this.a13;
|
||||||
|
float a21 = this.a21;
|
||||||
|
float a22 = this.a22;
|
||||||
|
float a23 = this.a23;
|
||||||
|
float a31 = this.a31;
|
||||||
|
float a32 = this.a32;
|
||||||
|
float a33 = this.a33;
|
||||||
|
for (int i = 0; i < max; i += 2) {
|
||||||
|
float x = points[i];
|
||||||
|
float y = points[i + 1];
|
||||||
|
float denominator = a13 * x + a23 * y + a33;
|
||||||
|
points[i] = (a11 * x + a21 * y + a31) / denominator;
|
||||||
|
points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience method, not optimized for performance. */
|
||||||
|
public void transformPoints(float[] xValues, float[] yValues) {
|
||||||
|
int n = xValues.length;
|
||||||
|
for (int i = 0; i < n; i ++) {
|
||||||
|
float x = xValues[i];
|
||||||
|
float y = yValues[i];
|
||||||
|
float denominator = a13 * x + a23 * y + a33;
|
||||||
|
xValues[i] = (a11 * x + a21 * y + a31) / denominator;
|
||||||
|
yValues[i] = (a12 * x + a22 * y + a32) / denominator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PerspectiveTransform squareToQuadrilateral(float x0, float y0,
|
||||||
|
float x1, float y1,
|
||||||
|
float x2, float y2,
|
||||||
|
float x3, float y3) {
|
||||||
|
float dy2 = y3 - y2;
|
||||||
|
float dy3 = y0 - y1 + y2 - y3;
|
||||||
|
if (dy2 == 0.0f && dy3 == 0.0f) {
|
||||||
|
return new PerspectiveTransform(x1 - x0, x2 - x1, x0,
|
||||||
|
y1 - y0, y2 - y1, y0,
|
||||||
|
0.0f, 0.0f, 1.0f);
|
||||||
|
} else {
|
||||||
|
float dx1 = x1 - x2;
|
||||||
|
float dx2 = x3 - x2;
|
||||||
|
float dx3 = x0 - x1 + x2 - x3;
|
||||||
|
float dy1 = y1 - y2;
|
||||||
|
float denominator = dx1 * dy2 - dx2 * dy1;
|
||||||
|
float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
|
||||||
|
float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
|
||||||
|
return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0,
|
||||||
|
y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0,
|
||||||
|
a13, a23, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PerspectiveTransform quadrilateralToSquare(float x0, float y0,
|
||||||
|
float x1, float y1,
|
||||||
|
float x2, float y2,
|
||||||
|
float x3, float y3) {
|
||||||
|
// Here, the adjoint serves as the inverse:
|
||||||
|
return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
PerspectiveTransform buildAdjoint() {
|
||||||
|
// Adjoint is the transpose of the cofactor matrix:
|
||||||
|
return new PerspectiveTransform(a22 * a33 - a23 * a32,
|
||||||
|
a23 * a31 - a21 * a33,
|
||||||
|
a21 * a32 - a22 * a31,
|
||||||
|
a13 * a32 - a12 * a33,
|
||||||
|
a11 * a33 - a13 * a31,
|
||||||
|
a12 * a31 - a11 * a32,
|
||||||
|
a12 * a23 - a13 * a22,
|
||||||
|
a13 * a21 - a11 * a23,
|
||||||
|
a11 * a22 - a12 * a21);
|
||||||
|
}
|
||||||
|
|
||||||
|
PerspectiveTransform times(PerspectiveTransform other) {
|
||||||
|
return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13,
|
||||||
|
a11 * other.a21 + a21 * other.a22 + a31 * other.a23,
|
||||||
|
a11 * other.a31 + a21 * other.a32 + a31 * other.a33,
|
||||||
|
a12 * other.a11 + a22 * other.a12 + a32 * other.a13,
|
||||||
|
a12 * other.a21 + a22 * other.a22 + a32 * other.a23,
|
||||||
|
a12 * other.a31 + a22 * other.a32 + a32 * other.a33,
|
||||||
|
a13 * other.a11 + a23 * other.a12 + a33 * other.a13,
|
||||||
|
a13 * other.a21 + a23 * other.a22 + a33 * other.a23,
|
||||||
|
a13 * other.a31 + a23 * other.a32 + a33 * other.a33);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
191
src/com/google/zxing/common/StringUtils.java
Normal file
191
src/com/google/zxing/common/StringUtils.java
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common string-related functions.
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class StringUtils {
|
||||||
|
|
||||||
|
private static final String PLATFORM_DEFAULT_ENCODING =
|
||||||
|
System.getProperty("file.encoding");
|
||||||
|
public static final String SHIFT_JIS = "SJIS";
|
||||||
|
private static final String EUC_JP = "EUC_JP";
|
||||||
|
private static final String UTF8 = "UTF8";
|
||||||
|
private static final String ISO88591 = "ISO8859_1";
|
||||||
|
private static final boolean ASSUME_SHIFT_JIS =
|
||||||
|
SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) ||
|
||||||
|
EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING);
|
||||||
|
|
||||||
|
private StringUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bytes bytes encoding a string, whose encoding should be guessed
|
||||||
|
* @param hints decode hints if applicable
|
||||||
|
* @return name of guessed encoding; at the moment will only guess one of:
|
||||||
|
* {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform
|
||||||
|
* default encoding if none of these can possibly be correct
|
||||||
|
*/
|
||||||
|
public static String guessEncoding(byte[] bytes, Hashtable hints) {
|
||||||
|
if (hints != null) {
|
||||||
|
String characterSet = (String) hints.get(DecodeHintType.CHARACTER_SET);
|
||||||
|
if (characterSet != null) {
|
||||||
|
return characterSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Does it start with the UTF-8 byte order mark? then guess it's UTF-8
|
||||||
|
if (bytes.length > 3 &&
|
||||||
|
bytes[0] == (byte) 0xEF &&
|
||||||
|
bytes[1] == (byte) 0xBB &&
|
||||||
|
bytes[2] == (byte) 0xBF) {
|
||||||
|
return UTF8;
|
||||||
|
}
|
||||||
|
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
|
||||||
|
// which should be by far the most common encodings. ISO-8859-1
|
||||||
|
// should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
|
||||||
|
// uses this as a first byte of a two-byte character. If we see this
|
||||||
|
// followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
|
||||||
|
// If we see something else in that second byte, we'll make the risky guess
|
||||||
|
// that it's UTF-8.
|
||||||
|
int length = bytes.length;
|
||||||
|
boolean canBeISO88591 = true;
|
||||||
|
boolean canBeShiftJIS = true;
|
||||||
|
boolean canBeUTF8 = true;
|
||||||
|
int utf8BytesLeft = 0;
|
||||||
|
int maybeDoubleByteCount = 0;
|
||||||
|
int maybeSingleByteKatakanaCount = 0;
|
||||||
|
boolean sawLatin1Supplement = false;
|
||||||
|
boolean sawUTF8Start = false;
|
||||||
|
boolean lastWasPossibleDoubleByteStart = false;
|
||||||
|
|
||||||
|
for (int i = 0;
|
||||||
|
i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
|
||||||
|
i++) {
|
||||||
|
|
||||||
|
int value = bytes[i] & 0xFF;
|
||||||
|
|
||||||
|
// UTF-8 stuff
|
||||||
|
if (value >= 0x80 && value <= 0xBF) {
|
||||||
|
if (utf8BytesLeft > 0) {
|
||||||
|
utf8BytesLeft--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (utf8BytesLeft > 0) {
|
||||||
|
canBeUTF8 = false;
|
||||||
|
}
|
||||||
|
if (value >= 0xC0 && value <= 0xFD) {
|
||||||
|
sawUTF8Start = true;
|
||||||
|
int valueCopy = value;
|
||||||
|
while ((valueCopy & 0x40) != 0) {
|
||||||
|
utf8BytesLeft++;
|
||||||
|
valueCopy <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO-8859-1 stuff
|
||||||
|
|
||||||
|
if ((value == 0xC2 || value == 0xC3) && i < length - 1) {
|
||||||
|
// This is really a poor hack. The slightly more exotic characters people might want to put in
|
||||||
|
// a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings
|
||||||
|
// that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF].
|
||||||
|
int nextValue = bytes[i + 1] & 0xFF;
|
||||||
|
if (nextValue <= 0xBF &&
|
||||||
|
((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) {
|
||||||
|
sawLatin1Supplement = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value >= 0x7F && value <= 0x9F) {
|
||||||
|
canBeISO88591 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift_JIS stuff
|
||||||
|
|
||||||
|
if (value >= 0xA1 && value <= 0xDF) {
|
||||||
|
// count the number of characters that might be a Shift_JIS single-byte Katakana character
|
||||||
|
if (!lastWasPossibleDoubleByteStart) {
|
||||||
|
maybeSingleByteKatakanaCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!lastWasPossibleDoubleByteStart &&
|
||||||
|
((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
|
||||||
|
canBeShiftJIS = false;
|
||||||
|
}
|
||||||
|
if (((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF))) {
|
||||||
|
// These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
|
||||||
|
// second byte.
|
||||||
|
if (lastWasPossibleDoubleByteStart) {
|
||||||
|
// If we just checked this and the last byte for being a valid double-byte
|
||||||
|
// char, don't check starting on this byte. If this and the last byte
|
||||||
|
// formed a valid pair, then this shouldn't be checked to see if it starts
|
||||||
|
// a double byte pair of course.
|
||||||
|
lastWasPossibleDoubleByteStart = false;
|
||||||
|
} else {
|
||||||
|
// ... otherwise do check to see if this plus the next byte form a valid
|
||||||
|
// double byte pair encoding a character.
|
||||||
|
lastWasPossibleDoubleByteStart = true;
|
||||||
|
if (i >= bytes.length - 1) {
|
||||||
|
canBeShiftJIS = false;
|
||||||
|
} else {
|
||||||
|
int nextValue = bytes[i + 1] & 0xFF;
|
||||||
|
if (nextValue < 0x40 || nextValue > 0xFC) {
|
||||||
|
canBeShiftJIS = false;
|
||||||
|
} else {
|
||||||
|
maybeDoubleByteCount++;
|
||||||
|
}
|
||||||
|
// There is some conflicting information out there about which bytes can follow which in
|
||||||
|
// double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lastWasPossibleDoubleByteStart = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (utf8BytesLeft > 0) {
|
||||||
|
canBeUTF8 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Easy -- if assuming Shift_JIS and no evidence it can't be, done
|
||||||
|
if (canBeShiftJIS && ASSUME_SHIFT_JIS) {
|
||||||
|
return SHIFT_JIS;
|
||||||
|
}
|
||||||
|
if (canBeUTF8 && sawUTF8Start) {
|
||||||
|
return UTF8;
|
||||||
|
}
|
||||||
|
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
|
||||||
|
// - If we saw
|
||||||
|
// - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or
|
||||||
|
// - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1),
|
||||||
|
// - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
|
||||||
|
if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
|
||||||
|
return SHIFT_JIS;
|
||||||
|
}
|
||||||
|
// Otherwise, we default to ISO-8859-1 unless we know it can't be
|
||||||
|
if (!sawLatin1Supplement && canBeISO88591) {
|
||||||
|
return ISO88591;
|
||||||
|
}
|
||||||
|
// Otherwise, we take a wild guess with platform encoding
|
||||||
|
return PLATFORM_DEFAULT_ENCODING;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.NotFoundException;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
|
||||||
|
* It looks within a mostly white region of an image for a region of black and white, but mostly
|
||||||
|
* black. It returns the four corners of the region, as best it can determine.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class MonochromeRectangleDetector {
|
||||||
|
|
||||||
|
private static final int MAX_MODULES = 32;
|
||||||
|
|
||||||
|
private final BitMatrix image;
|
||||||
|
|
||||||
|
public MonochromeRectangleDetector(BitMatrix image) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
||||||
|
* white, in an image.</p>
|
||||||
|
*
|
||||||
|
* @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
|
||||||
|
* last points are opposed on the diagonal, as are the second and third. The first point will be
|
||||||
|
* the topmost point and the last, the bottommost. The second point will be leftmost and the
|
||||||
|
* third, the rightmost
|
||||||
|
* @throws NotFoundException if no Data Matrix Code can be found
|
||||||
|
*/
|
||||||
|
public ResultPoint[] detect() throws NotFoundException {
|
||||||
|
int height = image.getHeight();
|
||||||
|
int width = image.getWidth();
|
||||||
|
int halfHeight = height >> 1;
|
||||||
|
int halfWidth = width >> 1;
|
||||||
|
int deltaY = Math.max(1, height / (MAX_MODULES << 3));
|
||||||
|
int deltaX = Math.max(1, width / (MAX_MODULES << 3));
|
||||||
|
|
||||||
|
int top = 0;
|
||||||
|
int bottom = height;
|
||||||
|
int left = 0;
|
||||||
|
int right = width;
|
||||||
|
ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right,
|
||||||
|
halfHeight, -deltaY, top, bottom, halfWidth >> 1);
|
||||||
|
top = (int) pointA.getY() - 1;
|
||||||
|
ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right,
|
||||||
|
halfHeight, 0, top, bottom, halfHeight >> 1);
|
||||||
|
left = (int) pointB.getX() - 1;
|
||||||
|
ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right,
|
||||||
|
halfHeight, 0, top, bottom, halfHeight >> 1);
|
||||||
|
right = (int) pointC.getX() + 1;
|
||||||
|
ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right,
|
||||||
|
halfHeight, deltaY, top, bottom, halfWidth >> 1);
|
||||||
|
bottom = (int) pointD.getY() + 1;
|
||||||
|
|
||||||
|
// Go try to find point A again with better information -- might have been off at first.
|
||||||
|
pointA = findCornerFromCenter(halfWidth, 0, left, right,
|
||||||
|
halfHeight, -deltaY, top, bottom, halfWidth >> 2);
|
||||||
|
|
||||||
|
return new ResultPoint[] { pointA, pointB, pointC, pointD };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
|
||||||
|
* point which should be within the barcode.
|
||||||
|
*
|
||||||
|
* @param centerX center's x component (horizontal)
|
||||||
|
* @param deltaX same as deltaY but change in x per step instead
|
||||||
|
* @param left minimum value of x
|
||||||
|
* @param right maximum value of x
|
||||||
|
* @param centerY center's y component (vertical)
|
||||||
|
* @param deltaY change in y per step. If scanning up this is negative; down, positive;
|
||||||
|
* left or right, 0
|
||||||
|
* @param top minimum value of y to search through (meaningless when di == 0)
|
||||||
|
* @param bottom maximum value of y
|
||||||
|
* @param maxWhiteRun maximum run of white pixels that can still be considered to be within
|
||||||
|
* the barcode
|
||||||
|
* @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
|
||||||
|
* @throws NotFoundException if such a point cannot be found
|
||||||
|
*/
|
||||||
|
private ResultPoint findCornerFromCenter(int centerX, int deltaX, int left, int right,
|
||||||
|
int centerY, int deltaY, int top, int bottom, int maxWhiteRun) throws NotFoundException {
|
||||||
|
int[] lastRange = null;
|
||||||
|
for (int y = centerY, x = centerX;
|
||||||
|
y < bottom && y >= top && x < right && x >= left;
|
||||||
|
y += deltaY, x += deltaX) {
|
||||||
|
int[] range;
|
||||||
|
if (deltaX == 0) {
|
||||||
|
// horizontal slices, up and down
|
||||||
|
range = blackWhiteRange(y, maxWhiteRun, left, right, true);
|
||||||
|
} else {
|
||||||
|
// vertical slices, left and right
|
||||||
|
range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);
|
||||||
|
}
|
||||||
|
if (range == null) {
|
||||||
|
if (lastRange == null) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
// lastRange was found
|
||||||
|
if (deltaX == 0) {
|
||||||
|
int lastY = y - deltaY;
|
||||||
|
if (lastRange[0] < centerX) {
|
||||||
|
if (lastRange[1] > centerX) {
|
||||||
|
// straddle, choose one or the other based on direction
|
||||||
|
return new ResultPoint(deltaY > 0 ? lastRange[0] : lastRange[1], lastY);
|
||||||
|
}
|
||||||
|
return new ResultPoint(lastRange[0], lastY);
|
||||||
|
} else {
|
||||||
|
return new ResultPoint(lastRange[1], lastY);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int lastX = x - deltaX;
|
||||||
|
if (lastRange[0] < centerY) {
|
||||||
|
if (lastRange[1] > centerY) {
|
||||||
|
return new ResultPoint(lastX, deltaX < 0 ? lastRange[0] : lastRange[1]);
|
||||||
|
}
|
||||||
|
return new ResultPoint(lastX, lastRange[0]);
|
||||||
|
} else {
|
||||||
|
return new ResultPoint(lastX, lastRange[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastRange = range;
|
||||||
|
}
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
|
||||||
|
* be part of a Data Matrix barcode.
|
||||||
|
*
|
||||||
|
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
|
||||||
|
* where we are scanning. If scanning vertically it's the column, the fixed horizontal location
|
||||||
|
* @param maxWhiteRun largest run of white pixels that can still be considered part of the
|
||||||
|
* barcode region
|
||||||
|
* @param minDim minimum pixel location, horizontally or vertically, to consider
|
||||||
|
* @param maxDim maximum pixel location, horizontally or vertically, to consider
|
||||||
|
* @param horizontal if true, we're scanning left-right, instead of up-down
|
||||||
|
* @return int[] with start and end of found range, or null if no such range is found
|
||||||
|
* (e.g. only white was found)
|
||||||
|
*/
|
||||||
|
private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim,
|
||||||
|
boolean horizontal) {
|
||||||
|
|
||||||
|
int center = (minDim + maxDim) >> 1;
|
||||||
|
|
||||||
|
// Scan left/up first
|
||||||
|
int start = center;
|
||||||
|
while (start >= minDim) {
|
||||||
|
if (horizontal ? image.get(start, fixedDimension) : image.get(fixedDimension, start)) {
|
||||||
|
start--;
|
||||||
|
} else {
|
||||||
|
int whiteRunStart = start;
|
||||||
|
do {
|
||||||
|
start--;
|
||||||
|
} while (start >= minDim && !(horizontal ? image.get(start, fixedDimension) :
|
||||||
|
image.get(fixedDimension, start)));
|
||||||
|
int whiteRunSize = whiteRunStart - start;
|
||||||
|
if (start < minDim || whiteRunSize > maxWhiteRun) {
|
||||||
|
start = whiteRunStart;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start++;
|
||||||
|
|
||||||
|
// Then try right/down
|
||||||
|
int end = center;
|
||||||
|
while (end < maxDim) {
|
||||||
|
if (horizontal ? image.get(end, fixedDimension) : image.get(fixedDimension, end)) {
|
||||||
|
end++;
|
||||||
|
} else {
|
||||||
|
int whiteRunStart = end;
|
||||||
|
do {
|
||||||
|
end++;
|
||||||
|
} while (end < maxDim && !(horizontal ? image.get(end, fixedDimension) :
|
||||||
|
image.get(fixedDimension, end)));
|
||||||
|
int whiteRunSize = end - whiteRunStart;
|
||||||
|
if (end >= maxDim || whiteRunSize > maxWhiteRun) {
|
||||||
|
end = whiteRunStart;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end--;
|
||||||
|
|
||||||
|
return end > start ? new int[]{start, end} : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
316
src/com/google/zxing/common/detector/WhiteRectangleDetector.java
Normal file
316
src/com/google/zxing/common/detector/WhiteRectangleDetector.java
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common.detector;
|
||||||
|
|
||||||
|
import com.google.zxing.NotFoundException;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Detects a candidate barcode-like rectangular region within an image. It
|
||||||
|
* starts around the center of the image, increases the size of the candidate
|
||||||
|
* region until it finds a white rectangular region. By keeping track of the
|
||||||
|
* last black points it encountered, it determines the corners of the barcode.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author David Olivier
|
||||||
|
*/
|
||||||
|
public final class WhiteRectangleDetector {
|
||||||
|
|
||||||
|
private static final int INIT_SIZE = 40;
|
||||||
|
private static final int CORR = 1;
|
||||||
|
|
||||||
|
private final BitMatrix image;
|
||||||
|
private final int height;
|
||||||
|
private final int width;
|
||||||
|
|
||||||
|
public WhiteRectangleDetector(BitMatrix image) {
|
||||||
|
this.image = image;
|
||||||
|
height = image.getHeight();
|
||||||
|
width = image.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Detects a candidate barcode-like rectangular region within an image. It
|
||||||
|
* starts around the center of the image, increases the size of the candidate
|
||||||
|
* region until it finds a white rectangular region.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return {@link ResultPoint}[] describing the corners of the rectangular
|
||||||
|
* region. The first and last points are opposed on the diagonal, as
|
||||||
|
* are the second and third. The first point will be the topmost
|
||||||
|
* point and the last, the bottommost. The second point will be
|
||||||
|
* leftmost and the third, the rightmost
|
||||||
|
* @throws NotFoundException if no Data Matrix Code can be found
|
||||||
|
*/
|
||||||
|
public ResultPoint[] detect() throws NotFoundException {
|
||||||
|
|
||||||
|
int left = (width - INIT_SIZE) >> 1;
|
||||||
|
int right = (width + INIT_SIZE) >> 1;
|
||||||
|
int up = (height - INIT_SIZE) >> 1;
|
||||||
|
int down = (height + INIT_SIZE) >> 1;
|
||||||
|
boolean sizeExceeded = false;
|
||||||
|
boolean aBlackPointFoundOnBorder = true;
|
||||||
|
boolean atLeastOneBlackPointFoundOnBorder = false;
|
||||||
|
|
||||||
|
while (aBlackPointFoundOnBorder) {
|
||||||
|
|
||||||
|
aBlackPointFoundOnBorder = false;
|
||||||
|
|
||||||
|
// .....
|
||||||
|
// . |
|
||||||
|
// .....
|
||||||
|
boolean rightBorderNotWhite = true;
|
||||||
|
while (rightBorderNotWhite && right < width) {
|
||||||
|
rightBorderNotWhite = containsBlackPoint(up, down, right, false);
|
||||||
|
if (rightBorderNotWhite) {
|
||||||
|
right++;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (right >= width) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .....
|
||||||
|
// . .
|
||||||
|
// .___.
|
||||||
|
boolean bottomBorderNotWhite = true;
|
||||||
|
while (bottomBorderNotWhite && down < height) {
|
||||||
|
bottomBorderNotWhite = containsBlackPoint(left, right, down, true);
|
||||||
|
if (bottomBorderNotWhite) {
|
||||||
|
down++;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (down >= height) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .....
|
||||||
|
// | .
|
||||||
|
// .....
|
||||||
|
boolean leftBorderNotWhite = true;
|
||||||
|
while (leftBorderNotWhite && left >= 0) {
|
||||||
|
leftBorderNotWhite = containsBlackPoint(up, down, left, false);
|
||||||
|
if (leftBorderNotWhite) {
|
||||||
|
left--;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left < 0) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .___.
|
||||||
|
// . .
|
||||||
|
// .....
|
||||||
|
boolean topBorderNotWhite = true;
|
||||||
|
while (topBorderNotWhite && up >= 0) {
|
||||||
|
topBorderNotWhite = containsBlackPoint(left, right, up, true);
|
||||||
|
if (topBorderNotWhite) {
|
||||||
|
up--;
|
||||||
|
aBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up < 0) {
|
||||||
|
sizeExceeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aBlackPointFoundOnBorder) {
|
||||||
|
atLeastOneBlackPointFoundOnBorder = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) {
|
||||||
|
|
||||||
|
int maxSize = right - left;
|
||||||
|
|
||||||
|
ResultPoint z = null;
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
z = getBlackPointOnSegment(left, down - i, left + i, down);
|
||||||
|
if (z != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z == null) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultPoint t = null;
|
||||||
|
//go down right
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
t = getBlackPointOnSegment(left, up + i, left + i, up);
|
||||||
|
if (t != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == null) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultPoint x = null;
|
||||||
|
//go down left
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
x = getBlackPointOnSegment(right, up + i, right - i, up);
|
||||||
|
if (x != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x == null) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultPoint y = null;
|
||||||
|
//go up left
|
||||||
|
for (int i = 1; i < maxSize; i++) {
|
||||||
|
y = getBlackPointOnSegment(right, down - i, right - i, down);
|
||||||
|
if (y != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y == null) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return centerEdges(y, z, x, t);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends up being a bit faster than Math.round(). This merely rounds its
|
||||||
|
* argument to the nearest int, where x.5 rounds up.
|
||||||
|
*/
|
||||||
|
private static int round(float d) {
|
||||||
|
return (int) (d + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) {
|
||||||
|
int dist = distanceL2(aX, aY, bX, bY);
|
||||||
|
float xStep = (bX - aX) / dist;
|
||||||
|
float yStep = (bY - aY) / dist;
|
||||||
|
|
||||||
|
for (int i = 0; i < dist; i++) {
|
||||||
|
int x = round(aX + i * xStep);
|
||||||
|
int y = round(aY + i * yStep);
|
||||||
|
if (image.get(x, y)) {
|
||||||
|
return new ResultPoint(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int distanceL2(float aX, float aY, float bX, float bY) {
|
||||||
|
float xDiff = aX - bX;
|
||||||
|
float yDiff = aY - bY;
|
||||||
|
return round((float) Math.sqrt(xDiff * xDiff + yDiff * yDiff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* recenters the points of a constant distance towards the center
|
||||||
|
*
|
||||||
|
* @param y bottom most point
|
||||||
|
* @param z left most point
|
||||||
|
* @param x right most point
|
||||||
|
* @param t top most point
|
||||||
|
* @return {@link ResultPoint}[] describing the corners of the rectangular
|
||||||
|
* region. The first and last points are opposed on the diagonal, as
|
||||||
|
* are the second and third. The first point will be the topmost
|
||||||
|
* point and the last, the bottommost. The second point will be
|
||||||
|
* leftmost and the third, the rightmost
|
||||||
|
*/
|
||||||
|
private ResultPoint[] centerEdges(ResultPoint y, ResultPoint z,
|
||||||
|
ResultPoint x, ResultPoint t) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// t t
|
||||||
|
// z x
|
||||||
|
// x OR z
|
||||||
|
// y y
|
||||||
|
//
|
||||||
|
|
||||||
|
float yi = y.getX();
|
||||||
|
float yj = y.getY();
|
||||||
|
float zi = z.getX();
|
||||||
|
float zj = z.getY();
|
||||||
|
float xi = x.getX();
|
||||||
|
float xj = x.getY();
|
||||||
|
float ti = t.getX();
|
||||||
|
float tj = t.getY();
|
||||||
|
|
||||||
|
if (yi < width / 2) {
|
||||||
|
return new ResultPoint[]{
|
||||||
|
new ResultPoint(ti - CORR, tj + CORR),
|
||||||
|
new ResultPoint(zi + CORR, zj + CORR),
|
||||||
|
new ResultPoint(xi - CORR, xj - CORR),
|
||||||
|
new ResultPoint(yi + CORR, yj - CORR)};
|
||||||
|
} else {
|
||||||
|
return new ResultPoint[]{
|
||||||
|
new ResultPoint(ti + CORR, tj + CORR),
|
||||||
|
new ResultPoint(zi + CORR, zj - CORR),
|
||||||
|
new ResultPoint(xi - CORR, xj + CORR),
|
||||||
|
new ResultPoint(yi - CORR, yj - CORR)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a segment contains a black point
|
||||||
|
*
|
||||||
|
* @param a min value of the scanned coordinate
|
||||||
|
* @param b max value of the scanned coordinate
|
||||||
|
* @param fixed value of fixed coordinate
|
||||||
|
* @param horizontal set to true if scan must be horizontal, false if vertical
|
||||||
|
* @return true if a black point has been found, else false.
|
||||||
|
*/
|
||||||
|
private boolean containsBlackPoint(int a, int b, int fixed, boolean horizontal) {
|
||||||
|
|
||||||
|
if (horizontal) {
|
||||||
|
for (int x = a; x <= b; x++) {
|
||||||
|
if (image.get(x, fixed)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int y = a; y <= b; y++) {
|
||||||
|
if (image.get(fixed, y)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
139
src/com/google/zxing/common/reedsolomon/GF256.java
Normal file
139
src/com/google/zxing/common/reedsolomon/GF256.java
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common.reedsolomon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This class contains utility methods for performing mathematical operations over
|
||||||
|
* the Galois Field GF(256). Operations use a given primitive polynomial in calculations.</p>
|
||||||
|
*
|
||||||
|
* <p>Throughout this package, elements of GF(256) are represented as an <code>int</code>
|
||||||
|
* for convenience and speed (but at the cost of memory).
|
||||||
|
* Only the bottom 8 bits are really used.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class GF256 {
|
||||||
|
|
||||||
|
public static final GF256 QR_CODE_FIELD = new GF256(0x011D); // x^8 + x^4 + x^3 + x^2 + 1
|
||||||
|
public static final GF256 DATA_MATRIX_FIELD = new GF256(0x012D); // x^8 + x^5 + x^3 + x^2 + 1
|
||||||
|
|
||||||
|
private final int[] expTable;
|
||||||
|
private final int[] logTable;
|
||||||
|
private final GF256Poly zero;
|
||||||
|
private final GF256Poly one;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a representation of GF(256) using the given primitive polynomial.
|
||||||
|
*
|
||||||
|
* @param primitive irreducible polynomial whose coefficients are represented by
|
||||||
|
* the bits of an int, where the least-significant bit represents the constant
|
||||||
|
* coefficient
|
||||||
|
*/
|
||||||
|
private GF256(int primitive) {
|
||||||
|
expTable = new int[256];
|
||||||
|
logTable = new int[256];
|
||||||
|
int x = 1;
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
expTable[i] = x;
|
||||||
|
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
|
||||||
|
if (x >= 0x100) {
|
||||||
|
x ^= primitive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 255; i++) {
|
||||||
|
logTable[expTable[i]] = i;
|
||||||
|
}
|
||||||
|
// logTable[0] == 0 but this should never be used
|
||||||
|
zero = new GF256Poly(this, new int[]{0});
|
||||||
|
one = new GF256Poly(this, new int[]{1});
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly getZero() {
|
||||||
|
return zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly getOne() {
|
||||||
|
return one;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the monomial representing coefficient * x^degree
|
||||||
|
*/
|
||||||
|
GF256Poly buildMonomial(int degree, int coefficient) {
|
||||||
|
if (degree < 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (coefficient == 0) {
|
||||||
|
return zero;
|
||||||
|
}
|
||||||
|
int[] coefficients = new int[degree + 1];
|
||||||
|
coefficients[0] = coefficient;
|
||||||
|
return new GF256Poly(this, coefficients);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements both addition and subtraction -- they are the same in GF(256).
|
||||||
|
*
|
||||||
|
* @return sum/difference of a and b
|
||||||
|
*/
|
||||||
|
static int addOrSubtract(int a, int b) {
|
||||||
|
return a ^ b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 2 to the power of a in GF(256)
|
||||||
|
*/
|
||||||
|
int exp(int a) {
|
||||||
|
return expTable[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return base 2 log of a in GF(256)
|
||||||
|
*/
|
||||||
|
int log(int a) {
|
||||||
|
if (a == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
return logTable[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return multiplicative inverse of a
|
||||||
|
*/
|
||||||
|
int inverse(int a) {
|
||||||
|
if (a == 0) {
|
||||||
|
throw new ArithmeticException();
|
||||||
|
}
|
||||||
|
return expTable[255 - logTable[a]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a
|
||||||
|
* @param b
|
||||||
|
* @return product of a and b in GF(256)
|
||||||
|
*/
|
||||||
|
int multiply(int a, int b) {
|
||||||
|
if (a == 0 || b == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int logSum = logTable[a] + logTable[b];
|
||||||
|
// index is a sped-up alternative to logSum % 255 since sum
|
||||||
|
// is in [0,510]. Thanks to jmsachs for the idea
|
||||||
|
return expTable[(logSum & 0xFF) + (logSum >>> 8)];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
263
src/com/google/zxing/common/reedsolomon/GF256Poly.java
Normal file
263
src/com/google/zxing/common/reedsolomon/GF256Poly.java
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common.reedsolomon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Represents a polynomial whose coefficients are elements of GF(256).
|
||||||
|
* Instances of this class are immutable.</p>
|
||||||
|
*
|
||||||
|
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||||
|
* port of his C++ Reed-Solomon implementation.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class GF256Poly {
|
||||||
|
|
||||||
|
private final GF256 field;
|
||||||
|
private final int[] coefficients;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param field the {@link GF256} instance representing the field to use
|
||||||
|
* to perform computations
|
||||||
|
* @param coefficients coefficients as ints representing elements of GF(256), arranged
|
||||||
|
* from most significant (highest-power term) coefficient to least significant
|
||||||
|
* @throws IllegalArgumentException if argument is null or empty,
|
||||||
|
* or if leading coefficient is 0 and this is not a
|
||||||
|
* constant polynomial (that is, it is not the monomial "0")
|
||||||
|
*/
|
||||||
|
GF256Poly(GF256 field, int[] coefficients) {
|
||||||
|
if (coefficients == null || coefficients.length == 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.field = field;
|
||||||
|
int coefficientsLength = coefficients.length;
|
||||||
|
if (coefficientsLength > 1 && coefficients[0] == 0) {
|
||||||
|
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||||
|
int firstNonZero = 1;
|
||||||
|
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
|
||||||
|
firstNonZero++;
|
||||||
|
}
|
||||||
|
if (firstNonZero == coefficientsLength) {
|
||||||
|
this.coefficients = field.getZero().coefficients;
|
||||||
|
} else {
|
||||||
|
this.coefficients = new int[coefficientsLength - firstNonZero];
|
||||||
|
System.arraycopy(coefficients,
|
||||||
|
firstNonZero,
|
||||||
|
this.coefficients,
|
||||||
|
0,
|
||||||
|
this.coefficients.length);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.coefficients = coefficients;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] getCoefficients() {
|
||||||
|
return coefficients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return degree of this polynomial
|
||||||
|
*/
|
||||||
|
int getDegree() {
|
||||||
|
return coefficients.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true iff this polynomial is the monomial "0"
|
||||||
|
*/
|
||||||
|
boolean isZero() {
|
||||||
|
return coefficients[0] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return coefficient of x^degree term in this polynomial
|
||||||
|
*/
|
||||||
|
int getCoefficient(int degree) {
|
||||||
|
return coefficients[coefficients.length - 1 - degree];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return evaluation of this polynomial at a given point
|
||||||
|
*/
|
||||||
|
int evaluateAt(int a) {
|
||||||
|
if (a == 0) {
|
||||||
|
// Just return the x^0 coefficient
|
||||||
|
return getCoefficient(0);
|
||||||
|
}
|
||||||
|
int size = coefficients.length;
|
||||||
|
if (a == 1) {
|
||||||
|
// Just the sum of the coefficients
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
result = GF256.addOrSubtract(result, coefficients[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int result = coefficients[0];
|
||||||
|
for (int i = 1; i < size; i++) {
|
||||||
|
result = GF256.addOrSubtract(field.multiply(a, result), coefficients[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly addOrSubtract(GF256Poly other) {
|
||||||
|
if (!field.equals(other.field)) {
|
||||||
|
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
|
||||||
|
}
|
||||||
|
if (isZero()) {
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
if (other.isZero()) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] smallerCoefficients = this.coefficients;
|
||||||
|
int[] largerCoefficients = other.coefficients;
|
||||||
|
if (smallerCoefficients.length > largerCoefficients.length) {
|
||||||
|
int[] temp = smallerCoefficients;
|
||||||
|
smallerCoefficients = largerCoefficients;
|
||||||
|
largerCoefficients = temp;
|
||||||
|
}
|
||||||
|
int[] sumDiff = new int[largerCoefficients.length];
|
||||||
|
int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
|
||||||
|
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||||
|
System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
|
||||||
|
|
||||||
|
for (int i = lengthDiff; i < largerCoefficients.length; i++) {
|
||||||
|
sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GF256Poly(field, sumDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly multiply(GF256Poly other) {
|
||||||
|
if (!field.equals(other.field)) {
|
||||||
|
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
|
||||||
|
}
|
||||||
|
if (isZero() || other.isZero()) {
|
||||||
|
return field.getZero();
|
||||||
|
}
|
||||||
|
int[] aCoefficients = this.coefficients;
|
||||||
|
int aLength = aCoefficients.length;
|
||||||
|
int[] bCoefficients = other.coefficients;
|
||||||
|
int bLength = bCoefficients.length;
|
||||||
|
int[] product = new int[aLength + bLength - 1];
|
||||||
|
for (int i = 0; i < aLength; i++) {
|
||||||
|
int aCoeff = aCoefficients[i];
|
||||||
|
for (int j = 0; j < bLength; j++) {
|
||||||
|
product[i + j] = GF256.addOrSubtract(product[i + j],
|
||||||
|
field.multiply(aCoeff, bCoefficients[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new GF256Poly(field, product);
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly multiply(int scalar) {
|
||||||
|
if (scalar == 0) {
|
||||||
|
return field.getZero();
|
||||||
|
}
|
||||||
|
if (scalar == 1) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
int size = coefficients.length;
|
||||||
|
int[] product = new int[size];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
product[i] = field.multiply(coefficients[i], scalar);
|
||||||
|
}
|
||||||
|
return new GF256Poly(field, product);
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly multiplyByMonomial(int degree, int coefficient) {
|
||||||
|
if (degree < 0) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (coefficient == 0) {
|
||||||
|
return field.getZero();
|
||||||
|
}
|
||||||
|
int size = coefficients.length;
|
||||||
|
int[] product = new int[size + degree];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
product[i] = field.multiply(coefficients[i], coefficient);
|
||||||
|
}
|
||||||
|
return new GF256Poly(field, product);
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly[] divide(GF256Poly other) {
|
||||||
|
if (!field.equals(other.field)) {
|
||||||
|
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
|
||||||
|
}
|
||||||
|
if (other.isZero()) {
|
||||||
|
throw new IllegalArgumentException("Divide by 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly quotient = field.getZero();
|
||||||
|
GF256Poly remainder = this;
|
||||||
|
|
||||||
|
int denominatorLeadingTerm = other.getCoefficient(other.getDegree());
|
||||||
|
int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);
|
||||||
|
|
||||||
|
while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
|
||||||
|
int degreeDifference = remainder.getDegree() - other.getDegree();
|
||||||
|
int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
|
||||||
|
GF256Poly term = other.multiplyByMonomial(degreeDifference, scale);
|
||||||
|
GF256Poly iterationQuotient = field.buildMonomial(degreeDifference, scale);
|
||||||
|
quotient = quotient.addOrSubtract(iterationQuotient);
|
||||||
|
remainder = remainder.addOrSubtract(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GF256Poly[] { quotient, remainder };
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer result = new StringBuffer(8 * getDegree());
|
||||||
|
for (int degree = getDegree(); degree >= 0; degree--) {
|
||||||
|
int coefficient = getCoefficient(degree);
|
||||||
|
if (coefficient != 0) {
|
||||||
|
if (coefficient < 0) {
|
||||||
|
result.append(" - ");
|
||||||
|
coefficient = -coefficient;
|
||||||
|
} else {
|
||||||
|
if (result.length() > 0) {
|
||||||
|
result.append(" + ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (degree == 0 || coefficient != 1) {
|
||||||
|
int alphaPower = field.log(coefficient);
|
||||||
|
if (alphaPower == 0) {
|
||||||
|
result.append('1');
|
||||||
|
} else if (alphaPower == 1) {
|
||||||
|
result.append('a');
|
||||||
|
} else {
|
||||||
|
result.append("a^");
|
||||||
|
result.append(alphaPower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (degree != 0) {
|
||||||
|
if (degree == 1) {
|
||||||
|
result.append('x');
|
||||||
|
} else {
|
||||||
|
result.append("x^");
|
||||||
|
result.append(degree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
194
src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
Normal file
194
src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common.reedsolomon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Implements Reed-Solomon decoding, as the name implies.</p>
|
||||||
|
*
|
||||||
|
* <p>The algorithm will not be explained here, but the following references were helpful
|
||||||
|
* in creating this implementation:</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Bruce Maggs.
|
||||||
|
* <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
|
||||||
|
* "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
|
||||||
|
* <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
|
||||||
|
* "Chapter 5. Generalized Reed-Solomon Codes"</a>
|
||||||
|
* (see discussion of Euclidean algorithm)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||||
|
* port of his C++ Reed-Solomon implementation.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author William Rucklidge
|
||||||
|
* @author sanfordsquires
|
||||||
|
*/
|
||||||
|
public final class ReedSolomonDecoder {
|
||||||
|
|
||||||
|
private final GF256 field;
|
||||||
|
|
||||||
|
public ReedSolomonDecoder(GF256 field) {
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Decodes given set of received codewords, which include both data and error-correction
|
||||||
|
* codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
|
||||||
|
* in the input.</p>
|
||||||
|
*
|
||||||
|
* @param received data and error-correction codewords
|
||||||
|
* @param twoS number of error-correction codewords available
|
||||||
|
* @throws ReedSolomonException if decoding fails for any reason
|
||||||
|
*/
|
||||||
|
public void decode(int[] received, int twoS) throws ReedSolomonException {
|
||||||
|
GF256Poly poly = new GF256Poly(field, received);
|
||||||
|
int[] syndromeCoefficients = new int[twoS];
|
||||||
|
boolean dataMatrix = field.equals(GF256.DATA_MATRIX_FIELD);
|
||||||
|
boolean noError = true;
|
||||||
|
for (int i = 0; i < twoS; i++) {
|
||||||
|
// Thanks to sanfordsquires for this fix:
|
||||||
|
int eval = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i));
|
||||||
|
syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
|
||||||
|
if (eval != 0) {
|
||||||
|
noError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (noError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GF256Poly syndrome = new GF256Poly(field, syndromeCoefficients);
|
||||||
|
GF256Poly[] sigmaOmega =
|
||||||
|
runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
|
||||||
|
GF256Poly sigma = sigmaOmega[0];
|
||||||
|
GF256Poly omega = sigmaOmega[1];
|
||||||
|
int[] errorLocations = findErrorLocations(sigma);
|
||||||
|
int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations, dataMatrix);
|
||||||
|
for (int i = 0; i < errorLocations.length; i++) {
|
||||||
|
int position = received.length - 1 - field.log(errorLocations[i]);
|
||||||
|
if (position < 0) {
|
||||||
|
throw new ReedSolomonException("Bad error location");
|
||||||
|
}
|
||||||
|
received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GF256Poly[] runEuclideanAlgorithm(GF256Poly a, GF256Poly b, int R)
|
||||||
|
throws ReedSolomonException {
|
||||||
|
// Assume a's degree is >= b's
|
||||||
|
if (a.getDegree() < b.getDegree()) {
|
||||||
|
GF256Poly temp = a;
|
||||||
|
a = b;
|
||||||
|
b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
GF256Poly rLast = a;
|
||||||
|
GF256Poly r = b;
|
||||||
|
GF256Poly sLast = field.getOne();
|
||||||
|
GF256Poly s = field.getZero();
|
||||||
|
GF256Poly tLast = field.getZero();
|
||||||
|
GF256Poly t = field.getOne();
|
||||||
|
|
||||||
|
// Run Euclidean algorithm until r's degree is less than R/2
|
||||||
|
while (r.getDegree() >= R / 2) {
|
||||||
|
GF256Poly rLastLast = rLast;
|
||||||
|
GF256Poly sLastLast = sLast;
|
||||||
|
GF256Poly tLastLast = tLast;
|
||||||
|
rLast = r;
|
||||||
|
sLast = s;
|
||||||
|
tLast = t;
|
||||||
|
|
||||||
|
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||||
|
if (rLast.isZero()) {
|
||||||
|
// Oops, Euclidean algorithm already terminated?
|
||||||
|
throw new ReedSolomonException("r_{i-1} was zero");
|
||||||
|
}
|
||||||
|
r = rLastLast;
|
||||||
|
GF256Poly q = field.getZero();
|
||||||
|
int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
|
||||||
|
int dltInverse = field.inverse(denominatorLeadingTerm);
|
||||||
|
while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
|
||||||
|
int degreeDiff = r.getDegree() - rLast.getDegree();
|
||||||
|
int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
|
||||||
|
q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
|
||||||
|
r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
s = q.multiply(sLast).addOrSubtract(sLastLast);
|
||||||
|
t = q.multiply(tLast).addOrSubtract(tLastLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sigmaTildeAtZero = t.getCoefficient(0);
|
||||||
|
if (sigmaTildeAtZero == 0) {
|
||||||
|
throw new ReedSolomonException("sigmaTilde(0) was zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
int inverse = field.inverse(sigmaTildeAtZero);
|
||||||
|
GF256Poly sigma = t.multiply(inverse);
|
||||||
|
GF256Poly omega = r.multiply(inverse);
|
||||||
|
return new GF256Poly[]{sigma, omega};
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] findErrorLocations(GF256Poly errorLocator) throws ReedSolomonException {
|
||||||
|
// This is a direct application of Chien's search
|
||||||
|
int numErrors = errorLocator.getDegree();
|
||||||
|
if (numErrors == 1) { // shortcut
|
||||||
|
return new int[] { errorLocator.getCoefficient(1) };
|
||||||
|
}
|
||||||
|
int[] result = new int[numErrors];
|
||||||
|
int e = 0;
|
||||||
|
for (int i = 1; i < 256 && e < numErrors; i++) {
|
||||||
|
if (errorLocator.evaluateAt(i) == 0) {
|
||||||
|
result[e] = field.inverse(i);
|
||||||
|
e++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e != numErrors) {
|
||||||
|
throw new ReedSolomonException("Error locator degree does not match number of roots");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] findErrorMagnitudes(GF256Poly errorEvaluator, int[] errorLocations, boolean dataMatrix) {
|
||||||
|
// This is directly applying Forney's Formula
|
||||||
|
int s = errorLocations.length;
|
||||||
|
int[] result = new int[s];
|
||||||
|
for (int i = 0; i < s; i++) {
|
||||||
|
int xiInverse = field.inverse(errorLocations[i]);
|
||||||
|
int denominator = 1;
|
||||||
|
for (int j = 0; j < s; j++) {
|
||||||
|
if (i != j) {
|
||||||
|
//denominator = field.multiply(denominator,
|
||||||
|
// GF256.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
|
||||||
|
// Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
|
||||||
|
// Below is a funny-looking workaround from Steven Parkes
|
||||||
|
int term = field.multiply(errorLocations[j], xiInverse);
|
||||||
|
int termPlus1 = ((term & 0x1) == 0) ? (term | 1) : (term & ~1);
|
||||||
|
denominator = field.multiply(denominator, termPlus1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),
|
||||||
|
field.inverse(denominator));
|
||||||
|
// Thanks to sanfordsquires for this fix:
|
||||||
|
if (dataMatrix) {
|
||||||
|
result[i] = field.multiply(result[i], xiInverse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common.reedsolomon;
|
||||||
|
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Implements Reed-Solomon enbcoding, as the name implies.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author William Rucklidge
|
||||||
|
*/
|
||||||
|
public final class ReedSolomonEncoder {
|
||||||
|
|
||||||
|
private final GF256 field;
|
||||||
|
private final Vector cachedGenerators;
|
||||||
|
|
||||||
|
public ReedSolomonEncoder(GF256 field) {
|
||||||
|
if (!GF256.QR_CODE_FIELD.equals(field)) {
|
||||||
|
throw new IllegalArgumentException("Only QR Code is supported at this time");
|
||||||
|
}
|
||||||
|
this.field = field;
|
||||||
|
this.cachedGenerators = new Vector();
|
||||||
|
cachedGenerators.addElement(new GF256Poly(field, new int[] { 1 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
private GF256Poly buildGenerator(int degree) {
|
||||||
|
if (degree >= cachedGenerators.size()) {
|
||||||
|
GF256Poly lastGenerator = (GF256Poly) cachedGenerators.elementAt(cachedGenerators.size() - 1);
|
||||||
|
for (int d = cachedGenerators.size(); d <= degree; d++) {
|
||||||
|
GF256Poly nextGenerator = lastGenerator.multiply(new GF256Poly(field, new int[] { 1, field.exp(d - 1) }));
|
||||||
|
cachedGenerators.addElement(nextGenerator);
|
||||||
|
lastGenerator = nextGenerator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (GF256Poly) cachedGenerators.elementAt(degree);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(int[] toEncode, int ecBytes) {
|
||||||
|
if (ecBytes == 0) {
|
||||||
|
throw new IllegalArgumentException("No error correction bytes");
|
||||||
|
}
|
||||||
|
int dataBytes = toEncode.length - ecBytes;
|
||||||
|
if (dataBytes <= 0) {
|
||||||
|
throw new IllegalArgumentException("No data bytes provided");
|
||||||
|
}
|
||||||
|
GF256Poly generator = buildGenerator(ecBytes);
|
||||||
|
int[] infoCoefficients = new int[dataBytes];
|
||||||
|
System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
|
||||||
|
GF256Poly info = new GF256Poly(field, infoCoefficients);
|
||||||
|
info = info.multiplyByMonomial(ecBytes, 1);
|
||||||
|
GF256Poly remainder = info.divide(generator)[1];
|
||||||
|
int[] coefficients = remainder.getCoefficients();
|
||||||
|
int numZeroCoefficients = ecBytes - coefficients.length;
|
||||||
|
for (int i = 0; i < numZeroCoefficients; i++) {
|
||||||
|
toEncode[dataBytes + i] = 0;
|
||||||
|
}
|
||||||
|
System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.common.reedsolomon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
|
||||||
|
* there are too many errors to correct.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class ReedSolomonException extends Exception {
|
||||||
|
|
||||||
|
public ReedSolomonException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
161
src/com/google/zxing/datamatrix/DataMatrixReader.java
Normal file
161
src/com/google/zxing/datamatrix/DataMatrixReader.java
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.datamatrix;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.BinaryBitmap;
|
||||||
|
import com.google.zxing.ChecksumException;
|
||||||
|
import com.google.zxing.DecodeHintType;
|
||||||
|
import com.google.zxing.FormatException;
|
||||||
|
import com.google.zxing.NotFoundException;
|
||||||
|
import com.google.zxing.Reader;
|
||||||
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.ResultMetadataType;
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.common.DecoderResult;
|
||||||
|
import com.google.zxing.common.DetectorResult;
|
||||||
|
import com.google.zxing.datamatrix.decoder.Decoder;
|
||||||
|
import com.google.zxing.datamatrix.detector.Detector;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation can detect and decode Data Matrix codes in an image.
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class DataMatrixReader implements Reader {
|
||||||
|
|
||||||
|
private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||||
|
|
||||||
|
private final Decoder decoder = new Decoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates and decodes a Data Matrix code in an image.
|
||||||
|
*
|
||||||
|
* @return a String representing the content encoded by the Data Matrix code
|
||||||
|
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||||
|
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||||
|
* @throws ChecksumException if error correction fails
|
||||||
|
*/
|
||||||
|
public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {
|
||||||
|
return decode(image, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result decode(BinaryBitmap image, Hashtable hints)
|
||||||
|
throws NotFoundException, ChecksumException, FormatException {
|
||||||
|
DecoderResult decoderResult;
|
||||||
|
ResultPoint[] points;
|
||||||
|
if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
|
||||||
|
BitMatrix bits = extractPureBits(image.getBlackMatrix());
|
||||||
|
decoderResult = decoder.decode(bits);
|
||||||
|
points = NO_POINTS;
|
||||||
|
} else {
|
||||||
|
DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect();
|
||||||
|
decoderResult = decoder.decode(detectorResult.getBits());
|
||||||
|
points = detectorResult.getPoints();
|
||||||
|
}
|
||||||
|
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,
|
||||||
|
BarcodeFormat.DATA_MATRIX);
|
||||||
|
if (decoderResult.getByteSegments() != null) {
|
||||||
|
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.getByteSegments());
|
||||||
|
}
|
||||||
|
if (decoderResult.getECLevel() != null) {
|
||||||
|
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.getECLevel().toString());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image
|
||||||
|
* which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border
|
||||||
|
* around it. This is a specialized method that works exceptionally fast in this special
|
||||||
|
* case.
|
||||||
|
*
|
||||||
|
* @see com.google.zxing.qrcode.QRCodeReader#extractPureBits(BitMatrix)
|
||||||
|
*/
|
||||||
|
private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {
|
||||||
|
|
||||||
|
int height = image.getHeight();
|
||||||
|
int width = image.getWidth();
|
||||||
|
int minDimension = Math.min(height, width);
|
||||||
|
|
||||||
|
// And then keep tracking across the top-left black module to determine module size
|
||||||
|
//int moduleEnd = borderWidth;
|
||||||
|
int[] leftTopBlack = image.getTopLeftOnBit();
|
||||||
|
if (leftTopBlack == null) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
int x = leftTopBlack[0];
|
||||||
|
int y = leftTopBlack[1];
|
||||||
|
while (x < minDimension && y < minDimension && image.get(x, y)) {
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
if (x == minDimension) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
int moduleSize = x - leftTopBlack[0];
|
||||||
|
|
||||||
|
// And now find where the rightmost black module on the first row ends
|
||||||
|
int rowEndOfSymbol = width - 1;
|
||||||
|
while (rowEndOfSymbol >= 0 && !image.get(rowEndOfSymbol, y)) {
|
||||||
|
rowEndOfSymbol--;
|
||||||
|
}
|
||||||
|
if (rowEndOfSymbol < 0) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
rowEndOfSymbol++;
|
||||||
|
|
||||||
|
// Make sure width of barcode is a multiple of module size
|
||||||
|
if ((rowEndOfSymbol - x) % moduleSize != 0) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
int dimension = 2 + ((rowEndOfSymbol - x) / moduleSize);
|
||||||
|
|
||||||
|
y += moduleSize;
|
||||||
|
|
||||||
|
// Push in the "border" by half the module width so that we start
|
||||||
|
// sampling in the middle of the module. Just in case the image is a
|
||||||
|
// little off, this will help recover.
|
||||||
|
x -= moduleSize >> 1;
|
||||||
|
y -= moduleSize >> 1;
|
||||||
|
|
||||||
|
if ((x + (dimension - 1) * moduleSize) >= width ||
|
||||||
|
(y + (dimension - 1) * moduleSize) >= height) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now just read off the bits
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
int iOffset = y + i * moduleSize;
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
if (image.get(x + j * moduleSize, iOffset)) {
|
||||||
|
bits.set(j, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
446
src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java
Normal file
446
src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.FormatException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
final class BitMatrixParser {
|
||||||
|
|
||||||
|
private final BitMatrix mappingBitMatrix;
|
||||||
|
private final BitMatrix readMappingMatrix;
|
||||||
|
private final Version version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bitMatrix {@link BitMatrix} to parse
|
||||||
|
* @throws FormatException if dimension is < 10 or > 144 or not 0 mod 2
|
||||||
|
*/
|
||||||
|
BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
|
||||||
|
int dimension = bitMatrix.getHeight();
|
||||||
|
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
version = readVersion(bitMatrix);
|
||||||
|
this.mappingBitMatrix = extractDataRegion(bitMatrix);
|
||||||
|
// TODO(bbrown): Make this work for rectangular symbols
|
||||||
|
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates the version object based on the dimension of the original bit matrix from
|
||||||
|
* the datamatrix code.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>
|
||||||
|
*
|
||||||
|
* @param bitMatrix Original {@link BitMatrix} including alignment patterns
|
||||||
|
* @return {@link Version} encapsulating the Data Matrix Code's "version"
|
||||||
|
* @throws FormatException if the dimensions of the mapping matrix are not valid
|
||||||
|
* Data Matrix dimensions.
|
||||||
|
*/
|
||||||
|
Version readVersion(BitMatrix bitMatrix) throws FormatException {
|
||||||
|
|
||||||
|
if (version != null) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bbrown): make this work for rectangular dimensions as well.
|
||||||
|
int numRows = bitMatrix.getHeight();
|
||||||
|
int numColumns = numRows;
|
||||||
|
|
||||||
|
return Version.getVersionForDimensions(numRows, numColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns)
|
||||||
|
* in the correct order in order to reconstitute the codewords bytes contained within the
|
||||||
|
* Data Matrix Code.</p>
|
||||||
|
*
|
||||||
|
* @return bytes encoded within the Data Matrix Code
|
||||||
|
* @throws FormatException if the exact number of bytes expected is not read
|
||||||
|
*/
|
||||||
|
byte[] readCodewords() throws FormatException {
|
||||||
|
|
||||||
|
byte[] result = new byte[version.getTotalCodewords()];
|
||||||
|
int resultOffset = 0;
|
||||||
|
|
||||||
|
int row = 4;
|
||||||
|
int column = 0;
|
||||||
|
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
|
||||||
|
int numRows = mappingBitMatrix.getHeight();
|
||||||
|
int numColumns = numRows;
|
||||||
|
|
||||||
|
boolean corner1Read = false;
|
||||||
|
boolean corner2Read = false;
|
||||||
|
boolean corner3Read = false;
|
||||||
|
boolean corner4Read = false;
|
||||||
|
|
||||||
|
// Read all of the codewords
|
||||||
|
do {
|
||||||
|
// Check the four corner cases
|
||||||
|
if ((row == numRows) && (column == 0) && !corner1Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner1(numRows, numColumns);
|
||||||
|
row -= 2;
|
||||||
|
column +=2;
|
||||||
|
corner1Read = true;
|
||||||
|
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner2(numRows, numColumns);
|
||||||
|
row -= 2;
|
||||||
|
column +=2;
|
||||||
|
corner2Read = true;
|
||||||
|
} else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner3(numRows, numColumns);
|
||||||
|
row -= 2;
|
||||||
|
column +=2;
|
||||||
|
corner3Read = true;
|
||||||
|
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {
|
||||||
|
result[resultOffset++] = (byte) readCorner4(numRows, numColumns);
|
||||||
|
row -= 2;
|
||||||
|
column +=2;
|
||||||
|
corner4Read = true;
|
||||||
|
} else {
|
||||||
|
// Sweep upward diagonally to the right
|
||||||
|
do {
|
||||||
|
if ((row < numRows) && (column >= 0) && !readMappingMatrix.get(column, row)) {
|
||||||
|
result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);
|
||||||
|
}
|
||||||
|
row -= 2;
|
||||||
|
column +=2;
|
||||||
|
} while ((row >= 0) && (column < numColumns));
|
||||||
|
row += 1;
|
||||||
|
column +=3;
|
||||||
|
|
||||||
|
// Sweep downward diagonally to the left
|
||||||
|
do {
|
||||||
|
if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get(column, row)) {
|
||||||
|
result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);
|
||||||
|
}
|
||||||
|
row += 2;
|
||||||
|
column -=2;
|
||||||
|
} while ((row < numRows) && (column >= 0));
|
||||||
|
row += 3;
|
||||||
|
column +=1;
|
||||||
|
}
|
||||||
|
} while ((row < numRows) || (column < numColumns));
|
||||||
|
|
||||||
|
if (resultOffset != version.getTotalCodewords()) {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
|
||||||
|
*
|
||||||
|
* @param row Row to read in the mapping matrix
|
||||||
|
* @param column Column to read in the mapping matrix
|
||||||
|
* @param numRows Number of rows in the mapping matrix
|
||||||
|
* @param numColumns Number of columns in the mapping matrix
|
||||||
|
* @return value of the given bit in the mapping matrix
|
||||||
|
*/
|
||||||
|
boolean readModule(int row, int column, int numRows, int numColumns) {
|
||||||
|
// Adjust the row and column indices based on boundary wrapping
|
||||||
|
if (row < 0) {
|
||||||
|
row += numRows;
|
||||||
|
column += 4 - ((numRows + 4) & 0x07);
|
||||||
|
}
|
||||||
|
if (column < 0) {
|
||||||
|
column += numColumns;
|
||||||
|
row += 4 - ((numColumns + 4) & 0x07);
|
||||||
|
}
|
||||||
|
readMappingMatrix.set(column, row);
|
||||||
|
return mappingBitMatrix.get(column, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the standard Utah-shaped pattern.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
|
||||||
|
*
|
||||||
|
* @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
|
||||||
|
* @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
|
||||||
|
* @param numRows Number of rows in the mapping matrix
|
||||||
|
* @param numColumns Number of columns in the mapping matrix
|
||||||
|
* @return byte from the utah shape
|
||||||
|
*/
|
||||||
|
int readUtah(int row, int column, int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(row - 2, column - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 2, column - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 1, column - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 1, column - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row - 1, column, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row, column - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row, column - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(row, column, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 1.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.3</p>
|
||||||
|
*
|
||||||
|
* @param numRows Number of rows in the mapping matrix
|
||||||
|
* @param numColumns Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 1
|
||||||
|
*/
|
||||||
|
int readCorner1(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 2.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.4</p>
|
||||||
|
*
|
||||||
|
* @param numRows Number of rows in the mapping matrix
|
||||||
|
* @param numColumns Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 2
|
||||||
|
*/
|
||||||
|
int readCorner2(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 4, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 3.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.5</p>
|
||||||
|
*
|
||||||
|
* @param numRows Number of rows in the mapping matrix
|
||||||
|
* @param numColumns Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 3
|
||||||
|
*/
|
||||||
|
int readCorner3(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 3, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Reads the 8 bits of the special corner condition 4.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, Figure F.6</p>
|
||||||
|
*
|
||||||
|
* @param numRows Number of rows in the mapping matrix
|
||||||
|
* @param numColumns Number of columns in the mapping matrix
|
||||||
|
* @return byte from the Corner condition 4
|
||||||
|
*/
|
||||||
|
int readCorner4(int numRows, int numColumns) {
|
||||||
|
int currentByte = 0;
|
||||||
|
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
currentByte <<= 1;
|
||||||
|
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
||||||
|
currentByte |= 1;
|
||||||
|
}
|
||||||
|
return currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Extracts the data region from a {@link BitMatrix} that contains
|
||||||
|
* alignment patterns.</p>
|
||||||
|
*
|
||||||
|
* @param bitMatrix Original {@link BitMatrix} with alignment patterns
|
||||||
|
* @return BitMatrix that has the alignment patterns removed
|
||||||
|
*/
|
||||||
|
BitMatrix extractDataRegion(BitMatrix bitMatrix) {
|
||||||
|
int symbolSizeRows = version.getSymbolSizeRows();
|
||||||
|
int symbolSizeColumns = version.getSymbolSizeColumns();
|
||||||
|
|
||||||
|
// TODO(bbrown): Make this work with rectangular codes
|
||||||
|
if (bitMatrix.getHeight() != symbolSizeRows) {
|
||||||
|
throw new IllegalArgumentException("Dimension of bitMarix must match the version size");
|
||||||
|
}
|
||||||
|
|
||||||
|
int dataRegionSizeRows = version.getDataRegionSizeRows();
|
||||||
|
int dataRegionSizeColumns = version.getDataRegionSizeColumns();
|
||||||
|
|
||||||
|
int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;
|
||||||
|
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
|
||||||
|
|
||||||
|
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
|
||||||
|
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
|
||||||
|
|
||||||
|
// TODO(bbrown): Make this work with rectangular codes
|
||||||
|
BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow);
|
||||||
|
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
|
||||||
|
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
|
||||||
|
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
|
||||||
|
int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;
|
||||||
|
for (int i = 0; i < dataRegionSizeRows; ++i) {
|
||||||
|
int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
|
||||||
|
int writeRowOffset = dataRegionRowOffset + i;
|
||||||
|
for (int j = 0; j < dataRegionSizeColumns; ++j) {
|
||||||
|
int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
|
||||||
|
if (bitMatrix.get(readColumnOffset, readRowOffset)) {
|
||||||
|
int writeColumnOffset = dataRegionColumnOffset + j;
|
||||||
|
bitMatrixWithoutAlignment.set(writeColumnOffset, writeRowOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bitMatrixWithoutAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
118
src/com/google/zxing/datamatrix/decoder/DataBlock.java
Normal file
118
src/com/google/zxing/datamatrix/decoder/DataBlock.java
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.datamatrix.decoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
|
||||||
|
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||||
|
* is represented by an instance of this class.</p>
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
final class DataBlock {
|
||||||
|
|
||||||
|
private final int numDataCodewords;
|
||||||
|
private final byte[] codewords;
|
||||||
|
|
||||||
|
private DataBlock(int numDataCodewords, byte[] codewords) {
|
||||||
|
this.numDataCodewords = numDataCodewords;
|
||||||
|
this.codewords = codewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them.
|
||||||
|
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||||
|
* method will separate the data into original blocks.</p>
|
||||||
|
*
|
||||||
|
* @param rawCodewords bytes as read directly from the Data Matrix Code
|
||||||
|
* @param version version of the Data Matrix Code
|
||||||
|
* @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
|
||||||
|
* Data Matrix Code
|
||||||
|
*/
|
||||||
|
static DataBlock[] getDataBlocks(byte[] rawCodewords,
|
||||||
|
Version version) {
|
||||||
|
// Figure out the number and size of data blocks used by this version
|
||||||
|
Version.ECBlocks ecBlocks = version.getECBlocks();
|
||||||
|
|
||||||
|
// First count the total number of data blocks
|
||||||
|
int totalBlocks = 0;
|
||||||
|
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
|
||||||
|
for (int i = 0; i < ecBlockArray.length; i++) {
|
||||||
|
totalBlocks += ecBlockArray[i].getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||||
|
DataBlock[] result = new DataBlock[totalBlocks];
|
||||||
|
int numResultBlocks = 0;
|
||||||
|
for (int j = 0; j < ecBlockArray.length; j++) {
|
||||||
|
Version.ECB ecBlock = ecBlockArray[j];
|
||||||
|
for (int i = 0; i < ecBlock.getCount(); i++) {
|
||||||
|
int numDataCodewords = ecBlock.getDataCodewords();
|
||||||
|
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
|
||||||
|
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All blocks have the same amount of data, except that the last n
|
||||||
|
// (where n may be 0) have 1 less byte. Figure out where these start.
|
||||||
|
// TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144
|
||||||
|
int longerBlocksTotalCodewords = result[0].codewords.length;
|
||||||
|
//int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1;
|
||||||
|
|
||||||
|
int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.getECCodewords();
|
||||||
|
int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1;
|
||||||
|
// The last elements of result may be 1 element shorter for 144 matrix
|
||||||
|
// first fill out as many elements as all of them have minus 1
|
||||||
|
int rawCodewordsOffset = 0;
|
||||||
|
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
|
||||||
|
for (int j = 0; j < numResultBlocks; j++) {
|
||||||
|
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill out the last data block in the longer ones
|
||||||
|
boolean specialVersion = version.getVersionNumber() == 24;
|
||||||
|
int numLongerBlocks = specialVersion ? 8 : numResultBlocks;
|
||||||
|
for (int j = 0; j < numLongerBlocks; j++) {
|
||||||
|
result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now add in error correction blocks
|
||||||
|
int max = result[0].codewords.length;
|
||||||
|
for (int i = longerBlocksNumDataCodewords; i < max; i++) {
|
||||||
|
for (int j = 0; j < numResultBlocks; j++) {
|
||||||
|
int iOffset = (specialVersion && j > 7) ? i - 1 : i;
|
||||||
|
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawCodewordsOffset != rawCodewords.length) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNumDataCodewords() {
|
||||||
|
return numDataCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getCodewords() {
|
||||||
|
return codewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,462 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.FormatException;
|
||||||
|
import com.google.zxing.common.BitSource;
|
||||||
|
import com.google.zxing.common.DecoderResult;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||||
|
* in one Data Matrix Code. This class decodes the bits back into text.</p>
|
||||||
|
*
|
||||||
|
* <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p>
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
final class DecodedBitStreamParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, Annex C Table C.1
|
||||||
|
* The C40 Basic Character Set (*'s used for placeholders for the shift values)
|
||||||
|
*/
|
||||||
|
private static final char[] C40_BASIC_SET_CHARS = {
|
||||||
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||||
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final char[] C40_SHIFT2_SET_CHARS = {
|
||||||
|
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
|
||||||
|
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, Annex C Table C.2
|
||||||
|
* The Text Basic Character Set (*'s used for placeholders for the shift values)
|
||||||
|
*/
|
||||||
|
private static final char[] TEXT_BASIC_SET_CHARS = {
|
||||||
|
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||||
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final char[] TEXT_SHIFT3_SET_CHARS = {
|
||||||
|
'\'', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||||
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int PAD_ENCODE = 0; // Not really an encoding
|
||||||
|
private static final int ASCII_ENCODE = 1;
|
||||||
|
private static final int C40_ENCODE = 2;
|
||||||
|
private static final int TEXT_ENCODE = 3;
|
||||||
|
private static final int ANSIX12_ENCODE = 4;
|
||||||
|
private static final int EDIFACT_ENCODE = 5;
|
||||||
|
private static final int BASE256_ENCODE = 6;
|
||||||
|
|
||||||
|
private DecodedBitStreamParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static DecoderResult decode(byte[] bytes) throws FormatException {
|
||||||
|
BitSource bits = new BitSource(bytes);
|
||||||
|
StringBuffer result = new StringBuffer(100);
|
||||||
|
StringBuffer resultTrailer = new StringBuffer(0);
|
||||||
|
Vector byteSegments = new Vector(1);
|
||||||
|
int mode = ASCII_ENCODE;
|
||||||
|
do {
|
||||||
|
if (mode == ASCII_ENCODE) {
|
||||||
|
mode = decodeAsciiSegment(bits, result, resultTrailer);
|
||||||
|
} else {
|
||||||
|
switch (mode) {
|
||||||
|
case C40_ENCODE:
|
||||||
|
decodeC40Segment(bits, result);
|
||||||
|
break;
|
||||||
|
case TEXT_ENCODE:
|
||||||
|
decodeTextSegment(bits, result);
|
||||||
|
break;
|
||||||
|
case ANSIX12_ENCODE:
|
||||||
|
decodeAnsiX12Segment(bits, result);
|
||||||
|
break;
|
||||||
|
case EDIFACT_ENCODE:
|
||||||
|
decodeEdifactSegment(bits, result);
|
||||||
|
break;
|
||||||
|
case BASE256_ENCODE:
|
||||||
|
decodeBase256Segment(bits, result, byteSegments);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
mode = ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
} while (mode != PAD_ENCODE && bits.available() > 0);
|
||||||
|
if (resultTrailer.length() > 0) {
|
||||||
|
result.append(resultTrailer.toString());
|
||||||
|
}
|
||||||
|
return new DecoderResult(bytes, result.toString(), byteSegments.isEmpty() ? null : byteSegments, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
|
||||||
|
*/
|
||||||
|
private static int decodeAsciiSegment(BitSource bits, StringBuffer result, StringBuffer resultTrailer)
|
||||||
|
throws FormatException {
|
||||||
|
boolean upperShift = false;
|
||||||
|
do {
|
||||||
|
int oneByte = bits.readBits(8);
|
||||||
|
if (oneByte == 0) {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
|
||||||
|
oneByte = upperShift ? (oneByte + 128) : oneByte;
|
||||||
|
upperShift = false;
|
||||||
|
result.append((char) (oneByte - 1));
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
} else if (oneByte == 129) { // Pad
|
||||||
|
return PAD_ENCODE;
|
||||||
|
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
|
||||||
|
int value = oneByte - 130;
|
||||||
|
if (value < 10) { // padd with '0' for single digit values
|
||||||
|
result.append('0');
|
||||||
|
}
|
||||||
|
result.append(value);
|
||||||
|
} else if (oneByte == 230) { // Latch to C40 encodation
|
||||||
|
return C40_ENCODE;
|
||||||
|
} else if (oneByte == 231) { // Latch to Base 256 encodation
|
||||||
|
return BASE256_ENCODE;
|
||||||
|
} else if (oneByte == 232) { // FNC1
|
||||||
|
//throw ReaderException.getInstance();
|
||||||
|
// Ignore this symbol for now
|
||||||
|
} else if (oneByte == 233) { // Structured Append
|
||||||
|
//throw ReaderException.getInstance();
|
||||||
|
// Ignore this symbol for now
|
||||||
|
} else if (oneByte == 234) { // Reader Programming
|
||||||
|
//throw ReaderException.getInstance();
|
||||||
|
// Ignore this symbol for now
|
||||||
|
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
|
||||||
|
upperShift = true;
|
||||||
|
} else if (oneByte == 236) { // 05 Macro
|
||||||
|
result.append("[)>\u001E05\u001D");
|
||||||
|
resultTrailer.insert(0, "\u001E\u0004");
|
||||||
|
} else if (oneByte == 237) { // 06 Macro
|
||||||
|
result.append("[)>\u001E06\u001D");
|
||||||
|
resultTrailer.insert(0, "\u001E\u0004");
|
||||||
|
} else if (oneByte == 238) { // Latch to ANSI X12 encodation
|
||||||
|
return ANSIX12_ENCODE;
|
||||||
|
} else if (oneByte == 239) { // Latch to Text encodation
|
||||||
|
return TEXT_ENCODE;
|
||||||
|
} else if (oneByte == 240) { // Latch to EDIFACT encodation
|
||||||
|
return EDIFACT_ENCODE;
|
||||||
|
} else if (oneByte == 241) { // ECI Character
|
||||||
|
// TODO(bbrown): I think we need to support ECI
|
||||||
|
//throw ReaderException.getInstance();
|
||||||
|
// Ignore this symbol for now
|
||||||
|
} else if (oneByte >= 242) { // Not to be used in ASCII encodation
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
return ASCII_ENCODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
|
||||||
|
*/
|
||||||
|
private static void decodeC40Segment(BitSource bits, StringBuffer result) throws FormatException {
|
||||||
|
// Three C40 values are encoded in a 16-bit value as
|
||||||
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
|
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
|
||||||
|
boolean upperShift = false;
|
||||||
|
|
||||||
|
int[] cValues = new int[3];
|
||||||
|
do {
|
||||||
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
|
if (bits.available() == 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int firstByte = bits.readBits(8);
|
||||||
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTwoBytes(firstByte, bits.readBits(8), cValues);
|
||||||
|
|
||||||
|
int shift = 0;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int cValue = cValues[i];
|
||||||
|
switch (shift) {
|
||||||
|
case 0:
|
||||||
|
if (cValue < 3) {
|
||||||
|
shift = cValue + 1;
|
||||||
|
} else {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (C40_BASIC_SET_CHARS[cValue] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(C40_BASIC_SET_CHARS[cValue]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (cValue + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(cValue);
|
||||||
|
}
|
||||||
|
shift = 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (cValue < 27) {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(C40_SHIFT2_SET_CHARS[cValue]);
|
||||||
|
}
|
||||||
|
} else if (cValue == 27) { // FNC1
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
} else if (cValue == 30) { // Upper Shift
|
||||||
|
upperShift = true;
|
||||||
|
} else {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
shift = 0;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (cValue + 224));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append((char) (cValue + 96));
|
||||||
|
}
|
||||||
|
shift = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
|
||||||
|
*/
|
||||||
|
private static void decodeTextSegment(BitSource bits, StringBuffer result) throws FormatException {
|
||||||
|
// Three Text values are encoded in a 16-bit value as
|
||||||
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
|
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
|
||||||
|
boolean upperShift = false;
|
||||||
|
|
||||||
|
int[] cValues = new int[3];
|
||||||
|
do {
|
||||||
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
|
if (bits.available() == 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int firstByte = bits.readBits(8);
|
||||||
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTwoBytes(firstByte, bits.readBits(8), cValues);
|
||||||
|
|
||||||
|
int shift = 0;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int cValue = cValues[i];
|
||||||
|
switch (shift) {
|
||||||
|
case 0:
|
||||||
|
if (cValue < 3) {
|
||||||
|
shift = cValue + 1;
|
||||||
|
} else {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (TEXT_BASIC_SET_CHARS[cValue] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(TEXT_BASIC_SET_CHARS[cValue]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (cValue + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(cValue);
|
||||||
|
}
|
||||||
|
shift = 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// Shift 2 for Text is the same encoding as C40
|
||||||
|
if (cValue < 27) {
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(C40_SHIFT2_SET_CHARS[cValue]);
|
||||||
|
}
|
||||||
|
} else if (cValue == 27) { // FNC1
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
} else if (cValue == 30) { // Upper Shift
|
||||||
|
upperShift = true;
|
||||||
|
} else {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
shift = 0;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (upperShift) {
|
||||||
|
result.append((char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128));
|
||||||
|
upperShift = false;
|
||||||
|
} else {
|
||||||
|
result.append(TEXT_SHIFT3_SET_CHARS[cValue]);
|
||||||
|
}
|
||||||
|
shift = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.7
|
||||||
|
*/
|
||||||
|
private static void decodeAnsiX12Segment(BitSource bits, StringBuffer result) throws FormatException {
|
||||||
|
// Three ANSI X12 values are encoded in a 16-bit value as
|
||||||
|
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||||
|
|
||||||
|
int[] cValues = new int[3];
|
||||||
|
do {
|
||||||
|
// If there is only one byte left then it will be encoded as ASCII
|
||||||
|
if (bits.available() == 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int firstByte = bits.readBits(8);
|
||||||
|
if (firstByte == 254) { // Unlatch codeword
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTwoBytes(firstByte, bits.readBits(8), cValues);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
int cValue = cValues[i];
|
||||||
|
if (cValue == 0) { // X12 segment terminator <CR>
|
||||||
|
result.append('\r');
|
||||||
|
} else if (cValue == 1) { // X12 segment separator *
|
||||||
|
result.append('*');
|
||||||
|
} else if (cValue == 2) { // X12 sub-element separator >
|
||||||
|
result.append('>');
|
||||||
|
} else if (cValue == 3) { // space
|
||||||
|
result.append(' ');
|
||||||
|
} else if (cValue < 14) { // 0 - 9
|
||||||
|
result.append((char) (cValue + 44));
|
||||||
|
} else if (cValue < 40) { // A - Z
|
||||||
|
result.append((char) (cValue + 51));
|
||||||
|
} else {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (bits.available() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void parseTwoBytes(int firstByte, int secondByte, int[] result) {
|
||||||
|
int fullBitValue = (firstByte << 8) + secondByte - 1;
|
||||||
|
int temp = fullBitValue / 1600;
|
||||||
|
result[0] = temp;
|
||||||
|
fullBitValue -= temp * 1600;
|
||||||
|
temp = fullBitValue / 40;
|
||||||
|
result[1] = temp;
|
||||||
|
result[2] = fullBitValue - temp * 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.8 and Annex C Table C.3
|
||||||
|
*/
|
||||||
|
private static void decodeEdifactSegment(BitSource bits, StringBuffer result) {
|
||||||
|
boolean unlatch = false;
|
||||||
|
do {
|
||||||
|
// If there is only two or less bytes left then it will be encoded as ASCII
|
||||||
|
if (bits.available() <= 16) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
int edifactValue = bits.readBits(6);
|
||||||
|
|
||||||
|
// Check for the unlatch character
|
||||||
|
if (edifactValue == 0x2B67) { // 011111
|
||||||
|
unlatch = true;
|
||||||
|
// If we encounter the unlatch code then continue reading because the Codeword triple
|
||||||
|
// is padded with 0's
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unlatch) {
|
||||||
|
if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit
|
||||||
|
edifactValue |= 64; // Add a leading 01 to the 6 bit binary value
|
||||||
|
}
|
||||||
|
result.append(edifactValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!unlatch && bits.available() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
|
||||||
|
*/
|
||||||
|
private static void decodeBase256Segment(BitSource bits, StringBuffer result, Vector byteSegments)
|
||||||
|
throws FormatException {
|
||||||
|
// Figure out how long the Base 256 Segment is.
|
||||||
|
int d1 = bits.readBits(8);
|
||||||
|
int count;
|
||||||
|
if (d1 == 0) { // Read the remainder of the symbol
|
||||||
|
count = bits.available() / 8;
|
||||||
|
} else if (d1 < 250) {
|
||||||
|
count = d1;
|
||||||
|
} else {
|
||||||
|
count = 250 * (d1 - 249) + bits.readBits(8);
|
||||||
|
}
|
||||||
|
byte[] bytes = new byte[count];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Have seen this particular error in the wild, such as at
|
||||||
|
// http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
|
||||||
|
if (bits.available() < 8) {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
bytes[i] = unrandomize255State(bits.readBits(8), i);
|
||||||
|
}
|
||||||
|
byteSegments.addElement(bytes);
|
||||||
|
try {
|
||||||
|
result.append(new String(bytes, "ISO8859_1"));
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
throw new RuntimeException("Platform does not support required encoding: " + uee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006, Annex B, B.2
|
||||||
|
*/
|
||||||
|
private static byte unrandomize255State(int randomizedBase256Codeword,
|
||||||
|
int base256CodewordPosition) {
|
||||||
|
int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1;
|
||||||
|
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
|
||||||
|
return (byte) (tempVariable >= 0 ? tempVariable : (tempVariable + 256));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
134
src/com/google/zxing/datamatrix/decoder/Decoder.java
Normal file
134
src/com/google/zxing/datamatrix/decoder/Decoder.java
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.ChecksumException;
|
||||||
|
import com.google.zxing.FormatException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.common.DecoderResult;
|
||||||
|
import com.google.zxing.common.reedsolomon.GF256;
|
||||||
|
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||||
|
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting
|
||||||
|
* the Data Matrix Code from an image.</p>
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class Decoder {
|
||||||
|
|
||||||
|
private final ReedSolomonDecoder rsDecoder;
|
||||||
|
|
||||||
|
public Decoder() {
|
||||||
|
rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans.
|
||||||
|
* "true" is taken to mean a black module.</p>
|
||||||
|
*
|
||||||
|
* @param image booleans representing white/black Data Matrix Code modules
|
||||||
|
* @return text and bytes encoded within the Data Matrix Code
|
||||||
|
* @throws FormatException if the Data Matrix Code cannot be decoded
|
||||||
|
* @throws ChecksumException if error correction fails
|
||||||
|
*/
|
||||||
|
public DecoderResult decode(boolean[][] image) throws FormatException, ChecksumException {
|
||||||
|
int dimension = image.length;
|
||||||
|
BitMatrix bits = new BitMatrix(dimension);
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
for (int j = 0; j < dimension; j++) {
|
||||||
|
if (image[i][j]) {
|
||||||
|
bits.set(j, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decode(bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken
|
||||||
|
* to mean a black module.</p>
|
||||||
|
*
|
||||||
|
* @param bits booleans representing white/black Data Matrix Code modules
|
||||||
|
* @return text and bytes encoded within the Data Matrix Code
|
||||||
|
* @throws FormatException if the Data Matrix Code cannot be decoded
|
||||||
|
* @throws ChecksumException if error correction fails
|
||||||
|
*/
|
||||||
|
public DecoderResult decode(BitMatrix bits) throws FormatException, ChecksumException {
|
||||||
|
|
||||||
|
// Construct a parser and read version, error-correction level
|
||||||
|
BitMatrixParser parser = new BitMatrixParser(bits);
|
||||||
|
Version version = parser.readVersion(bits);
|
||||||
|
|
||||||
|
// Read codewords
|
||||||
|
byte[] codewords = parser.readCodewords();
|
||||||
|
// Separate into data blocks
|
||||||
|
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version);
|
||||||
|
|
||||||
|
// Count total number of data bytes
|
||||||
|
int totalBytes = 0;
|
||||||
|
for (int i = 0; i < dataBlocks.length; i++) {
|
||||||
|
totalBytes += dataBlocks[i].getNumDataCodewords();
|
||||||
|
}
|
||||||
|
byte[] resultBytes = new byte[totalBytes];
|
||||||
|
int resultOffset = 0;
|
||||||
|
|
||||||
|
// Error-correct and copy data blocks together into a stream of bytes
|
||||||
|
for (int j = 0; j < dataBlocks.length; j++) {
|
||||||
|
DataBlock dataBlock = dataBlocks[j];
|
||||||
|
byte[] codewordBytes = dataBlock.getCodewords();
|
||||||
|
int numDataCodewords = dataBlock.getNumDataCodewords();
|
||||||
|
correctErrors(codewordBytes, numDataCodewords);
|
||||||
|
for (int i = 0; i < numDataCodewords; i++) {
|
||||||
|
resultBytes[resultOffset++] = codewordBytes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the contents of that stream of bytes
|
||||||
|
return DecodedBitStreamParser.decode(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||||
|
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||||
|
*
|
||||||
|
* @param codewordBytes data and error correction codewords
|
||||||
|
* @param numDataCodewords number of codewords that are data bytes
|
||||||
|
* @throws ChecksumException if error correction fails
|
||||||
|
*/
|
||||||
|
private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {
|
||||||
|
int numCodewords = codewordBytes.length;
|
||||||
|
// First read into an array of ints
|
||||||
|
int[] codewordsInts = new int[numCodewords];
|
||||||
|
for (int i = 0; i < numCodewords; i++) {
|
||||||
|
codewordsInts[i] = codewordBytes[i] & 0xFF;
|
||||||
|
}
|
||||||
|
int numECCodewords = codewordBytes.length - numDataCodewords;
|
||||||
|
try {
|
||||||
|
rsDecoder.decode(codewordsInts, numECCodewords);
|
||||||
|
} catch (ReedSolomonException rse) {
|
||||||
|
throw ChecksumException.getChecksumInstance();
|
||||||
|
}
|
||||||
|
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||||
|
// We don't care about errors in the error-correction codewords
|
||||||
|
for (int i = 0; i < numDataCodewords; i++) {
|
||||||
|
codewordBytes[i] = (byte) codewordsInts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
242
src/com/google/zxing/datamatrix/decoder/Version.java
Normal file
242
src/com/google/zxing/datamatrix/decoder/Version.java
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 ZXing authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.zxing.datamatrix.decoder;
|
||||||
|
|
||||||
|
import com.google.zxing.FormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Version object encapsulates attributes about a particular
|
||||||
|
* size Data Matrix Code.
|
||||||
|
*
|
||||||
|
* @author bbrown@google.com (Brian Brown)
|
||||||
|
*/
|
||||||
|
public final class Version {
|
||||||
|
|
||||||
|
private static final Version[] VERSIONS = buildVersions();
|
||||||
|
|
||||||
|
private final int versionNumber;
|
||||||
|
private final int symbolSizeRows;
|
||||||
|
private final int symbolSizeColumns;
|
||||||
|
private final int dataRegionSizeRows;
|
||||||
|
private final int dataRegionSizeColumns;
|
||||||
|
private final ECBlocks ecBlocks;
|
||||||
|
private final int totalCodewords;
|
||||||
|
|
||||||
|
private Version(int versionNumber,
|
||||||
|
int symbolSizeRows,
|
||||||
|
int symbolSizeColumns,
|
||||||
|
int dataRegionSizeRows,
|
||||||
|
int dataRegionSizeColumns,
|
||||||
|
ECBlocks ecBlocks) {
|
||||||
|
this.versionNumber = versionNumber;
|
||||||
|
this.symbolSizeRows = symbolSizeRows;
|
||||||
|
this.symbolSizeColumns = symbolSizeColumns;
|
||||||
|
this.dataRegionSizeRows = dataRegionSizeRows;
|
||||||
|
this.dataRegionSizeColumns = dataRegionSizeColumns;
|
||||||
|
this.ecBlocks = ecBlocks;
|
||||||
|
|
||||||
|
// Calculate the total number of codewords
|
||||||
|
int total = 0;
|
||||||
|
int ecCodewords = ecBlocks.getECCodewords();
|
||||||
|
ECB[] ecbArray = ecBlocks.getECBlocks();
|
||||||
|
for (int i = 0; i < ecbArray.length; i++) {
|
||||||
|
ECB ecBlock = ecbArray[i];
|
||||||
|
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
|
||||||
|
}
|
||||||
|
this.totalCodewords = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVersionNumber() {
|
||||||
|
return versionNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSymbolSizeRows() {
|
||||||
|
return symbolSizeRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSymbolSizeColumns() {
|
||||||
|
return symbolSizeColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataRegionSizeRows() {
|
||||||
|
return dataRegionSizeRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataRegionSizeColumns() {
|
||||||
|
return dataRegionSizeColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalCodewords() {
|
||||||
|
return totalCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECBlocks getECBlocks() {
|
||||||
|
return ecBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Deduces version information from Data Matrix dimensions.</p>
|
||||||
|
*
|
||||||
|
* @param numRows Number of rows in modules
|
||||||
|
* @param numColumns Number of columns in modules
|
||||||
|
* @return {@link Version} for a Data Matrix Code of those dimensions
|
||||||
|
* @throws FormatException if dimensions do correspond to a valid Data Matrix size
|
||||||
|
*/
|
||||||
|
public static Version getVersionForDimensions(int numRows, int numColumns) throws FormatException {
|
||||||
|
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bbrown): This is doing a linear search through the array of versions.
|
||||||
|
// If we interleave the rectangular versions with the square versions we could
|
||||||
|
// do a binary search.
|
||||||
|
int numVersions = VERSIONS.length;
|
||||||
|
for (int i = 0; i < numVersions; ++i){
|
||||||
|
Version version = VERSIONS[i];
|
||||||
|
if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
|
||||||
|
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
|
||||||
|
* each set of blocks. It also holds the number of error-correction codewords per block since it
|
||||||
|
* will be the same across all blocks within one version.</p>
|
||||||
|
*/
|
||||||
|
static final class ECBlocks {
|
||||||
|
private final int ecCodewords;
|
||||||
|
private final ECB[] ecBlocks;
|
||||||
|
|
||||||
|
private ECBlocks(int ecCodewords, ECB ecBlocks) {
|
||||||
|
this.ecCodewords = ecCodewords;
|
||||||
|
this.ecBlocks = new ECB[] { ecBlocks };
|
||||||
|
}
|
||||||
|
|
||||||
|
private ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) {
|
||||||
|
this.ecCodewords = ecCodewords;
|
||||||
|
this.ecBlocks = new ECB[] { ecBlocks1, ecBlocks2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
int getECCodewords() {
|
||||||
|
return ecCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECB[] getECBlocks() {
|
||||||
|
return ecBlocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
|
||||||
|
* This includes the number of data codewords, and the number of times a block with these
|
||||||
|
* parameters is used consecutively in the Data Matrix code version's format.</p>
|
||||||
|
*/
|
||||||
|
static final class ECB {
|
||||||
|
private final int count;
|
||||||
|
private final int dataCodewords;
|
||||||
|
|
||||||
|
private ECB(int count, int dataCodewords) {
|
||||||
|
this.count = count;
|
||||||
|
this.dataCodewords = dataCodewords;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDataCodewords() {
|
||||||
|
return dataCodewords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(versionNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See ISO 16022:2006 5.5.1 Table 7
|
||||||
|
*/
|
||||||
|
private static Version[] buildVersions() {
|
||||||
|
return new Version[]{
|
||||||
|
new Version(1, 10, 10, 8, 8,
|
||||||
|
new ECBlocks(5, new ECB(1, 3))),
|
||||||
|
new Version(2, 12, 12, 10, 10,
|
||||||
|
new ECBlocks(7, new ECB(1, 5))),
|
||||||
|
new Version(3, 14, 14, 12, 12,
|
||||||
|
new ECBlocks(10, new ECB(1, 8))),
|
||||||
|
new Version(4, 16, 16, 14, 14,
|
||||||
|
new ECBlocks(12, new ECB(1, 12))),
|
||||||
|
new Version(5, 18, 18, 16, 16,
|
||||||
|
new ECBlocks(14, new ECB(1, 18))),
|
||||||
|
new Version(6, 20, 20, 18, 18,
|
||||||
|
new ECBlocks(18, new ECB(1, 22))),
|
||||||
|
new Version(7, 22, 22, 20, 20,
|
||||||
|
new ECBlocks(20, new ECB(1, 30))),
|
||||||
|
new Version(8, 24, 24, 22, 22,
|
||||||
|
new ECBlocks(24, new ECB(1, 36))),
|
||||||
|
new Version(9, 26, 26, 24, 24,
|
||||||
|
new ECBlocks(28, new ECB(1, 44))),
|
||||||
|
new Version(10, 32, 32, 14, 14,
|
||||||
|
new ECBlocks(36, new ECB(1, 62))),
|
||||||
|
new Version(11, 36, 36, 16, 16,
|
||||||
|
new ECBlocks(42, new ECB(1, 86))),
|
||||||
|
new Version(12, 40, 40, 18, 18,
|
||||||
|
new ECBlocks(48, new ECB(1, 114))),
|
||||||
|
new Version(13, 44, 44, 20, 20,
|
||||||
|
new ECBlocks(56, new ECB(1, 144))),
|
||||||
|
new Version(14, 48, 48, 22, 22,
|
||||||
|
new ECBlocks(68, new ECB(1, 174))),
|
||||||
|
new Version(15, 52, 52, 24, 24,
|
||||||
|
new ECBlocks(42, new ECB(2, 102))),
|
||||||
|
new Version(16, 64, 64, 14, 14,
|
||||||
|
new ECBlocks(56, new ECB(2, 140))),
|
||||||
|
new Version(17, 72, 72, 16, 16,
|
||||||
|
new ECBlocks(36, new ECB(4, 92))),
|
||||||
|
new Version(18, 80, 80, 18, 18,
|
||||||
|
new ECBlocks(48, new ECB(4, 114))),
|
||||||
|
new Version(19, 88, 88, 20, 20,
|
||||||
|
new ECBlocks(56, new ECB(4, 144))),
|
||||||
|
new Version(20, 96, 96, 22, 22,
|
||||||
|
new ECBlocks(68, new ECB(4, 174))),
|
||||||
|
new Version(21, 104, 104, 24, 24,
|
||||||
|
new ECBlocks(56, new ECB(6, 136))),
|
||||||
|
new Version(22, 120, 120, 18, 18,
|
||||||
|
new ECBlocks(68, new ECB(6, 175))),
|
||||||
|
new Version(23, 132, 132, 20, 20,
|
||||||
|
new ECBlocks(62, new ECB(8, 163))),
|
||||||
|
new Version(24, 144, 144, 22, 22,
|
||||||
|
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))),
|
||||||
|
new Version(25, 8, 18, 6, 16,
|
||||||
|
new ECBlocks(7, new ECB(1, 5))),
|
||||||
|
new Version(26, 8, 32, 6, 14,
|
||||||
|
new ECBlocks(11, new ECB(1, 10))),
|
||||||
|
new Version(27, 12, 26, 10, 24,
|
||||||
|
new ECBlocks(14, new ECB(1, 16))),
|
||||||
|
new Version(28, 12, 36, 10, 16,
|
||||||
|
new ECBlocks(18, new ECB(1, 22))),
|
||||||
|
new Version(29, 16, 36, 10, 16,
|
||||||
|
new ECBlocks(24, new ECB(1, 32))),
|
||||||
|
new Version(30, 16, 48, 14, 22,
|
||||||
|
new ECBlocks(28, new ECB(1, 49)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user