forked from I2P_Developers/i2p.i2p
Partial zxing package
This is a small portion of zxing, including only what's required to generate QR codes. Pulled from https://github.com/zxing/zxing on Jan. 4, 2016, rev 4e3abafe3008e02695f894eccf05f8257fca4ee9 dated Dec. 9, 2015. Contains only the files we need. Unmodified. License: Apache 2.0
This commit is contained in:
134
apps/imagegen/zxing/AUTHORS
Normal file
134
apps/imagegen/zxing/AUTHORS
Normal file
@@ -0,0 +1,134 @@
|
||||
This project consists of contributions from several people, recognized here for convenience,
|
||||
in alphabetical order.
|
||||
|
||||
Agustín Delgado (Servinform S.A.)
|
||||
Aitor Almeida (University of Deusto)
|
||||
Alasdair Mackintosh (Google)
|
||||
Alex Dupre
|
||||
Alexander Martin (Haase & Martin GmbH)
|
||||
Alexander Schmidt
|
||||
Anders Borg
|
||||
Andreas Pillath
|
||||
Andrew Walbran (Google)
|
||||
Andrey Sitnik
|
||||
Androida.hu / http://www.androida.hu/
|
||||
Antonio Manuel Benjumea (Servinform S.A.)
|
||||
Asier Iturralde
|
||||
Asmuri Anwar
|
||||
atereshchuk
|
||||
Betaminos
|
||||
Brian Brown (Google)
|
||||
Bruce Allen
|
||||
Chang Hyun Park
|
||||
Christian Brunschen (Google)
|
||||
Christoph Schulz (creatale GmbH)
|
||||
crowdin.net
|
||||
Daniel Switkin (Google)
|
||||
Dave MacLachlan (Google)
|
||||
David Phillip Oster (Google)
|
||||
David Albert (Bug Labs)
|
||||
David Olivier
|
||||
David Walker (Google)
|
||||
Diego Pierotto
|
||||
Dion Hardy
|
||||
drejc83
|
||||
Eduardo Castillejo (University of Deusto)
|
||||
Emanuele Aina
|
||||
Eric Kobrin (Velocitude)
|
||||
evansepdx
|
||||
Erik Barbara
|
||||
Facundo Farias
|
||||
fakhri626
|
||||
fics.danmark
|
||||
Francois B. (Google)
|
||||
Frank Yellin
|
||||
Fred Lin (Anobiit)
|
||||
gcstang
|
||||
Guenther Grau
|
||||
Guillaume Cottenceau
|
||||
Guillaume Le Biller
|
||||
Hannes Erven
|
||||
Hartmut Neubauer (Schweers Informationstechnologie GmbH)
|
||||
hosigumayuugi
|
||||
hypest (Barcorama project)
|
||||
Ian W. Davis
|
||||
Isaac Potoczny-Jones
|
||||
Ivan Poliakov
|
||||
Jacob Haynes (Google)
|
||||
Jeff Breidenbach (Google)
|
||||
jjYBdx4IL
|
||||
Joan Montané (Softcatalà.cat)
|
||||
John Connolly (Bug Labs)
|
||||
Jonas Petersson (Prisjakt)
|
||||
Joseph Wain (Google)
|
||||
Juha Kuitunen
|
||||
Juho Mikkonen
|
||||
Jukka Nurmi
|
||||
jwicks
|
||||
Kamil Kaczmarczyk
|
||||
Kazuki Nishiura
|
||||
Kevin O'Sullivan (SITA)
|
||||
Kevin Xue (NetDragon Websoft Inc., China)
|
||||
Lachezar Dobrev
|
||||
Loránt Gerencsér
|
||||
Luiz Silva
|
||||
Luka Finžgar
|
||||
Łukasz Stefaniak
|
||||
Malte Starostik
|
||||
Manuel Kasten
|
||||
Marcelo
|
||||
Marcin Górny
|
||||
Markus Helm
|
||||
Mateusz Jędrasik
|
||||
Matthew Schulkind (Google)
|
||||
Matt York (LifeMarks)
|
||||
MicheleMas
|
||||
mike32767
|
||||
mikej06 / micjahn
|
||||
Mike Kicinski
|
||||
Mohamad Fairol
|
||||
Morgan Courbet
|
||||
Nikolaos Ftylitakis
|
||||
Nikolaus Huber
|
||||
Olexandr Nesterenko
|
||||
Olli
|
||||
Pablo Orduña (University of Deusto)
|
||||
Paul Hackenberger
|
||||
perennialmind
|
||||
Radu Silaghi
|
||||
Ralf Kistner
|
||||
Randy Shen (Acer)
|
||||
Rasmus Schrøder Sørensen
|
||||
Richard Hřivňák
|
||||
Romain Pechayre
|
||||
Roman Nurik (Google)
|
||||
Rustam Abdullaev
|
||||
Ryan Alford
|
||||
Sanford Squires
|
||||
Satoshi Kametaya
|
||||
Shachar Shemesh
|
||||
Sean Owen (Google)
|
||||
Shiyuan Guo / 郭世元
|
||||
ShumovichY
|
||||
Simon Flannery (Ericsson)
|
||||
slepmog
|
||||
Stephen Furlani
|
||||
Steven Parkes
|
||||
stoty74
|
||||
Suraj Supekar
|
||||
Sven Klinkhamer
|
||||
taku0
|
||||
techinstantdx
|
||||
tgibson11
|
||||
Thanapon Namjittrong
|
||||
Thomas Gerbet
|
||||
Thomas Weißschuh
|
||||
Tim Gernat
|
||||
v.anestis
|
||||
Vince Francis (LifeMarks)
|
||||
Wolfgang Jung
|
||||
Yahoe001
|
||||
Yakov Okshtein (Google)
|
||||
Yao Wei
|
||||
Younes CHTIOUI
|
||||
Yunhao Tian
|
||||
357
apps/imagegen/zxing/CHANGES
Normal file
357
apps/imagegen/zxing/CHANGES
Normal file
@@ -0,0 +1,357 @@
|
||||
0.1 (09 Nov 2007)
|
||||
- Initial release
|
||||
|
||||
0.1.1 (11 Nov 2007)
|
||||
- Fixed bug decoding version 0 QR Codes
|
||||
- Now default zoom is 2.5x in Java ME client
|
||||
|
||||
0.1.2 (28 Nov 2007)
|
||||
- Issue 11 fixed: Build problems on Windows
|
||||
- Can now build "ZXingReaderBasic" which does not require JSR-234
|
||||
- Issue 14 fixed: release .zip builds into one directory
|
||||
|
||||
0.1.3 (7 Dec 2007)
|
||||
- Unit test for QR Code decoding
|
||||
- Added EAN-13 support
|
||||
- Now builds with class file format version 1.2 -- may solve some compatibility issues?
|
||||
- Fixed obfuscation step bug causing NoClassDefFoundError
|
||||
|
||||
0.1.4 (13 Dec 2007)
|
||||
- Added Blackberry client build script -- does not yet work
|
||||
- Big change to handling of content of barcodes:
|
||||
- com.google.zxing.client.result moved from core-ext to core
|
||||
- Rewritten for J2ME
|
||||
- core-ext removed
|
||||
- J2ME client now uses this code for better parsing/handling of results
|
||||
|
||||
0.2 (07 Jan 2008)
|
||||
- Few small bug fixes in AlignmentPatternFinder, MultiFormatReader
|
||||
(thank K. Kakima, Andreas)
|
||||
- LCDUIImageMonochromeBitmapSource now public
|
||||
- Notably improved decoding for Basic version -- Regular version still
|
||||
your best bet if you can run it though
|
||||
|
||||
0.2.1 (16 Jan 2008)
|
||||
- Slight tweaks and enhancements to decode and runtime performance of UPCDecoder
|
||||
- Added black-box test suites against a known set of images
|
||||
(Thanks to Enrique G. S. for additional images)
|
||||
|
||||
0.2.2 (22 Jan 2008)
|
||||
- Now includes junit locally to build tests
|
||||
- Fixed "MIDlet" name in MANIFEST.MF, which may solve some problems
|
||||
- Friendlier error message when barcode can't be found
|
||||
|
||||
0.3 (04 Feb 2008)
|
||||
- Major refactoring of 1D barcode decoding
|
||||
- Added support for UPC-E, EAN-8
|
||||
- Also added Code 39, Code 128 support -- may need refinement
|
||||
- Now any "game" key triggers photo capture
|
||||
- Workaround implemented for Nokias that use "capture://image" in MMAPI
|
||||
- UPC codes now trigger lookup to upcdatabase.com
|
||||
|
||||
0.4 (15 Feb 2008)
|
||||
- First release of Android client -- only guaranteed to work with "M3"
|
||||
builds so far.
|
||||
- Major reorganization of current test cases and data
|
||||
- Worked around ProGuard issue with paths with spaces on Windows
|
||||
- Phones that don't like FocusControl.AUTO_LOCK won't cause an error now
|
||||
- Fixed ArrayIndexOutOfBoundsException in 1D barcode decoders
|
||||
- Worked around "java.lang.Error 136" from Nokia N70 (?)
|
||||
- Fixed bug in Shift_JIS detection and implemented basic UTF-8 detection
|
||||
for nonstandard QR Codes that don't use ISO-8859-1
|
||||
|
||||
0.4.5 (22 Feb 2008)
|
||||
- Checked in first minimal working Blackberry client. Still quite crude.
|
||||
- Fixed UTF-8 detection again
|
||||
- Refactored Reed-Solomon to allow different GF(256) primitive polynomials,
|
||||
in preparation for Data Matrix
|
||||
- Slight improvement to 1D decoding accuracy, most noticeable in
|
||||
improvements to Code 128 decoding
|
||||
- Bug fix in Code39Reader -- typo in encoding of letter "I" and "0"
|
||||
- Bug fix in 1D readers -- error in counting black/white pixel counts
|
||||
in certain situations
|
||||
- Now supports Extended Code 39
|
||||
- Tiny bug fix to QRCodeReader for images that extend all the way
|
||||
to top or left
|
||||
- Android client now builds with ProGuard optimization
|
||||
|
||||
0.5 (05 Mar 2008)
|
||||
- Improvement in QR Code decoding accuracy on non-JSR-234 phones
|
||||
- Supports "URLTO" data in barcodes.
|
||||
- Restructured ParsedReaderResult parsing code to not use so many
|
||||
exceptions, for more efficiency.
|
||||
- "TRY_HARDER" decoding hint now supported. Will do more work in some cases
|
||||
to find an image, such as scan for 1D barcodes at a right angle and
|
||||
backwards. Only recommended for non-mobile applications.
|
||||
- Result now indicates the format of the decoded barcode with a
|
||||
BarcodeFormat instance
|
||||
- More intelligent handling of POSSIBLE_FORMATS decoding hint
|
||||
- Fixed sub-optimal handling of EAN-13 barcodes which encode a string
|
||||
starting with "0". These are now returned as a full 13-digit string
|
||||
from EAN13Reader, but will continue to be construed as 12-digit UPC-A
|
||||
codes from the MultiFormatReader.
|
||||
- A host of small bug fixes, including bug in Version 25 QR Code support
|
||||
- A round of code cleanup and refactoring from IntelliJ inspections
|
||||
|
||||
0.5.5 (12 Mar 2008)
|
||||
- Revert change that rejected BufferedImage of type TYPE_CUSTOM in javase
|
||||
- Now supports KDDI/AU / Softbank address book format
|
||||
- Now can read Android "Intent" URIs (Android only)
|
||||
- Modest improvements to efficiency of J2ME client: shorter autofocus,
|
||||
better use of threads
|
||||
- First checkin of Data Matrix decoder code. Not yet enabled in reader
|
||||
applications.
|
||||
- "Result" object now returns raw bytes decoded; maybe useful in some cases
|
||||
- More unit tests for common code, EAN-8, UPC-E
|
||||
|
||||
0.6 (28 Mar 2008)
|
||||
- Updated android client to work on latest M5 SDK; M3-compatiable version
|
||||
preserved in android-m3
|
||||
- Added tel: URL support to clients
|
||||
- Added geo: URL support; only enabled for Android client so far
|
||||
- Added initial support for short NDEF messages, but not yet enabled
|
||||
- Bug fix to corner-case of perspective transformation code
|
||||
- Now attempts to configure camera's exposure controls if available
|
||||
- Fixed rotation transform used in "try harder" mode; result did not
|
||||
contain the whole image
|
||||
- "Try harder" mode tries 2D formats first to avoid false positives
|
||||
in detailed search for 1D codes, in some cases
|
||||
- More aggressive inlining by ProGuard, notable performance boost
|
||||
- Small Reed-Solomon optimizations for 0- and 1-error case
|
||||
- Refactored GridSampler out into common package for use by Data Matrix
|
||||
- GridSampler now smarter about bounds checking and point nudging;
|
||||
should avoid some ArrayIndexOutOfBoundsException issues
|
||||
- More unit tests
|
||||
|
||||
0.7 (5 May 2008)
|
||||
- Added support for MobileTag message formats
|
||||
- Added source to zxing.org
|
||||
- Significant work on Android clients, including UI overhaul and
|
||||
continuous scan mode
|
||||
- Bug fix: ResultPoints from 1D barcodes were wrong
|
||||
- SKIP_N_BARCODES hint now gone, and associated logic has been transferred
|
||||
to individual project users, as it is fairly app-specific at this point
|
||||
and is beginning to complicate the core library. We can help other users
|
||||
who need this functionality on a case-by-case basis.
|
||||
- Bug fix in AddressBookAUParsedResult parsing
|
||||
- Possible workaround for NoSuchMethodError on Nokias
|
||||
- New shell of an iPhone client committed
|
||||
- ... and other assorted small improvements and fixes
|
||||
|
||||
0.8 (16 Jun 2008)
|
||||
- FinderPatternFinder now isn't fooled by false-positive finder patterns
|
||||
in some rare cases
|
||||
- Added SMSTO: support, expanded mailto: support to include subject and body
|
||||
params
|
||||
- Now should properly unescape URLs in appropriate cases; better handling of
|
||||
mailto: and tel: URIs (won't get modified by the library)
|
||||
- Optimized 1D decoding to use integer math
|
||||
- Small bug fix / improvement in BlackPointEstimator's estimates
|
||||
- 1D decoding now applies a light sharpening filter
|
||||
- Refactored and simplified MonochromeBitmapSource implementations
|
||||
- Removed android-m3/ directory, no longer in active development. The current
|
||||
Android client remains in android/ (M5 SDK)
|
||||
- Restored ability to read upside-down 1D codes without try-harder mode
|
||||
- Added a crude GUI-based decoder to javase/, for the interested
|
||||
- core/ is now independent of MIDP; only requires CLDC 1.1
|
||||
- Added optimized build of core library
|
||||
- Unit tests now use local copies of images
|
||||
|
||||
0.9 (11 July 2008)
|
||||
- First draft of iPhone client committed
|
||||
- core module now built in a way that makes it function as an OSGi bundle,
|
||||
and also supports deployment in BugLabs's BUG platform.
|
||||
- Added client for BUG platform courtesy of buglabs.net
|
||||
- Big refactoring of com.google.zxing.client.result to separate idea of result
|
||||
parser from result value objects
|
||||
- Added basic vCard 2.1 support
|
||||
- Added basic BIZCARD format support
|
||||
- Added basic iCal format support
|
||||
- Added new calendar event parsed result type
|
||||
- Fewer false positives in 1D detectors, notably Code 39 and Code 128;
|
||||
new unit tests for false positives
|
||||
- Now parses and tries to act on ECI designators in QR Codes
|
||||
- Merged "basic" and "regular" J2ME readers into one unified app that should
|
||||
work on platforms with and without JSR-234 support
|
||||
- Removed dependence on Ant 1.7 (1.6 needed now)
|
||||
- Web application at zxing.org now shows raw bytes
|
||||
- Far more black box tests
|
||||
- Assorted small bug fixes
|
||||
|
||||
1.0 (12 September 2008)
|
||||
- All new RIM client from LifeMarks
|
||||
- Initial Data Matrix detector included
|
||||
- New and much improved Android 0.9-SDK compatible Android client
|
||||
- Better 1D detection, especially UPC-E, due to loosened quite zone checks
|
||||
- URIParsedResult now tries to warn about malicious URIs
|
||||
- New parser for ISBNs
|
||||
- New QR code blackbox images
|
||||
|
||||
1.1 (22 October 2008)
|
||||
- First public release of iPhone client called Barcodes
|
||||
- Completely new multi-action Android client UI, released as Barcode Scanner v2.3
|
||||
- Code 128 parsing fixes
|
||||
- Dozens of new blackbox test images
|
||||
- Many small core library parsing improvements and bug fixes
|
||||
- Improved quiet zone enforcement which reduces false positives significantly
|
||||
- Better calendar event and MECARD support
|
||||
- Compatibility with the Android 1.0 SDK
|
||||
- Added androidtest app for testing intents and capturing blackbox test images
|
||||
|
||||
1.2 (23 December 2008)
|
||||
- New Android benchmark application to time decoding performance
|
||||
- Dramatic performance improvements for worst case images (up to 2x faster)
|
||||
- Expanded blackbox unit tests with many new test images
|
||||
- Many small decoding improvements and bug fixes
|
||||
- New Share functionality in the Android client which encodes contacts, bookmarks, or the
|
||||
clipboard contents as a QR Code for a friend to scan with their phone
|
||||
- Locale-specific Android client which picks the right TLD when connecting to Google
|
||||
- Support for Furigana in the Android client, and better parsing of Kanji in core
|
||||
- Added the ability to import and export multiple phone numbers and emails for a single contact
|
||||
- Native QR Encoding added to the ZXing library - no need to send request to ChartServer
|
||||
- New ITF barcode format support from Kevin O'Sullivan at SITA
|
||||
- New HTML help system for the Android client
|
||||
- New web-based QR Code encoder at zxing.appspot.com based on GWT
|
||||
|
||||
1.3 (11 March 2009)
|
||||
- New C# port from Mohamad Fairol
|
||||
- ITF format scanning now on by default
|
||||
- Many new blackbox test images
|
||||
- Better QR Code detection
|
||||
- Better Android Intent handling
|
||||
- Improved email parsing
|
||||
- New Android Integration module to make life easier for third party ZXing users
|
||||
- More robust URI parsing
|
||||
- Many other small improvements and bug fixes
|
||||
|
||||
1.4 (26 Oct 2009)
|
||||
- Wider viewfinder in Android app to account for better close-range focus
|
||||
- More Android app translations: Spanish, Italian, Russian, French
|
||||
- Support for more devices, Cupcake, Donut
|
||||
- Worked around two important, related callback issues in Donut/Cupcake that stopped scanning
|
||||
- New custom search in Android app
|
||||
- New framework for evaluating the black point of an image
|
||||
- Many many small bug fixes, refactorings, small improvements, new versions of dependencies
|
||||
|
||||
1.5 (5 March 2010)
|
||||
- Android app:
|
||||
- Shopper integration
|
||||
- Tries to activate front light on some devices
|
||||
- Xperia et al. support with new screen sizes
|
||||
- Translations: Dutch, Swedish, Czech, Finnish
|
||||
- "Thinking" visualization
|
||||
- Updated C++ port, including new Data Matrix port
|
||||
- Updated C# port
|
||||
- Preliminary RSS-14 support
|
||||
- Reconceived support for various image sources and thresholding
|
||||
- Other small bug fixes and performance improvements
|
||||
|
||||
1.6 (20 Sept 2010)
|
||||
- Many, many updates to C++, Symbian, iPhone ports
|
||||
- RSS 14, Expanded support
|
||||
- Code 93 support
|
||||
- Codabar support
|
||||
- Data Matrix support reaches release quality
|
||||
- UPC/EAN +5 extension support
|
||||
- Android app:
|
||||
- Share history support
|
||||
- Bulk scan mode
|
||||
- Wi-fi configuration support
|
||||
- Format available in callback for web-based integration
|
||||
- Updated or new translations for:
|
||||
Hungarian, Danish, Chinese, Arabic, Dutch, Slovenian, Turkish
|
||||
- UI beautification
|
||||
- Added ability to detect multiple barcodes per image
|
||||
- Many small bug fixes and enhancements
|
||||
|
||||
1.7 (8 June 2011)
|
||||
- Initial support for Aztec codes
|
||||
- Added the ability to read rectangular DataMatrix codes
|
||||
- Added new lengths for ITF
|
||||
- Dozens of bug fixes to core decoding routines
|
||||
- New C# port
|
||||
- Android app:
|
||||
- Tablet support
|
||||
- Many translations added and updated
|
||||
- Support for new Intent parameters
|
||||
- Many bug fixes and small features
|
||||
- Mostly rewritten iPhone port
|
||||
- New ZXingWidget library to include scanning in any iOS app
|
||||
- Made CommandLineRunner multithreaded and recursive
|
||||
|
||||
2.0 (3 Feb 2012)
|
||||
|
||||
- core/ has been rewritten to work with Java 6
|
||||
- core/ has new functionality:
|
||||
- PDF 417 encoding (from Barcode4J)
|
||||
- Codabar encoding
|
||||
- partial Maxicode decoding support
|
||||
- android/ and androidtest/ have been rewritten to work with Android 2.1+
|
||||
- Uses new camera APIs
|
||||
- Uses new contact APIs too
|
||||
- (See separate Barcode Scanner release notes for version 4.0)
|
||||
- zxing.appspot.com/ has been rewritten to work with GWT 2.4
|
||||
- cpp/, objc/ and iphone/ have received many ports of fixes and other functionality from Java, and changes to work with more recent iOS
|
||||
- bug/, rim/, and javame/ have been removed
|
||||
- Many, many small bug fixes and optimizations, mostly to core/ and android/
|
||||
|
||||
2.1 (22 Oct 2012)
|
||||
|
||||
- Many small fixes to handling of QR code data formats, like exotic URLs, and contact info
|
||||
- Many small fixes to decoding and detection common barcode formats, like PDF417, Aztec. Small improvements
|
||||
too like partial error correction for PDF417
|
||||
- Many small updates to the Android app, Barcode Scanner, including rare bug fixes and translation updates.
|
||||
The 2.1 release corresponds substantially to v4.3.1.
|
||||
- Many ports of Java updates to C++ port
|
||||
|
||||
2.2 (14 May 2013)
|
||||
|
||||
- Retire Symbian port
|
||||
- Retire C# port
|
||||
- Improved PDF417 decoding, including macro PDF417
|
||||
- Added Aztec and Data Matrix encoders
|
||||
- Added RSS Expanded decoder
|
||||
- Java, C++ and iPhone build system updates, including migration to Maven for most Java modules
|
||||
- Many small fixes and improvements
|
||||
- Many small updates to the Android app, Barcode Scanner, including rare bug fixes and translation updates.
|
||||
The 2.2 release corresponds substantially to v4.3.2.
|
||||
- Update zxing.org web-based decoder for Amazon Elastic Beanstalk (and redeployed there)
|
||||
- Many ports of Java updates to C++ and C# ports
|
||||
- Standardize test system image processing and image format for reproducibility
|
||||
|
||||
2.3.0 (1 Dec 2013)
|
||||
|
||||
- Added clone of Google Chart Server QR code encoder API to zxingorg/, at endpoint /chart
|
||||
- Updated zxing.appspot.com generator to remove deprecated API usage, use latest GWT / App Engine
|
||||
- Added skeleton Google Glass Mirror API app
|
||||
- Improve and standardize Maven build, standardize directories and version naming
|
||||
- Barcode Scanner Android up requires Android 4 and is updated to use Android 4+ APIs
|
||||
- javase/ and zxingorg/ modules now use Java 7
|
||||
- Many small fixes and improvements
|
||||
- (Last release including cpp/ and iphone/ code)
|
||||
|
||||
3.0.0 (3 March 2014)
|
||||
|
||||
- iphone/, cpp/, objc/, and glass-mirror/ are retired
|
||||
- Java 7 now used for all Java-based modules
|
||||
- Project moved to github.com/zxing/zxing
|
||||
- Many small bug fixes and improvements
|
||||
|
||||
3.0.1 (18 April 2014)
|
||||
|
||||
- Several small bug fixes
|
||||
|
||||
3.1.0 (22 May 2014)
|
||||
|
||||
- Retire actionscript port
|
||||
- Small bug fixes
|
||||
|
||||
3.2.0 (15 Feb 2015)
|
||||
|
||||
- Several bug fixes, especially for Data Matrix and PDF417 parsing
|
||||
- Retired jruby binding
|
||||
- Barcode Scanner can now disable history
|
||||
|
||||
3.2.1 (20 Aug 2015)
|
||||
|
||||
- Small bug fixes
|
||||
245
apps/imagegen/zxing/LICENSE
Normal file
245
apps/imagegen/zxing/LICENSE
Normal file
@@ -0,0 +1,245 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
========================================================================
|
||||
jai-imageio
|
||||
========================================================================
|
||||
|
||||
Copyright (c) 2005 Sun Microsystems, Inc.
|
||||
Copyright © 2010-2014 University of Manchester
|
||||
Copyright © 2010-2015 Stian Soiland-Reyes
|
||||
Copyright © 2015 Peter Hull
|
||||
All Rights Reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistribution of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistribution in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
Neither the name of Sun Microsystems, Inc. or the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
This software is provided "AS IS," without a warranty of any
|
||||
kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
|
||||
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
|
||||
EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
|
||||
NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
|
||||
USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
|
||||
DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
|
||||
ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
|
||||
CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
|
||||
REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
|
||||
INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
You acknowledge that this software is not designed or intended for
|
||||
use in the design, construction, operation or maintenance of any
|
||||
nuclear facility.
|
||||
83
apps/imagegen/zxing/README.md
Normal file
83
apps/imagegen/zxing/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
<img align="right" src="https://raw.github.com/wiki/zxing/zxing/zxing-logo.png"/>
|
||||
|
||||
ZXing ("zebra crossing") is an open-source, multi-format 1D/2D barcode image processing
|
||||
library implemented in Java, with ports to other languages.
|
||||
|
||||
## Supported Formats
|
||||
|
||||
| 1D product | 1D industrial | 2D
|
||||
| ---------- | ------------- | --------------
|
||||
| UPC-A | Code 39 | QR Code
|
||||
| UPC-E | Code 93 | Data Matrix
|
||||
| EAN-8 | Code 128 | Aztec (beta)
|
||||
| EAN-13 | Codabar | PDF 417 (beta)
|
||||
| | ITF |
|
||||
| | RSS-14 |
|
||||
| | RSS-Expanded |
|
||||
|
||||
## Components
|
||||
|
||||
### Active
|
||||
|
||||
| Module | Description
|
||||
| ------------------- | -----------
|
||||
| core | The core image decoding library, and test code
|
||||
| javase | JavaSE-specific client code
|
||||
| android | Android client Barcode Scanner [](https://play.google.com/store/apps/details?id=com.google.zxing.client.android)
|
||||
| androidtest | Android test app, ZXing Test
|
||||
| android-integration | Supports integration with Barcode Scanner via `Intent`
|
||||
| android-core | Android-related code shared among `android`, `androidtest`, `glass`
|
||||
| glass | Simple Google Glass application
|
||||
| zxingorg | The source behind `zxing.org`
|
||||
| zxing.appspot.com | The source behind web-based barcode generator at `zxing.appspot.com`
|
||||
|
||||
### Available in previous releases
|
||||
|
||||
| Module | Description
|
||||
| ------ | -----------
|
||||
| [cpp](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/cpp) | C++ port
|
||||
| [iphone](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/iphone) | iPhone client
|
||||
| [objc](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/objc) | Objective C port
|
||||
| [actionscript](https://github.com/zxing/zxing/tree/c1df162b95e07928afbd4830798cc1408af1ac67/actionscript) | Partial ActionScript port
|
||||
| [jruby](https://github.com/zxing/zxing/tree/a95a8fee842f67fb43799a8e0e70e4c68b509c43/jruby) | JRuby wrapper
|
||||
|
||||
### ZXing-based third-party open source projects
|
||||
|
||||
| Module | Description
|
||||
| --------------------------------------------------------------- | -----------
|
||||
| [QZXing](https://sourceforge.net/projects/qzxing) | port to Qt framework
|
||||
| [zxing-cpp](https://github.com/glassechidna/zxing-cpp) | port to C++ (forked from the [deprecated official C++ port](https://github.com/zxing/zxing/tree/00f634024ceeee591f54e6984ea7dd666fab22ae/cpp))
|
||||
| [zxing_cpp.rb](https://github.com/glassechidna/zxing_cpp.rb) | bindings for Ruby (not just JRuby), powered by [zxing-cpp](https://github.com/glassechidna/zxing-cpp)
|
||||
| [python-zxing](https://github.com/oostendo/python-zxing) | bindings for Python
|
||||
| [ZXing .NET](http://zxingnet.codeplex.com/) | port to .NET and C#, and related Windows platform
|
||||
|
||||
### Other related third-party open source projects
|
||||
|
||||
| Module | Description
|
||||
| ---------------------------------------------- | -----------
|
||||
| [Barcode4J](http://barcode4j.sourceforge.net/) | Generator library in Java
|
||||
| [ZBar](http://zbar.sourceforge.net/) | Reader library in C99
|
||||
| [OkapiBarcode](https://github.com/woo-j/OkapiBarcode) |
|
||||
|
||||
## Links
|
||||
|
||||
* [Online Decoder](http://zxing.org/w/decode.jspx)
|
||||
* [QR Code Generator](http://zxing.appspot.com/generator)
|
||||
* [Javadoc](http://zxing.github.io/zxing/apidocs/)
|
||||
* [Documentation Site](http://zxing.github.io/zxing/)
|
||||
* [Google+](https://plus.google.com/u/0/b/105889184633382354358/105889184633382354358/posts)
|
||||
|
||||
## Contacting
|
||||
|
||||
Post to the [discussion forum](https://groups.google.com/group/zxing) or tag a question with [`zxing`
|
||||
on StackOverflow](http://stackoverflow.com/questions/tagged/zxing).
|
||||
|
||||
## Etcetera
|
||||
|
||||
[](https://travis-ci.org/zxing/zxing)
|
||||
[](https://scan.coverity.com/projects/1924)
|
||||
[](https://codecov.io/github/zxing/zxing?branch=master)
|
||||
|
||||
QR code is trademarked by Denso Wave, inc. Thanks to Haase & Martin OHG for contributing the logo.
|
||||
|
||||
Optimized with [](http://www.ej-technologies.com/products/jprofiler/overview.html)
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Enumerates barcode formats known to this package. Please keep alphabetized.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum BarcodeFormat {
|
||||
|
||||
/** Aztec 2D barcode format. */
|
||||
AZTEC,
|
||||
|
||||
/** CODABAR 1D format. */
|
||||
CODABAR,
|
||||
|
||||
/** Code 39 1D format. */
|
||||
CODE_39,
|
||||
|
||||
/** Code 93 1D format. */
|
||||
CODE_93,
|
||||
|
||||
/** Code 128 1D format. */
|
||||
CODE_128,
|
||||
|
||||
/** Data Matrix 2D barcode format. */
|
||||
DATA_MATRIX,
|
||||
|
||||
/** EAN-8 1D format. */
|
||||
EAN_8,
|
||||
|
||||
/** EAN-13 1D format. */
|
||||
EAN_13,
|
||||
|
||||
/** ITF (Interleaved Two of Five) 1D format. */
|
||||
ITF,
|
||||
|
||||
/** MaxiCode 2D barcode format. */
|
||||
MAXICODE,
|
||||
|
||||
/** PDF417 format. */
|
||||
PDF_417,
|
||||
|
||||
/** QR Code 2D barcode format. */
|
||||
QR_CODE,
|
||||
|
||||
/** RSS 14 */
|
||||
RSS_14,
|
||||
|
||||
/** RSS EXPANDED */
|
||||
RSS_EXPANDED,
|
||||
|
||||
/** UPC-A 1D format. */
|
||||
UPC_A,
|
||||
|
||||
/** UPC-E 1D format. */
|
||||
UPC_E,
|
||||
|
||||
/** UPC/EAN extension format. Not a stand-alone format. */
|
||||
UPC_EAN_EXTENSION
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private ChecksumException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private ChecksumException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public static ChecksumException getChecksumInstance() {
|
||||
return isStackTrace ? new ChecksumException() : INSTANCE;
|
||||
}
|
||||
|
||||
public static ChecksumException getChecksumInstance(Throwable cause) {
|
||||
return isStackTrace ? new ChecksumException(cause) : INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* These are a set of hints that you may pass to Writers to specify their behavior.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public enum EncodeHintType {
|
||||
|
||||
/**
|
||||
* Specifies what degree of error correction to use, for example in QR Codes.
|
||||
* Type depends on the encoder. For example for QR codes it's type
|
||||
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
|
||||
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
|
||||
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
|
||||
* In all cases, it can also be a {@link String} representation of the desired value as well.
|
||||
* Note: an Aztec symbol should have a minimum of 25% EC words.
|
||||
*/
|
||||
ERROR_CORRECTION,
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use where applicable (type {@link String})
|
||||
*/
|
||||
CHARACTER_SET,
|
||||
|
||||
/**
|
||||
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
|
||||
*/
|
||||
DATA_MATRIX_SHAPE,
|
||||
|
||||
/**
|
||||
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated use width/height params in
|
||||
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
MIN_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated without replacement
|
||||
*/
|
||||
@Deprecated
|
||||
MAX_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
|
||||
* by format; for example it controls margin before and after the barcode horizontally for
|
||||
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
MARGIN,
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String} value).
|
||||
*/
|
||||
PDF417_COMPACT,
|
||||
|
||||
/**
|
||||
* Specifies what compaction mode to use for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
|
||||
* enum values).
|
||||
*/
|
||||
PDF417_COMPACTION,
|
||||
|
||||
/**
|
||||
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
|
||||
*/
|
||||
PDF417_DIMENSIONS,
|
||||
|
||||
/**
|
||||
* Specifies the required number of layers for an Aztec code.
|
||||
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
|
||||
* 0 indicates to use the minimum number of layers (the default).
|
||||
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
AZTEC_LAYERS,
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private FormatException() {
|
||||
}
|
||||
|
||||
private FormatException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public static FormatException getFormatInstance() {
|
||||
return isStackTrace ? new FormatException() : INSTANCE;
|
||||
}
|
||||
|
||||
public static FormatException getFormatInstance(Throwable cause) {
|
||||
return isStackTrace ? new FormatException(cause) : INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
// disable stack traces when not running inside test units
|
||||
protected static final boolean isStackTrace =
|
||||
System.getProperty("surefire.test.class.path") != null;
|
||||
protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0];
|
||||
|
||||
ReaderException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ReaderException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
// Prevent stack traces from being taken
|
||||
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
|
||||
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
|
||||
@Override
|
||||
public final Throwable fillInStackTrace() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The base class for all objects which encode/generate a barcode image.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public interface Writer {
|
||||
|
||||
/**
|
||||
* Encode a barcode using the default settings.
|
||||
*
|
||||
* @param contents The contents to encode in the barcode
|
||||
* @param format The barcode format to generate
|
||||
* @param width The preferred width in pixels
|
||||
* @param height The preferred height in pixels
|
||||
* @return {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
|
||||
throws WriterException;
|
||||
|
||||
/**
|
||||
* @param contents The contents to encode in the barcode
|
||||
* @param format The barcode format to generate
|
||||
* @param width The preferred width in pixels
|
||||
* @param height The preferred height in pixels
|
||||
* @param hints Additional parameters to supply to the encoder
|
||||
* @return {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height,
|
||||
Map<EncodeHintType,?> hints)
|
||||
throws WriterException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* A base class which covers the range of exceptions which may occur when encoding a barcode using
|
||||
* the Writer framework.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class WriterException extends Exception {
|
||||
|
||||
public WriterException() {
|
||||
}
|
||||
|
||||
public WriterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public WriterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class BitArray implements Cloneable {
|
||||
|
||||
private int[] bits;
|
||||
private int size;
|
||||
|
||||
public BitArray() {
|
||||
this.size = 0;
|
||||
this.bits = new int[1];
|
||||
}
|
||||
|
||||
public BitArray(int size) {
|
||||
this.size = size;
|
||||
this.bits = makeArray(size);
|
||||
}
|
||||
|
||||
// For testing only
|
||||
BitArray(int[] bits, int size) {
|
||||
this.bits = bits;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getSizeInBytes() {
|
||||
return (size + 7) / 8;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int size) {
|
||||
if (size > bits.length * 32) {
|
||||
int[] newBits = makeArray(size);
|
||||
System.arraycopy(bits, 0, newBits, 0, bits.length);
|
||||
this.bits = newBits;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param i bit to get
|
||||
* @return true iff bit i is set
|
||||
*/
|
||||
public boolean get(int i) {
|
||||
return (bits[i / 32] & (1 << (i & 0x1F))) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public void set(int i) {
|
||||
bits[i / 32] |= 1 << (i & 0x1F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public void flip(int i) {
|
||||
bits[i / 32] ^= 1 << (i & 0x1F);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from first bit to check
|
||||
* @return index of first bit that is set, starting from the given index, or size if none are set
|
||||
* at or beyond this given index
|
||||
* @see #getNextUnset(int)
|
||||
*/
|
||||
public int getNextSet(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
int bitsOffset = from / 32;
|
||||
int currentBits = bits[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= ~((1 << (from & 0x1F)) - 1);
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == bits.length) {
|
||||
return size;
|
||||
}
|
||||
currentBits = bits[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
|
||||
return result > size ? size : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from index to start looking for unset bit
|
||||
* @return index of next unset bit, or {@code size} if none are unset until the end
|
||||
* @see #getNextSet(int)
|
||||
*/
|
||||
public int getNextUnset(int from) {
|
||||
if (from >= size) {
|
||||
return size;
|
||||
}
|
||||
int bitsOffset = from / 32;
|
||||
int currentBits = ~bits[bitsOffset];
|
||||
// mask off lesser bits first
|
||||
currentBits &= ~((1 << (from & 0x1F)) - 1);
|
||||
while (currentBits == 0) {
|
||||
if (++bitsOffset == bits.length) {
|
||||
return size;
|
||||
}
|
||||
currentBits = ~bits[bitsOffset];
|
||||
}
|
||||
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
|
||||
return result > size ? size : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a block of 32 bits, starting at bit i.
|
||||
*
|
||||
* @param i first bit to set
|
||||
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
|
||||
* corresponds to bit i, the next-least-significant to i+1, and so on.
|
||||
*/
|
||||
public void setBulk(int i, int newBits) {
|
||||
bits[i / 32] = newBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a range of bits.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
*/
|
||||
public void setRange(int start, int end) {
|
||||
if (end < start) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (end == start) {
|
||||
return;
|
||||
}
|
||||
end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
int firstInt = start / 32;
|
||||
int lastInt = end / 32;
|
||||
for (int i = firstInt; i <= lastInt; i++) {
|
||||
int firstBit = i > firstInt ? 0 : start & 0x1F;
|
||||
int lastBit = i < lastInt ? 31 : end & 0x1F;
|
||||
int mask;
|
||||
if (firstBit == 0 && lastBit == 31) {
|
||||
mask = -1;
|
||||
} else {
|
||||
mask = 0;
|
||||
for (int j = firstBit; j <= lastBit; j++) {
|
||||
mask |= 1 << j;
|
||||
}
|
||||
}
|
||||
bits[i] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all bits (sets to false).
|
||||
*/
|
||||
public void clear() {
|
||||
int max = bits.length;
|
||||
for (int i = 0; i < max; i++) {
|
||||
bits[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Efficient method to check if a range of bits is set, or not set.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
|
||||
* @return true iff all bits are set or not set in range, according to value argument
|
||||
* @throws IllegalArgumentException if end is less than or equal to start
|
||||
*/
|
||||
public boolean isRange(int start, int end, boolean value) {
|
||||
if (end < start) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (end == start) {
|
||||
return true; // empty range matches
|
||||
}
|
||||
end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
int firstInt = start / 32;
|
||||
int lastInt = end / 32;
|
||||
for (int i = firstInt; i <= lastInt; i++) {
|
||||
int firstBit = i > firstInt ? 0 : start & 0x1F;
|
||||
int lastBit = i < lastInt ? 31 : end & 0x1F;
|
||||
int mask;
|
||||
if (firstBit == 0 && lastBit == 31) {
|
||||
mask = -1;
|
||||
} else {
|
||||
mask = 0;
|
||||
for (int j = firstBit; j <= lastBit; j++) {
|
||||
mask |= 1 << j;
|
||||
}
|
||||
}
|
||||
|
||||
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
|
||||
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
|
||||
if ((bits[i] & mask) != (value ? mask : 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void appendBit(boolean bit) {
|
||||
ensureCapacity(size + 1);
|
||||
if (bit) {
|
||||
bits[size / 32] |= 1 << (size & 0x1F);
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the least-significant bits, from value, in order from most-significant to
|
||||
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
|
||||
* 0, 1, 1, 1, 1, 0 in that order.
|
||||
*
|
||||
* @param value {@code int} containing bits to append
|
||||
* @param numBits bits from value to append
|
||||
*/
|
||||
public void appendBits(int value, int numBits) {
|
||||
if (numBits < 0 || numBits > 32) {
|
||||
throw new IllegalArgumentException("Num bits must be between 0 and 32");
|
||||
}
|
||||
ensureCapacity(size + numBits);
|
||||
for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
|
||||
appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendBitArray(BitArray other) {
|
||||
int otherSize = other.size;
|
||||
ensureCapacity(size + otherSize);
|
||||
for (int i = 0; i < otherSize; i++) {
|
||||
appendBit(other.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public void xor(BitArray other) {
|
||||
if (bits.length != other.bits.length) {
|
||||
throw new IllegalArgumentException("Sizes don't match");
|
||||
}
|
||||
for (int i = 0; i < bits.length; i++) {
|
||||
// The last byte could be incomplete (i.e. not have 8 bits in
|
||||
// it) but there is no problem since 0 XOR 0 == 0.
|
||||
bits[i] ^= other.bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bitOffset first bit to start writing
|
||||
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
|
||||
* of the internal representation, which is exposed by {@link #getBitArray()}
|
||||
* @param offset position in array to start writing
|
||||
* @param numBytes how many bytes to write
|
||||
*/
|
||||
public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
int theByte = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (get(bitOffset)) {
|
||||
theByte |= 1 << (7 - j);
|
||||
}
|
||||
bitOffset++;
|
||||
}
|
||||
array[offset + i] = (byte) theByte;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return underlying array of ints. The first element holds the first 32 bits, and the least
|
||||
* significant bit is bit 0.
|
||||
*/
|
||||
public int[] getBitArray() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses all bits in the array.
|
||||
*/
|
||||
public void reverse() {
|
||||
int[] newBits = new int[bits.length];
|
||||
// reverse all int's first
|
||||
int len = ((size-1) / 32);
|
||||
int oldBitsLen = len + 1;
|
||||
for (int i = 0; i < oldBitsLen; i++) {
|
||||
long x = (long) bits[i];
|
||||
x = ((x >> 1) & 0x55555555L) | ((x & 0x55555555L) << 1);
|
||||
x = ((x >> 2) & 0x33333333L) | ((x & 0x33333333L) << 2);
|
||||
x = ((x >> 4) & 0x0f0f0f0fL) | ((x & 0x0f0f0f0fL) << 4);
|
||||
x = ((x >> 8) & 0x00ff00ffL) | ((x & 0x00ff00ffL) << 8);
|
||||
x = ((x >> 16) & 0x0000ffffL) | ((x & 0x0000ffffL) << 16);
|
||||
newBits[len - i] = (int) x;
|
||||
}
|
||||
// now correct the int's if the bit size isn't a multiple of 32
|
||||
if (size != oldBitsLen * 32) {
|
||||
int leftOffset = oldBitsLen * 32 - size;
|
||||
int mask = 1;
|
||||
for (int i = 0; i < 31 - leftOffset; i++) {
|
||||
mask = (mask << 1) | 1;
|
||||
}
|
||||
int currentInt = (newBits[0] >> leftOffset) & mask;
|
||||
for (int i = 1; i < oldBitsLen; i++) {
|
||||
int nextInt = newBits[i];
|
||||
currentInt |= nextInt << (32 - leftOffset);
|
||||
newBits[i - 1] = currentInt;
|
||||
currentInt = (nextInt >> leftOffset) & mask;
|
||||
}
|
||||
newBits[oldBitsLen - 1] = currentInt;
|
||||
}
|
||||
bits = newBits;
|
||||
}
|
||||
|
||||
private static int[] makeArray(int size) {
|
||||
return new int[(size + 31) / 32];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof BitArray)) {
|
||||
return false;
|
||||
}
|
||||
BitArray other = (BitArray) o;
|
||||
return size == other.size && Arrays.equals(bits, other.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * size + Arrays.hashCode(bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if ((i & 0x07) == 0) {
|
||||
result.append(' ');
|
||||
}
|
||||
result.append(get(i) ? 'X' : '.');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitArray clone() {
|
||||
return new BitArray(bits.clone(), size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
|
||||
* module, x is the column position, and y is the row position. The ordering is always x, y.
|
||||
* The origin is at the top-left.</p>
|
||||
*
|
||||
* <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
|
||||
* with a new int. This is done intentionally so that we can copy out a row into a BitArray very
|
||||
* efficiently.</p>
|
||||
*
|
||||
* <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
|
||||
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class BitMatrix implements Cloneable {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int rowSize;
|
||||
private final int[] bits;
|
||||
|
||||
// A helper to construct a square matrix.
|
||||
public BitMatrix(int dimension) {
|
||||
this(dimension, dimension);
|
||||
}
|
||||
|
||||
public BitMatrix(int width, int height) {
|
||||
if (width < 1 || height < 1) {
|
||||
throw new IllegalArgumentException("Both dimensions must be greater than 0");
|
||||
}
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.rowSize = (width + 31) / 32;
|
||||
bits = new int[rowSize * height];
|
||||
}
|
||||
|
||||
private BitMatrix(int width, int height, int rowSize, int[] bits) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.rowSize = rowSize;
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
public static BitMatrix parse(String stringRepresentation, String setString, String unsetString) {
|
||||
if (stringRepresentation == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
boolean[] bits = new boolean[stringRepresentation.length()];
|
||||
int bitsPos = 0;
|
||||
int rowStartPos = 0;
|
||||
int rowLength = -1;
|
||||
int nRows = 0;
|
||||
int pos = 0;
|
||||
while (pos < stringRepresentation.length()) {
|
||||
if (stringRepresentation.charAt(pos) == '\n' ||
|
||||
stringRepresentation.charAt(pos) == '\r') {
|
||||
if (bitsPos > rowStartPos) {
|
||||
if (rowLength == -1) {
|
||||
rowLength = bitsPos - rowStartPos;
|
||||
} else if (bitsPos - rowStartPos != rowLength) {
|
||||
throw new IllegalArgumentException("row lengths do not match");
|
||||
}
|
||||
rowStartPos = bitsPos;
|
||||
nRows++;
|
||||
}
|
||||
pos++;
|
||||
} else if (stringRepresentation.substring(pos, pos + setString.length()).equals(setString)) {
|
||||
pos += setString.length();
|
||||
bits[bitsPos] = true;
|
||||
bitsPos++;
|
||||
} else if (stringRepresentation.substring(pos, pos + unsetString.length()).equals(unsetString)) {
|
||||
pos += unsetString.length();
|
||||
bits[bitsPos] = false;
|
||||
bitsPos++;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"illegal character encountered: " + stringRepresentation.substring(pos));
|
||||
}
|
||||
}
|
||||
|
||||
// no EOL at end?
|
||||
if (bitsPos > rowStartPos) {
|
||||
if(rowLength == -1) {
|
||||
rowLength = bitsPos - rowStartPos;
|
||||
} else if (bitsPos - rowStartPos != rowLength) {
|
||||
throw new IllegalArgumentException("row lengths do not match");
|
||||
}
|
||||
nRows++;
|
||||
}
|
||||
|
||||
BitMatrix matrix = new BitMatrix(rowLength, nRows);
|
||||
for (int i = 0; i < bitsPos; i++) {
|
||||
if (bits[i]) {
|
||||
matrix.set(i % rowLength, i / rowLength);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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 / 32);
|
||||
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 / 32);
|
||||
bits[offset] |= 1 << (x & 0x1f);
|
||||
}
|
||||
|
||||
public void unset(int x, int y) {
|
||||
int offset = y * rowSize + (x / 32);
|
||||
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 / 32);
|
||||
bits[offset] ^= 1 << (x & 0x1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding
|
||||
* mask bit is set.
|
||||
*
|
||||
* @param mask XOR mask
|
||||
*/
|
||||
public void xor(BitMatrix mask) {
|
||||
if (width != mask.getWidth() || height != mask.getHeight()
|
||||
|| rowSize != mask.getRowSize()) {
|
||||
throw new IllegalArgumentException("input matrix dimensions do not match");
|
||||
}
|
||||
BitArray rowArray = new BitArray(width / 32 + 1);
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * rowSize;
|
||||
int[] row = mask.getRow(y, rowArray).getBitArray();
|
||||
for (int x = 0; x < rowSize; x++) {
|
||||
bits[offset + x] ^= row[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 / 32)] |= 1 << (x & 0x1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fast method to retrieve one row of data from the matrix as a BitArray.
|
||||
*
|
||||
* @param y The row to retrieve
|
||||
* @param row An optional caller-allocated BitArray, will be allocated if null or too small
|
||||
* @return The resulting BitArray - this reference should always be used even when passing
|
||||
* your own row
|
||||
*/
|
||||
public BitArray getRow(int y, BitArray row) {
|
||||
if (row == null || row.getSize() < width) {
|
||||
row = new BitArray(width);
|
||||
} else {
|
||||
row.clear();
|
||||
}
|
||||
int offset = y * rowSize;
|
||||
for (int x = 0; x < rowSize; x++) {
|
||||
row.setBulk(x * 32, bits[offset + x]);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param y row to set
|
||||
* @param row {@link BitArray} to copy from
|
||||
*/
|
||||
public void setRow(int y, BitArray row) {
|
||||
System.arraycopy(row.getBitArray(), 0, bits, y * rowSize, rowSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
|
||||
*/
|
||||
public void rotate180() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
BitArray topRow = new BitArray(width);
|
||||
BitArray bottomRow = new BitArray(width);
|
||||
for (int i = 0; i < (height+1) / 2; i++) {
|
||||
topRow = getRow(i, topRow);
|
||||
bottomRow = getRow(height - 1 - i, bottomRow);
|
||||
topRow.reverse();
|
||||
bottomRow.reverse();
|
||||
setRow(i, bottomRow);
|
||||
setRow(height - 1 - i, topRow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful in detecting the enclosing rectangle of a 'pure' barcode.
|
||||
*
|
||||
* @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white
|
||||
*/
|
||||
public int[] getEnclosingRectangle() {
|
||||
int left = width;
|
||||
int top = height;
|
||||
int right = -1;
|
||||
int bottom = -1;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x32 = 0; x32 < rowSize; x32++) {
|
||||
int theBits = bits[y * rowSize + x32];
|
||||
if (theBits != 0) {
|
||||
if (y < top) {
|
||||
top = y;
|
||||
}
|
||||
if (y > bottom) {
|
||||
bottom = y;
|
||||
}
|
||||
if (x32 * 32 < left) {
|
||||
int bit = 0;
|
||||
while ((theBits << (31 - bit)) == 0) {
|
||||
bit++;
|
||||
}
|
||||
if ((x32 * 32 + bit) < left) {
|
||||
left = x32 * 32 + bit;
|
||||
}
|
||||
}
|
||||
if (x32 * 32 + 31 > right) {
|
||||
int bit = 31;
|
||||
while ((theBits >>> bit) == 0) {
|
||||
bit--;
|
||||
}
|
||||
if ((x32 * 32 + bit) > right) {
|
||||
right = x32 * 32 + bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int width = right - left;
|
||||
int height = bottom - top;
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new int[] {left, top, width, height};
|
||||
}
|
||||
|
||||
/**
|
||||
* This is useful in detecting a corner of a 'pure' barcode.
|
||||
*
|
||||
* @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white
|
||||
*/
|
||||
public int[] getTopLeftOnBit() {
|
||||
int bitsOffset = 0;
|
||||
while (bitsOffset < bits.length && bits[bitsOffset] == 0) {
|
||||
bitsOffset++;
|
||||
}
|
||||
if (bitsOffset == bits.length) {
|
||||
return null;
|
||||
}
|
||||
int y = bitsOffset / rowSize;
|
||||
int x = (bitsOffset % rowSize) * 32;
|
||||
|
||||
int theBits = bits[bitsOffset];
|
||||
int bit = 0;
|
||||
while ((theBits << (31-bit)) == 0) {
|
||||
bit++;
|
||||
}
|
||||
x += bit;
|
||||
return new int[] {x, y};
|
||||
}
|
||||
|
||||
public int[] getBottomRightOnBit() {
|
||||
int bitsOffset = bits.length - 1;
|
||||
while (bitsOffset >= 0 && bits[bitsOffset] == 0) {
|
||||
bitsOffset--;
|
||||
}
|
||||
if (bitsOffset < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int y = bitsOffset / rowSize;
|
||||
int x = (bitsOffset % rowSize) * 32;
|
||||
|
||||
int theBits = bits[bitsOffset];
|
||||
int bit = 31;
|
||||
while ((theBits >>> bit) == 0) {
|
||||
bit--;
|
||||
}
|
||||
x += bit;
|
||||
|
||||
return new int[] {x, y};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The width of the matrix
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the matrix
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The row size of the matrix
|
||||
*/
|
||||
public int getRowSize() {
|
||||
return rowSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof BitMatrix)) {
|
||||
return false;
|
||||
}
|
||||
BitMatrix other = (BitMatrix) o;
|
||||
return width == other.width && height == other.height && rowSize == other.rowSize &&
|
||||
Arrays.equals(bits, other.bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = width;
|
||||
hash = 31 * hash + width;
|
||||
hash = 31 * hash + height;
|
||||
hash = 31 * hash + rowSize;
|
||||
hash = 31 * hash + Arrays.hashCode(bits);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string representation using "X" for set and " " for unset bits
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString("X ", " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param setString representation of a set bit
|
||||
* @param unsetString representation of an unset bit
|
||||
* @return string representation of entire matrix utilizing given strings
|
||||
*/
|
||||
public String toString(String setString, String unsetString) {
|
||||
return toString(setString, unsetString, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param setString representation of a set bit
|
||||
* @param unsetString representation of an unset bit
|
||||
* @param lineSeparator newline character in string representation
|
||||
* @return string representation of entire matrix utilizing given strings and line separator
|
||||
* @deprecated call {@link #toString(String,String)} only, which uses \n line separator always
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(String setString, String unsetString, String lineSeparator) {
|
||||
StringBuilder result = new StringBuilder(height * (width + 1));
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
result.append(get(x, y) ? setString : unsetString);
|
||||
}
|
||||
result.append(lineSeparator);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix clone() {
|
||||
return new BitMatrix(width, height, rowSize, bits.clone());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
|
||||
* of ISO 18004.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum CharacterSetECI {
|
||||
|
||||
// Enum name is a Java encoding valid for java.lang and java.io
|
||||
Cp437(new int[]{0,2}),
|
||||
ISO8859_1(new int[]{1,3}, "ISO-8859-1"),
|
||||
ISO8859_2(4, "ISO-8859-2"),
|
||||
ISO8859_3(5, "ISO-8859-3"),
|
||||
ISO8859_4(6, "ISO-8859-4"),
|
||||
ISO8859_5(7, "ISO-8859-5"),
|
||||
ISO8859_6(8, "ISO-8859-6"),
|
||||
ISO8859_7(9, "ISO-8859-7"),
|
||||
ISO8859_8(10, "ISO-8859-8"),
|
||||
ISO8859_9(11, "ISO-8859-9"),
|
||||
ISO8859_10(12, "ISO-8859-10"),
|
||||
ISO8859_11(13, "ISO-8859-11"),
|
||||
ISO8859_13(15, "ISO-8859-13"),
|
||||
ISO8859_14(16, "ISO-8859-14"),
|
||||
ISO8859_15(17, "ISO-8859-15"),
|
||||
ISO8859_16(18, "ISO-8859-16"),
|
||||
SJIS(20, "Shift_JIS"),
|
||||
Cp1250(21, "windows-1250"),
|
||||
Cp1251(22, "windows-1251"),
|
||||
Cp1252(23, "windows-1252"),
|
||||
Cp1256(24, "windows-1256"),
|
||||
UnicodeBigUnmarked(25, "UTF-16BE", "UnicodeBig"),
|
||||
UTF8(26, "UTF-8"),
|
||||
ASCII(new int[] {27, 170}, "US-ASCII"),
|
||||
Big5(28),
|
||||
GB18030(29, "GB2312", "EUC_CN", "GBK"),
|
||||
EUC_KR(30, "EUC-KR");
|
||||
|
||||
private static final Map<Integer,CharacterSetECI> VALUE_TO_ECI = new HashMap<>();
|
||||
private static final Map<String,CharacterSetECI> NAME_TO_ECI = new HashMap<>();
|
||||
static {
|
||||
for (CharacterSetECI eci : values()) {
|
||||
for (int value : eci.values) {
|
||||
VALUE_TO_ECI.put(value, eci);
|
||||
}
|
||||
NAME_TO_ECI.put(eci.name(), eci);
|
||||
for (String name : eci.otherEncodingNames) {
|
||||
NAME_TO_ECI.put(name, eci);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final int[] values;
|
||||
private final String[] otherEncodingNames;
|
||||
|
||||
CharacterSetECI(int value) {
|
||||
this(new int[] {value});
|
||||
}
|
||||
|
||||
CharacterSetECI(int value, String... otherEncodingNames) {
|
||||
this.values = new int[] {value};
|
||||
this.otherEncodingNames = otherEncodingNames;
|
||||
}
|
||||
|
||||
CharacterSetECI(int[] values, String... otherEncodingNames) {
|
||||
this.values = values;
|
||||
this.otherEncodingNames = otherEncodingNames;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return values[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value character set ECI value
|
||||
* @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but
|
||||
* unsupported
|
||||
* @throws FormatException if ECI value is invalid
|
||||
*/
|
||||
public static CharacterSetECI getCharacterSetECIByValue(int value) throws FormatException {
|
||||
if (value < 0 || value >= 900) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
return VALUE_TO_ECI.get(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name character set ECI encoding name
|
||||
* @return CharacterSetECI representing ECI for character encoding, or null if it is legal
|
||||
* but unsupported
|
||||
*/
|
||||
public static CharacterSetECI getCharacterSetECIByName(String name) {
|
||||
return NAME_TO_ECI.get(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.common.reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>This class contains utility methods for performing mathematical operations over
|
||||
* the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
|
||||
*
|
||||
* <p>Throughout this package, elements of the GF are represented as an {@code int}
|
||||
* for convenience and speed (but at the cost of memory).
|
||||
* </p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class GenericGF {
|
||||
|
||||
public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||
public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
|
||||
public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
|
||||
public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
|
||||
public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
||||
public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
||||
public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256;
|
||||
public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6;
|
||||
|
||||
private final int[] expTable;
|
||||
private final int[] logTable;
|
||||
private final GenericGFPoly zero;
|
||||
private final GenericGFPoly one;
|
||||
private final int size;
|
||||
private final int primitive;
|
||||
private final int generatorBase;
|
||||
|
||||
/**
|
||||
* Create a representation of GF(size) using the given primitive polynomial.
|
||||
*
|
||||
* @param primitive irreducible polynomial whose coefficients are represented by
|
||||
* the bits of an int, where the least-significant bit represents the constant
|
||||
* coefficient
|
||||
* @param size the size of the field
|
||||
* @param b the factor b in the generator polynomial can be 0- or 1-based
|
||||
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
|
||||
* In most cases it should be 1, but for QR code it is 0.
|
||||
*/
|
||||
public GenericGF(int primitive, int size, int b) {
|
||||
this.primitive = primitive;
|
||||
this.size = size;
|
||||
this.generatorBase = b;
|
||||
|
||||
expTable = new int[size];
|
||||
logTable = new int[size];
|
||||
int x = 1;
|
||||
for (int i = 0; i < size; i++) {
|
||||
expTable[i] = x;
|
||||
x *= 2; // we're assuming the generator alpha is 2
|
||||
if (x >= size) {
|
||||
x ^= primitive;
|
||||
x &= size-1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < size-1; i++) {
|
||||
logTable[expTable[i]] = i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
zero = new GenericGFPoly(this, new int[]{0});
|
||||
one = new GenericGFPoly(this, new int[]{1});
|
||||
}
|
||||
|
||||
GenericGFPoly getZero() {
|
||||
return zero;
|
||||
}
|
||||
|
||||
GenericGFPoly getOne() {
|
||||
return one;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the monomial representing coefficient * x^degree
|
||||
*/
|
||||
GenericGFPoly 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 GenericGFPoly(this, coefficients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements both addition and subtraction -- they are the same in GF(size).
|
||||
*
|
||||
* @return sum/difference of a and b
|
||||
*/
|
||||
static int addOrSubtract(int a, int b) {
|
||||
return a ^ b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2 to the power of a in GF(size)
|
||||
*/
|
||||
int exp(int a) {
|
||||
return expTable[a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return base 2 log of a in GF(size)
|
||||
*/
|
||||
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[size - logTable[a] - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return product of a and b in GF(size)
|
||||
*/
|
||||
int multiply(int a, int b) {
|
||||
if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return expTable[(logTable[a] + logTable[b]) % (size - 1)];
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getGeneratorBase() {
|
||||
return generatorBase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GF(0x" + Integer.toHexString(primitive) + ',' + size + ')';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>Represents a polynomial whose coefficients are elements of a GF.
|
||||
* Instances of this class are immutable.</p>
|
||||
*
|
||||
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||
* port of his C++ Reed-Solomon implementation.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class GenericGFPoly {
|
||||
|
||||
private final GenericGF field;
|
||||
private final int[] coefficients;
|
||||
|
||||
/**
|
||||
* @param field the {@link GenericGF} instance representing the field to use
|
||||
* to perform computations
|
||||
* @param coefficients coefficients as ints representing elements of GF(size), arranged
|
||||
* from most significant (highest-power term) coefficient to least significant
|
||||
* @throws IllegalArgumentException if argument is null or empty,
|
||||
* or if leading coefficient is 0 and this is not a
|
||||
* constant polynomial (that is, it is not the monomial "0")
|
||||
*/
|
||||
GenericGFPoly(GenericGF field, int[] coefficients) {
|
||||
if (coefficients.length == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.field = field;
|
||||
int coefficientsLength = coefficients.length;
|
||||
if (coefficientsLength > 1 && coefficients[0] == 0) {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
int firstNonZero = 1;
|
||||
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
|
||||
firstNonZero++;
|
||||
}
|
||||
if (firstNonZero == coefficientsLength) {
|
||||
this.coefficients = new int[]{0};
|
||||
} else {
|
||||
this.coefficients = new int[coefficientsLength - firstNonZero];
|
||||
System.arraycopy(coefficients,
|
||||
firstNonZero,
|
||||
this.coefficients,
|
||||
0,
|
||||
this.coefficients.length);
|
||||
}
|
||||
} else {
|
||||
this.coefficients = coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
int[] getCoefficients() {
|
||||
return coefficients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
int getDegree() {
|
||||
return coefficients.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
boolean isZero() {
|
||||
return coefficients[0] == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
int getCoefficient(int degree) {
|
||||
return coefficients[coefficients.length - 1 - degree];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
int evaluateAt(int a) {
|
||||
if (a == 0) {
|
||||
// Just return the x^0 coefficient
|
||||
return getCoefficient(0);
|
||||
}
|
||||
int size = coefficients.length;
|
||||
if (a == 1) {
|
||||
// Just the sum of the coefficients
|
||||
int result = 0;
|
||||
for (int coefficient : coefficients) {
|
||||
result = GenericGF.addOrSubtract(result, coefficient);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int result = coefficients[0];
|
||||
for (int i = 1; i < size; i++) {
|
||||
result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GenericGFPoly addOrSubtract(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if (isZero()) {
|
||||
return other;
|
||||
}
|
||||
if (other.isZero()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
int[] smallerCoefficients = this.coefficients;
|
||||
int[] largerCoefficients = other.coefficients;
|
||||
if (smallerCoefficients.length > largerCoefficients.length) {
|
||||
int[] temp = smallerCoefficients;
|
||||
smallerCoefficients = largerCoefficients;
|
||||
largerCoefficients = temp;
|
||||
}
|
||||
int[] sumDiff = new int[largerCoefficients.length];
|
||||
int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
|
||||
|
||||
for (int i = lengthDiff; i < largerCoefficients.length; i++) {
|
||||
sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||
}
|
||||
|
||||
return new GenericGFPoly(field, sumDiff);
|
||||
}
|
||||
|
||||
GenericGFPoly multiply(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if (isZero() || other.isZero()) {
|
||||
return field.getZero();
|
||||
}
|
||||
int[] aCoefficients = this.coefficients;
|
||||
int aLength = aCoefficients.length;
|
||||
int[] bCoefficients = other.coefficients;
|
||||
int bLength = bCoefficients.length;
|
||||
int[] product = new int[aLength + bLength - 1];
|
||||
for (int i = 0; i < aLength; i++) {
|
||||
int aCoeff = aCoefficients[i];
|
||||
for (int j = 0; j < bLength; j++) {
|
||||
product[i + j] = GenericGF.addOrSubtract(product[i + j],
|
||||
field.multiply(aCoeff, bCoefficients[j]));
|
||||
}
|
||||
}
|
||||
return new GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly multiply(int scalar) {
|
||||
if (scalar == 0) {
|
||||
return field.getZero();
|
||||
}
|
||||
if (scalar == 1) {
|
||||
return this;
|
||||
}
|
||||
int size = coefficients.length;
|
||||
int[] product = new int[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field.multiply(coefficients[i], scalar);
|
||||
}
|
||||
return new GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly multiplyByMonomial(int degree, int coefficient) {
|
||||
if (degree < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (coefficient == 0) {
|
||||
return field.getZero();
|
||||
}
|
||||
int size = coefficients.length;
|
||||
int[] product = new int[size + degree];
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field.multiply(coefficients[i], coefficient);
|
||||
}
|
||||
return new GenericGFPoly(field, product);
|
||||
}
|
||||
|
||||
GenericGFPoly[] divide(GenericGFPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if (other.isZero()) {
|
||||
throw new IllegalArgumentException("Divide by 0");
|
||||
}
|
||||
|
||||
GenericGFPoly quotient = field.getZero();
|
||||
GenericGFPoly remainder = this;
|
||||
|
||||
int denominatorLeadingTerm = other.getCoefficient(other.getDegree());
|
||||
int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);
|
||||
|
||||
while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
|
||||
int degreeDifference = remainder.getDegree() - other.getDegree();
|
||||
int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
|
||||
GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale);
|
||||
GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);
|
||||
quotient = quotient.addOrSubtract(iterationQuotient);
|
||||
remainder = remainder.addOrSubtract(term);
|
||||
}
|
||||
|
||||
return new GenericGFPoly[] { quotient, remainder };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(8 * getDegree());
|
||||
for (int degree = getDegree(); degree >= 0; degree--) {
|
||||
int coefficient = getCoefficient(degree);
|
||||
if (coefficient != 0) {
|
||||
if (coefficient < 0) {
|
||||
result.append(" - ");
|
||||
coefficient = -coefficient;
|
||||
} else {
|
||||
if (result.length() > 0) {
|
||||
result.append(" + ");
|
||||
}
|
||||
}
|
||||
if (degree == 0 || coefficient != 1) {
|
||||
int alphaPower = field.log(coefficient);
|
||||
if (alphaPower == 0) {
|
||||
result.append('1');
|
||||
} else if (alphaPower == 1) {
|
||||
result.append('a');
|
||||
} else {
|
||||
result.append("a^");
|
||||
result.append(alphaPower);
|
||||
}
|
||||
}
|
||||
if (degree != 0) {
|
||||
if (degree == 1) {
|
||||
result.append('x');
|
||||
} else {
|
||||
result.append("x^");
|
||||
result.append(degree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2008 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.common.reedsolomon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Implements Reed-Solomon enbcoding, as the name implies.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author William Rucklidge
|
||||
*/
|
||||
public final class ReedSolomonEncoder {
|
||||
|
||||
private final GenericGF field;
|
||||
private final List<GenericGFPoly> cachedGenerators;
|
||||
|
||||
public ReedSolomonEncoder(GenericGF field) {
|
||||
this.field = field;
|
||||
this.cachedGenerators = new ArrayList<>();
|
||||
cachedGenerators.add(new GenericGFPoly(field, new int[]{1}));
|
||||
}
|
||||
|
||||
private GenericGFPoly buildGenerator(int degree) {
|
||||
if (degree >= cachedGenerators.size()) {
|
||||
GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1);
|
||||
for (int d = cachedGenerators.size(); d <= degree; d++) {
|
||||
GenericGFPoly nextGenerator = lastGenerator.multiply(
|
||||
new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) }));
|
||||
cachedGenerators.add(nextGenerator);
|
||||
lastGenerator = nextGenerator;
|
||||
}
|
||||
}
|
||||
return cachedGenerators.get(degree);
|
||||
}
|
||||
|
||||
public void encode(int[] toEncode, int ecBytes) {
|
||||
if (ecBytes == 0) {
|
||||
throw new IllegalArgumentException("No error correction bytes");
|
||||
}
|
||||
int dataBytes = toEncode.length - ecBytes;
|
||||
if (dataBytes <= 0) {
|
||||
throw new IllegalArgumentException("No data bytes provided");
|
||||
}
|
||||
GenericGFPoly generator = buildGenerator(ecBytes);
|
||||
int[] infoCoefficients = new int[dataBytes];
|
||||
System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
|
||||
GenericGFPoly info = new GenericGFPoly(field, infoCoefficients);
|
||||
info = info.multiplyByMonomial(ecBytes, 1);
|
||||
GenericGFPoly remainder = info.divide(generator)[1];
|
||||
int[] coefficients = remainder.getCoefficients();
|
||||
int numZeroCoefficients = ecBytes - coefficients.length;
|
||||
for (int i = 0; i < numZeroCoefficients; i++) {
|
||||
toEncode[dataBytes + i] = 0;
|
||||
}
|
||||
System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.qrcode;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.Writer;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.encoder.ByteMatrix;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.google.zxing.qrcode.encoder.Encoder;
|
||||
import com.google.zxing.qrcode.encoder.QRCode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This object renders a QR Code as a BitMatrix 2D array of greyscale values.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class QRCodeWriter implements Writer {
|
||||
|
||||
private static final int QUIET_ZONE_SIZE = 4;
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
|
||||
throws WriterException {
|
||||
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height,
|
||||
Map<EncodeHintType,?> hints) throws WriterException {
|
||||
|
||||
if (contents.isEmpty()) {
|
||||
throw new IllegalArgumentException("Found empty contents");
|
||||
}
|
||||
|
||||
if (format != BarcodeFormat.QR_CODE) {
|
||||
throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
|
||||
height);
|
||||
}
|
||||
|
||||
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
|
||||
int quietZone = QUIET_ZONE_SIZE;
|
||||
if (hints != null) {
|
||||
if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
|
||||
}
|
||||
if (hints.containsKey(EncodeHintType.MARGIN)) {
|
||||
quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
|
||||
}
|
||||
}
|
||||
|
||||
QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
|
||||
return renderResult(code, width, height, quietZone);
|
||||
}
|
||||
|
||||
// Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
|
||||
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
|
||||
private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
|
||||
ByteMatrix input = code.getMatrix();
|
||||
if (input == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int inputWidth = input.getWidth();
|
||||
int inputHeight = input.getHeight();
|
||||
int qrWidth = inputWidth + (quietZone * 2);
|
||||
int qrHeight = inputHeight + (quietZone * 2);
|
||||
int outputWidth = Math.max(width, qrWidth);
|
||||
int outputHeight = Math.max(height, qrHeight);
|
||||
|
||||
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
|
||||
// Padding includes both the quiet zone and the extra white pixels to accommodate the requested
|
||||
// dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
|
||||
// If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
|
||||
// handle all the padding from 100x100 (the actual QR) up to 200x160.
|
||||
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
|
||||
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
|
||||
|
||||
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
|
||||
|
||||
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
|
||||
// Write the contents of this row of the barcode
|
||||
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
|
||||
if (input.get(inputX, inputY) == 1) {
|
||||
output.setRegion(outputX, outputY, multiple, multiple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
|
||||
* defined by the QR code standard.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum ErrorCorrectionLevel {
|
||||
|
||||
/** L = ~7% correction */
|
||||
L(0x01),
|
||||
/** M = ~15% correction */
|
||||
M(0x00),
|
||||
/** Q = ~25% correction */
|
||||
Q(0x03),
|
||||
/** H = ~30% correction */
|
||||
H(0x02);
|
||||
|
||||
private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
|
||||
|
||||
private final int bits;
|
||||
|
||||
ErrorCorrectionLevel(int bits) {
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits int containing the two bits encoding a QR Code's error correction level
|
||||
* @return ErrorCorrectionLevel representing the encoded error correction level
|
||||
*/
|
||||
public static ErrorCorrectionLevel forBits(int bits) {
|
||||
if (bits < 0 || bits >= FOR_BITS.length) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return FOR_BITS[bits];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a QR Code's format information, including the data mask used and
|
||||
* error correction level.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see DataMask
|
||||
* @see ErrorCorrectionLevel
|
||||
*/
|
||||
final class FormatInformation {
|
||||
|
||||
private static final int FORMAT_INFO_MASK_QR = 0x5412;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, Annex C, Table C.1
|
||||
*/
|
||||
private static final int[][] FORMAT_INFO_DECODE_LOOKUP = {
|
||||
{0x5412, 0x00},
|
||||
{0x5125, 0x01},
|
||||
{0x5E7C, 0x02},
|
||||
{0x5B4B, 0x03},
|
||||
{0x45F9, 0x04},
|
||||
{0x40CE, 0x05},
|
||||
{0x4F97, 0x06},
|
||||
{0x4AA0, 0x07},
|
||||
{0x77C4, 0x08},
|
||||
{0x72F3, 0x09},
|
||||
{0x7DAA, 0x0A},
|
||||
{0x789D, 0x0B},
|
||||
{0x662F, 0x0C},
|
||||
{0x6318, 0x0D},
|
||||
{0x6C41, 0x0E},
|
||||
{0x6976, 0x0F},
|
||||
{0x1689, 0x10},
|
||||
{0x13BE, 0x11},
|
||||
{0x1CE7, 0x12},
|
||||
{0x19D0, 0x13},
|
||||
{0x0762, 0x14},
|
||||
{0x0255, 0x15},
|
||||
{0x0D0C, 0x16},
|
||||
{0x083B, 0x17},
|
||||
{0x355F, 0x18},
|
||||
{0x3068, 0x19},
|
||||
{0x3F31, 0x1A},
|
||||
{0x3A06, 0x1B},
|
||||
{0x24B4, 0x1C},
|
||||
{0x2183, 0x1D},
|
||||
{0x2EDA, 0x1E},
|
||||
{0x2BED, 0x1F},
|
||||
};
|
||||
|
||||
/**
|
||||
* Offset i holds the number of 1 bits in the binary representation of i
|
||||
*/
|
||||
private static final int[] BITS_SET_IN_HALF_BYTE =
|
||||
{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
|
||||
|
||||
private final ErrorCorrectionLevel errorCorrectionLevel;
|
||||
private final byte dataMask;
|
||||
|
||||
private FormatInformation(int formatInfo) {
|
||||
// Bits 3,4
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
|
||||
// Bottom 3 bits
|
||||
dataMask = (byte) (formatInfo & 0x07);
|
||||
}
|
||||
|
||||
static int numBitsDiffering(int a, int b) {
|
||||
a ^= b; // a now has a 1 bit exactly where its bit differs with b's
|
||||
// Count bits set quickly with a series of lookups:
|
||||
return BITS_SET_IN_HALF_BYTE[a & 0x0F] +
|
||||
BITS_SET_IN_HALF_BYTE[(a >>> 4 & 0x0F)] +
|
||||
BITS_SET_IN_HALF_BYTE[(a >>> 8 & 0x0F)] +
|
||||
BITS_SET_IN_HALF_BYTE[(a >>> 12 & 0x0F)] +
|
||||
BITS_SET_IN_HALF_BYTE[(a >>> 16 & 0x0F)] +
|
||||
BITS_SET_IN_HALF_BYTE[(a >>> 20 & 0x0F)] +
|
||||
BITS_SET_IN_HALF_BYTE[(a >>> 24 & 0x0F)] +
|
||||
BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maskedFormatInfo1 format info indicator, with mask still applied
|
||||
* @param maskedFormatInfo2 second copy of same info; both are checked at the same time
|
||||
* to establish best match
|
||||
* @return information about the format it specifies, or {@code null}
|
||||
* if doesn't seem to match any known pattern
|
||||
*/
|
||||
static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
|
||||
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
|
||||
if (formatInfo != null) {
|
||||
return formatInfo;
|
||||
}
|
||||
// Should return null, but, some QR codes apparently
|
||||
// do not mask this info. Try again by actually masking the pattern
|
||||
// first
|
||||
return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
|
||||
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
|
||||
}
|
||||
|
||||
private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
int bestDifference = Integer.MAX_VALUE;
|
||||
int bestFormatInfo = 0;
|
||||
for (int[] decodeInfo : FORMAT_INFO_DECODE_LOOKUP) {
|
||||
int targetInfo = decodeInfo[0];
|
||||
if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
|
||||
// Found an exact match
|
||||
return new FormatInformation(decodeInfo[1]);
|
||||
}
|
||||
int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
|
||||
if (bitsDifference < bestDifference) {
|
||||
bestFormatInfo = decodeInfo[1];
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
if (maskedFormatInfo1 != maskedFormatInfo2) {
|
||||
// also try the other option
|
||||
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
|
||||
if (bitsDifference < bestDifference) {
|
||||
bestFormatInfo = decodeInfo[1];
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
|
||||
// differing means we found a match
|
||||
if (bestDifference <= 3) {
|
||||
return new FormatInformation(bestFormatInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ErrorCorrectionLevel getErrorCorrectionLevel() {
|
||||
return errorCorrectionLevel;
|
||||
}
|
||||
|
||||
byte getDataMask() {
|
||||
return dataMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof FormatInformation)) {
|
||||
return false;
|
||||
}
|
||||
FormatInformation other = (FormatInformation) o;
|
||||
return this.errorCorrectionLevel == other.errorCorrectionLevel &&
|
||||
this.dataMask == other.dataMask;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
|
||||
* data can be encoded to bits in the QR code standard.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum Mode {
|
||||
|
||||
TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode...
|
||||
NUMERIC(new int[]{10, 12, 14}, 0x01),
|
||||
ALPHANUMERIC(new int[]{9, 11, 13}, 0x02),
|
||||
STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported
|
||||
BYTE(new int[]{8, 16, 16}, 0x04),
|
||||
ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply
|
||||
KANJI(new int[]{8, 10, 12}, 0x08),
|
||||
FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05),
|
||||
FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09),
|
||||
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
|
||||
HANZI(new int[]{8, 10, 12}, 0x0D);
|
||||
|
||||
private final int[] characterCountBitsForVersions;
|
||||
private final int bits;
|
||||
|
||||
Mode(int[] characterCountBitsForVersions, int bits) {
|
||||
this.characterCountBitsForVersions = characterCountBitsForVersions;
|
||||
this.bits = bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits four bits encoding a QR Code data mode
|
||||
* @return Mode encoded by these bits
|
||||
* @throws IllegalArgumentException if bits do not correspond to a known mode
|
||||
*/
|
||||
public static Mode forBits(int bits) {
|
||||
switch (bits) {
|
||||
case 0x0:
|
||||
return TERMINATOR;
|
||||
case 0x1:
|
||||
return NUMERIC;
|
||||
case 0x2:
|
||||
return ALPHANUMERIC;
|
||||
case 0x3:
|
||||
return STRUCTURED_APPEND;
|
||||
case 0x4:
|
||||
return BYTE;
|
||||
case 0x5:
|
||||
return FNC1_FIRST_POSITION;
|
||||
case 0x7:
|
||||
return ECI;
|
||||
case 0x8:
|
||||
return KANJI;
|
||||
case 0x9:
|
||||
return FNC1_SECOND_POSITION;
|
||||
case 0xD:
|
||||
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
|
||||
return HANZI;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version version in question
|
||||
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
* count of characters that will follow encoded in this Mode
|
||||
*/
|
||||
public int getCharacterCountBits(Version version) {
|
||||
int number = version.getVersionNumber();
|
||||
int offset;
|
||||
if (number <= 9) {
|
||||
offset = 0;
|
||||
} else if (number <= 26) {
|
||||
offset = 1;
|
||||
} else {
|
||||
offset = 2;
|
||||
}
|
||||
return characterCountBitsForVersions[offset];
|
||||
}
|
||||
|
||||
public int getBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* 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.qrcode.decoder;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex D
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class Version {
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex D.
|
||||
* Element i represents the raw version bits that specify version i + 7
|
||||
*/
|
||||
private static final int[] VERSION_DECODE_INFO = {
|
||||
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
|
||||
0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
|
||||
0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
|
||||
0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
|
||||
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
|
||||
0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
|
||||
0x2542E, 0x26A64, 0x27541, 0x28C69
|
||||
};
|
||||
|
||||
private static final Version[] VERSIONS = buildVersions();
|
||||
|
||||
private final int versionNumber;
|
||||
private final int[] alignmentPatternCenters;
|
||||
private final ECBlocks[] ecBlocks;
|
||||
private final int totalCodewords;
|
||||
|
||||
private Version(int versionNumber,
|
||||
int[] alignmentPatternCenters,
|
||||
ECBlocks... ecBlocks) {
|
||||
this.versionNumber = versionNumber;
|
||||
this.alignmentPatternCenters = alignmentPatternCenters;
|
||||
this.ecBlocks = ecBlocks;
|
||||
int total = 0;
|
||||
int ecCodewords = ecBlocks[0].getECCodewordsPerBlock();
|
||||
ECB[] ecbArray = ecBlocks[0].getECBlocks();
|
||||
for (ECB ecBlock : ecbArray) {
|
||||
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
|
||||
}
|
||||
this.totalCodewords = total;
|
||||
}
|
||||
|
||||
public int getVersionNumber() {
|
||||
return versionNumber;
|
||||
}
|
||||
|
||||
public int[] getAlignmentPatternCenters() {
|
||||
return alignmentPatternCenters;
|
||||
}
|
||||
|
||||
public int getTotalCodewords() {
|
||||
return totalCodewords;
|
||||
}
|
||||
|
||||
public int getDimensionForVersion() {
|
||||
return 17 + 4 * versionNumber;
|
||||
}
|
||||
|
||||
public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) {
|
||||
return ecBlocks[ecLevel.ordinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deduces version information purely from QR Code dimensions.</p>
|
||||
*
|
||||
* @param dimension dimension in modules
|
||||
* @return Version for a QR Code of that dimension
|
||||
* @throws FormatException if dimension is not 1 mod 4
|
||||
*/
|
||||
public static Version getProvisionalVersionForDimension(int dimension) throws FormatException {
|
||||
if (dimension % 4 != 1) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
try {
|
||||
return getVersionForNumber((dimension - 17) / 4);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public static Version getVersionForNumber(int versionNumber) {
|
||||
if (versionNumber < 1 || versionNumber > 40) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return VERSIONS[versionNumber - 1];
|
||||
}
|
||||
|
||||
static Version decodeVersionInformation(int versionBits) {
|
||||
int bestDifference = Integer.MAX_VALUE;
|
||||
int bestVersion = 0;
|
||||
for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {
|
||||
int targetVersion = VERSION_DECODE_INFO[i];
|
||||
// Do the version info bits match exactly? done.
|
||||
if (targetVersion == versionBits) {
|
||||
return getVersionForNumber(i + 7);
|
||||
}
|
||||
// Otherwise see if this is the closest to a real version info bit string
|
||||
// we have seen so far
|
||||
int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
|
||||
if (bitsDifference < bestDifference) {
|
||||
bestVersion = i + 7;
|
||||
bestDifference = bitsDifference;
|
||||
}
|
||||
}
|
||||
// We can tolerate up to 3 bits of error since no two version info codewords will
|
||||
// differ in less than 8 bits.
|
||||
if (bestDifference <= 3) {
|
||||
return getVersionForNumber(bestVersion);
|
||||
}
|
||||
// If we didn't find a close enough match, fail
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex E
|
||||
*/
|
||||
BitMatrix buildFunctionPattern() {
|
||||
int dimension = getDimensionForVersion();
|
||||
BitMatrix bitMatrix = new BitMatrix(dimension);
|
||||
|
||||
// Top left finder pattern + separator + format
|
||||
bitMatrix.setRegion(0, 0, 9, 9);
|
||||
// Top right finder pattern + separator + format
|
||||
bitMatrix.setRegion(dimension - 8, 0, 8, 9);
|
||||
// Bottom left finder pattern + separator + format
|
||||
bitMatrix.setRegion(0, dimension - 8, 9, 8);
|
||||
|
||||
// Alignment patterns
|
||||
int max = alignmentPatternCenters.length;
|
||||
for (int x = 0; x < max; x++) {
|
||||
int i = alignmentPatternCenters[x] - 2;
|
||||
for (int y = 0; y < max; y++) {
|
||||
if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) {
|
||||
// No alignment patterns near the three finder paterns
|
||||
continue;
|
||||
}
|
||||
bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical timing pattern
|
||||
bitMatrix.setRegion(6, 9, 1, dimension - 17);
|
||||
// Horizontal timing pattern
|
||||
bitMatrix.setRegion(9, 6, dimension - 17, 1);
|
||||
|
||||
if (versionNumber > 6) {
|
||||
// Version info, top right
|
||||
bitMatrix.setRegion(dimension - 11, 0, 3, 6);
|
||||
// Version info, bottom left
|
||||
bitMatrix.setRegion(0, dimension - 11, 6, 3);
|
||||
}
|
||||
|
||||
return bitMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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>
|
||||
*/
|
||||
public static final class ECBlocks {
|
||||
private final int ecCodewordsPerBlock;
|
||||
private final ECB[] ecBlocks;
|
||||
|
||||
ECBlocks(int ecCodewordsPerBlock, ECB... ecBlocks) {
|
||||
this.ecCodewordsPerBlock = ecCodewordsPerBlock;
|
||||
this.ecBlocks = ecBlocks;
|
||||
}
|
||||
|
||||
public int getECCodewordsPerBlock() {
|
||||
return ecCodewordsPerBlock;
|
||||
}
|
||||
|
||||
public int getNumBlocks() {
|
||||
int total = 0;
|
||||
for (ECB ecBlock : ecBlocks) {
|
||||
total += ecBlock.getCount();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public int getTotalECCodewords() {
|
||||
return ecCodewordsPerBlock * getNumBlocks();
|
||||
}
|
||||
|
||||
public 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 QR code version's format.</p>
|
||||
*/
|
||||
public static final class ECB {
|
||||
private final int count;
|
||||
private final int dataCodewords;
|
||||
|
||||
ECB(int count, int dataCodewords) {
|
||||
this.count = count;
|
||||
this.dataCodewords = dataCodewords;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getDataCodewords() {
|
||||
return dataCodewords;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(versionNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 6.5.1 Table 9
|
||||
*/
|
||||
private static Version[] buildVersions() {
|
||||
return new Version[]{
|
||||
new Version(1, new int[]{},
|
||||
new ECBlocks(7, new ECB(1, 19)),
|
||||
new ECBlocks(10, new ECB(1, 16)),
|
||||
new ECBlocks(13, new ECB(1, 13)),
|
||||
new ECBlocks(17, new ECB(1, 9))),
|
||||
new Version(2, new int[]{6, 18},
|
||||
new ECBlocks(10, new ECB(1, 34)),
|
||||
new ECBlocks(16, new ECB(1, 28)),
|
||||
new ECBlocks(22, new ECB(1, 22)),
|
||||
new ECBlocks(28, new ECB(1, 16))),
|
||||
new Version(3, new int[]{6, 22},
|
||||
new ECBlocks(15, new ECB(1, 55)),
|
||||
new ECBlocks(26, new ECB(1, 44)),
|
||||
new ECBlocks(18, new ECB(2, 17)),
|
||||
new ECBlocks(22, new ECB(2, 13))),
|
||||
new Version(4, new int[]{6, 26},
|
||||
new ECBlocks(20, new ECB(1, 80)),
|
||||
new ECBlocks(18, new ECB(2, 32)),
|
||||
new ECBlocks(26, new ECB(2, 24)),
|
||||
new ECBlocks(16, new ECB(4, 9))),
|
||||
new Version(5, new int[]{6, 30},
|
||||
new ECBlocks(26, new ECB(1, 108)),
|
||||
new ECBlocks(24, new ECB(2, 43)),
|
||||
new ECBlocks(18, new ECB(2, 15),
|
||||
new ECB(2, 16)),
|
||||
new ECBlocks(22, new ECB(2, 11),
|
||||
new ECB(2, 12))),
|
||||
new Version(6, new int[]{6, 34},
|
||||
new ECBlocks(18, new ECB(2, 68)),
|
||||
new ECBlocks(16, new ECB(4, 27)),
|
||||
new ECBlocks(24, new ECB(4, 19)),
|
||||
new ECBlocks(28, new ECB(4, 15))),
|
||||
new Version(7, new int[]{6, 22, 38},
|
||||
new ECBlocks(20, new ECB(2, 78)),
|
||||
new ECBlocks(18, new ECB(4, 31)),
|
||||
new ECBlocks(18, new ECB(2, 14),
|
||||
new ECB(4, 15)),
|
||||
new ECBlocks(26, new ECB(4, 13),
|
||||
new ECB(1, 14))),
|
||||
new Version(8, new int[]{6, 24, 42},
|
||||
new ECBlocks(24, new ECB(2, 97)),
|
||||
new ECBlocks(22, new ECB(2, 38),
|
||||
new ECB(2, 39)),
|
||||
new ECBlocks(22, new ECB(4, 18),
|
||||
new ECB(2, 19)),
|
||||
new ECBlocks(26, new ECB(4, 14),
|
||||
new ECB(2, 15))),
|
||||
new Version(9, new int[]{6, 26, 46},
|
||||
new ECBlocks(30, new ECB(2, 116)),
|
||||
new ECBlocks(22, new ECB(3, 36),
|
||||
new ECB(2, 37)),
|
||||
new ECBlocks(20, new ECB(4, 16),
|
||||
new ECB(4, 17)),
|
||||
new ECBlocks(24, new ECB(4, 12),
|
||||
new ECB(4, 13))),
|
||||
new Version(10, new int[]{6, 28, 50},
|
||||
new ECBlocks(18, new ECB(2, 68),
|
||||
new ECB(2, 69)),
|
||||
new ECBlocks(26, new ECB(4, 43),
|
||||
new ECB(1, 44)),
|
||||
new ECBlocks(24, new ECB(6, 19),
|
||||
new ECB(2, 20)),
|
||||
new ECBlocks(28, new ECB(6, 15),
|
||||
new ECB(2, 16))),
|
||||
new Version(11, new int[]{6, 30, 54},
|
||||
new ECBlocks(20, new ECB(4, 81)),
|
||||
new ECBlocks(30, new ECB(1, 50),
|
||||
new ECB(4, 51)),
|
||||
new ECBlocks(28, new ECB(4, 22),
|
||||
new ECB(4, 23)),
|
||||
new ECBlocks(24, new ECB(3, 12),
|
||||
new ECB(8, 13))),
|
||||
new Version(12, new int[]{6, 32, 58},
|
||||
new ECBlocks(24, new ECB(2, 92),
|
||||
new ECB(2, 93)),
|
||||
new ECBlocks(22, new ECB(6, 36),
|
||||
new ECB(2, 37)),
|
||||
new ECBlocks(26, new ECB(4, 20),
|
||||
new ECB(6, 21)),
|
||||
new ECBlocks(28, new ECB(7, 14),
|
||||
new ECB(4, 15))),
|
||||
new Version(13, new int[]{6, 34, 62},
|
||||
new ECBlocks(26, new ECB(4, 107)),
|
||||
new ECBlocks(22, new ECB(8, 37),
|
||||
new ECB(1, 38)),
|
||||
new ECBlocks(24, new ECB(8, 20),
|
||||
new ECB(4, 21)),
|
||||
new ECBlocks(22, new ECB(12, 11),
|
||||
new ECB(4, 12))),
|
||||
new Version(14, new int[]{6, 26, 46, 66},
|
||||
new ECBlocks(30, new ECB(3, 115),
|
||||
new ECB(1, 116)),
|
||||
new ECBlocks(24, new ECB(4, 40),
|
||||
new ECB(5, 41)),
|
||||
new ECBlocks(20, new ECB(11, 16),
|
||||
new ECB(5, 17)),
|
||||
new ECBlocks(24, new ECB(11, 12),
|
||||
new ECB(5, 13))),
|
||||
new Version(15, new int[]{6, 26, 48, 70},
|
||||
new ECBlocks(22, new ECB(5, 87),
|
||||
new ECB(1, 88)),
|
||||
new ECBlocks(24, new ECB(5, 41),
|
||||
new ECB(5, 42)),
|
||||
new ECBlocks(30, new ECB(5, 24),
|
||||
new ECB(7, 25)),
|
||||
new ECBlocks(24, new ECB(11, 12),
|
||||
new ECB(7, 13))),
|
||||
new Version(16, new int[]{6, 26, 50, 74},
|
||||
new ECBlocks(24, new ECB(5, 98),
|
||||
new ECB(1, 99)),
|
||||
new ECBlocks(28, new ECB(7, 45),
|
||||
new ECB(3, 46)),
|
||||
new ECBlocks(24, new ECB(15, 19),
|
||||
new ECB(2, 20)),
|
||||
new ECBlocks(30, new ECB(3, 15),
|
||||
new ECB(13, 16))),
|
||||
new Version(17, new int[]{6, 30, 54, 78},
|
||||
new ECBlocks(28, new ECB(1, 107),
|
||||
new ECB(5, 108)),
|
||||
new ECBlocks(28, new ECB(10, 46),
|
||||
new ECB(1, 47)),
|
||||
new ECBlocks(28, new ECB(1, 22),
|
||||
new ECB(15, 23)),
|
||||
new ECBlocks(28, new ECB(2, 14),
|
||||
new ECB(17, 15))),
|
||||
new Version(18, new int[]{6, 30, 56, 82},
|
||||
new ECBlocks(30, new ECB(5, 120),
|
||||
new ECB(1, 121)),
|
||||
new ECBlocks(26, new ECB(9, 43),
|
||||
new ECB(4, 44)),
|
||||
new ECBlocks(28, new ECB(17, 22),
|
||||
new ECB(1, 23)),
|
||||
new ECBlocks(28, new ECB(2, 14),
|
||||
new ECB(19, 15))),
|
||||
new Version(19, new int[]{6, 30, 58, 86},
|
||||
new ECBlocks(28, new ECB(3, 113),
|
||||
new ECB(4, 114)),
|
||||
new ECBlocks(26, new ECB(3, 44),
|
||||
new ECB(11, 45)),
|
||||
new ECBlocks(26, new ECB(17, 21),
|
||||
new ECB(4, 22)),
|
||||
new ECBlocks(26, new ECB(9, 13),
|
||||
new ECB(16, 14))),
|
||||
new Version(20, new int[]{6, 34, 62, 90},
|
||||
new ECBlocks(28, new ECB(3, 107),
|
||||
new ECB(5, 108)),
|
||||
new ECBlocks(26, new ECB(3, 41),
|
||||
new ECB(13, 42)),
|
||||
new ECBlocks(30, new ECB(15, 24),
|
||||
new ECB(5, 25)),
|
||||
new ECBlocks(28, new ECB(15, 15),
|
||||
new ECB(10, 16))),
|
||||
new Version(21, new int[]{6, 28, 50, 72, 94},
|
||||
new ECBlocks(28, new ECB(4, 116),
|
||||
new ECB(4, 117)),
|
||||
new ECBlocks(26, new ECB(17, 42)),
|
||||
new ECBlocks(28, new ECB(17, 22),
|
||||
new ECB(6, 23)),
|
||||
new ECBlocks(30, new ECB(19, 16),
|
||||
new ECB(6, 17))),
|
||||
new Version(22, new int[]{6, 26, 50, 74, 98},
|
||||
new ECBlocks(28, new ECB(2, 111),
|
||||
new ECB(7, 112)),
|
||||
new ECBlocks(28, new ECB(17, 46)),
|
||||
new ECBlocks(30, new ECB(7, 24),
|
||||
new ECB(16, 25)),
|
||||
new ECBlocks(24, new ECB(34, 13))),
|
||||
new Version(23, new int[]{6, 30, 54, 78, 102},
|
||||
new ECBlocks(30, new ECB(4, 121),
|
||||
new ECB(5, 122)),
|
||||
new ECBlocks(28, new ECB(4, 47),
|
||||
new ECB(14, 48)),
|
||||
new ECBlocks(30, new ECB(11, 24),
|
||||
new ECB(14, 25)),
|
||||
new ECBlocks(30, new ECB(16, 15),
|
||||
new ECB(14, 16))),
|
||||
new Version(24, new int[]{6, 28, 54, 80, 106},
|
||||
new ECBlocks(30, new ECB(6, 117),
|
||||
new ECB(4, 118)),
|
||||
new ECBlocks(28, new ECB(6, 45),
|
||||
new ECB(14, 46)),
|
||||
new ECBlocks(30, new ECB(11, 24),
|
||||
new ECB(16, 25)),
|
||||
new ECBlocks(30, new ECB(30, 16),
|
||||
new ECB(2, 17))),
|
||||
new Version(25, new int[]{6, 32, 58, 84, 110},
|
||||
new ECBlocks(26, new ECB(8, 106),
|
||||
new ECB(4, 107)),
|
||||
new ECBlocks(28, new ECB(8, 47),
|
||||
new ECB(13, 48)),
|
||||
new ECBlocks(30, new ECB(7, 24),
|
||||
new ECB(22, 25)),
|
||||
new ECBlocks(30, new ECB(22, 15),
|
||||
new ECB(13, 16))),
|
||||
new Version(26, new int[]{6, 30, 58, 86, 114},
|
||||
new ECBlocks(28, new ECB(10, 114),
|
||||
new ECB(2, 115)),
|
||||
new ECBlocks(28, new ECB(19, 46),
|
||||
new ECB(4, 47)),
|
||||
new ECBlocks(28, new ECB(28, 22),
|
||||
new ECB(6, 23)),
|
||||
new ECBlocks(30, new ECB(33, 16),
|
||||
new ECB(4, 17))),
|
||||
new Version(27, new int[]{6, 34, 62, 90, 118},
|
||||
new ECBlocks(30, new ECB(8, 122),
|
||||
new ECB(4, 123)),
|
||||
new ECBlocks(28, new ECB(22, 45),
|
||||
new ECB(3, 46)),
|
||||
new ECBlocks(30, new ECB(8, 23),
|
||||
new ECB(26, 24)),
|
||||
new ECBlocks(30, new ECB(12, 15),
|
||||
new ECB(28, 16))),
|
||||
new Version(28, new int[]{6, 26, 50, 74, 98, 122},
|
||||
new ECBlocks(30, new ECB(3, 117),
|
||||
new ECB(10, 118)),
|
||||
new ECBlocks(28, new ECB(3, 45),
|
||||
new ECB(23, 46)),
|
||||
new ECBlocks(30, new ECB(4, 24),
|
||||
new ECB(31, 25)),
|
||||
new ECBlocks(30, new ECB(11, 15),
|
||||
new ECB(31, 16))),
|
||||
new Version(29, new int[]{6, 30, 54, 78, 102, 126},
|
||||
new ECBlocks(30, new ECB(7, 116),
|
||||
new ECB(7, 117)),
|
||||
new ECBlocks(28, new ECB(21, 45),
|
||||
new ECB(7, 46)),
|
||||
new ECBlocks(30, new ECB(1, 23),
|
||||
new ECB(37, 24)),
|
||||
new ECBlocks(30, new ECB(19, 15),
|
||||
new ECB(26, 16))),
|
||||
new Version(30, new int[]{6, 26, 52, 78, 104, 130},
|
||||
new ECBlocks(30, new ECB(5, 115),
|
||||
new ECB(10, 116)),
|
||||
new ECBlocks(28, new ECB(19, 47),
|
||||
new ECB(10, 48)),
|
||||
new ECBlocks(30, new ECB(15, 24),
|
||||
new ECB(25, 25)),
|
||||
new ECBlocks(30, new ECB(23, 15),
|
||||
new ECB(25, 16))),
|
||||
new Version(31, new int[]{6, 30, 56, 82, 108, 134},
|
||||
new ECBlocks(30, new ECB(13, 115),
|
||||
new ECB(3, 116)),
|
||||
new ECBlocks(28, new ECB(2, 46),
|
||||
new ECB(29, 47)),
|
||||
new ECBlocks(30, new ECB(42, 24),
|
||||
new ECB(1, 25)),
|
||||
new ECBlocks(30, new ECB(23, 15),
|
||||
new ECB(28, 16))),
|
||||
new Version(32, new int[]{6, 34, 60, 86, 112, 138},
|
||||
new ECBlocks(30, new ECB(17, 115)),
|
||||
new ECBlocks(28, new ECB(10, 46),
|
||||
new ECB(23, 47)),
|
||||
new ECBlocks(30, new ECB(10, 24),
|
||||
new ECB(35, 25)),
|
||||
new ECBlocks(30, new ECB(19, 15),
|
||||
new ECB(35, 16))),
|
||||
new Version(33, new int[]{6, 30, 58, 86, 114, 142},
|
||||
new ECBlocks(30, new ECB(17, 115),
|
||||
new ECB(1, 116)),
|
||||
new ECBlocks(28, new ECB(14, 46),
|
||||
new ECB(21, 47)),
|
||||
new ECBlocks(30, new ECB(29, 24),
|
||||
new ECB(19, 25)),
|
||||
new ECBlocks(30, new ECB(11, 15),
|
||||
new ECB(46, 16))),
|
||||
new Version(34, new int[]{6, 34, 62, 90, 118, 146},
|
||||
new ECBlocks(30, new ECB(13, 115),
|
||||
new ECB(6, 116)),
|
||||
new ECBlocks(28, new ECB(14, 46),
|
||||
new ECB(23, 47)),
|
||||
new ECBlocks(30, new ECB(44, 24),
|
||||
new ECB(7, 25)),
|
||||
new ECBlocks(30, new ECB(59, 16),
|
||||
new ECB(1, 17))),
|
||||
new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150},
|
||||
new ECBlocks(30, new ECB(12, 121),
|
||||
new ECB(7, 122)),
|
||||
new ECBlocks(28, new ECB(12, 47),
|
||||
new ECB(26, 48)),
|
||||
new ECBlocks(30, new ECB(39, 24),
|
||||
new ECB(14, 25)),
|
||||
new ECBlocks(30, new ECB(22, 15),
|
||||
new ECB(41, 16))),
|
||||
new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154},
|
||||
new ECBlocks(30, new ECB(6, 121),
|
||||
new ECB(14, 122)),
|
||||
new ECBlocks(28, new ECB(6, 47),
|
||||
new ECB(34, 48)),
|
||||
new ECBlocks(30, new ECB(46, 24),
|
||||
new ECB(10, 25)),
|
||||
new ECBlocks(30, new ECB(2, 15),
|
||||
new ECB(64, 16))),
|
||||
new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158},
|
||||
new ECBlocks(30, new ECB(17, 122),
|
||||
new ECB(4, 123)),
|
||||
new ECBlocks(28, new ECB(29, 46),
|
||||
new ECB(14, 47)),
|
||||
new ECBlocks(30, new ECB(49, 24),
|
||||
new ECB(10, 25)),
|
||||
new ECBlocks(30, new ECB(24, 15),
|
||||
new ECB(46, 16))),
|
||||
new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162},
|
||||
new ECBlocks(30, new ECB(4, 122),
|
||||
new ECB(18, 123)),
|
||||
new ECBlocks(28, new ECB(13, 46),
|
||||
new ECB(32, 47)),
|
||||
new ECBlocks(30, new ECB(48, 24),
|
||||
new ECB(14, 25)),
|
||||
new ECBlocks(30, new ECB(42, 15),
|
||||
new ECB(32, 16))),
|
||||
new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166},
|
||||
new ECBlocks(30, new ECB(20, 117),
|
||||
new ECB(4, 118)),
|
||||
new ECBlocks(28, new ECB(40, 47),
|
||||
new ECB(7, 48)),
|
||||
new ECBlocks(30, new ECB(43, 24),
|
||||
new ECB(22, 25)),
|
||||
new ECBlocks(30, new ECB(10, 15),
|
||||
new ECB(67, 16))),
|
||||
new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170},
|
||||
new ECBlocks(30, new ECB(19, 118),
|
||||
new ECB(6, 119)),
|
||||
new ECBlocks(28, new ECB(18, 47),
|
||||
new ECB(31, 48)),
|
||||
new ECBlocks(30, new ECB(34, 24),
|
||||
new ECB(34, 25)),
|
||||
new ECBlocks(30, new ECB(20, 15),
|
||||
new ECB(61, 16)))
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.qrcode.encoder;
|
||||
|
||||
final class BlockPair {
|
||||
|
||||
private final byte[] dataBytes;
|
||||
private final byte[] errorCorrectionBytes;
|
||||
|
||||
BlockPair(byte[] data, byte[] errorCorrection) {
|
||||
dataBytes = data;
|
||||
errorCorrectionBytes = errorCorrection;
|
||||
}
|
||||
|
||||
public byte[] getDataBytes() {
|
||||
return dataBytes;
|
||||
}
|
||||
|
||||
public byte[] getErrorCorrectionBytes() {
|
||||
return errorCorrectionBytes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.qrcode.encoder;
|
||||
|
||||
/**
|
||||
* JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
|
||||
* -1, 0, and 1, I'm going to use less memory and go with bytes.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class ByteMatrix {
|
||||
|
||||
private final byte[][] bytes;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public ByteMatrix(int width, int height) {
|
||||
bytes = new byte[height][width];
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public byte get(int x, int y) {
|
||||
return bytes[y][x];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an internal representation as bytes, in row-major order. array[y][x] represents point (x,y)
|
||||
*/
|
||||
public byte[][] getArray() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void set(int x, int y, byte value) {
|
||||
bytes[y][x] = value;
|
||||
}
|
||||
|
||||
public void set(int x, int y, int value) {
|
||||
bytes[y][x] = (byte) value;
|
||||
}
|
||||
|
||||
public void set(int x, int y, boolean value) {
|
||||
bytes[y][x] = (byte) (value ? 1 : 0);
|
||||
}
|
||||
|
||||
public void clear(byte value) {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
bytes[y][x] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(2 * width * height + 2);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
switch (bytes[y][x]) {
|
||||
case 0:
|
||||
result.append(" 0");
|
||||
break;
|
||||
case 1:
|
||||
result.append(" 1");
|
||||
break;
|
||||
default:
|
||||
result.append(" ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.append('\n');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* 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.qrcode.encoder;
|
||||
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.CharacterSetECI;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.google.zxing.qrcode.decoder.Mode;
|
||||
import com.google.zxing.qrcode.decoder.Version;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author satorux@google.com (Satoru Takabayashi) - creator
|
||||
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
|
||||
*/
|
||||
public final class Encoder {
|
||||
|
||||
// The original table is defined in the table 5 of JISX0510:2004 (p.19).
|
||||
private static final int[] ALPHANUMERIC_TABLE = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
|
||||
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
|
||||
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
|
||||
};
|
||||
|
||||
static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
|
||||
|
||||
private Encoder() {
|
||||
}
|
||||
|
||||
// The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
|
||||
// Basically it applies four rules and summate all penalties.
|
||||
private static int calculateMaskPenalty(ByteMatrix matrix) {
|
||||
return MaskUtil.applyMaskPenaltyRule1(matrix)
|
||||
+ MaskUtil.applyMaskPenaltyRule2(matrix)
|
||||
+ MaskUtil.applyMaskPenaltyRule3(matrix)
|
||||
+ MaskUtil.applyMaskPenaltyRule4(matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param content text to encode
|
||||
* @param ecLevel error correction level to use
|
||||
* @return {@link QRCode} representing the encoded QR code
|
||||
* @throws WriterException if encoding can't succeed, because of for example invalid content
|
||||
* or configuration
|
||||
*/
|
||||
public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) throws WriterException {
|
||||
return encode(content, ecLevel, null);
|
||||
}
|
||||
|
||||
public static QRCode encode(String content,
|
||||
ErrorCorrectionLevel ecLevel,
|
||||
Map<EncodeHintType,?> hints) throws WriterException {
|
||||
|
||||
// Determine what character encoding has been specified by the caller, if any
|
||||
String encoding = DEFAULT_BYTE_MODE_ENCODING;
|
||||
if (hints != null && hints.containsKey(EncodeHintType.CHARACTER_SET)) {
|
||||
encoding = hints.get(EncodeHintType.CHARACTER_SET).toString();
|
||||
}
|
||||
|
||||
// Pick an encoding mode appropriate for the content. Note that this will not attempt to use
|
||||
// multiple modes / segments even if that were more efficient. Twould be nice.
|
||||
Mode mode = chooseMode(content, encoding);
|
||||
|
||||
// This will store the header information, like mode and
|
||||
// length, as well as "header" segments like an ECI segment.
|
||||
BitArray headerBits = new BitArray();
|
||||
|
||||
// Append ECI segment if applicable
|
||||
if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
|
||||
CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
|
||||
if (eci != null) {
|
||||
appendECI(eci, headerBits);
|
||||
}
|
||||
}
|
||||
|
||||
// (With ECI in place,) Write the mode marker
|
||||
appendModeInfo(mode, headerBits);
|
||||
|
||||
// Collect data within the main segment, separately, to count its size if needed. Don't add it to
|
||||
// main payload yet.
|
||||
BitArray dataBits = new BitArray();
|
||||
appendBytes(content, mode, dataBits, encoding);
|
||||
|
||||
// Hard part: need to know version to know how many bits length takes. But need to know how many
|
||||
// bits it takes to know version. First we take a guess at version by assuming version will be
|
||||
// the minimum, 1:
|
||||
|
||||
int provisionalBitsNeeded = headerBits.getSize()
|
||||
+ mode.getCharacterCountBits(Version.getVersionForNumber(1))
|
||||
+ dataBits.getSize();
|
||||
Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);
|
||||
|
||||
// Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
|
||||
|
||||
int bitsNeeded = headerBits.getSize()
|
||||
+ mode.getCharacterCountBits(provisionalVersion)
|
||||
+ dataBits.getSize();
|
||||
Version version = chooseVersion(bitsNeeded, ecLevel);
|
||||
|
||||
BitArray headerAndDataBits = new BitArray();
|
||||
headerAndDataBits.appendBitArray(headerBits);
|
||||
// Find "length" of main segment and write it
|
||||
int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length();
|
||||
appendLengthInfo(numLetters, version, mode, headerAndDataBits);
|
||||
// Put data together into the overall payload
|
||||
headerAndDataBits.appendBitArray(dataBits);
|
||||
|
||||
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||
int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords();
|
||||
|
||||
// Terminate the bits properly.
|
||||
terminateBits(numDataBytes, headerAndDataBits);
|
||||
|
||||
// Interleave data bits with error correction code.
|
||||
BitArray finalBits = interleaveWithECBytes(headerAndDataBits,
|
||||
version.getTotalCodewords(),
|
||||
numDataBytes,
|
||||
ecBlocks.getNumBlocks());
|
||||
|
||||
QRCode qrCode = new QRCode();
|
||||
|
||||
qrCode.setECLevel(ecLevel);
|
||||
qrCode.setMode(mode);
|
||||
qrCode.setVersion(version);
|
||||
|
||||
// Choose the mask pattern and set to "qrCode".
|
||||
int dimension = version.getDimensionForVersion();
|
||||
ByteMatrix matrix = new ByteMatrix(dimension, dimension);
|
||||
int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
|
||||
qrCode.setMaskPattern(maskPattern);
|
||||
|
||||
// Build the matrix and set it to "qrCode".
|
||||
MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
|
||||
qrCode.setMatrix(matrix);
|
||||
|
||||
return qrCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the code point of the table used in alphanumeric mode or
|
||||
* -1 if there is no corresponding code in the table.
|
||||
*/
|
||||
static int getAlphanumericCode(int code) {
|
||||
if (code < ALPHANUMERIC_TABLE.length) {
|
||||
return ALPHANUMERIC_TABLE[code];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static Mode chooseMode(String content) {
|
||||
return chooseMode(content, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
|
||||
* if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
|
||||
*/
|
||||
private static Mode chooseMode(String content, String encoding) {
|
||||
if ("Shift_JIS".equals(encoding) && isOnlyDoubleByteKanji(content)) {
|
||||
// Choose Kanji mode if all input are double-byte characters
|
||||
return Mode.KANJI;
|
||||
}
|
||||
boolean hasNumeric = false;
|
||||
boolean hasAlphanumeric = false;
|
||||
for (int i = 0; i < content.length(); ++i) {
|
||||
char c = content.charAt(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
hasNumeric = true;
|
||||
} else if (getAlphanumericCode(c) != -1) {
|
||||
hasAlphanumeric = true;
|
||||
} else {
|
||||
return Mode.BYTE;
|
||||
}
|
||||
}
|
||||
if (hasAlphanumeric) {
|
||||
return Mode.ALPHANUMERIC;
|
||||
}
|
||||
if (hasNumeric) {
|
||||
return Mode.NUMERIC;
|
||||
}
|
||||
return Mode.BYTE;
|
||||
}
|
||||
|
||||
private static boolean isOnlyDoubleByteKanji(String content) {
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = content.getBytes("Shift_JIS");
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
return false;
|
||||
}
|
||||
int length = bytes.length;
|
||||
if (length % 2 != 0) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < length; i += 2) {
|
||||
int byte1 = bytes[i] & 0xFF;
|
||||
if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int chooseMaskPattern(BitArray bits,
|
||||
ErrorCorrectionLevel ecLevel,
|
||||
Version version,
|
||||
ByteMatrix matrix) throws WriterException {
|
||||
|
||||
int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
|
||||
int bestMaskPattern = -1;
|
||||
// We try all mask patterns to choose the best one.
|
||||
for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {
|
||||
MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
|
||||
int penalty = calculateMaskPenalty(matrix);
|
||||
if (penalty < minPenalty) {
|
||||
minPenalty = penalty;
|
||||
bestMaskPattern = maskPattern;
|
||||
}
|
||||
}
|
||||
return bestMaskPattern;
|
||||
}
|
||||
|
||||
private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException {
|
||||
// In the following comments, we use numbers of Version 7-H.
|
||||
for (int versionNum = 1; versionNum <= 40; versionNum++) {
|
||||
Version version = Version.getVersionForNumber(versionNum);
|
||||
// numBytes = 196
|
||||
int numBytes = version.getTotalCodewords();
|
||||
// getNumECBytes = 130
|
||||
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||
int numEcBytes = ecBlocks.getTotalECCodewords();
|
||||
// getNumDataBytes = 196 - 130 = 66
|
||||
int numDataBytes = numBytes - numEcBytes;
|
||||
int totalInputBytes = (numInputBits + 7) / 8;
|
||||
if (numDataBytes >= totalInputBytes) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
throw new WriterException("Data too big");
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
|
||||
*/
|
||||
static void terminateBits(int numDataBytes, BitArray bits) throws WriterException {
|
||||
int capacity = numDataBytes * 8;
|
||||
if (bits.getSize() > capacity) {
|
||||
throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " +
|
||||
capacity);
|
||||
}
|
||||
for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
|
||||
bits.appendBit(false);
|
||||
}
|
||||
// Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
|
||||
// If the last byte isn't 8-bit aligned, we'll add padding bits.
|
||||
int numBitsInLastByte = bits.getSize() & 0x07;
|
||||
if (numBitsInLastByte > 0) {
|
||||
for (int i = numBitsInLastByte; i < 8; i++) {
|
||||
bits.appendBit(false);
|
||||
}
|
||||
}
|
||||
// If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
|
||||
int numPaddingBytes = numDataBytes - bits.getSizeInBytes();
|
||||
for (int i = 0; i < numPaddingBytes; ++i) {
|
||||
bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
|
||||
}
|
||||
if (bits.getSize() != capacity) {
|
||||
throw new WriterException("Bits size does not equal capacity");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of data bytes and number of error correction bytes for block id "blockID". Store
|
||||
* the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
|
||||
* JISX0510:2004 (p.30)
|
||||
*/
|
||||
static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes,
|
||||
int numDataBytes,
|
||||
int numRSBlocks,
|
||||
int blockID,
|
||||
int[] numDataBytesInBlock,
|
||||
int[] numECBytesInBlock) throws WriterException {
|
||||
if (blockID >= numRSBlocks) {
|
||||
throw new WriterException("Block ID too large");
|
||||
}
|
||||
// numRsBlocksInGroup2 = 196 % 5 = 1
|
||||
int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
|
||||
// numRsBlocksInGroup1 = 5 - 1 = 4
|
||||
int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
|
||||
// numTotalBytesInGroup1 = 196 / 5 = 39
|
||||
int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
|
||||
// numTotalBytesInGroup2 = 39 + 1 = 40
|
||||
int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
|
||||
// numDataBytesInGroup1 = 66 / 5 = 13
|
||||
int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
|
||||
// numDataBytesInGroup2 = 13 + 1 = 14
|
||||
int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
|
||||
// numEcBytesInGroup1 = 39 - 13 = 26
|
||||
int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
|
||||
// numEcBytesInGroup2 = 40 - 14 = 26
|
||||
int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
|
||||
// Sanity checks.
|
||||
// 26 = 26
|
||||
if (numEcBytesInGroup1 != numEcBytesInGroup2) {
|
||||
throw new WriterException("EC bytes mismatch");
|
||||
}
|
||||
// 5 = 4 + 1.
|
||||
if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {
|
||||
throw new WriterException("RS blocks mismatch");
|
||||
}
|
||||
// 196 = (13 + 26) * 4 + (14 + 26) * 1
|
||||
if (numTotalBytes !=
|
||||
((numDataBytesInGroup1 + numEcBytesInGroup1) *
|
||||
numRsBlocksInGroup1) +
|
||||
((numDataBytesInGroup2 + numEcBytesInGroup2) *
|
||||
numRsBlocksInGroup2)) {
|
||||
throw new WriterException("Total bytes mismatch");
|
||||
}
|
||||
|
||||
if (blockID < numRsBlocksInGroup1) {
|
||||
numDataBytesInBlock[0] = numDataBytesInGroup1;
|
||||
numECBytesInBlock[0] = numEcBytesInGroup1;
|
||||
} else {
|
||||
numDataBytesInBlock[0] = numDataBytesInGroup2;
|
||||
numECBytesInBlock[0] = numEcBytesInGroup2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interleave "bits" with corresponding error correction bytes. On success, store the result in
|
||||
* "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
|
||||
*/
|
||||
static BitArray interleaveWithECBytes(BitArray bits,
|
||||
int numTotalBytes,
|
||||
int numDataBytes,
|
||||
int numRSBlocks) throws WriterException {
|
||||
|
||||
// "bits" must have "getNumDataBytes" bytes of data.
|
||||
if (bits.getSizeInBytes() != numDataBytes) {
|
||||
throw new WriterException("Number of bits and data bytes does not match");
|
||||
}
|
||||
|
||||
// Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
|
||||
// store the divided data bytes blocks and error correction bytes blocks into "blocks".
|
||||
int dataBytesOffset = 0;
|
||||
int maxNumDataBytes = 0;
|
||||
int maxNumEcBytes = 0;
|
||||
|
||||
// Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
|
||||
Collection<BlockPair> blocks = new ArrayList<>(numRSBlocks);
|
||||
|
||||
for (int i = 0; i < numRSBlocks; ++i) {
|
||||
int[] numDataBytesInBlock = new int[1];
|
||||
int[] numEcBytesInBlock = new int[1];
|
||||
getNumDataBytesAndNumECBytesForBlockID(
|
||||
numTotalBytes, numDataBytes, numRSBlocks, i,
|
||||
numDataBytesInBlock, numEcBytesInBlock);
|
||||
|
||||
int size = numDataBytesInBlock[0];
|
||||
byte[] dataBytes = new byte[size];
|
||||
bits.toBytes(8*dataBytesOffset, dataBytes, 0, size);
|
||||
byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
|
||||
blocks.add(new BlockPair(dataBytes, ecBytes));
|
||||
|
||||
maxNumDataBytes = Math.max(maxNumDataBytes, size);
|
||||
maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length);
|
||||
dataBytesOffset += numDataBytesInBlock[0];
|
||||
}
|
||||
if (numDataBytes != dataBytesOffset) {
|
||||
throw new WriterException("Data bytes does not match offset");
|
||||
}
|
||||
|
||||
BitArray result = new BitArray();
|
||||
|
||||
// First, place data blocks.
|
||||
for (int i = 0; i < maxNumDataBytes; ++i) {
|
||||
for (BlockPair block : blocks) {
|
||||
byte[] dataBytes = block.getDataBytes();
|
||||
if (i < dataBytes.length) {
|
||||
result.appendBits(dataBytes[i], 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then, place error correction blocks.
|
||||
for (int i = 0; i < maxNumEcBytes; ++i) {
|
||||
for (BlockPair block : blocks) {
|
||||
byte[] ecBytes = block.getErrorCorrectionBytes();
|
||||
if (i < ecBytes.length) {
|
||||
result.appendBits(ecBytes[i], 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numTotalBytes != result.getSizeInBytes()) { // Should be same.
|
||||
throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
|
||||
result.getSizeInBytes() + " differ.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) {
|
||||
int numDataBytes = dataBytes.length;
|
||||
int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
|
||||
for (int i = 0; i < numDataBytes; i++) {
|
||||
toEncode[i] = dataBytes[i] & 0xFF;
|
||||
}
|
||||
new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock);
|
||||
|
||||
byte[] ecBytes = new byte[numEcBytesInBlock];
|
||||
for (int i = 0; i < numEcBytesInBlock; i++) {
|
||||
ecBytes[i] = (byte) toEncode[numDataBytes + i];
|
||||
}
|
||||
return ecBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append mode info. On success, store the result in "bits".
|
||||
*/
|
||||
static void appendModeInfo(Mode mode, BitArray bits) {
|
||||
bits.appendBits(mode.getBits(), 4);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append length info. On success, store the result in "bits".
|
||||
*/
|
||||
static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException {
|
||||
int numBits = mode.getCharacterCountBits(version);
|
||||
if (numLetters >= (1 << numBits)) {
|
||||
throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1));
|
||||
}
|
||||
bits.appendBits(numLetters, numBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
|
||||
*/
|
||||
static void appendBytes(String content,
|
||||
Mode mode,
|
||||
BitArray bits,
|
||||
String encoding) throws WriterException {
|
||||
switch (mode) {
|
||||
case NUMERIC:
|
||||
appendNumericBytes(content, bits);
|
||||
break;
|
||||
case ALPHANUMERIC:
|
||||
appendAlphanumericBytes(content, bits);
|
||||
break;
|
||||
case BYTE:
|
||||
append8BitBytes(content, bits, encoding);
|
||||
break;
|
||||
case KANJI:
|
||||
appendKanjiBytes(content, bits);
|
||||
break;
|
||||
default:
|
||||
throw new WriterException("Invalid mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
static void appendNumericBytes(CharSequence content, BitArray bits) {
|
||||
int length = content.length();
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
int num1 = content.charAt(i) - '0';
|
||||
if (i + 2 < length) {
|
||||
// Encode three numeric letters in ten bits.
|
||||
int num2 = content.charAt(i + 1) - '0';
|
||||
int num3 = content.charAt(i + 2) - '0';
|
||||
bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
|
||||
i += 3;
|
||||
} else if (i + 1 < length) {
|
||||
// Encode two numeric letters in seven bits.
|
||||
int num2 = content.charAt(i + 1) - '0';
|
||||
bits.appendBits(num1 * 10 + num2, 7);
|
||||
i += 2;
|
||||
} else {
|
||||
// Encode one numeric letter in four bits.
|
||||
bits.appendBits(num1, 4);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void appendAlphanumericBytes(CharSequence content, BitArray bits) throws WriterException {
|
||||
int length = content.length();
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
int code1 = getAlphanumericCode(content.charAt(i));
|
||||
if (code1 == -1) {
|
||||
throw new WriterException();
|
||||
}
|
||||
if (i + 1 < length) {
|
||||
int code2 = getAlphanumericCode(content.charAt(i + 1));
|
||||
if (code2 == -1) {
|
||||
throw new WriterException();
|
||||
}
|
||||
// Encode two alphanumeric letters in 11 bits.
|
||||
bits.appendBits(code1 * 45 + code2, 11);
|
||||
i += 2;
|
||||
} else {
|
||||
// Encode one alphanumeric letter in six bits.
|
||||
bits.appendBits(code1, 6);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void append8BitBytes(String content, BitArray bits, String encoding)
|
||||
throws WriterException {
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = content.getBytes(encoding);
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new WriterException(uee);
|
||||
}
|
||||
for (byte b : bytes) {
|
||||
bits.appendBits(b, 8);
|
||||
}
|
||||
}
|
||||
|
||||
static void appendKanjiBytes(String content, BitArray bits) throws WriterException {
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = content.getBytes("Shift_JIS");
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new WriterException(uee);
|
||||
}
|
||||
int length = bytes.length;
|
||||
for (int i = 0; i < length; i += 2) {
|
||||
int byte1 = bytes[i] & 0xFF;
|
||||
int byte2 = bytes[i + 1] & 0xFF;
|
||||
int code = (byte1 << 8) | byte2;
|
||||
int subtracted = -1;
|
||||
if (code >= 0x8140 && code <= 0x9ffc) {
|
||||
subtracted = code - 0x8140;
|
||||
} else if (code >= 0xe040 && code <= 0xebbf) {
|
||||
subtracted = code - 0xc140;
|
||||
}
|
||||
if (subtracted == -1) {
|
||||
throw new WriterException("Invalid byte sequence");
|
||||
}
|
||||
int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
|
||||
bits.appendBits(encoded, 13);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendECI(CharacterSetECI eci, BitArray bits) {
|
||||
bits.appendBits(Mode.ECI.getBits(), 4);
|
||||
// This is correct for values up to 127, which is all we need now.
|
||||
bits.appendBits(eci.getValue(), 8);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.qrcode.encoder;
|
||||
|
||||
/**
|
||||
* @author Satoru Takabayashi
|
||||
* @author Daniel Switkin
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class MaskUtil {
|
||||
|
||||
// Penalty weights from section 6.8.2.1
|
||||
private static final int N1 = 3;
|
||||
private static final int N2 = 3;
|
||||
private static final int N3 = 40;
|
||||
private static final int N4 = 10;
|
||||
|
||||
private MaskUtil() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
|
||||
* give penalty to them. Example: 00000 or 11111.
|
||||
*/
|
||||
static int applyMaskPenaltyRule1(ByteMatrix matrix) {
|
||||
return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
|
||||
* penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a
|
||||
* penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.
|
||||
*/
|
||||
static int applyMaskPenaltyRule2(ByteMatrix matrix) {
|
||||
int penalty = 0;
|
||||
byte[][] array = matrix.getArray();
|
||||
int width = matrix.getWidth();
|
||||
int height = matrix.getHeight();
|
||||
for (int y = 0; y < height - 1; y++) {
|
||||
for (int x = 0; x < width - 1; x++) {
|
||||
int value = array[y][x];
|
||||
if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {
|
||||
penalty++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return N2 * penalty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply mask penalty rule 3 and return the penalty. Find consecutive runs of 1:1:3:1:1:4
|
||||
* starting with black, or 4:1:1:3:1:1 starting with white, and give penalty to them. If we
|
||||
* find patterns like 000010111010000, we give penalty once.
|
||||
*/
|
||||
static int applyMaskPenaltyRule3(ByteMatrix matrix) {
|
||||
int numPenalties = 0;
|
||||
byte[][] array = matrix.getArray();
|
||||
int width = matrix.getWidth();
|
||||
int height = matrix.getHeight();
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
byte[] arrayY = array[y]; // We can at least optimize this access
|
||||
if (x + 6 < width &&
|
||||
arrayY[x] == 1 &&
|
||||
arrayY[x + 1] == 0 &&
|
||||
arrayY[x + 2] == 1 &&
|
||||
arrayY[x + 3] == 1 &&
|
||||
arrayY[x + 4] == 1 &&
|
||||
arrayY[x + 5] == 0 &&
|
||||
arrayY[x + 6] == 1 &&
|
||||
(isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11))) {
|
||||
numPenalties++;
|
||||
}
|
||||
if (y + 6 < height &&
|
||||
array[y][x] == 1 &&
|
||||
array[y + 1][x] == 0 &&
|
||||
array[y + 2][x] == 1 &&
|
||||
array[y + 3][x] == 1 &&
|
||||
array[y + 4][x] == 1 &&
|
||||
array[y + 5][x] == 0 &&
|
||||
array[y + 6][x] == 1 &&
|
||||
(isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11))) {
|
||||
numPenalties++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return numPenalties * N3;
|
||||
}
|
||||
|
||||
private static boolean isWhiteHorizontal(byte[] rowArray, int from, int to) {
|
||||
for (int i = from; i < to; i++) {
|
||||
if (i >= 0 && i < rowArray.length && rowArray[i] == 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isWhiteVertical(byte[][] array, int col, int from, int to) {
|
||||
for (int i = from; i < to; i++) {
|
||||
if (i >= 0 && i < array.length && array[i][col] == 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
|
||||
* penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance.
|
||||
*/
|
||||
static int applyMaskPenaltyRule4(ByteMatrix matrix) {
|
||||
int numDarkCells = 0;
|
||||
byte[][] array = matrix.getArray();
|
||||
int width = matrix.getWidth();
|
||||
int height = matrix.getHeight();
|
||||
for (int y = 0; y < height; y++) {
|
||||
byte[] arrayY = array[y];
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (arrayY[x] == 1) {
|
||||
numDarkCells++;
|
||||
}
|
||||
}
|
||||
}
|
||||
int numTotalCells = matrix.getHeight() * matrix.getWidth();
|
||||
int fivePercentVariances = Math.abs(numDarkCells * 2 - numTotalCells) * 10 / numTotalCells;
|
||||
return fivePercentVariances * N4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
|
||||
* pattern conditions.
|
||||
*/
|
||||
static boolean getDataMaskBit(int maskPattern, int x, int y) {
|
||||
int intermediate;
|
||||
int temp;
|
||||
switch (maskPattern) {
|
||||
case 0:
|
||||
intermediate = (y + x) & 0x1;
|
||||
break;
|
||||
case 1:
|
||||
intermediate = y & 0x1;
|
||||
break;
|
||||
case 2:
|
||||
intermediate = x % 3;
|
||||
break;
|
||||
case 3:
|
||||
intermediate = (y + x) % 3;
|
||||
break;
|
||||
case 4:
|
||||
intermediate = ((y / 2) + (x / 3)) & 0x1;
|
||||
break;
|
||||
case 5:
|
||||
temp = y * x;
|
||||
intermediate = (temp & 0x1) + (temp % 3);
|
||||
break;
|
||||
case 6:
|
||||
temp = y * x;
|
||||
intermediate = ((temp & 0x1) + (temp % 3)) & 0x1;
|
||||
break;
|
||||
case 7:
|
||||
temp = y * x;
|
||||
intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern);
|
||||
}
|
||||
return intermediate == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
|
||||
* vertical and horizontal orders respectively.
|
||||
*/
|
||||
private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
|
||||
int penalty = 0;
|
||||
int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();
|
||||
int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();
|
||||
byte[][] array = matrix.getArray();
|
||||
for (int i = 0; i < iLimit; i++) {
|
||||
int numSameBitCells = 0;
|
||||
int prevBit = -1;
|
||||
for (int j = 0; j < jLimit; j++) {
|
||||
int bit = isHorizontal ? array[i][j] : array[j][i];
|
||||
if (bit == prevBit) {
|
||||
numSameBitCells++;
|
||||
} else {
|
||||
if (numSameBitCells >= 5) {
|
||||
penalty += N1 + (numSameBitCells - 5);
|
||||
}
|
||||
numSameBitCells = 1; // Include the cell itself.
|
||||
prevBit = bit;
|
||||
}
|
||||
}
|
||||
if (numSameBitCells >= 5) {
|
||||
penalty += N1 + (numSameBitCells - 5);
|
||||
}
|
||||
}
|
||||
return penalty;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* 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.qrcode.encoder;
|
||||
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.google.zxing.qrcode.decoder.Version;
|
||||
|
||||
/**
|
||||
* @author satorux@google.com (Satoru Takabayashi) - creator
|
||||
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
|
||||
*/
|
||||
final class MatrixUtil {
|
||||
|
||||
private MatrixUtil() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private static final int[][] POSITION_DETECTION_PATTERN = {
|
||||
{1, 1, 1, 1, 1, 1, 1},
|
||||
{1, 0, 0, 0, 0, 0, 1},
|
||||
{1, 0, 1, 1, 1, 0, 1},
|
||||
{1, 0, 1, 1, 1, 0, 1},
|
||||
{1, 0, 1, 1, 1, 0, 1},
|
||||
{1, 0, 0, 0, 0, 0, 1},
|
||||
{1, 1, 1, 1, 1, 1, 1},
|
||||
};
|
||||
|
||||
private static final int[][] POSITION_ADJUSTMENT_PATTERN = {
|
||||
{1, 1, 1, 1, 1},
|
||||
{1, 0, 0, 0, 1},
|
||||
{1, 0, 1, 0, 1},
|
||||
{1, 0, 0, 0, 1},
|
||||
{1, 1, 1, 1, 1},
|
||||
};
|
||||
|
||||
// From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
|
||||
private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = {
|
||||
{-1, -1, -1, -1, -1, -1, -1}, // Version 1
|
||||
{ 6, 18, -1, -1, -1, -1, -1}, // Version 2
|
||||
{ 6, 22, -1, -1, -1, -1, -1}, // Version 3
|
||||
{ 6, 26, -1, -1, -1, -1, -1}, // Version 4
|
||||
{ 6, 30, -1, -1, -1, -1, -1}, // Version 5
|
||||
{ 6, 34, -1, -1, -1, -1, -1}, // Version 6
|
||||
{ 6, 22, 38, -1, -1, -1, -1}, // Version 7
|
||||
{ 6, 24, 42, -1, -1, -1, -1}, // Version 8
|
||||
{ 6, 26, 46, -1, -1, -1, -1}, // Version 9
|
||||
{ 6, 28, 50, -1, -1, -1, -1}, // Version 10
|
||||
{ 6, 30, 54, -1, -1, -1, -1}, // Version 11
|
||||
{ 6, 32, 58, -1, -1, -1, -1}, // Version 12
|
||||
{ 6, 34, 62, -1, -1, -1, -1}, // Version 13
|
||||
{ 6, 26, 46, 66, -1, -1, -1}, // Version 14
|
||||
{ 6, 26, 48, 70, -1, -1, -1}, // Version 15
|
||||
{ 6, 26, 50, 74, -1, -1, -1}, // Version 16
|
||||
{ 6, 30, 54, 78, -1, -1, -1}, // Version 17
|
||||
{ 6, 30, 56, 82, -1, -1, -1}, // Version 18
|
||||
{ 6, 30, 58, 86, -1, -1, -1}, // Version 19
|
||||
{ 6, 34, 62, 90, -1, -1, -1}, // Version 20
|
||||
{ 6, 28, 50, 72, 94, -1, -1}, // Version 21
|
||||
{ 6, 26, 50, 74, 98, -1, -1}, // Version 22
|
||||
{ 6, 30, 54, 78, 102, -1, -1}, // Version 23
|
||||
{ 6, 28, 54, 80, 106, -1, -1}, // Version 24
|
||||
{ 6, 32, 58, 84, 110, -1, -1}, // Version 25
|
||||
{ 6, 30, 58, 86, 114, -1, -1}, // Version 26
|
||||
{ 6, 34, 62, 90, 118, -1, -1}, // Version 27
|
||||
{ 6, 26, 50, 74, 98, 122, -1}, // Version 28
|
||||
{ 6, 30, 54, 78, 102, 126, -1}, // Version 29
|
||||
{ 6, 26, 52, 78, 104, 130, -1}, // Version 30
|
||||
{ 6, 30, 56, 82, 108, 134, -1}, // Version 31
|
||||
{ 6, 34, 60, 86, 112, 138, -1}, // Version 32
|
||||
{ 6, 30, 58, 86, 114, 142, -1}, // Version 33
|
||||
{ 6, 34, 62, 90, 118, 146, -1}, // Version 34
|
||||
{ 6, 30, 54, 78, 102, 126, 150}, // Version 35
|
||||
{ 6, 24, 50, 76, 102, 128, 154}, // Version 36
|
||||
{ 6, 28, 54, 80, 106, 132, 158}, // Version 37
|
||||
{ 6, 32, 58, 84, 110, 136, 162}, // Version 38
|
||||
{ 6, 26, 54, 82, 110, 138, 166}, // Version 39
|
||||
{ 6, 30, 58, 86, 114, 142, 170}, // Version 40
|
||||
};
|
||||
|
||||
// Type info cells at the left top corner.
|
||||
private static final int[][] TYPE_INFO_COORDINATES = {
|
||||
{8, 0},
|
||||
{8, 1},
|
||||
{8, 2},
|
||||
{8, 3},
|
||||
{8, 4},
|
||||
{8, 5},
|
||||
{8, 7},
|
||||
{8, 8},
|
||||
{7, 8},
|
||||
{5, 8},
|
||||
{4, 8},
|
||||
{3, 8},
|
||||
{2, 8},
|
||||
{1, 8},
|
||||
{0, 8},
|
||||
};
|
||||
|
||||
// From Appendix D in JISX0510:2004 (p. 67)
|
||||
private static final int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101
|
||||
|
||||
// From Appendix C in JISX0510:2004 (p.65).
|
||||
private static final int TYPE_INFO_POLY = 0x537;
|
||||
private static final int TYPE_INFO_MASK_PATTERN = 0x5412;
|
||||
|
||||
// Set all cells to -1. -1 means that the cell is empty (not set yet).
|
||||
//
|
||||
// JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding
|
||||
// with the ByteMatrix initialized all to zero.
|
||||
static void clearMatrix(ByteMatrix matrix) {
|
||||
matrix.clear((byte) -1);
|
||||
}
|
||||
|
||||
// Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On
|
||||
// success, store the result in "matrix" and return true.
|
||||
static void buildMatrix(BitArray dataBits,
|
||||
ErrorCorrectionLevel ecLevel,
|
||||
Version version,
|
||||
int maskPattern,
|
||||
ByteMatrix matrix) throws WriterException {
|
||||
clearMatrix(matrix);
|
||||
embedBasicPatterns(version, matrix);
|
||||
// Type information appear with any version.
|
||||
embedTypeInfo(ecLevel, maskPattern, matrix);
|
||||
// Version info appear if version >= 7.
|
||||
maybeEmbedVersionInfo(version, matrix);
|
||||
// Data should be embedded at end.
|
||||
embedDataBits(dataBits, maskPattern, matrix);
|
||||
}
|
||||
|
||||
// Embed basic patterns. On success, modify the matrix and return true.
|
||||
// The basic patterns are:
|
||||
// - Position detection patterns
|
||||
// - Timing patterns
|
||||
// - Dark dot at the left bottom corner
|
||||
// - Position adjustment patterns, if need be
|
||||
static void embedBasicPatterns(Version version, ByteMatrix matrix) throws WriterException {
|
||||
// Let's get started with embedding big squares at corners.
|
||||
embedPositionDetectionPatternsAndSeparators(matrix);
|
||||
// Then, embed the dark dot at the left bottom corner.
|
||||
embedDarkDotAtLeftBottomCorner(matrix);
|
||||
|
||||
// Position adjustment patterns appear if version >= 2.
|
||||
maybeEmbedPositionAdjustmentPatterns(version, matrix);
|
||||
// Timing patterns should be embedded after position adj. patterns.
|
||||
embedTimingPatterns(matrix);
|
||||
}
|
||||
|
||||
// Embed type information. On success, modify the matrix.
|
||||
static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)
|
||||
throws WriterException {
|
||||
BitArray typeInfoBits = new BitArray();
|
||||
makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);
|
||||
|
||||
for (int i = 0; i < typeInfoBits.getSize(); ++i) {
|
||||
// Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
|
||||
// "typeInfoBits".
|
||||
boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i);
|
||||
|
||||
// Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
|
||||
int x1 = TYPE_INFO_COORDINATES[i][0];
|
||||
int y1 = TYPE_INFO_COORDINATES[i][1];
|
||||
matrix.set(x1, y1, bit);
|
||||
|
||||
if (i < 8) {
|
||||
// Right top corner.
|
||||
int x2 = matrix.getWidth() - i - 1;
|
||||
int y2 = 8;
|
||||
matrix.set(x2, y2, bit);
|
||||
} else {
|
||||
// Left bottom corner.
|
||||
int x2 = 8;
|
||||
int y2 = matrix.getHeight() - 7 + (i - 8);
|
||||
matrix.set(x2, y2, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Embed version information if need be. On success, modify the matrix and return true.
|
||||
// See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
|
||||
static void maybeEmbedVersionInfo(Version version, ByteMatrix matrix) throws WriterException {
|
||||
if (version.getVersionNumber() < 7) { // Version info is necessary if version >= 7.
|
||||
return; // Don't need version info.
|
||||
}
|
||||
BitArray versionInfoBits = new BitArray();
|
||||
makeVersionInfoBits(version, versionInfoBits);
|
||||
|
||||
int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0.
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
// Place bits in LSB (least significant bit) to MSB order.
|
||||
boolean bit = versionInfoBits.get(bitIndex);
|
||||
bitIndex--;
|
||||
// Left bottom corner.
|
||||
matrix.set(i, matrix.getHeight() - 11 + j, bit);
|
||||
// Right bottom corner.
|
||||
matrix.set(matrix.getHeight() - 11 + j, i, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.
|
||||
// For debugging purposes, it skips masking process if "getMaskPattern" is -1.
|
||||
// See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
|
||||
static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix)
|
||||
throws WriterException {
|
||||
int bitIndex = 0;
|
||||
int direction = -1;
|
||||
// Start from the right bottom cell.
|
||||
int x = matrix.getWidth() - 1;
|
||||
int y = matrix.getHeight() - 1;
|
||||
while (x > 0) {
|
||||
// Skip the vertical timing pattern.
|
||||
if (x == 6) {
|
||||
x -= 1;
|
||||
}
|
||||
while (y >= 0 && y < matrix.getHeight()) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int xx = x - i;
|
||||
// Skip the cell if it's not empty.
|
||||
if (!isEmpty(matrix.get(xx, y))) {
|
||||
continue;
|
||||
}
|
||||
boolean bit;
|
||||
if (bitIndex < dataBits.getSize()) {
|
||||
bit = dataBits.get(bitIndex);
|
||||
++bitIndex;
|
||||
} else {
|
||||
// Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
|
||||
// in 8.4.9 of JISX0510:2004 (p. 24).
|
||||
bit = false;
|
||||
}
|
||||
|
||||
// Skip masking if mask_pattern is -1.
|
||||
if (maskPattern != -1 && MaskUtil.getDataMaskBit(maskPattern, xx, y)) {
|
||||
bit = !bit;
|
||||
}
|
||||
matrix.set(xx, y, bit);
|
||||
}
|
||||
y += direction;
|
||||
}
|
||||
direction = -direction; // Reverse the direction.
|
||||
y += direction;
|
||||
x -= 2; // Move to the left.
|
||||
}
|
||||
// All bits should be consumed.
|
||||
if (bitIndex != dataBits.getSize()) {
|
||||
throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
// Return the position of the most significant bit set (to one) in the "value". The most
|
||||
// significant bit is position 32. If there is no bit set, return 0. Examples:
|
||||
// - findMSBSet(0) => 0
|
||||
// - findMSBSet(1) => 1
|
||||
// - findMSBSet(255) => 8
|
||||
static int findMSBSet(int value) {
|
||||
int numDigits = 0;
|
||||
while (value != 0) {
|
||||
value >>>= 1;
|
||||
++numDigits;
|
||||
}
|
||||
return numDigits;
|
||||
}
|
||||
|
||||
// Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH
|
||||
// code is used for encoding type information and version information.
|
||||
// Example: Calculation of version information of 7.
|
||||
// f(x) is created from 7.
|
||||
// - 7 = 000111 in 6 bits
|
||||
// - f(x) = x^2 + x^1 + x^0
|
||||
// g(x) is given by the standard (p. 67)
|
||||
// - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
|
||||
// Multiply f(x) by x^(18 - 6)
|
||||
// - f'(x) = f(x) * x^(18 - 6)
|
||||
// - f'(x) = x^14 + x^13 + x^12
|
||||
// Calculate the remainder of f'(x) / g(x)
|
||||
// x^2
|
||||
// __________________________________________________
|
||||
// g(x) )x^14 + x^13 + x^12
|
||||
// x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
|
||||
// --------------------------------------------------
|
||||
// x^11 + x^10 + x^7 + x^4 + x^2
|
||||
//
|
||||
// The remainder is x^11 + x^10 + x^7 + x^4 + x^2
|
||||
// Encode it in binary: 110010010100
|
||||
// The return value is 0xc94 (1100 1001 0100)
|
||||
//
|
||||
// Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
|
||||
// operations. We don't care if cofficients are positive or negative.
|
||||
static int calculateBCHCode(int value, int poly) {
|
||||
if (poly == 0) {
|
||||
throw new IllegalArgumentException("0 polynomial");
|
||||
}
|
||||
// If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
|
||||
// from 13 to make it 12.
|
||||
int msbSetInPoly = findMSBSet(poly);
|
||||
value <<= msbSetInPoly - 1;
|
||||
// Do the division business using exclusive-or operations.
|
||||
while (findMSBSet(value) >= msbSetInPoly) {
|
||||
value ^= poly << (findMSBSet(value) - msbSetInPoly);
|
||||
}
|
||||
// Now the "value" is the remainder (i.e. the BCH code)
|
||||
return value;
|
||||
}
|
||||
|
||||
// Make bit vector of type information. On success, store the result in "bits" and return true.
|
||||
// Encode error correction level and mask pattern. See 8.9 of
|
||||
// JISX0510:2004 (p.45) for details.
|
||||
static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)
|
||||
throws WriterException {
|
||||
if (!QRCode.isValidMaskPattern(maskPattern)) {
|
||||
throw new WriterException("Invalid mask pattern");
|
||||
}
|
||||
int typeInfo = (ecLevel.getBits() << 3) | maskPattern;
|
||||
bits.appendBits(typeInfo, 5);
|
||||
|
||||
int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
|
||||
bits.appendBits(bchCode, 10);
|
||||
|
||||
BitArray maskBits = new BitArray();
|
||||
maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
|
||||
bits.xor(maskBits);
|
||||
|
||||
if (bits.getSize() != 15) { // Just in case.
|
||||
throw new WriterException("should not happen but we got: " + bits.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
// Make bit vector of version information. On success, store the result in "bits" and return true.
|
||||
// See 8.10 of JISX0510:2004 (p.45) for details.
|
||||
static void makeVersionInfoBits(Version version, BitArray bits) throws WriterException {
|
||||
bits.appendBits(version.getVersionNumber(), 6);
|
||||
int bchCode = calculateBCHCode(version.getVersionNumber(), VERSION_INFO_POLY);
|
||||
bits.appendBits(bchCode, 12);
|
||||
|
||||
if (bits.getSize() != 18) { // Just in case.
|
||||
throw new WriterException("should not happen but we got: " + bits.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if "value" is empty.
|
||||
private static boolean isEmpty(int value) {
|
||||
return value == -1;
|
||||
}
|
||||
|
||||
private static void embedTimingPatterns(ByteMatrix matrix) {
|
||||
// -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
|
||||
// separation patterns (size 1). Thus, 8 = 7 + 1.
|
||||
for (int i = 8; i < matrix.getWidth() - 8; ++i) {
|
||||
int bit = (i + 1) % 2;
|
||||
// Horizontal line.
|
||||
if (isEmpty(matrix.get(i, 6))) {
|
||||
matrix.set(i, 6, bit);
|
||||
}
|
||||
// Vertical line.
|
||||
if (isEmpty(matrix.get(6, i))) {
|
||||
matrix.set(6, i, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
|
||||
private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
|
||||
if (matrix.get(8, matrix.getHeight() - 8) == 0) {
|
||||
throw new WriterException();
|
||||
}
|
||||
matrix.set(8, matrix.getHeight() - 8, 1);
|
||||
}
|
||||
|
||||
private static void embedHorizontalSeparationPattern(int xStart,
|
||||
int yStart,
|
||||
ByteMatrix matrix) throws WriterException {
|
||||
for (int x = 0; x < 8; ++x) {
|
||||
if (!isEmpty(matrix.get(xStart + x, yStart))) {
|
||||
throw new WriterException();
|
||||
}
|
||||
matrix.set(xStart + x, yStart, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void embedVerticalSeparationPattern(int xStart,
|
||||
int yStart,
|
||||
ByteMatrix matrix) throws WriterException {
|
||||
for (int y = 0; y < 7; ++y) {
|
||||
if (!isEmpty(matrix.get(xStart, yStart + y))) {
|
||||
throw new WriterException();
|
||||
}
|
||||
matrix.set(xStart, yStart + y, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we cannot unify the function with embedPositionDetectionPattern() despite they are
|
||||
// almost identical, since we cannot write a function that takes 2D arrays in different sizes in
|
||||
// C/C++. We should live with the fact.
|
||||
private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix) {
|
||||
for (int y = 0; y < 5; ++y) {
|
||||
for (int x = 0; x < 5; ++x) {
|
||||
matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix) {
|
||||
for (int y = 0; y < 7; ++y) {
|
||||
for (int x = 0; x < 7; ++x) {
|
||||
matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Embed position detection patterns and surrounding vertical/horizontal separators.
|
||||
private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException {
|
||||
// Embed three big squares at corners.
|
||||
int pdpWidth = POSITION_DETECTION_PATTERN[0].length;
|
||||
// Left top corner.
|
||||
embedPositionDetectionPattern(0, 0, matrix);
|
||||
// Right top corner.
|
||||
embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix);
|
||||
// Left bottom corner.
|
||||
embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
|
||||
|
||||
// Embed horizontal separation patterns around the squares.
|
||||
int hspWidth = 8;
|
||||
// Left top corner.
|
||||
embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
|
||||
// Right top corner.
|
||||
embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth,
|
||||
hspWidth - 1, matrix);
|
||||
// Left bottom corner.
|
||||
embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
|
||||
|
||||
// Embed vertical separation patterns around the squares.
|
||||
int vspSize = 7;
|
||||
// Left top corner.
|
||||
embedVerticalSeparationPattern(vspSize, 0, matrix);
|
||||
// Right top corner.
|
||||
embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix);
|
||||
// Left bottom corner.
|
||||
embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize,
|
||||
matrix);
|
||||
}
|
||||
|
||||
// Embed position adjustment patterns if need be.
|
||||
private static void maybeEmbedPositionAdjustmentPatterns(Version version, ByteMatrix matrix) {
|
||||
if (version.getVersionNumber() < 2) { // The patterns appear if version >= 2
|
||||
return;
|
||||
}
|
||||
int index = version.getVersionNumber() - 1;
|
||||
int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
|
||||
int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length;
|
||||
for (int i = 0; i < numCoordinates; ++i) {
|
||||
for (int j = 0; j < numCoordinates; ++j) {
|
||||
int y = coordinates[i];
|
||||
int x = coordinates[j];
|
||||
if (x == -1 || y == -1) {
|
||||
continue;
|
||||
}
|
||||
// If the cell is unset, we embed the position adjustment pattern here.
|
||||
if (isEmpty(matrix.get(x, y))) {
|
||||
// -2 is necessary since the x/y coordinates point to the center of the pattern, not the
|
||||
// left top corner.
|
||||
embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.qrcode.encoder;
|
||||
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.google.zxing.qrcode.decoder.Mode;
|
||||
import com.google.zxing.qrcode.decoder.Version;
|
||||
|
||||
/**
|
||||
* @author satorux@google.com (Satoru Takabayashi) - creator
|
||||
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
|
||||
*/
|
||||
public final class QRCode {
|
||||
|
||||
public static final int NUM_MASK_PATTERNS = 8;
|
||||
|
||||
private Mode mode;
|
||||
private ErrorCorrectionLevel ecLevel;
|
||||
private Version version;
|
||||
private int maskPattern;
|
||||
private ByteMatrix matrix;
|
||||
|
||||
public QRCode() {
|
||||
maskPattern = -1;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public ErrorCorrectionLevel getECLevel() {
|
||||
return ecLevel;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getMaskPattern() {
|
||||
return maskPattern;
|
||||
}
|
||||
|
||||
public ByteMatrix getMatrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(200);
|
||||
result.append("<<\n");
|
||||
result.append(" mode: ");
|
||||
result.append(mode);
|
||||
result.append("\n ecLevel: ");
|
||||
result.append(ecLevel);
|
||||
result.append("\n version: ");
|
||||
result.append(version);
|
||||
result.append("\n maskPattern: ");
|
||||
result.append(maskPattern);
|
||||
if (matrix == null) {
|
||||
result.append("\n matrix: null\n");
|
||||
} else {
|
||||
result.append("\n matrix:\n");
|
||||
result.append(matrix);
|
||||
}
|
||||
result.append(">>\n");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public void setMode(Mode value) {
|
||||
mode = value;
|
||||
}
|
||||
|
||||
public void setECLevel(ErrorCorrectionLevel value) {
|
||||
ecLevel = value;
|
||||
}
|
||||
|
||||
public void setVersion(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setMaskPattern(int value) {
|
||||
maskPattern = value;
|
||||
}
|
||||
|
||||
public void setMatrix(ByteMatrix value) {
|
||||
matrix = value;
|
||||
}
|
||||
|
||||
// Check if "mask_pattern" is valid.
|
||||
public static boolean isValidMaskPattern(int maskPattern) {
|
||||
return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.j2se;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* Encapsulates custom configuration used in methods of {@link MatrixToImageWriter}.
|
||||
*/
|
||||
public final class MatrixToImageConfig {
|
||||
|
||||
public static final int BLACK = 0xFF000000;
|
||||
public static final int WHITE = 0xFFFFFFFF;
|
||||
|
||||
private final int onColor;
|
||||
private final int offColor;
|
||||
|
||||
/**
|
||||
* Creates a default config with on color {@link #BLACK} and off color {@link #WHITE}, generating normal
|
||||
* black-on-white barcodes.
|
||||
*/
|
||||
public MatrixToImageConfig() {
|
||||
this(BLACK, WHITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onColor pixel on color, specified as an ARGB value as an int
|
||||
* @param offColor pixel off color, specified as an ARGB value as an int
|
||||
*/
|
||||
public MatrixToImageConfig(int onColor, int offColor) {
|
||||
this.onColor = onColor;
|
||||
this.offColor = offColor;
|
||||
}
|
||||
|
||||
public int getPixelOnColor() {
|
||||
return onColor;
|
||||
}
|
||||
|
||||
public int getPixelOffColor() {
|
||||
return offColor;
|
||||
}
|
||||
|
||||
int getBufferedImageColorModel() {
|
||||
if (onColor == BLACK && offColor == WHITE) {
|
||||
// Use faster BINARY if colors match default
|
||||
return BufferedImage.TYPE_BYTE_BINARY;
|
||||
}
|
||||
if (hasTransparency(onColor) || hasTransparency(offColor)) {
|
||||
// Use ARGB representation if colors specify non-opaque alpha
|
||||
return BufferedImage.TYPE_INT_ARGB;
|
||||
}
|
||||
// Default otherwise to RGB representation with ignored alpha channel
|
||||
return BufferedImage.TYPE_INT_RGB;
|
||||
}
|
||||
|
||||
private static boolean hasTransparency(int argb) {
|
||||
return (argb & 0xFF000000) != 0xFF000000;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.client.j2se;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Writes a {@link BitMatrix} to {@link BufferedImage},
|
||||
* file or stream. Provided here instead of core since it depends on
|
||||
* Java SE libraries.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class MatrixToImageWriter {
|
||||
|
||||
private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();
|
||||
|
||||
private MatrixToImageWriter() {}
|
||||
|
||||
/**
|
||||
* Renders a {@link BitMatrix} as an image, where "false" bits are rendered
|
||||
* as white, and "true" bits are rendered as black. Uses default configuration.
|
||||
*
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @return {@link BufferedImage} representation of the input
|
||||
*/
|
||||
public static BufferedImage toBufferedImage(BitMatrix matrix) {
|
||||
return toBufferedImage(matrix, DEFAULT_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* As {@link #toBufferedImage(BitMatrix)}, but allows customization of the output.
|
||||
*
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @param config output configuration
|
||||
* @return {@link BufferedImage} representation of the input
|
||||
*/
|
||||
public static BufferedImage toBufferedImage(BitMatrix matrix, MatrixToImageConfig config) {
|
||||
int width = matrix.getWidth();
|
||||
int height = matrix.getHeight();
|
||||
BufferedImage image = new BufferedImage(width, height, config.getBufferedImageColorModel());
|
||||
int onColor = config.getPixelOnColor();
|
||||
int offColor = config.getPixelOffColor();
|
||||
int[] pixels = new int[width * height];
|
||||
int index = 0;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
pixels[index++] = matrix.get(x, y) ? onColor : offColor;
|
||||
}
|
||||
}
|
||||
image.setRGB(0, 0, width, height, pixels, 0, width);
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @param format image format
|
||||
* @param file file {@link File} to write image to
|
||||
* @throws IOException if writes to the file fail
|
||||
* @deprecated use {@link #writeToPath(BitMatrix, String, Path)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
|
||||
writeToPath(matrix, format, file.toPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@link BitMatrix} to a file with default configuration.
|
||||
*
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @param format image format
|
||||
* @param file file {@link Path} to write image to
|
||||
* @throws IOException if writes to the stream fail
|
||||
* @see #toBufferedImage(BitMatrix)
|
||||
*/
|
||||
public static void writeToPath(BitMatrix matrix, String format, Path file) throws IOException {
|
||||
writeToPath(matrix, format, file, DEFAULT_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @param format image format
|
||||
* @param file file {@link File} to write image to
|
||||
* @param config output configuration
|
||||
* @throws IOException if writes to the file fail
|
||||
* @deprecated use {@link #writeToPath(BitMatrix, String, Path, MatrixToImageConfig)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void writeToFile(BitMatrix matrix, String format, File file, MatrixToImageConfig config)
|
||||
throws IOException {
|
||||
writeToPath(matrix, format, file.toPath(), config);
|
||||
}
|
||||
|
||||
/**
|
||||
* As {@link #writeToFile(BitMatrix, String, File)}, but allows customization of the output.
|
||||
*
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @param format image format
|
||||
* @param file file {@link Path} to write image to
|
||||
* @param config output configuration
|
||||
* @throws IOException if writes to the file fail
|
||||
*/
|
||||
public static void writeToPath(BitMatrix matrix, String format, Path file, MatrixToImageConfig config)
|
||||
throws IOException {
|
||||
BufferedImage image = toBufferedImage(matrix, config);
|
||||
if (!ImageIO.write(image, format, file.toFile())) {
|
||||
throw new IOException("Could not write an image of format " + format + " to " + file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@link BitMatrix} to a stream with default configuration.
|
||||
*
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @param format image format
|
||||
* @param stream {@link OutputStream} to write image to
|
||||
* @throws IOException if writes to the stream fail
|
||||
* @see #toBufferedImage(BitMatrix)
|
||||
*/
|
||||
public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException {
|
||||
writeToStream(matrix, format, stream, DEFAULT_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* As {@link #writeToStream(BitMatrix, String, OutputStream)}, but allows customization of the output.
|
||||
*
|
||||
* @param matrix {@link BitMatrix} to write
|
||||
* @param format image format
|
||||
* @param stream {@link OutputStream} to write image to
|
||||
* @param config output configuration
|
||||
* @throws IOException if writes to the stream fail
|
||||
*/
|
||||
public static void writeToStream(BitMatrix matrix, String format, OutputStream stream, MatrixToImageConfig config)
|
||||
throws IOException {
|
||||
BufferedImage image = toBufferedImage(matrix, config);
|
||||
if (!ImageIO.write(image, format, stream)) {
|
||||
throw new IOException("Could not write an image of format " + format);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user