From bdd6066fc353289e78cb510c8f330d4328de5a12 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 22 Jan 2016 19:03:06 +0000
Subject: [PATCH] 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

---
 apps/imagegen/zxing/AUTHORS                   | 134 ++++
 apps/imagegen/zxing/CHANGES                   | 357 +++++++++++
 apps/imagegen/zxing/LICENSE                   | 245 ++++++++
 apps/imagegen/zxing/README.md                 |  83 +++
 .../java/com/google/zxing/BarcodeFormat.java  |  77 +++
 .../com/google/zxing/ChecksumException.java   |  47 ++
 .../java/com/google/zxing/EncodeHintType.java |  98 +++
 .../com/google/zxing/FormatException.java     |  47 ++
 .../com/google/zxing/ReaderException.java     |  49 ++
 .../main/java/com/google/zxing/Writer.java    |  59 ++
 .../com/google/zxing/WriterException.java     |  38 ++
 .../com/google/zxing/common/BitArray.java     | 375 ++++++++++++
 .../com/google/zxing/common/BitMatrix.java    | 449 ++++++++++++++
 .../google/zxing/common/CharacterSetECI.java  | 118 ++++
 .../zxing/common/reedsolomon/GenericGF.java   | 166 +++++
 .../common/reedsolomon/GenericGFPoly.java     | 264 ++++++++
 .../reedsolomon/ReedSolomonEncoder.java       |  74 +++
 .../com/google/zxing/qrcode/QRCodeWriter.java | 118 ++++
 .../qrcode/decoder/ErrorCorrectionLevel.java  |  60 ++
 .../qrcode/decoder/FormatInformation.java     | 172 ++++++
 .../com/google/zxing/qrcode/decoder/Mode.java | 102 ++++
 .../google/zxing/qrcode/decoder/Version.java  | 578 ++++++++++++++++++
 .../zxing/qrcode/encoder/BlockPair.java       |  37 ++
 .../zxing/qrcode/encoder/ByteMatrix.java      |  98 +++
 .../google/zxing/qrcode/encoder/Encoder.java  | 578 ++++++++++++++++++
 .../google/zxing/qrcode/encoder/MaskUtil.java | 217 +++++++
 .../zxing/qrcode/encoder/MatrixUtil.java      | 485 +++++++++++++++
 .../google/zxing/qrcode/encoder/QRCode.java   | 108 ++++
 .../client/j2se/MatrixToImageConfig.java      |  74 +++
 .../client/j2se/MatrixToImageWriter.java      | 162 +++++
 30 files changed, 5469 insertions(+)
 create mode 100644 apps/imagegen/zxing/AUTHORS
 create mode 100644 apps/imagegen/zxing/CHANGES
 create mode 100644 apps/imagegen/zxing/LICENSE
 create mode 100644 apps/imagegen/zxing/README.md
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java
 create mode 100644 apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java
 create mode 100644 apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java
 create mode 100644 apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java

diff --git a/apps/imagegen/zxing/AUTHORS b/apps/imagegen/zxing/AUTHORS
new file mode 100644
index 0000000000..d6aeafeef2
--- /dev/null
+++ b/apps/imagegen/zxing/AUTHORS
@@ -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
diff --git a/apps/imagegen/zxing/CHANGES b/apps/imagegen/zxing/CHANGES
new file mode 100644
index 0000000000..0fe754a4cc
--- /dev/null
+++ b/apps/imagegen/zxing/CHANGES
@@ -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
diff --git a/apps/imagegen/zxing/LICENSE b/apps/imagegen/zxing/LICENSE
new file mode 100644
index 0000000000..510991edab
--- /dev/null
+++ b/apps/imagegen/zxing/LICENSE
@@ -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.
\ No newline at end of file
diff --git a/apps/imagegen/zxing/README.md b/apps/imagegen/zxing/README.md
new file mode 100644
index 0000000000..b0036f7e88
--- /dev/null
+++ b/apps/imagegen/zxing/README.md
@@ -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 [![Barcode Scanner](http://www.android.com/images/brand/android_app_on_play_logo_small.png)](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
+
+[![Build Status](https://travis-ci.org/zxing/zxing.png?branch=master)](https://travis-ci.org/zxing/zxing)
+[![Coverity Status](https://scan.coverity.com/projects/1924/badge.svg)](https://scan.coverity.com/projects/1924)
+[![codecov.io](https://codecov.io/github/zxing/zxing/coverage.svg?branch=master)](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 [![JProfiler](http://www.ej-technologies.com/images/banners/jprofiler_small.png)](http://www.ej-technologies.com/products/jprofiler/overview.html)
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java
new file mode 100644
index 0000000000..7f6a0ef586
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/BarcodeFormat.java
@@ -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
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java
new file mode 100644
index 0000000000..c5acbe3ee8
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ChecksumException.java
@@ -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;
+  }
+}
\ No newline at end of file
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java
new file mode 100644
index 0000000000..eb7938e422
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/EncodeHintType.java
@@ -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,
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java
new file mode 100644
index 0000000000..b046b822d3
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/FormatException.java
@@ -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;
+  }
+}
\ No newline at end of file
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java
new file mode 100644
index 0000000000..5f2c12e432
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/ReaderException.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java
new file mode 100644
index 0000000000..f405fd844f
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/Writer.java
@@ -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;
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java
new file mode 100644
index 0000000000..c61800b93a
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/WriterException.java
@@ -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);
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java
new file mode 100644
index 0000000000..0af7dd72ce
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitArray.java
@@ -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);
+  }
+
+}
\ No newline at end of file
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java
new file mode 100644
index 0000000000..30e2358cb0
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/BitMatrix.java
@@ -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());
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java
new file mode 100644
index 0000000000..7c3853ca89
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/CharacterSetECI.java
@@ -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);
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java
new file mode 100644
index 0000000000..9ede5d80a6
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java
@@ -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 + ')';
+  }
+  
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java
new file mode 100644
index 0000000000..8887e0ee12
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java
@@ -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();
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
new file mode 100644
index 0000000000..ff813cb1ff
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
@@ -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);
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java
new file mode 100644
index 0000000000..ce7ee0736b
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
new file mode 100644
index 0000000000..0f1e113877
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
@@ -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];
+  }
+
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java
new file mode 100644
index 0000000000..894e321754
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java
new file mode 100644
index 0000000000..b7e9ab3a91
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Mode.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java
new file mode 100644
index 0000000000..cc4052508c
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/decoder/Version.java
@@ -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)))
+    };
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java
new file mode 100644
index 0000000000..5714d9c3a4
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java
new file mode 100644
index 0000000000..d7fef0601a
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java
@@ -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();
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java
new file mode 100644
index 0000000000..013b1fa2d4
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java
@@ -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);
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java
new file mode 100644
index 0000000000..b2204d4a97
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java
new file mode 100644
index 0000000000..dac25f8de0
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java
@@ -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);
+        }
+      }
+    }
+  }
+
+}
diff --git a/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java
new file mode 100644
index 0000000000..b560de92f3
--- /dev/null
+++ b/apps/imagegen/zxing/core/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java
new file mode 100644
index 0000000000..d4a563da38
--- /dev/null
+++ b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageConfig.java
@@ -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;
+  }
+
+}
diff --git a/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java
new file mode 100644
index 0000000000..07bdb81492
--- /dev/null
+++ b/apps/imagegen/zxing/javase/src/main/java/com/google/zxing/client/j2se/MatrixToImageWriter.java
@@ -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);
+    }
+  }
+
+}
-- 
GitLab