[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/caches/build_file_checksums.ser\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n/app/build\n/scanner/build\n"
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <Objective-C-extensions>\n      <file>\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Import\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Macro\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Typedef\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Enum\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Constant\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Global\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Struct\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"FunctionPredecl\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Function\" />\n      </file>\n      <class>\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Property\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Synthesize\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"InitMethod\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"StaticMethod\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"InstanceMethod\" />\n        <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"DeallocMethod\" />\n      </class>\n      <extensions>\n        <pair source=\"cpp\" header=\"h\" fileNamingConvention=\"NONE\" />\n        <pair source=\"c\" header=\"h\" fileNamingConvention=\"NONE\" />\n      </extensions>\n    </Objective-C-extensions>\n  </code_scheme>\n</component>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n            <option value=\"$PROJECT_DIR$/scanner\" />\n          </set>\n        </option>\n        <option name=\"resolveModulePerSourceSet\" value=\"false\" />\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"7\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"javax.annotation.CheckForNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"4\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n          <item index=\"5\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.Nullable\" />\n          <item index=\"6\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.RecentlyNullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"6\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n          <item index=\"4\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.NonNull\" />\n          <item index=\"5\" class=\"java.lang.String\" itemvalue=\"androidx.annotation.RecentlyNonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_8\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "README.md",
    "content": "# AiYaScanner\n:fire: zxing and zbar combined with scan code.  只为真正的 zxing zbar 结合二维码扫描：https://github.com/nanchen2251/AiYaScanner\n\nTODO：\n1. 剥离 zxing（已完成）；\n2. 优化 zxing 预览和解码（已完成）；\n3. 加入 zbar 解码（已完成）；\n4. 兼容优化；（部分完成）；\n5. 二维码放大缩小(已完成)；\n6. 目前解码为了兼容，是串行的，相反拖长了时间，后面会改成并行的。\n7. 自动放缩；\n8. 目前为了速度，**仅支持二维码**，后面暴露接口支持其他格式；\n\n**更多有效的优化，请大家一起来提 PR！**\n**有任何需求或者 BUG,请提 issues！**\n\n\n## 效果图<br>\n![](https://github.com/nanchen2251/AiYaScanner/blob/master/screenshot/screenshot.gif)\n\n#### ⊙开源不易，希望给个 star 或者 fork 奖励\n#### ⊙拥抱开源：https://github.com/nanchen2251/\n#### ⊙交流群（拒绝无脑问）：118116509 <a target=\"_blank\" href=\"//shang.qq.com/wpa/qunwpa?idkey=e6ad4af66393684e1d0c9441403b049d2d5670ec0ce9f72150e694cbb7c16b0a\"><img border=\"0\" src=\"http://pub.idqqimg.com/wpa/images/group.png\" alt=\"Android神技侧漏交流群\" title=\"Android神技侧漏交流群\"></a>( 点击图标即可加入 )<br>\n\n## 特点\n  1、只为极速扫码而生。<br>\n  2、默认只支持二维码扫描。<br>\n  3、支持双击放大缩小。<br>\n  4、支持多指放大缩小。<br>\n## 使用方法\n#### 1、添加依赖<br>\n##### Step 1. Add it in your root build.gradle at the end of repositories:\n```java\nallprojects {\n\t\trepositories {\n\t\t\t...\n\t\t\tmaven { url 'https://jitpack.io' }\n\t\t}\n\t}\n```\n##### Step 2. Add the dependency\n```java\ndependencies {\n\t        implementation 'com.github.nanchen2251:AiYaScanner:1.0.7'\n\t}\n```\n#### 2、在Activity里面使用<br>\n```java\n   // 必须自己先申请相机和存储权限\n   CaptureActivity.startForResult(MainActivity.this, 1024);\n   \n   @Override\n    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        if (resultCode == RESULT_OK && requestCode == 1024 && data != null) {\n            String result = data.getStringExtra(\"result\");\n            Toast.makeText(getApplicationContext(), \"扫码结果：\" + result, Toast.LENGTH_SHORT).show();\n        }\n    }\n```\n#### 3、你也可以自己编写你的扫一扫 UI\n```\n// Activity 请继承于 BaseCaptureActivity\n// 你也可以不继承，但记得编写相关方法\n```\n\n该项目参考了：\n\n* [https://github.com/zxing/zxing](https://github.com/zxing/zxing) \n* [https://github.com/ZBar/ZBar](https://github.com/ZBar/ZBar)\n\n### 关于作者\n    南尘<br>\n    四川成都<br>\n    [其它开源](https://github.com/nanchen2251/)<br>\n    [个人博客](https://nanchen2251.github.io/)<br>\n    [简书](http://www.jianshu.com/u/f690947ed5a6)<br>\n    [博客园](http://www.cnblogs.com/liushilin/)<br>\n    交流群：118116509<br>\n    欢迎投稿(关注)我的唯一公众号，公众号搜索 nanchen 或者扫描下方二维码：<br>\n    ![](https://github.com/nanchen2251/Blogs/blob/master/images/nanchen12.jpg)\n\n\n#### 有码走遍天下 无码寸步难行（引自网络）\n\n> 1024 - 梦想，永不止步!  \n爱编程 不爱Bug  \n爱加班 不爱黑眼圈  \n固执 但不偏执  \n疯狂 但不疯癫  \n生活里的菜鸟  \n工作中的大神  \n身怀宝藏，一心憧憬星辰大海  \n追求极致，目标始于高山之巅  \n一群怀揣好奇，梦想改变世界的孩子  \n一群追日逐浪，正在改变世界的极客  \n你们用最美的语言，诠释着科技的力量  \n你们用极速的创新，引领着时代的变迁  \n  \n------至所有正在努力奋斗的程序猿们！加油！！  \n    \n## Licenses\n```\n Copyright 2019 nanchen(刘世麟)\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n```\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 28\n    defaultConfig {\n        applicationId \"com.nanchen.aiyascanner\"\n        minSdkVersion 16\n        targetSdkVersion 28\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'com.android.support:appcompat-v7:28.0.0'\n    implementation 'com.android.support.constraint:constraint-layout:1.1.3'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'com.android.support.test:runner:1.0.2'\n    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'\n    implementation project(':scanner')\n    implementation 'com.github.tbruyelle:rxpermissions:0.10.2'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/androidTest/java/com/nanchen/aiyascanner/ExampleInstrumentedTest.java",
    "content": "package com.nanchen.aiyascanner;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.nanchen.aiyascanner\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.nanchen.aiyascanner\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/nanchen/aiyascanner/MainActivity.java",
    "content": "package com.nanchen.aiyascanner;\n\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.nanchen.scanner.module.CaptureActivity;\n\n\npublic class MainActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {\n            @SuppressLint(\"CheckResult\")\n            @Override\n            public void onClick(View v) {\n                CaptureActivity.startForResult(MainActivity.this, 1024);\n            }\n        });\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        if (resultCode == RESULT_OK && requestCode == 1024 && data != null) {\n            String result = data.getStringExtra(\"result\");\n            Toast.makeText(getApplicationContext(), \"扫码结果：\" + result, Toast.LENGTH_SHORT).show();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#008577\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.constraint.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n    <Button\n        android:id=\"@+id/text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/scanner_test\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n</android.support.constraint.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#008577</color>\n    <color name=\"colorPrimaryDark\">#00574B</color>\n    <color name=\"colorAccent\">#D81B60</color>\n    <color name=\"white\">#ffffff</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">AiYaScanner</string>\n    <string name=\"scanner_test\">点击测试扫一扫!</string>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/nanchen/aiyascanner/ExampleUnitTest.java",
    "content": "package com.nanchen.aiyascanner;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    \n    repositories {\n        google()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.2.1'\n        \n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n        maven { url 'https://jitpack.io' }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.6-all.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\n\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "scanner/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "scanner/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 28\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 28\n        versionCode 6\n        versionName \"1.0.6\"\n        ndk {\n            abiFilters \"armeabi\"\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'com.android.support.constraint:constraint-layout:1.1.3'\n    implementation files('libs/zbar-1.0.0.jar')\n    implementation 'com.android.support:appcompat-v7:28.0.0'\n    // 把 google 的 core 库直接引入\n    //    api 'com.google.zxing:core:3.3.3'\n}\n"
  },
  {
    "path": "scanner/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "scanner/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.nanchen.scanner\">\n\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.VIBRATE\" />\n    <uses-permission android:name=\"android.permission.FLASHLIGHT\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\" />\n\n    <uses-feature android:name=\"android.hardware.camera.any\" />\n    <uses-feature\n        android:name=\"android.hardware.camera.autofocus\"\n        android:required=\"false\" />\n    <uses-feature\n        android:name=\"android.hardware.camera.flash\"\n        android:required=\"false\" />\n\n    <application>\n\n        <activity\n            android:name=\".module.CaptureActivity\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".utils.PermissionUtils$PermissionActivity\"\n            android:theme=\"@style/ActivityTranslucent\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/BarcodeFormat.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * Enumerates barcode formats known to this package. Please keep alphabetized.\n *\n * @author Sean Owen\n */\npublic enum BarcodeFormat {\n\n  /** Aztec 2D barcode format. */\n  AZTEC,\n\n  /** CODABAR 1D format. */\n  CODABAR,\n\n  /** Code 39 1D format. */\n  CODE_39,\n\n  /** Code 93 1D format. */\n  CODE_93,\n\n  /** Code 128 1D format. */\n  CODE_128,\n\n  /** Data Matrix 2D barcode format. */\n  DATA_MATRIX,\n\n  /** EAN-8 1D format. */\n  EAN_8,\n\n  /** EAN-13 1D format. */\n  EAN_13,\n\n  /** ITF (Interleaved Two of Five) 1D format. */\n  ITF,\n\n  /** MaxiCode 2D barcode format. */\n  MAXICODE,\n\n  /** PDF417 format. */\n  PDF_417,\n\n  /** QR Code 2D barcode format. */\n  QR_CODE,\n\n  /** RSS 14 */\n  RSS_14,\n\n  /** RSS EXPANDED */\n  RSS_EXPANDED,\n\n  /** UPC-A 1D format. */\n  UPC_A,\n\n  /** UPC-E 1D format. */\n  UPC_E,\n\n  /** UPC/EAN extension format. Not a stand-alone format. */\n  UPC_EAN_EXTENSION\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/Binarizer.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * This class hierarchy provides a set of methods to convert luminance data to 1 bit data.\n * It allows the algorithm to vary polymorphically, for example allowing a very expensive\n * thresholding technique for servers and a fast one for mobile. It also permits the implementation\n * to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic abstract class Binarizer {\n\n  private final LuminanceSource source;\n\n  protected Binarizer(LuminanceSource source) {\n    this.source = source;\n  }\n\n  public final LuminanceSource getLuminanceSource() {\n    return source;\n  }\n\n  /**\n   * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return\n   * cached data. Callers should assume this method is expensive and call it as seldom as possible.\n   * This method is intended for decoding 1D barcodes and may choose to apply sharpening.\n   * For callers which only examine one row of pixels at a time, the same BitArray should be reused\n   * and passed in with each call for performance. However it is legal to keep more than one row\n   * at a time if needed.\n   *\n   * @param y The row to fetch, which must be in [0, bitmap height)\n   * @param row An optional preallocated array. If null or too small, it will be ignored.\n   *            If used, the Binarizer will call BitArray.clear(). Always use the returned object.\n   * @return The array of bits for this row (true means black).\n   * @throws NotFoundException if row can't be binarized\n   */\n  public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException;\n\n  /**\n   * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive\n   * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or\n   * may not apply sharpening. Therefore, a row from this matrix may not be identical to one\n   * fetched using getBlackRow(), so don't mix and match between them.\n   *\n   * @return The 2D array of bits for the image (true means black).\n   * @throws NotFoundException if image can't be binarized to make a matrix\n   */\n  public abstract BitMatrix getBlackMatrix() throws NotFoundException;\n\n  /**\n   * Creates a new object with the same type as this Binarizer implementation, but with pristine\n   * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache\n   * of 1 bit data. See Effective Java for why we can't use Java's clone() method.\n   *\n   * @param source The LuminanceSource this Binarizer will operate on.\n   * @return A new concrete Binarizer implementation object.\n   */\n  public abstract Binarizer createBinarizer(LuminanceSource source);\n\n  public final int getWidth() {\n    return source.getWidth();\n  }\n\n  public final int getHeight() {\n    return source.getHeight();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/BinaryBitmap.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects\n * accept a BinaryBitmap and attempt to decode it.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class BinaryBitmap {\n\n  private final Binarizer binarizer;\n  private BitMatrix matrix;\n\n  public BinaryBitmap(Binarizer binarizer) {\n    if (binarizer == null) {\n      throw new IllegalArgumentException(\"Binarizer must be non-null.\");\n    }\n    this.binarizer = binarizer;\n  }\n\n  /**\n   * @return The width of the bitmap.\n   */\n  public int getWidth() {\n    return binarizer.getWidth();\n  }\n\n  /**\n   * @return The height of the bitmap.\n   */\n  public int getHeight() {\n    return binarizer.getHeight();\n  }\n\n  /**\n   * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return\n   * cached data. Callers should assume this method is expensive and call it as seldom as possible.\n   * This method is intended for decoding 1D barcodes and may choose to apply sharpening.\n   *\n   * @param y The row to fetch, which must be in [0, bitmap height)\n   * @param row An optional preallocated array. If null or too small, it will be ignored.\n   *            If used, the Binarizer will call BitArray.clear(). Always use the returned object.\n   * @return The array of bits for this row (true means black).\n   * @throws NotFoundException if row can't be binarized\n   */\n  public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {\n    return binarizer.getBlackRow(y, row);\n  }\n\n  /**\n   * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive\n   * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or\n   * may not apply sharpening. Therefore, a row from this matrix may not be identical to one\n   * fetched using getBlackRow(), so don't mix and match between them.\n   *\n   * @return The 2D array of bits for the image (true means black).\n   * @throws NotFoundException if image can't be binarized to make a matrix\n   */\n  public BitMatrix getBlackMatrix() throws NotFoundException {\n    // The matrix is created on demand the first time it is requested, then cached. There are two\n    // reasons for this:\n    // 1. This work will never be done if the caller only installs 1D Reader objects, or if a\n    //    1D Reader finds a barcode before the 2D Readers run.\n    // 2. This work will only be done once even if the caller installs multiple 2D Readers.\n    if (matrix == null) {\n      matrix = binarizer.getBlackMatrix();\n    }\n    return matrix;\n  }\n\n  /**\n   * @return Whether this bitmap can be cropped.\n   */\n  public boolean isCropSupported() {\n    return binarizer.getLuminanceSource().isCropSupported();\n  }\n\n  /**\n   * Returns a new object with cropped image data. Implementations may keep a reference to the\n   * original data rather than a copy. Only callable if isCropSupported() is true.\n   *\n   * @param left The left coordinate, which must be in [0,getWidth())\n   * @param top The top coordinate, which must be in [0,getHeight())\n   * @param width The width of the rectangle to crop.\n   * @param height The height of the rectangle to crop.\n   * @return A cropped version of this object.\n   */\n  public BinaryBitmap crop(int left, int top, int width, int height) {\n    LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height);\n    return new BinaryBitmap(binarizer.createBinarizer(newSource));\n  }\n\n  /**\n   * @return Whether this bitmap supports counter-clockwise rotation.\n   */\n  public boolean isRotateSupported() {\n    return binarizer.getLuminanceSource().isRotateSupported();\n  }\n\n  /**\n   * Returns a new object with rotated image data by 90 degrees counterclockwise.\n   * Only callable if {@link #isRotateSupported()} is true.\n   *\n   * @return A rotated version of this object.\n   */\n  public BinaryBitmap rotateCounterClockwise() {\n    LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise();\n    return new BinaryBitmap(binarizer.createBinarizer(newSource));\n  }\n\n  /**\n   * Returns a new object with rotated image data by 45 degrees counterclockwise.\n   * Only callable if {@link #isRotateSupported()} is true.\n   *\n   * @return A rotated version of this object.\n   */\n  public BinaryBitmap rotateCounterClockwise45() {\n    LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise45();\n    return new BinaryBitmap(binarizer.createBinarizer(newSource));\n  }\n\n  @Override\n  public String toString() {\n    try {\n      return getBlackMatrix().toString();\n    } catch (NotFoundException e) {\n      return \"\";\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/ChecksumException.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * Thrown when a barcode was successfully detected and decoded, but\n * was not returned because its checksum feature failed.\n *\n * @author Sean Owen\n */\npublic final class ChecksumException extends ReaderException {\n\n  private static final ChecksumException INSTANCE = new ChecksumException();\n  static {\n    INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless\n  }\n\n  private ChecksumException() {\n    // do nothing\n  }\n\n  private ChecksumException(Throwable cause) {\n    super(cause);\n  }\n\n  public static ChecksumException getChecksumInstance() {\n    return isStackTrace ? new ChecksumException() : INSTANCE;\n  }\n\n  public static ChecksumException getChecksumInstance(Throwable cause) {\n    return isStackTrace ? new ChecksumException(cause) : INSTANCE;\n  }\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/DecodeHintType.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport java.util.List;\n\n/**\n * Encapsulates a type of hint that a caller may pass to a barcode reader to help it\n * more quickly or accurately decode it. It is up to implementations to decide what,\n * if anything, to do with the information that is supplied.\n *\n * @author Sean Owen\n * @author dswitkin@google.com (Daniel Switkin)\n * @see Reader#decode(BinaryBitmap,java.util.Map)\n */\npublic enum DecodeHintType {\n\n  /**\n   * Unspecified, application-specific hint. Maps to an unspecified {@link Object}.\n   */\n  OTHER(Object.class),\n\n  /**\n   * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;\n   * use {@link Boolean#TRUE}.\n   */\n  PURE_BARCODE(Void.class),\n\n  /**\n   * Image is known to be of one of a few possible formats.\n   * Maps to a {@link List} of {@link BarcodeFormat}s.\n   */\n  POSSIBLE_FORMATS(List.class),\n\n  /**\n   * Spend more time to try to find a barcode; optimize for accuracy, not speed.\n   * Doesn't matter what it maps to; use {@link Boolean#TRUE}.\n   */\n  TRY_HARDER(Void.class),\n\n  /**\n   * Specifies what character encoding to use when decoding, where applicable (type String)\n   */\n  CHARACTER_SET(String.class),\n\n  /**\n   * Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.\n   */\n  ALLOWED_LENGTHS(int[].class),\n\n  /**\n   * Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;\n   * use {@link Boolean#TRUE}.\n   */\n  ASSUME_CODE_39_CHECK_DIGIT(Void.class),\n\n  /**\n   * Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.\n   * For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;\n   * use {@link Boolean#TRUE}.\n   */\n  ASSUME_GS1(Void.class),\n\n  /**\n   * If true, return the start and end digits in a Codabar barcode instead of stripping them. They\n   * are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them\n   * to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}.\n   */\n  RETURN_CODABAR_START_END(Void.class),\n\n  /**\n   * The caller needs to be notified via callback when a possible {@link ResultPoint}\n   * is found. Maps to a {@link ResultPointCallback}.\n   */\n  NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class),\n\n\n  /**\n   * Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.\n   * Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5].\n   * If it is optional to have an extension, do not set this hint. If this is set,\n   * and a UPC or EAN barcode is found but an extension is not, then no result will be returned\n   * at all.\n   */\n  ALLOWED_EAN_EXTENSIONS(int[].class),\n\n  // End of enumeration values.\n  ;\n\n  /**\n   * Data type the hint is expecting.\n   * Among the possible values the {@link Void} stands out as being used for\n   * hints that do not expect a value to be supplied (flag hints). Such hints\n   * will possibly have their value ignored, or replaced by a\n   * {@link Boolean#TRUE}. Hint suppliers should probably use\n   * {@link Boolean#TRUE} as directed by the actual hint documentation.\n   */\n  private final Class<?> valueType;\n\n  DecodeHintType(Class<?> valueType) {\n    this.valueType = valueType;\n  }\n\n  public Class<?> getValueType() {\n    return valueType;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/Dimension.java",
    "content": "/*\n * Copyright 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * Simply encapsulates a width and height.\n */\npublic final class Dimension {\n\n  private final int width;\n  private final int height;\n\n  public Dimension(int width, int height) {\n    if (width < 0 || height < 0) {\n      throw new IllegalArgumentException();\n    }\n    this.width = width;\n    this.height = height;\n  }\n\n  public int getWidth() {\n    return width;\n  }\n\n  public int getHeight() {\n    return height;\n  }\n\n  @Override\n  public boolean equals(Object other) {\n    if (other instanceof Dimension) {\n      Dimension d = (Dimension) other;\n      return width == d.width && height == d.height;\n    }\n    return false;\n  }\n\n  @Override\n  public int hashCode() {\n      return width * 32713 + height;\n  }\n\n  @Override\n  public String toString() {\n    return width + \"x\" + height;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/EncodeHintType.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * These are a set of hints that you may pass to Writers to specify their behavior.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic enum EncodeHintType {\n\n  /**\n   * Specifies what degree of error correction to use, for example in QR Codes.\n   * Type depends on the encoder. For example for QR codes it's type\n   * {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.\n   * For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.\n   * For PDF417 it is of type {@link Integer}, valid values being 0 to 8.\n   * In all cases, it can also be a {@link String} representation of the desired value as well.\n   * Note: an Aztec symbol should have a minimum of 25% EC words.\n   */\n  ERROR_CORRECTION,\n\n  /**\n   * Specifies what character encoding to use where applicable (type {@link String})\n   */\n  CHARACTER_SET,\n\n  /**\n   * Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})\n   */\n  DATA_MATRIX_SHAPE,\n\n  /**\n   * Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.\n   *\n   * @deprecated use width/height params in\n   * {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}\n   */\n  @Deprecated\n  MIN_SIZE,\n\n  /**\n   * Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.\n   *\n   * @deprecated without replacement\n   */\n  @Deprecated\n  MAX_SIZE,\n\n  /**\n   * Specifies margin, in pixels, to use when generating the barcode. The meaning can vary\n   * by format; for example it controls margin before and after the barcode horizontally for\n   * most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).\n   */\n  MARGIN,\n\n  /**\n   * Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or \"true\" or \"false\"\n   * {@link String} value).\n   */\n  PDF417_COMPACT,\n\n  /**\n   * Specifies what compaction mode to use for PDF417 (type\n   * {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its\n   * enum values).\n   */\n  PDF417_COMPACTION,\n\n  /**\n   * Specifies the minimum and maximum number of rows and columns for PDF417 (type\n   * {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).\n   */\n  PDF417_DIMENSIONS,\n\n  /**\n   * Specifies the required number of layers for an Aztec code.\n   * A negative number (-1, -2, -3, -4) specifies a compact Aztec code.\n   * 0 indicates to use the minimum number of layers (the default).\n   * A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.\n   * (Type {@link Integer}, or {@link String} representation of the integer value).\n   */\n   AZTEC_LAYERS,\n\n   /**\n    * Specifies the exact version of QR code to be encoded.\n    * (Type {@link Integer}, or {@link String} representation of the integer value).\n    */\n   QR_VERSION,\n\n  /**\n   * Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or \"true\" or \"false\"\n   * {@link String } value).\n   */\n  GS1_FORMAT,\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/FormatException.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * Thrown when a barcode was successfully detected, but some aspect of\n * the content did not conform to the barcode's format rules. This could have\n * been due to a mis-detection.\n *\n * @author Sean Owen\n */\npublic final class FormatException extends ReaderException {\n\n  private static final FormatException INSTANCE = new FormatException();\n  static {\n    INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless\n  }\n\n  private FormatException() {\n  }\n\n  private FormatException(Throwable cause) {\n    super(cause);\n  }\n\n  public static FormatException getFormatInstance() {\n    return isStackTrace ? new FormatException() : INSTANCE;\n  }\n\n  public static FormatException getFormatInstance(Throwable cause) {\n    return isStackTrace ? new FormatException(cause) : INSTANCE;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/InvertedLuminanceSource.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes\n * white and vice versa, and each value becomes (255-value).\n *\n * @author Sean Owen\n */\npublic final class InvertedLuminanceSource extends LuminanceSource {\n\n  private final LuminanceSource delegate;\n\n  public InvertedLuminanceSource(LuminanceSource delegate) {\n    super(delegate.getWidth(), delegate.getHeight());\n    this.delegate = delegate;\n  }\n\n  @Override\n  public byte[] getRow(int y, byte[] row) {\n    row = delegate.getRow(y, row);\n    int width = getWidth();\n    for (int i = 0; i < width; i++) {\n      row[i] = (byte) (255 - (row[i] & 0xFF));\n    }\n    return row;\n  }\n\n  @Override\n  public byte[] getMatrix() {\n    byte[] matrix = delegate.getMatrix();\n    int length = getWidth() * getHeight();\n    byte[] invertedMatrix = new byte[length];\n    for (int i = 0; i < length; i++) {\n      invertedMatrix[i] = (byte) (255 - (matrix[i] & 0xFF));\n    }\n    return invertedMatrix;\n  }\n\n  @Override\n  public boolean isCropSupported() {\n    return delegate.isCropSupported();\n  }\n\n  @Override\n  public LuminanceSource crop(int left, int top, int width, int height) {\n    return new InvertedLuminanceSource(delegate.crop(left, top, width, height));\n  }\n\n  @Override\n  public boolean isRotateSupported() {\n    return delegate.isRotateSupported();\n  }\n\n  /**\n   * @return original delegate {@link LuminanceSource} since invert undoes itself\n   */\n  @Override\n  public LuminanceSource invert() {\n    return delegate;\n  }\n\n  @Override\n  public LuminanceSource rotateCounterClockwise() {\n    return new InvertedLuminanceSource(delegate.rotateCounterClockwise());\n  }\n\n  @Override\n  public LuminanceSource rotateCounterClockwise45() {\n    return new InvertedLuminanceSource(delegate.rotateCounterClockwise45());\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/LuminanceSource.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * The purpose of this class hierarchy is to abstract different bitmap implementations across\n * platforms into a standard interface for requesting greyscale luminance values. The interface\n * only provides immutable methods; therefore crop and rotation create copies. This is to ensure\n * that one Reader does not modify the original luminance source and leave it in an unknown state\n * for other Readers in the chain.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic abstract class LuminanceSource {\n\n  private final int width;\n  private final int height;\n\n  protected LuminanceSource(int width, int height) {\n    this.width = width;\n    this.height = height;\n  }\n\n  /**\n   * Fetches one row of luminance data from the underlying platform's bitmap. Values range from\n   * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have\n   * to bitwise and with 0xff for each value. It is preferable for implementations of this method\n   * to only fetch this row rather than the whole image, since no 2D Readers may be installed and\n   * getMatrix() may never be called.\n   *\n   * @param y The row to fetch, which must be in [0,getHeight())\n   * @param row An optional preallocated array. If null or too small, it will be ignored.\n   *            Always use the returned object, and ignore the .length of the array.\n   * @return An array containing the luminance data.\n   */\n  public abstract byte[] getRow(int y, byte[] row);\n\n  /**\n   * Fetches luminance data for the underlying bitmap. Values should be fetched using:\n   * {@code int luminance = array[y * width + x] & 0xff}\n   *\n   * @return A row-major 2D array of luminance values. Do not use result.length as it may be\n   *         larger than width * height bytes on some platforms. Do not modify the contents\n   *         of the result.\n   */\n  public abstract byte[] getMatrix();\n\n  /**\n   * @return The width of the bitmap.\n   */\n  public final int getWidth() {\n    return width;\n  }\n\n  /**\n   * @return The height of the bitmap.\n   */\n  public final int getHeight() {\n    return height;\n  }\n\n  /**\n   * @return Whether this subclass supports cropping.\n   */\n  public boolean isCropSupported() {\n    return false;\n  }\n\n  /**\n   * Returns a new object with cropped image data. Implementations may keep a reference to the\n   * original data rather than a copy. Only callable if isCropSupported() is true.\n   *\n   * @param left The left coordinate, which must be in [0,getWidth())\n   * @param top The top coordinate, which must be in [0,getHeight())\n   * @param width The width of the rectangle to crop.\n   * @param height The height of the rectangle to crop.\n   * @return A cropped version of this object.\n   */\n  public LuminanceSource crop(int left, int top, int width, int height) {\n    throw new UnsupportedOperationException(\"This luminance source does not support cropping.\");\n  }\n\n  /**\n   * @return Whether this subclass supports counter-clockwise rotation.\n   */\n  public boolean isRotateSupported() {\n    return false;\n  }\n\n  /**\n   * @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes\n   *  white and vice versa, and each value becomes (255-value).\n   */\n  public LuminanceSource invert() {\n    return new InvertedLuminanceSource(this);\n  }\n\n  /**\n   * Returns a new object with rotated image data by 90 degrees counterclockwise.\n   * Only callable if {@link #isRotateSupported()} is true.\n   *\n   * @return A rotated version of this object.\n   */\n  public LuminanceSource rotateCounterClockwise() {\n    throw new UnsupportedOperationException(\"This luminance source does not support rotation by 90 degrees.\");\n  }\n\n  /**\n   * Returns a new object with rotated image data by 45 degrees counterclockwise.\n   * Only callable if {@link #isRotateSupported()} is true.\n   *\n   * @return A rotated version of this object.\n   */\n  public LuminanceSource rotateCounterClockwise45() {\n    throw new UnsupportedOperationException(\"This luminance source does not support rotation by 45 degrees.\");\n  }\n\n  @Override\n  public final String toString() {\n    byte[] row = new byte[width];\n    StringBuilder result = new StringBuilder(height * (width + 1));\n    for (int y = 0; y < height; y++) {\n      row = getRow(y, row);\n      for (int x = 0; x < width; x++) {\n        int luminance = row[x] & 0xFF;\n        char c;\n        if (luminance < 0x40) {\n          c = '#';\n        } else if (luminance < 0x80) {\n          c = '+';\n        } else if (luminance < 0xC0) {\n          c = '.';\n        } else {\n          c = ' ';\n        }\n        result.append(c);\n      }\n      result.append('\\n');\n    }\n    return result.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/MultiFormatReader.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport com.google.zxing.aztec.AztecReader;\nimport com.google.zxing.datamatrix.DataMatrixReader;\nimport com.google.zxing.maxicode.MaxiCodeReader;\nimport com.google.zxing.oned.MultiFormatOneDReader;\nimport com.google.zxing.pdf417.PDF417Reader;\nimport com.google.zxing.qrcode.QRCodeReader;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * MultiFormatReader is a convenience class and the main entry point into the library for most uses.\n * By default it attempts to decode all barcode formats that the library supports. Optionally, you\n * can provide a hints object to request different behavior, for example only decoding QR codes.\n *\n * @author Sean Owen\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class MultiFormatReader implements Reader {\n\n  private static final Reader[] EMPTY_READER_ARRAY = new Reader[0];\n\n  private Map<DecodeHintType,?> hints;\n  private Reader[] readers;\n\n  /**\n   * This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it\n   * passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly.\n   * Use setHints() followed by decodeWithState() for continuous scan applications.\n   *\n   * @param image The pixel data to decode\n   * @return The contents of the image\n   * @throws NotFoundException Any errors which occurred\n   */\n  @Override\n  public Result decode(BinaryBitmap image) throws NotFoundException {\n    setHints(null);\n    return decodeInternal(image);\n  }\n\n  /**\n   * Decode an image using the hints provided. Does not honor existing state.\n   *\n   * @param image The pixel data to decode\n   * @param hints The hints to use, clearing the previous state.\n   * @return The contents of the image\n   * @throws NotFoundException Any errors which occurred\n   */\n  @Override\n  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {\n    setHints(hints);\n    return decodeInternal(image);\n  }\n\n  /**\n   * Decode an image using the state set up by calling setHints() previously. Continuous scan\n   * clients will get a <b>large</b> speed increase by using this instead of decode().\n   *\n   * @param image The pixel data to decode\n   * @return The contents of the image\n   * @throws NotFoundException Any errors which occurred\n   */\n  public Result decodeWithState(BinaryBitmap image) throws NotFoundException {\n    // Make sure to set up the default state so we don't crash\n    if (readers == null) {\n      setHints(null);\n    }\n    return decodeInternal(image);\n  }\n\n  /**\n   * This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls\n   * to decodeWithState(image) can reuse the same set of readers without reallocating memory. This\n   * is important for performance in continuous scan clients.\n   *\n   * @param hints The set of hints to use for subsequent calls to decode(image)\n   */\n  public void setHints(Map<DecodeHintType,?> hints) {\n    this.hints = hints;\n\n    boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);\n    @SuppressWarnings(\"unchecked\")\n    Collection<BarcodeFormat> formats =\n        hints == null ? null : (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);\n    Collection<Reader> readers = new ArrayList<>();\n    if (formats != null) {\n      boolean addOneDReader =\n          formats.contains(BarcodeFormat.UPC_A) ||\n          formats.contains(BarcodeFormat.UPC_E) ||\n          formats.contains(BarcodeFormat.EAN_13) ||\n          formats.contains(BarcodeFormat.EAN_8) ||\n          formats.contains(BarcodeFormat.CODABAR) ||\n          formats.contains(BarcodeFormat.CODE_39) ||\n          formats.contains(BarcodeFormat.CODE_93) ||\n          formats.contains(BarcodeFormat.CODE_128) ||\n          formats.contains(BarcodeFormat.ITF) ||\n          formats.contains(BarcodeFormat.RSS_14) ||\n          formats.contains(BarcodeFormat.RSS_EXPANDED);\n      // Put 1D readers upfront in \"normal\" mode\n      if (addOneDReader && !tryHarder) {\n        readers.add(new MultiFormatOneDReader(hints));\n      }\n      if (formats.contains(BarcodeFormat.QR_CODE)) {\n        readers.add(new QRCodeReader());\n      }\n      if (formats.contains(BarcodeFormat.DATA_MATRIX)) {\n        readers.add(new DataMatrixReader());\n      }\n      if (formats.contains(BarcodeFormat.AZTEC)) {\n        readers.add(new AztecReader());\n      }\n      if (formats.contains(BarcodeFormat.PDF_417)) {\n         readers.add(new PDF417Reader());\n      }\n      if (formats.contains(BarcodeFormat.MAXICODE)) {\n         readers.add(new MaxiCodeReader());\n      }\n      // At end in \"try harder\" mode\n      if (addOneDReader && tryHarder) {\n        readers.add(new MultiFormatOneDReader(hints));\n      }\n    }\n    if (readers.isEmpty()) {\n      if (!tryHarder) {\n        readers.add(new MultiFormatOneDReader(hints));\n      }\n\n      readers.add(new QRCodeReader());\n      readers.add(new DataMatrixReader());\n      readers.add(new AztecReader());\n      readers.add(new PDF417Reader());\n      readers.add(new MaxiCodeReader());\n\n      if (tryHarder) {\n        readers.add(new MultiFormatOneDReader(hints));\n      }\n    }\n    this.readers = readers.toArray(EMPTY_READER_ARRAY);\n  }\n\n  @Override\n  public void reset() {\n    if (readers != null) {\n      for (Reader reader : readers) {\n        reader.reset();\n      }\n    }\n  }\n\n  private Result decodeInternal(BinaryBitmap image) throws NotFoundException {\n    if (readers != null) {\n      for (Reader reader : readers) {\n        try {\n          return reader.decode(image, hints);\n        } catch (ReaderException re) {\n          // continue\n        }\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/MultiFormatWriter.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport com.google.zxing.aztec.AztecWriter;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.datamatrix.DataMatrixWriter;\nimport com.google.zxing.oned.CodaBarWriter;\nimport com.google.zxing.oned.Code128Writer;\nimport com.google.zxing.oned.Code39Writer;\nimport com.google.zxing.oned.Code93Writer;\nimport com.google.zxing.oned.EAN13Writer;\nimport com.google.zxing.oned.EAN8Writer;\nimport com.google.zxing.oned.ITFWriter;\nimport com.google.zxing.oned.UPCAWriter;\nimport com.google.zxing.oned.UPCEWriter;\nimport com.google.zxing.pdf417.PDF417Writer;\nimport com.google.zxing.qrcode.QRCodeWriter;\n\nimport java.util.Map;\n\n/**\n * This is a factory class which finds the appropriate Writer subclass for the BarcodeFormat\n * requested and encodes the barcode with the supplied contents.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class MultiFormatWriter implements Writer {\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height) throws WriterException {\n    return encode(contents, format, width, height, null);\n  }\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width, int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n\n    Writer writer;\n    switch (format) {\n      case EAN_8:\n        writer = new EAN8Writer();\n        break;\n      case UPC_E:\n        writer = new UPCEWriter();\n        break;\n      case EAN_13:\n        writer = new EAN13Writer();\n        break;\n      case UPC_A:\n        writer = new UPCAWriter();\n        break;\n      case QR_CODE:\n        writer = new QRCodeWriter();\n        break;\n      case CODE_39:\n        writer = new Code39Writer();\n        break;\n      case CODE_93:\n        writer = new Code93Writer();\n        break;\n      case CODE_128:\n        writer = new Code128Writer();\n        break;\n      case ITF:\n        writer = new ITFWriter();\n        break;\n      case PDF_417:\n        writer = new PDF417Writer();\n        break;\n      case CODABAR:\n        writer = new CodaBarWriter();\n        break;\n      case DATA_MATRIX:\n        writer = new DataMatrixWriter();\n        break;\n      case AZTEC:\n        writer = new AztecWriter();\n        break;\n      default:\n        throw new IllegalArgumentException(\"No encoder available for format \" + format);\n    }\n    return writer.encode(contents, format, width, height, hints);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/NotFoundException.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * Thrown when a barcode was not found in the image. It might have been\n * partially detected but could not be confirmed.\n *\n * @author Sean Owen\n */\npublic final class NotFoundException extends ReaderException {\n\n  private static final NotFoundException INSTANCE = new NotFoundException();\n  static {\n    INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless\n  }\n\n  private NotFoundException() {\n    // do nothing\n  }\n\n  public static NotFoundException getNotFoundInstance() {\n    return INSTANCE;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/PlanarYUVLuminanceSource.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * This object extends LuminanceSource around an array of YUV data returned from the camera driver,\n * with the option to crop to a rectangle within the full data. This can be used to exclude\n * superfluous pixels around the perimeter and speed up decoding.\n *\n * It works for any pixel format where the Y channel is planar and appears first, including\n * YCbCr_420_SP and YCbCr_422_SP.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class PlanarYUVLuminanceSource extends LuminanceSource {\n\n  private static final int THUMBNAIL_SCALE_FACTOR = 2;\n\n  private final byte[] yuvData;\n  private final int dataWidth;\n  private final int dataHeight;\n  private final int left;\n  private final int top;\n\n  public PlanarYUVLuminanceSource(byte[] yuvData,\n                                  int dataWidth,\n                                  int dataHeight,\n                                  int left,\n                                  int top,\n                                  int width,\n                                  int height,\n                                  boolean reverseHorizontal) {\n    super(width, height);\n\n    if (left + width > dataWidth || top + height > dataHeight) {\n      throw new IllegalArgumentException(\"Crop rectangle does not fit within image data.\");\n    }\n\n    this.yuvData = yuvData;\n    this.dataWidth = dataWidth;\n    this.dataHeight = dataHeight;\n    this.left = left;\n    this.top = top;\n    if (reverseHorizontal) {\n      reverseHorizontal(width, height);\n    }\n  }\n\n  @Override\n  public byte[] getRow(int y, byte[] row) {\n    if (y < 0 || y >= getHeight()) {\n      throw new IllegalArgumentException(\"Requested row is outside the image: \" + y);\n    }\n    int width = getWidth();\n    if (row == null || row.length < width) {\n      row = new byte[width];\n    }\n    int offset = (y + top) * dataWidth + left;\n    System.arraycopy(yuvData, offset, row, 0, width);\n    return row;\n  }\n\n  @Override\n  public byte[] getMatrix() {\n    int width = getWidth();\n    int height = getHeight();\n\n    // If the caller asks for the entire underlying image, save the copy and give them the\n    // original data. The docs specifically warn that result.length must be ignored.\n    if (width == dataWidth && height == dataHeight) {\n      return yuvData;\n    }\n\n    int area = width * height;\n    byte[] matrix = new byte[area];\n    int inputOffset = top * dataWidth + left;\n\n    // If the width matches the full width of the underlying data, perform a single copy.\n    if (width == dataWidth) {\n      System.arraycopy(yuvData, inputOffset, matrix, 0, area);\n      return matrix;\n    }\n\n    // Otherwise copy one cropped row at a time.\n    for (int y = 0; y < height; y++) {\n      int outputOffset = y * width;\n      System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width);\n      inputOffset += dataWidth;\n    }\n    return matrix;\n  }\n\n  @Override\n  public boolean isCropSupported() {\n    return true;\n  }\n\n  @Override\n  public LuminanceSource crop(int left, int top, int width, int height) {\n    return new PlanarYUVLuminanceSource(yuvData,\n                                        dataWidth,\n                                        dataHeight,\n                                        this.left + left,\n                                        this.top + top,\n                                        width,\n                                        height,\n                                        false);\n  }\n\n  public int[] renderThumbnail() {\n    int width = getWidth() / THUMBNAIL_SCALE_FACTOR;\n    int height = getHeight() / THUMBNAIL_SCALE_FACTOR;\n    int[] pixels = new int[width * height];\n    byte[] yuv = yuvData;\n    int inputOffset = top * dataWidth + left;\n\n    for (int y = 0; y < height; y++) {\n      int outputOffset = y * width;\n      for (int x = 0; x < width; x++) {\n        int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;\n        pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);\n      }\n      inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;\n    }\n    return pixels;\n  }\n\n  /**\n   * @return width of image from {@link #renderThumbnail()}\n   */\n  public int getThumbnailWidth() {\n    return getWidth() / THUMBNAIL_SCALE_FACTOR;\n  }\n\n  /**\n   * @return height of image from {@link #renderThumbnail()}\n   */\n  public int getThumbnailHeight() {\n    return getHeight() / THUMBNAIL_SCALE_FACTOR;\n  }\n\n  private void reverseHorizontal(int width, int height) {\n    byte[] yuvData = this.yuvData;\n    for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {\n      int middle = rowStart + width / 2;\n      for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {\n        byte temp = yuvData[x1];\n        yuvData[x1] = yuvData[x2];\n        yuvData[x2] = temp;\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/RGBLuminanceSource.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * This class is used to help decode images from files which arrive as RGB data from\n * an ARGB pixel array. It does not support rotation.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Betaminos\n */\npublic final class RGBLuminanceSource extends LuminanceSource {\n\n  private final byte[] luminances;\n  private final int dataWidth;\n  private final int dataHeight;\n  private final int left;\n  private final int top;\n\n  public RGBLuminanceSource(int width, int height, int[] pixels) {\n    super(width, height);\n\n    dataWidth = width;\n    dataHeight = height;\n    left = 0;\n    top = 0;\n\n    // In order to measure pure decoding speed, we convert the entire image to a greyscale array\n    // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.\n    //\n    // Total number of pixels suffices, can ignore shape\n    int size = width * height;\n    luminances = new byte[size];\n    for (int offset = 0; offset < size; offset++) {\n      int pixel = pixels[offset];\n      int r = (pixel >> 16) & 0xff; // red\n      int g2 = (pixel >> 7) & 0x1fe; // 2 * green\n      int b = pixel & 0xff; // blue\n      // Calculate green-favouring average cheaply\n      luminances[offset] = (byte) ((r + g2 + b) / 4);\n    }\n  }\n\n  private RGBLuminanceSource(byte[] pixels,\n                             int dataWidth,\n                             int dataHeight,\n                             int left,\n                             int top,\n                             int width,\n                             int height) {\n    super(width, height);\n    if (left + width > dataWidth || top + height > dataHeight) {\n      throw new IllegalArgumentException(\"Crop rectangle does not fit within image data.\");\n    }\n    this.luminances = pixels;\n    this.dataWidth = dataWidth;\n    this.dataHeight = dataHeight;\n    this.left = left;\n    this.top = top;\n  }\n\n  @Override\n  public byte[] getRow(int y, byte[] row) {\n    if (y < 0 || y >= getHeight()) {\n      throw new IllegalArgumentException(\"Requested row is outside the image: \" + y);\n    }\n    int width = getWidth();\n    if (row == null || row.length < width) {\n      row = new byte[width];\n    }\n    int offset = (y + top) * dataWidth + left;\n    System.arraycopy(luminances, offset, row, 0, width);\n    return row;\n  }\n\n  @Override\n  public byte[] getMatrix() {\n    int width = getWidth();\n    int height = getHeight();\n\n    // If the caller asks for the entire underlying image, save the copy and give them the\n    // original data. The docs specifically warn that result.length must be ignored.\n    if (width == dataWidth && height == dataHeight) {\n      return luminances;\n    }\n\n    int area = width * height;\n    byte[] matrix = new byte[area];\n    int inputOffset = top * dataWidth + left;\n\n    // If the width matches the full width of the underlying data, perform a single copy.\n    if (width == dataWidth) {\n      System.arraycopy(luminances, inputOffset, matrix, 0, area);\n      return matrix;\n    }\n\n    // Otherwise copy one cropped row at a time.\n    for (int y = 0; y < height; y++) {\n      int outputOffset = y * width;\n      System.arraycopy(luminances, inputOffset, matrix, outputOffset, width);\n      inputOffset += dataWidth;\n    }\n    return matrix;\n  }\n\n  @Override\n  public boolean isCropSupported() {\n    return true;\n  }\n\n  @Override\n  public LuminanceSource crop(int left, int top, int width, int height) {\n    return new RGBLuminanceSource(luminances,\n                                  dataWidth,\n                                  dataHeight,\n                                  this.left + left,\n                                  this.top + top,\n                                  width,\n                                  height);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/Reader.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport java.util.Map;\n\n/**\n * Implementations of this interface can decode an image of a barcode in some format into\n * the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can\n * decode a QR code. The decoder may optionally receive hints from the caller which may help\n * it decode more quickly or accurately.\n *\n * See {@link MultiFormatReader}, which attempts to determine what barcode\n * format is present within the image as well, and then decodes it accordingly.\n *\n * @author Sean Owen\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic interface Reader {\n\n  /**\n   * Locates and decodes a barcode in some format within an image.\n   *\n   * @param image image of barcode to decode\n   * @return String which the barcode encodes\n   * @throws NotFoundException if no potential barcode is found\n   * @throws ChecksumException if a potential barcode is found but does not pass its checksum\n   * @throws FormatException if a potential barcode is found but format is invalid\n   */\n  Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException;\n\n  /**\n   * Locates and decodes a barcode in some format within an image. This method also accepts\n   * hints, each possibly associated to some data, which may help the implementation decode.\n   *\n   * @param image image of barcode to decode\n   * @param hints passed as a {@link Map} from {@link DecodeHintType}\n   * to arbitrary data. The\n   * meaning of the data depends upon the hint type. The implementation may or may not do\n   * anything with these hints.\n   * @return String which the barcode encodes\n   * @throws NotFoundException if no potential barcode is found\n   * @throws ChecksumException if a potential barcode is found but does not pass its checksum\n   * @throws FormatException if a potential barcode is found but format is invalid\n   */\n  Result decode(BinaryBitmap image, Map<DecodeHintType, ?> hints)\n      throws NotFoundException, ChecksumException, FormatException;\n\n  /**\n   * Resets any internal state the implementation has after a decode, to prepare it\n   * for reuse.\n   */\n  void reset();\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/ReaderException.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * The general exception class throw when something goes wrong during decoding of a barcode.\n * This includes, but is not limited to, failing checksums / error correction algorithms, being\n * unable to locate finder timing patterns, and so on.\n *\n * @author Sean Owen\n */\npublic abstract class ReaderException extends Exception {\n\n  // disable stack traces when not running inside test units\n  protected static final boolean isStackTrace =\n      System.getProperty(\"surefire.test.class.path\") != null;\n  protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0];\n\n  ReaderException() {\n    // do nothing\n  }\n\n  ReaderException(Throwable cause) {\n    super(cause);\n  }\n\n  // Prevent stack traces from being taken\n  @Override\n  public final synchronized Throwable fillInStackTrace() {\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/Result.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport java.util.EnumMap;\nimport java.util.Map;\n\n/**\n * <p>Encapsulates the result of decoding a barcode within an image.</p>\n *\n * @author Sean Owen\n */\npublic final class Result {\n\n  private final String text;\n  private final byte[] rawBytes;\n  private final int numBits;\n  private ResultPoint[] resultPoints;\n  private final BarcodeFormat format;\n  private Map<ResultMetadataType,Object> resultMetadata;\n  private final long timestamp;\n\n  public Result(String text,\n                byte[] rawBytes,\n                ResultPoint[] resultPoints,\n                BarcodeFormat format) {\n    this(text, rawBytes, resultPoints, format, System.currentTimeMillis());\n  }\n\n  public Result(String text,\n                byte[] rawBytes,\n                ResultPoint[] resultPoints,\n                BarcodeFormat format,\n                long timestamp) {\n    this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.length,\n         resultPoints, format, timestamp);\n  }\n\n  public Result(String text,\n                byte[] rawBytes,\n                int numBits,\n                ResultPoint[] resultPoints,\n                BarcodeFormat format,\n                long timestamp) {\n    this.text = text;\n    this.rawBytes = rawBytes;\n    this.numBits = numBits;\n    this.resultPoints = resultPoints;\n    this.format = format;\n    this.resultMetadata = null;\n    this.timestamp = timestamp;\n  }\n\n  /**\n   * @return raw text encoded by the barcode\n   */\n  public String getText() {\n    return text;\n  }\n\n  /**\n   * @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}\n   */\n  public byte[] getRawBytes() {\n    return rawBytes;\n  }\n\n  /**\n   * @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length\n   * @since 3.3.0\n   */\n  public int getNumBits() {\n    return numBits;\n  }\n\n  /**\n   * @return points related to the barcode in the image. These are typically points\n   *         identifying finder patterns or the corners of the barcode. The exact meaning is\n   *         specific to the type of barcode that was decoded.\n   */\n  public ResultPoint[] getResultPoints() {\n    return resultPoints;\n  }\n\n  /**\n   * @return {@link BarcodeFormat} representing the format of the barcode that was decoded\n   */\n  public BarcodeFormat getBarcodeFormat() {\n    return format;\n  }\n\n  /**\n   * @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be\n   *   {@code null}. This contains optional metadata about what was detected about the barcode,\n   *   like orientation.\n   */\n  public Map<ResultMetadataType,Object> getResultMetadata() {\n    return resultMetadata;\n  }\n\n  public void putMetadata(ResultMetadataType type, Object value) {\n    if (resultMetadata == null) {\n      resultMetadata = new EnumMap<>(ResultMetadataType.class);\n    }\n    resultMetadata.put(type, value);\n  }\n\n  public void putAllMetadata(Map<ResultMetadataType,Object> metadata) {\n    if (metadata != null) {\n      if (resultMetadata == null) {\n        resultMetadata = metadata;\n      } else {\n        resultMetadata.putAll(metadata);\n      }\n    }\n  }\n\n  public void addResultPoints(ResultPoint[] newPoints) {\n    ResultPoint[] oldPoints = resultPoints;\n    if (oldPoints == null) {\n      resultPoints = newPoints;\n    } else if (newPoints != null && newPoints.length > 0) {\n      ResultPoint[] allPoints = new ResultPoint[oldPoints.length + newPoints.length];\n      System.arraycopy(oldPoints, 0, allPoints, 0, oldPoints.length);\n      System.arraycopy(newPoints, 0, allPoints, oldPoints.length, newPoints.length);\n      resultPoints = allPoints;\n    }\n  }\n\n  public long getTimestamp() {\n    return timestamp;\n  }\n\n  @Override\n  public String toString() {\n    return text;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/ResultMetadataType.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * Represents some type of metadata about the result of the decoding that the decoder\n * wishes to communicate back to the caller.\n *\n * @author Sean Owen\n */\npublic enum ResultMetadataType {\n\n  /**\n   * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.\n   */\n  OTHER,\n\n  /**\n   * Denotes the likely approximate orientation of the barcode in the image. This value\n   * is given as degrees rotated clockwise from the normal, upright orientation.\n   * For example a 1D barcode which was found by reading top-to-bottom would be\n   * said to have orientation \"90\". This key maps to an {@link Integer} whose\n   * value is in the range [0,360).\n   */\n  ORIENTATION,\n\n  /**\n   * <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'\n   * which is sometimes used to encode binary data. While {@link Result} makes available\n   * the complete raw bytes in the barcode for these formats, it does not offer the bytes\n   * from the byte segments alone.</p>\n   *\n   * <p>This maps to a {@link java.util.List} of byte arrays corresponding to the\n   * raw bytes in the byte segments in the barcode, in order.</p>\n   */\n  BYTE_SEGMENTS,\n\n  /**\n   * Error correction level used, if applicable. The value type depends on the\n   * format, but is typically a String.\n   */\n  ERROR_CORRECTION_LEVEL,\n\n  /**\n   * For some periodicals, indicates the issue number as an {@link Integer}.\n   */\n  ISSUE_NUMBER,\n\n  /**\n   * For some products, indicates the suggested retail price in the barcode as a\n   * formatted {@link String}.\n   */\n  SUGGESTED_PRICE,\n\n  /**\n   * For some products, the possible country of manufacture as a {@link String} denoting the\n   * ISO country code. Some map to multiple possible countries, like \"US/CA\".\n   */\n  POSSIBLE_COUNTRY,\n\n  /**\n   * For some products, the extension text\n   */\n  UPC_EAN_EXTENSION,\n\n  /**\n   * PDF417-specific metadata\n   */\n  PDF417_EXTRA_METADATA,\n\n  /**\n   * If the code format supports structured append and the current scanned code is part of one then the\n   * sequence number is given with it.\n   */\n  STRUCTURED_APPEND_SEQUENCE,\n\n  /**\n   * If the code format supports structured append and the current scanned code is part of one then the\n   * parity is given with it.\n   */\n  STRUCTURED_APPEND_PARITY,\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/ResultPoint.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport com.google.zxing.common.detector.MathUtils;\n\n/**\n * <p>Encapsulates a point of interest in an image containing a barcode. Typically, this\n * would be the location of a finder pattern or the corner of the barcode, for example.</p>\n *\n * @author Sean Owen\n */\npublic class ResultPoint {\n\n  private final float x;\n  private final float y;\n\n  public ResultPoint(float x, float y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  public final float getX() {\n    return x;\n  }\n\n  public final float getY() {\n    return y;\n  }\n\n  @Override\n  public final boolean equals(Object other) {\n    if (other instanceof ResultPoint) {\n      ResultPoint otherPoint = (ResultPoint) other;\n      return x == otherPoint.x && y == otherPoint.y;\n    }\n    return false;\n  }\n\n  @Override\n  public final int hashCode() {\n    return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);\n  }\n\n  @Override\n  public final String toString() {\n    return \"(\" + x + ',' + y + ')';\n  }\n\n  /**\n   * Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC\n   * and BC is less than AC, and the angle between BC and BA is less than 180 degrees.\n   *\n   * @param patterns array of three {@code ResultPoint} to order\n   */\n  public static void orderBestPatterns(ResultPoint[] patterns) {\n\n    // Find distances between pattern centers\n    float zeroOneDistance = distance(patterns[0], patterns[1]);\n    float oneTwoDistance = distance(patterns[1], patterns[2]);\n    float zeroTwoDistance = distance(patterns[0], patterns[2]);\n\n    ResultPoint pointA;\n    ResultPoint pointB;\n    ResultPoint pointC;\n    // Assume one closest to other two is B; A and C will just be guesses at first\n    if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {\n      pointB = patterns[0];\n      pointA = patterns[1];\n      pointC = patterns[2];\n    } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {\n      pointB = patterns[1];\n      pointA = patterns[0];\n      pointC = patterns[2];\n    } else {\n      pointB = patterns[2];\n      pointA = patterns[0];\n      pointC = patterns[1];\n    }\n\n    // Use cross product to figure out whether A and C are correct or flipped.\n    // This asks whether BC x BA has a positive z component, which is the arrangement\n    // we want for A, B, C. If it's negative, then we've got it flipped around and\n    // should swap A and C.\n    if (crossProductZ(pointA, pointB, pointC) < 0.0f) {\n      ResultPoint temp = pointA;\n      pointA = pointC;\n      pointC = temp;\n    }\n\n    patterns[0] = pointA;\n    patterns[1] = pointB;\n    patterns[2] = pointC;\n  }\n\n  /**\n   * @param pattern1 first pattern\n   * @param pattern2 second pattern\n   * @return distance between two points\n   */\n  public static float distance(ResultPoint pattern1, ResultPoint pattern2) {\n    return MathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y);\n  }\n\n  /**\n   * Returns the z component of the cross product between vectors BC and BA.\n   */\n  private static float crossProductZ(ResultPoint pointA,\n                                     ResultPoint pointB,\n                                     ResultPoint pointC) {\n    float bX = pointB.x;\n    float bY = pointB.y;\n    return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/ResultPointCallback.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * Callback which is invoked when a possible result point (significant\n * point in the barcode image such as a corner) is found.\n *\n * @see DecodeHintType#NEED_RESULT_POINT_CALLBACK\n */\npublic interface ResultPointCallback {\n\n  void foundPossibleResultPoint(ResultPoint point);\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/Writer.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\n\n/**\n * The base class for all objects which encode/generate a barcode image.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic interface Writer {\n\n  /**\n   * Encode a barcode using the default settings.\n   *\n   * @param contents The contents to encode in the barcode\n   * @param format The barcode format to generate\n   * @param width The preferred width in pixels\n   * @param height The preferred height in pixels\n   * @return {@link BitMatrix} representing encoded barcode image\n   * @throws WriterException if contents cannot be encoded legally in a format\n   */\n  BitMatrix encode(String contents, BarcodeFormat format, int width, int height)\n      throws WriterException;\n\n  /**\n   * @param contents The contents to encode in the barcode\n   * @param format The barcode format to generate\n   * @param width The preferred width in pixels\n   * @param height The preferred height in pixels\n   * @param hints Additional parameters to supply to the encoder\n   * @return {@link BitMatrix} representing encoded barcode image\n   * @throws WriterException if contents cannot be encoded legally in a format\n   */\n  BitMatrix encode(String contents,\n                   BarcodeFormat format,\n                   int width,\n                   int height,\n                   Map<EncodeHintType, ?> hints)\n      throws WriterException;\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/WriterException.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing;\n\n/**\n * A base class which covers the range of exceptions which may occur when encoding a barcode using\n * the Writer framework.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class WriterException extends Exception {\n\n  public WriterException() {\n  }\n\n  public WriterException(String message) {\n    super(message);\n  }\n \n  public WriterException(Throwable cause) {\n    super(cause);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/AztecDetectorResult.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec;\n\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DetectorResult;\n\n/**\n * <p>Extends {@link DetectorResult} with more information specific to the Aztec format,\n * like the number of layers and whether it's compact.</p>\n *\n * @author Sean Owen\n */\npublic final class AztecDetectorResult extends DetectorResult {\n\n  private final boolean compact;\n  private final int nbDatablocks;\n  private final int nbLayers;\n\n  public AztecDetectorResult(BitMatrix bits,\n                             ResultPoint[] points,\n                             boolean compact,\n                             int nbDatablocks,\n                             int nbLayers) {\n    super(bits, points);\n    this.compact = compact;\n    this.nbDatablocks = nbDatablocks;\n    this.nbLayers = nbLayers;\n  }\n\n  public int getNbLayers() {\n    return nbLayers;\n  }\n\n  public int getNbDatablocks() {\n    return nbDatablocks;\n  }\n\n  public boolean isCompact() {\n    return compact;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/AztecReader.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.aztec.decoder.Decoder;\nimport com.google.zxing.aztec.detector.Detector;\nimport com.google.zxing.common.DecoderResult;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * This implementation can detect and decode Aztec codes in an image.\n *\n * @author David Olivier\n */\npublic final class AztecReader implements Reader {\n\n  /**\n   * Locates and decodes a Data Matrix code in an image.\n   *\n   * @return a String representing the content encoded by the Data Matrix code\n   * @throws NotFoundException if a Data Matrix code cannot be found\n   * @throws FormatException if a Data Matrix code cannot be decoded\n   */\n  @Override\n  public Result decode(BinaryBitmap image) throws NotFoundException, FormatException {\n    return decode(image, null);\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)\n      throws NotFoundException, FormatException {\n\n    NotFoundException notFoundException = null;\n    FormatException formatException = null;\n    Detector detector = new Detector(image.getBlackMatrix());\n    ResultPoint[] points = null;\n    DecoderResult decoderResult = null;\n    try {\n      AztecDetectorResult detectorResult = detector.detect(false);\n      points = detectorResult.getPoints();\n      decoderResult = new Decoder().decode(detectorResult);\n    } catch (NotFoundException e) {\n      notFoundException = e;\n    } catch (FormatException e) {\n      formatException = e;\n    }\n    if (decoderResult == null) {\n      try {\n        AztecDetectorResult detectorResult = detector.detect(true);\n        points = detectorResult.getPoints();\n        decoderResult = new Decoder().decode(detectorResult);\n      } catch (NotFoundException | FormatException e) {\n        if (notFoundException != null) {\n          throw notFoundException;\n        }\n        if (formatException != null) {\n          throw formatException;\n        }\n        throw e;\n      }\n    }\n\n    if (hints != null) {\n      ResultPointCallback rpcb = (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);\n      if (rpcb != null) {\n        for (ResultPoint point : points) {\n          rpcb.foundPossibleResultPoint(point);\n        }\n      }\n    }\n\n    Result result = new Result(decoderResult.getText(),\n                               decoderResult.getRawBytes(),\n                               decoderResult.getNumBits(),\n                               points,\n                               BarcodeFormat.AZTEC,\n                               System.currentTimeMillis());\n\n    List<byte[]> byteSegments = decoderResult.getByteSegments();\n    if (byteSegments != null) {\n      result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);\n    }\n    String ecLevel = decoderResult.getECLevel();\n    if (ecLevel != null) {\n      result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);\n    }\n\n    return result;\n  }\n\n  @Override\n  public void reset() {\n    // do nothing\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/AztecWriter.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.Writer;\nimport com.google.zxing.aztec.encoder.AztecCode;\nimport com.google.zxing.aztec.encoder.Encoder;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\n\n/**\n * Renders an Aztec code as a {@link BitMatrix}.\n */\npublic final class AztecWriter implements Writer {\n\n  @Override\n  public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {\n    return encode(contents, format, width, height, null);\n  }\n\n  @Override\n  public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) {\n    Charset charset = StandardCharsets.ISO_8859_1;\n    int eccPercent = Encoder.DEFAULT_EC_PERCENT;\n    int layers = Encoder.DEFAULT_AZTEC_LAYERS;\n    if (hints != null) {\n      if (hints.containsKey(EncodeHintType.CHARACTER_SET)) {\n        charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());\n      }\n      if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {\n        eccPercent = Integer.parseInt(hints.get(EncodeHintType.ERROR_CORRECTION).toString());\n      }\n      if (hints.containsKey(EncodeHintType.AZTEC_LAYERS)) {\n        layers = Integer.parseInt(hints.get(EncodeHintType.AZTEC_LAYERS).toString());\n      }\n    }\n    return encode(contents, format, width, height, charset, eccPercent, layers);\n  }\n\n  private static BitMatrix encode(String contents, BarcodeFormat format,\n                                  int width, int height,\n                                  Charset charset, int eccPercent, int layers) {\n    if (format != BarcodeFormat.AZTEC) {\n      throw new IllegalArgumentException(\"Can only encode AZTEC, but got \" + format);\n    }\n    AztecCode aztec = Encoder.encode(contents.getBytes(charset), eccPercent, layers);\n    return renderResult(aztec, width, height);\n  }\n\n  private static BitMatrix renderResult(AztecCode code, int width, int height) {\n    BitMatrix input = code.getMatrix();\n    if (input == null) {\n      throw new IllegalStateException();\n    }\n    int inputWidth = input.getWidth();\n    int inputHeight = input.getHeight();\n    int outputWidth = Math.max(width, inputWidth);\n    int outputHeight = Math.max(height, inputHeight);\n\n    int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);\n    int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;\n    int topPadding = (outputHeight - (inputHeight * multiple)) / 2;\n\n    BitMatrix output = new BitMatrix(outputWidth, outputHeight);\n\n    for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {\n      // Write the contents of this row of the barcode\n      for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {\n        if (input.get(inputX, inputY)) {\n          output.setRegion(outputX, outputY, multiple, multiple);\n        }\n      }\n    }\n    return output;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/decoder/Decoder.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.decoder;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.aztec.AztecDetectorResult;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.reedsolomon.GenericGF;\nimport com.google.zxing.common.reedsolomon.ReedSolomonDecoder;\nimport com.google.zxing.common.reedsolomon.ReedSolomonException;\n\nimport java.util.Arrays;\n\n/**\n * <p>The main class which implements Aztec Code decoding -- as opposed to locating and extracting\n * the Aztec Code from an image.</p>\n *\n * @author David Olivier\n */\npublic final class Decoder {\n\n  private enum Table {\n    UPPER,\n    LOWER,\n    MIXED,\n    DIGIT,\n    PUNCT,\n    BINARY\n  }\n\n  private static final String[] UPPER_TABLE = {\n      \"CTRL_PS\", \" \", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\",\n      \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", \"CTRL_LL\", \"CTRL_ML\", \"CTRL_DL\", \"CTRL_BS\"\n  };\n\n  private static final String[] LOWER_TABLE = {\n      \"CTRL_PS\", \" \", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\",\n      \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"CTRL_US\", \"CTRL_ML\", \"CTRL_DL\", \"CTRL_BS\"\n  };\n\n  private static final String[] MIXED_TABLE = {\n      \"CTRL_PS\", \" \", \"\\1\", \"\\2\", \"\\3\", \"\\4\", \"\\5\", \"\\6\", \"\\7\", \"\\b\", \"\\t\", \"\\n\",\n      \"\\13\", \"\\f\", \"\\r\", \"\\33\", \"\\34\", \"\\35\", \"\\36\", \"\\37\", \"@\", \"\\\\\", \"^\", \"_\",\n      \"`\", \"|\", \"~\", \"\\177\", \"CTRL_LL\", \"CTRL_UL\", \"CTRL_PL\", \"CTRL_BS\"\n  };\n\n  private static final String[] PUNCT_TABLE = {\n      \"\", \"\\r\", \"\\r\\n\", \". \", \", \", \": \", \"!\", \"\\\"\", \"#\", \"$\", \"%\", \"&\", \"'\", \"(\", \")\",\n      \"*\", \"+\", \",\", \"-\", \".\", \"/\", \":\", \";\", \"<\", \"=\", \">\", \"?\", \"[\", \"]\", \"{\", \"}\", \"CTRL_UL\"\n  };\n\n  private static final String[] DIGIT_TABLE = {\n      \"CTRL_PS\", \" \", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \",\", \".\", \"CTRL_UL\", \"CTRL_US\"\n  };\n\n  private AztecDetectorResult ddata;\n\n  public DecoderResult decode(AztecDetectorResult detectorResult) throws FormatException {\n    ddata = detectorResult;\n    BitMatrix matrix = detectorResult.getBits();\n    boolean[] rawbits = extractBits(matrix);\n    boolean[] correctedBits = correctBits(rawbits);\n    byte[] rawBytes = convertBoolArrayToByteArray(correctedBits);\n    String result = getEncodedData(correctedBits);\n    DecoderResult decoderResult = new DecoderResult(rawBytes, result, null, null);\n    decoderResult.setNumBits(correctedBits.length);\n    return decoderResult;\n  }\n\n  // This method is used for testing the high-level encoder\n  public static String highLevelDecode(boolean[] correctedBits) {\n    return getEncodedData(correctedBits);\n  }\n\n  /**\n   * Gets the string encoded in the aztec code bits\n   *\n   * @return the decoded string\n   */\n  private static String getEncodedData(boolean[] correctedBits) {\n    int endIndex = correctedBits.length;\n    Table latchTable = Table.UPPER; // table most recently latched to\n    Table shiftTable = Table.UPPER; // table to use for the next read\n    StringBuilder result = new StringBuilder(20);\n    int index = 0;\n    while (index < endIndex) {\n      if (shiftTable == Table.BINARY) {\n        if (endIndex - index < 5) {\n          break;\n        }\n        int length = readCode(correctedBits, index, 5);\n        index += 5;\n        if (length == 0) {\n          if (endIndex - index < 11) {\n            break;\n          }\n          length = readCode(correctedBits, index, 11) + 31;\n          index += 11;\n        }\n        for (int charCount = 0; charCount < length; charCount++) {\n          if (endIndex - index < 8) {\n            index = endIndex;  // Force outer loop to exit\n            break;\n          }\n          int code = readCode(correctedBits, index, 8);\n          result.append((char) code);\n          index += 8;\n        }\n        // Go back to whatever mode we had been in\n        shiftTable = latchTable;\n      } else {\n        int size = shiftTable == Table.DIGIT ? 4 : 5;\n        if (endIndex - index < size) {\n          break;\n        }\n        int code = readCode(correctedBits, index, size);\n        index += size;\n        String str = getCharacter(shiftTable, code);\n        if (str.startsWith(\"CTRL_\")) {\n          // Table changes\n          // ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode from which it was invoked.\n          // That's including when that mode is a shift.\n          // Our test case dlusbs.png for issue #642 exercises that.\n          latchTable = shiftTable;  // Latch the current mode, so as to return to Upper after U/S B/S\n          shiftTable = getTable(str.charAt(5));\n          if (str.charAt(6) == 'L') {\n            latchTable = shiftTable;\n          }\n        } else {\n          result.append(str);\n          // Go back to whatever mode we had been in\n          shiftTable = latchTable;\n        }\n      }\n    }\n    return result.toString();\n  }\n\n  /**\n   * gets the table corresponding to the char passed\n   */\n  private static Table getTable(char t) {\n    switch (t) {\n      case 'L':\n        return Table.LOWER;\n      case 'P':\n        return Table.PUNCT;\n      case 'M':\n        return Table.MIXED;\n      case 'D':\n        return Table.DIGIT;\n      case 'B':\n        return Table.BINARY;\n      case 'U':\n      default:\n        return Table.UPPER;\n    }\n  }\n\n  /**\n   * Gets the character (or string) corresponding to the passed code in the given table\n   *\n   * @param table the table used\n   * @param code the code of the character\n   */\n  private static String getCharacter(Table table, int code) {\n    switch (table) {\n      case UPPER:\n        return UPPER_TABLE[code];\n      case LOWER:\n        return LOWER_TABLE[code];\n      case MIXED:\n        return MIXED_TABLE[code];\n      case PUNCT:\n        return PUNCT_TABLE[code];\n      case DIGIT:\n        return DIGIT_TABLE[code];\n      default:\n        // Should not reach here.\n        throw new IllegalStateException(\"Bad table\");\n    }\n  }\n\n  /**\n   * <p>Performs RS error correction on an array of bits.</p>\n   *\n   * @return the corrected array\n   * @throws FormatException if the input contains too many errors\n   */\n  private boolean[] correctBits(boolean[] rawbits) throws FormatException {\n    GenericGF gf;\n    int codewordSize;\n\n    if (ddata.getNbLayers() <= 2) {\n      codewordSize = 6;\n      gf = GenericGF.AZTEC_DATA_6;\n    } else if (ddata.getNbLayers() <= 8) {\n      codewordSize = 8;\n      gf = GenericGF.AZTEC_DATA_8;\n    } else if (ddata.getNbLayers() <= 22) {\n      codewordSize = 10;\n      gf = GenericGF.AZTEC_DATA_10;\n    } else {\n      codewordSize = 12;\n      gf = GenericGF.AZTEC_DATA_12;\n    }\n\n    int numDataCodewords = ddata.getNbDatablocks();\n    int numCodewords = rawbits.length / codewordSize;\n    if (numCodewords < numDataCodewords) {\n      throw FormatException.getFormatInstance();\n    }\n    int offset = rawbits.length % codewordSize;\n\n    int[] dataWords = new int[numCodewords];\n    for (int i = 0; i < numCodewords; i++, offset += codewordSize) {\n      dataWords[i] = readCode(rawbits, offset, codewordSize);\n    }\n\n    try {\n      ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(gf);\n      rsDecoder.decode(dataWords, numCodewords - numDataCodewords);\n    } catch (ReedSolomonException ex) {\n      throw FormatException.getFormatInstance(ex);\n    }\n\n    // Now perform the unstuffing operation.\n    // First, count how many bits are going to be thrown out as stuffing\n    int mask = (1 << codewordSize) - 1;\n    int stuffedBits = 0;\n    for (int i = 0; i < numDataCodewords; i++) {\n      int dataWord = dataWords[i];\n      if (dataWord == 0 || dataWord == mask) {\n        throw FormatException.getFormatInstance();\n      } else if (dataWord == 1 || dataWord == mask - 1) {\n        stuffedBits++;\n      }\n    }\n    // Now, actually unpack the bits and remove the stuffing\n    boolean[] correctedBits = new boolean[numDataCodewords * codewordSize - stuffedBits];\n    int index = 0;\n    for (int i = 0; i < numDataCodewords; i++) {\n      int dataWord = dataWords[i];\n      if (dataWord == 1 || dataWord == mask - 1) {\n        // next codewordSize-1 bits are all zeros or all ones\n        Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1);\n        index += codewordSize - 1;\n      } else {\n        for (int bit = codewordSize - 1; bit >= 0; --bit) {\n          correctedBits[index++] = (dataWord & (1 << bit)) != 0;\n        }\n      }\n    }\n    return correctedBits;\n  }\n\n  /**\n   * Gets the array of bits from an Aztec Code matrix\n   *\n   * @return the array of bits\n   */\n  private boolean[] extractBits(BitMatrix matrix) {\n    boolean compact = ddata.isCompact();\n    int layers = ddata.getNbLayers();\n    int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines\n    int[] alignmentMap = new int[baseMatrixSize];\n    boolean[] rawbits = new boolean[totalBitsInLayer(layers, compact)];\n\n    if (compact) {\n      for (int i = 0; i < alignmentMap.length; i++) {\n        alignmentMap[i] = i;\n      }\n    } else {\n      int matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);\n      int origCenter = baseMatrixSize / 2;\n      int center = matrixSize / 2;\n      for (int i = 0; i < origCenter; i++) {\n        int newOffset = i + i / 15;\n        alignmentMap[origCenter - i - 1] = center - newOffset - 1;\n        alignmentMap[origCenter + i] = center + newOffset + 1;\n      }\n    }\n    for (int i = 0, rowOffset = 0; i < layers; i++) {\n      int rowSize = (layers - i) * 4 + (compact ? 9 : 12);\n      // The top-left most point of this layer is <low, low> (not including alignment lines)\n      int low = i * 2;\n      // The bottom-right most point of this layer is <high, high> (not including alignment lines)\n      int high = baseMatrixSize - 1 - low;\n      // We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows\n      for (int j = 0; j < rowSize; j++) {\n        int columnOffset = j * 2;\n        for (int k = 0; k < 2; k++) {\n          // left column\n          rawbits[rowOffset + columnOffset + k] =\n              matrix.get(alignmentMap[low + k], alignmentMap[low + j]);\n          // bottom row\n          rawbits[rowOffset + 2 * rowSize + columnOffset + k] =\n              matrix.get(alignmentMap[low + j], alignmentMap[high - k]);\n          // right column\n          rawbits[rowOffset + 4 * rowSize + columnOffset + k] =\n              matrix.get(alignmentMap[high - k], alignmentMap[high - j]);\n          // top row\n          rawbits[rowOffset + 6 * rowSize + columnOffset + k] =\n              matrix.get(alignmentMap[high - j], alignmentMap[low + k]);\n        }\n      }\n      rowOffset += rowSize * 8;\n    }\n    return rawbits;\n  }\n\n  /**\n   * Reads a code of given length and at given index in an array of bits\n   */\n  private static int readCode(boolean[] rawbits, int startIndex, int length) {\n    int res = 0;\n    for (int i = startIndex; i < startIndex + length; i++) {\n      res <<= 1;\n      if (rawbits[i]) {\n        res |= 0x01;\n      }\n    }\n    return res;\n  }\n\n  /**\n   * Reads a code of length 8 in an array of bits, padding with zeros\n   */\n  private static byte readByte(boolean[] rawbits, int startIndex) {\n    int n = rawbits.length - startIndex;\n    if (n >= 8) {\n      return (byte) readCode(rawbits, startIndex, 8);\n    }\n    return (byte) (readCode(rawbits, startIndex, n) << (8 - n));\n  }\n\n  /**\n   * Packs a bit array into bytes, most significant bit first\n   */\n  static byte[] convertBoolArrayToByteArray(boolean[] boolArr) {\n    byte[] byteArr = new byte[(boolArr.length + 7) / 8];\n    for (int i = 0; i < byteArr.length; i++) {\n      byteArr[i] = readByte(boolArr, 8 * i);\n    }\n    return byteArr;\n  }\n\n  private static int totalBitsInLayer(int layers, boolean compact) {\n    return ((compact ? 88 : 112) + 16 * layers) * layers;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/detector/Detector.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.detector;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.aztec.AztecDetectorResult;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.GridSampler;\nimport com.google.zxing.common.detector.MathUtils;\nimport com.google.zxing.common.detector.WhiteRectangleDetector;\nimport com.google.zxing.common.reedsolomon.GenericGF;\nimport com.google.zxing.common.reedsolomon.ReedSolomonDecoder;\nimport com.google.zxing.common.reedsolomon.ReedSolomonException;\n\n/**\n * Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code\n * is rotated or skewed, or partially obscured.\n *\n * @author David Olivier\n * @author Frank Yellin\n */\npublic final class Detector {\n\n  private static final int[] EXPECTED_CORNER_BITS = {\n      0xee0,  // 07340  XXX .XX X.. ...\n      0x1dc,  // 00734  ... XXX .XX X..\n      0x83b,  // 04073  X.. ... XXX .XX\n      0x707,  // 03407 .XX X.. ... XXX\n  };\n\n  private final BitMatrix image;\n\n  private boolean compact;\n  private int nbLayers;\n  private int nbDataBlocks;\n  private int nbCenterLayers;\n  private int shift;\n\n  public Detector(BitMatrix image) {\n    this.image = image;\n  }\n\n  public AztecDetectorResult detect() throws NotFoundException {\n    return detect(false);\n  }\n\n  /**\n   * Detects an Aztec Code in an image.\n   *\n   * @param isMirror if true, image is a mirror-image of original\n   * @return {@link AztecDetectorResult} encapsulating results of detecting an Aztec Code\n   * @throws NotFoundException if no Aztec Code can be found\n   */\n   public AztecDetectorResult detect(boolean isMirror) throws NotFoundException {\n\n    // 1. Get the center of the aztec matrix\n    Point pCenter = getMatrixCenter();\n\n    // 2. Get the center points of the four diagonal points just outside the bull's eye\n    //  [topRight, bottomRight, bottomLeft, topLeft]\n    ResultPoint[] bullsEyeCorners = getBullsEyeCorners(pCenter);\n\n    if (isMirror) {\n      ResultPoint temp = bullsEyeCorners[0];\n      bullsEyeCorners[0] = bullsEyeCorners[2];\n      bullsEyeCorners[2] = temp;\n    }\n\n    // 3. Get the size of the matrix and other parameters from the bull's eye\n    extractParameters(bullsEyeCorners);\n\n    // 4. Sample the grid\n    BitMatrix bits = sampleGrid(image,\n                                bullsEyeCorners[shift % 4],\n                                bullsEyeCorners[(shift + 1) % 4],\n                                bullsEyeCorners[(shift + 2) % 4],\n                                bullsEyeCorners[(shift + 3) % 4]);\n\n    // 5. Get the corners of the matrix.\n    ResultPoint[] corners = getMatrixCornerPoints(bullsEyeCorners);\n\n    return new AztecDetectorResult(bits, corners, compact, nbDataBlocks, nbLayers);\n  }\n\n  /**\n   * Extracts the number of data layers and data blocks from the layer around the bull's eye.\n   *\n   * @param bullsEyeCorners the array of bull's eye corners\n   * @throws NotFoundException in case of too many errors or invalid parameters\n   */\n  private void extractParameters(ResultPoint[] bullsEyeCorners) throws NotFoundException {\n    if (!isValid(bullsEyeCorners[0]) || !isValid(bullsEyeCorners[1]) ||\n        !isValid(bullsEyeCorners[2]) || !isValid(bullsEyeCorners[3])) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    int length = 2 * nbCenterLayers;\n    // Get the bits around the bull's eye\n    int[] sides = {\n        sampleLine(bullsEyeCorners[0], bullsEyeCorners[1], length), // Right side\n        sampleLine(bullsEyeCorners[1], bullsEyeCorners[2], length), // Bottom\n        sampleLine(bullsEyeCorners[2], bullsEyeCorners[3], length), // Left side\n        sampleLine(bullsEyeCorners[3], bullsEyeCorners[0], length)  // Top\n    };\n\n    // bullsEyeCorners[shift] is the corner of the bulls'eye that has three\n    // orientation marks.\n    // sides[shift] is the row/column that goes from the corner with three\n    // orientation marks to the corner with two.\n    shift = getRotation(sides, length);\n\n    // Flatten the parameter bits into a single 28- or 40-bit long\n    long parameterData = 0;\n    for (int i = 0; i < 4; i++) {\n      int side = sides[(shift + i) % 4];\n      if (compact) {\n        // Each side of the form ..XXXXXXX. where Xs are parameter data\n        parameterData <<= 7;\n        parameterData += (side >> 1) & 0x7F;\n      } else {\n        // Each side of the form ..XXXXX.XXXXX. where Xs are parameter data\n        parameterData <<= 10;\n        parameterData += ((side >> 2) & (0x1f << 5)) + ((side >> 1) & 0x1F);\n      }\n    }\n\n    // Corrects parameter data using RS.  Returns just the data portion\n    // without the error correction.\n    int correctedData = getCorrectedParameterData(parameterData, compact);\n\n    if (compact) {\n      // 8 bits:  2 bits layers and 6 bits data blocks\n      nbLayers = (correctedData >> 6) + 1;\n      nbDataBlocks = (correctedData & 0x3F) + 1;\n    } else {\n      // 16 bits:  5 bits layers and 11 bits data blocks\n      nbLayers = (correctedData >> 11) + 1;\n      nbDataBlocks = (correctedData & 0x7FF) + 1;\n    }\n  }\n\n  private static int getRotation(int[] sides, int length) throws NotFoundException {\n    // In a normal pattern, we expect to See\n    //   **    .*             D       A\n    //   *      *\n    //\n    //   .      *\n    //   ..    ..             C       B\n    //\n    // Grab the 3 bits from each of the sides the form the locator pattern and concatenate\n    // into a 12-bit integer.  Start with the bit at A\n    int cornerBits = 0;\n    for (int side : sides) {\n      // XX......X where X's are orientation marks\n      int t = ((side >> (length - 2)) << 1) + (side & 1);\n      cornerBits = (cornerBits << 3) + t;\n    }\n    // Mov the bottom bit to the top, so that the three bits of the locator pattern at A are\n    // together.  cornerBits is now:\n    //  3 orientation bits at A || 3 orientation bits at B || ... || 3 orientation bits at D\n    cornerBits = ((cornerBits & 1) << 11) + (cornerBits >> 1);\n    // The result shift indicates which element of BullsEyeCorners[] goes into the top-left\n    // corner. Since the four rotation values have a Hamming distance of 8, we\n    // can easily tolerate two errors.\n    for (int shift = 0; shift < 4; shift++) {\n      if (Integer.bitCount(cornerBits ^ EXPECTED_CORNER_BITS[shift]) <= 2) {\n        return shift;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * Corrects the parameter bits using Reed-Solomon algorithm.\n   *\n   * @param parameterData parameter bits\n   * @param compact true if this is a compact Aztec code\n   * @throws NotFoundException if the array contains too many errors\n   */\n  private static int getCorrectedParameterData(long parameterData, boolean compact) throws NotFoundException {\n    int numCodewords;\n    int numDataCodewords;\n\n    if (compact) {\n      numCodewords = 7;\n      numDataCodewords = 2;\n    } else {\n      numCodewords = 10;\n      numDataCodewords = 4;\n    }\n\n    int numECCodewords = numCodewords - numDataCodewords;\n    int[] parameterWords = new int[numCodewords];\n    for (int i = numCodewords - 1; i >= 0; --i) {\n      parameterWords[i] = (int) parameterData & 0xF;\n      parameterData >>= 4;\n    }\n    try {\n      ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(GenericGF.AZTEC_PARAM);\n      rsDecoder.decode(parameterWords, numECCodewords);\n    } catch (ReedSolomonException ignored) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    // Toss the error correction.  Just return the data as an integer\n    int result = 0;\n    for (int i = 0; i < numDataCodewords; i++) {\n      result = (result << 4) + parameterWords[i];\n    }\n    return result;\n  }\n\n  /**\n   * Finds the corners of a bull-eye centered on the passed point.\n   * This returns the centers of the diagonal points just outside the bull's eye\n   * Returns [topRight, bottomRight, bottomLeft, topLeft]\n   *\n   * @param pCenter Center point\n   * @return The corners of the bull-eye\n   * @throws NotFoundException If no valid bull-eye can be found\n   */\n  private ResultPoint[] getBullsEyeCorners(Point pCenter) throws NotFoundException {\n\n    Point pina = pCenter;\n    Point pinb = pCenter;\n    Point pinc = pCenter;\n    Point pind = pCenter;\n\n    boolean color = true;\n\n    for (nbCenterLayers = 1; nbCenterLayers < 9; nbCenterLayers++) {\n      Point pouta = getFirstDifferent(pina, color, 1, -1);\n      Point poutb = getFirstDifferent(pinb, color, 1, 1);\n      Point poutc = getFirstDifferent(pinc, color, -1, 1);\n      Point poutd = getFirstDifferent(pind, color, -1, -1);\n\n      //d      a\n      //\n      //c      b\n\n      if (nbCenterLayers > 2) {\n        float q = distance(poutd, pouta) * nbCenterLayers / (distance(pind, pina) * (nbCenterLayers + 2));\n        if (q < 0.75 || q > 1.25 || !isWhiteOrBlackRectangle(pouta, poutb, poutc, poutd)) {\n          break;\n        }\n      }\n\n      pina = pouta;\n      pinb = poutb;\n      pinc = poutc;\n      pind = poutd;\n\n      color = !color;\n    }\n\n    if (nbCenterLayers != 5 && nbCenterLayers != 7) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    compact = nbCenterLayers == 5;\n\n    // Expand the square by .5 pixel in each direction so that we're on the border\n    // between the white square and the black square\n    ResultPoint pinax = new ResultPoint(pina.getX() + 0.5f, pina.getY() - 0.5f);\n    ResultPoint pinbx = new ResultPoint(pinb.getX() + 0.5f, pinb.getY() + 0.5f);\n    ResultPoint pincx = new ResultPoint(pinc.getX() - 0.5f, pinc.getY() + 0.5f);\n    ResultPoint pindx = new ResultPoint(pind.getX() - 0.5f, pind.getY() - 0.5f);\n\n    // Expand the square so that its corners are the centers of the points\n    // just outside the bull's eye.\n    return expandSquare(new ResultPoint[]{pinax, pinbx, pincx, pindx},\n                        2 * nbCenterLayers - 3,\n                        2 * nbCenterLayers);\n  }\n\n  /**\n   * Finds a candidate center point of an Aztec code from an image\n   *\n   * @return the center point\n   */\n  private Point getMatrixCenter() {\n\n    ResultPoint pointA;\n    ResultPoint pointB;\n    ResultPoint pointC;\n    ResultPoint pointD;\n\n    //Get a white rectangle that can be the border of the matrix in center bull's eye or\n    try {\n\n      ResultPoint[] cornerPoints = new WhiteRectangleDetector(image).detect();\n      pointA = cornerPoints[0];\n      pointB = cornerPoints[1];\n      pointC = cornerPoints[2];\n      pointD = cornerPoints[3];\n\n    } catch (NotFoundException e) {\n\n      // This exception can be in case the initial rectangle is white\n      // In that case, surely in the bull's eye, we try to expand the rectangle.\n      int cx = image.getWidth() / 2;\n      int cy = image.getHeight() / 2;\n      pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();\n      pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();\n      pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();\n      pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();\n\n    }\n\n    //Compute the center of the rectangle\n    int cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0f);\n    int cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0f);\n\n    // Redetermine the white rectangle starting from previously computed center.\n    // This will ensure that we end up with a white rectangle in center bull's eye\n    // in order to compute a more accurate center.\n    try {\n      ResultPoint[] cornerPoints = new WhiteRectangleDetector(image, 15, cx, cy).detect();\n      pointA = cornerPoints[0];\n      pointB = cornerPoints[1];\n      pointC = cornerPoints[2];\n      pointD = cornerPoints[3];\n    } catch (NotFoundException e) {\n      // This exception can be in case the initial rectangle is white\n      // In that case we try to expand the rectangle.\n      pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();\n      pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();\n      pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();\n      pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();\n    }\n\n    // Recompute the center of the rectangle\n    cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0f);\n    cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0f);\n\n    return new Point(cx, cy);\n  }\n\n  /**\n   * Gets the Aztec code corners from the bull's eye corners and the parameters.\n   *\n   * @param bullsEyeCorners the array of bull's eye corners\n   * @return the array of aztec code corners\n   */\n  private ResultPoint[] getMatrixCornerPoints(ResultPoint[] bullsEyeCorners) {\n    return expandSquare(bullsEyeCorners, 2 * nbCenterLayers, getDimension());\n  }\n\n  /**\n   * Creates a BitMatrix by sampling the provided image.\n   * topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the\n   * diagonal just outside the bull's eye.\n   */\n  private BitMatrix sampleGrid(BitMatrix image,\n                               ResultPoint topLeft,\n                               ResultPoint topRight,\n                               ResultPoint bottomRight,\n                               ResultPoint bottomLeft) throws NotFoundException {\n\n    GridSampler sampler = GridSampler.getInstance();\n    int dimension = getDimension();\n\n    float low = dimension / 2.0f - nbCenterLayers;\n    float high = dimension / 2.0f + nbCenterLayers;\n\n    return sampler.sampleGrid(image,\n                              dimension,\n                              dimension,\n                              low, low,   // topleft\n                              high, low,  // topright\n                              high, high, // bottomright\n                              low, high,  // bottomleft\n                              topLeft.getX(), topLeft.getY(),\n                              topRight.getX(), topRight.getY(),\n                              bottomRight.getX(), bottomRight.getY(),\n                              bottomLeft.getX(), bottomLeft.getY());\n  }\n\n  /**\n   * Samples a line.\n   *\n   * @param p1   start point (inclusive)\n   * @param p2   end point (exclusive)\n   * @param size number of bits\n   * @return the array of bits as an int (first bit is high-order bit of result)\n   */\n  private int sampleLine(ResultPoint p1, ResultPoint p2, int size) {\n    int result = 0;\n\n    float d = distance(p1, p2);\n    float moduleSize = d / size;\n    float px = p1.getX();\n    float py = p1.getY();\n    float dx = moduleSize * (p2.getX() - p1.getX()) / d;\n    float dy = moduleSize * (p2.getY() - p1.getY()) / d;\n    for (int i = 0; i < size; i++) {\n      if (image.get(MathUtils.round(px + i * dx), MathUtils.round(py + i * dy))) {\n        result |= 1 << (size - i - 1);\n      }\n    }\n    return result;\n  }\n\n  /**\n   * @return true if the border of the rectangle passed in parameter is compound of white points only\n   *         or black points only\n   */\n  private boolean isWhiteOrBlackRectangle(Point p1,\n                                          Point p2,\n                                          Point p3,\n                                          Point p4) {\n\n    int corr = 3;\n\n    p1 = new Point(p1.getX() - corr, p1.getY() + corr);\n    p2 = new Point(p2.getX() - corr, p2.getY() - corr);\n    p3 = new Point(p3.getX() + corr, p3.getY() - corr);\n    p4 = new Point(p4.getX() + corr, p4.getY() + corr);\n\n    int cInit = getColor(p4, p1);\n\n    if (cInit == 0) {\n      return false;\n    }\n\n    int c = getColor(p1, p2);\n\n    if (c != cInit) {\n      return false;\n    }\n\n    c = getColor(p2, p3);\n\n    if (c != cInit) {\n      return false;\n    }\n\n    c = getColor(p3, p4);\n\n    return c == cInit;\n\n  }\n\n  /**\n   * Gets the color of a segment\n   *\n   * @return 1 if segment more than 90% black, -1 if segment is more than 90% white, 0 else\n   */\n  private int getColor(Point p1, Point p2) {\n    float d = distance(p1, p2);\n    float dx = (p2.getX() - p1.getX()) / d;\n    float dy = (p2.getY() - p1.getY()) / d;\n    int error = 0;\n\n    float px = p1.getX();\n    float py = p1.getY();\n\n    boolean colorModel = image.get(p1.getX(), p1.getY());\n\n    int iMax = (int) Math.ceil(d);\n    for (int i = 0; i < iMax; i++) {\n      px += dx;\n      py += dy;\n      if (image.get(MathUtils.round(px), MathUtils.round(py)) != colorModel) {\n        error++;\n      }\n    }\n\n    float errRatio = error / d;\n\n    if (errRatio > 0.1f && errRatio < 0.9f) {\n      return 0;\n    }\n\n    return (errRatio <= 0.1f) == colorModel ? 1 : -1;\n  }\n\n  /**\n   * Gets the coordinate of the first point with a different color in the given direction\n   */\n  private Point getFirstDifferent(Point init, boolean color, int dx, int dy) {\n    int x = init.getX() + dx;\n    int y = init.getY() + dy;\n\n    while (isValid(x, y) && image.get(x, y) == color) {\n      x += dx;\n      y += dy;\n    }\n\n    x -= dx;\n    y -= dy;\n\n    while (isValid(x, y) && image.get(x, y) == color) {\n      x += dx;\n    }\n    x -= dx;\n\n    while (isValid(x, y) && image.get(x, y) == color) {\n      y += dy;\n    }\n    y -= dy;\n\n    return new Point(x, y);\n  }\n\n  /**\n   * Expand the square represented by the corner points by pushing out equally in all directions\n   *\n   * @param cornerPoints the corners of the square, which has the bull's eye at its center\n   * @param oldSide the original length of the side of the square in the target bit matrix\n   * @param newSide the new length of the size of the square in the target bit matrix\n   * @return the corners of the expanded square\n   */\n  private static ResultPoint[] expandSquare(ResultPoint[] cornerPoints, int oldSide, int newSide) {\n    float ratio = newSide / (2.0f * oldSide);\n    float dx = cornerPoints[0].getX() - cornerPoints[2].getX();\n    float dy = cornerPoints[0].getY() - cornerPoints[2].getY();\n    float centerx = (cornerPoints[0].getX() + cornerPoints[2].getX()) / 2.0f;\n    float centery = (cornerPoints[0].getY() + cornerPoints[2].getY()) / 2.0f;\n\n    ResultPoint result0 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);\n    ResultPoint result2 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);\n\n    dx = cornerPoints[1].getX() - cornerPoints[3].getX();\n    dy = cornerPoints[1].getY() - cornerPoints[3].getY();\n    centerx = (cornerPoints[1].getX() + cornerPoints[3].getX()) / 2.0f;\n    centery = (cornerPoints[1].getY() + cornerPoints[3].getY()) / 2.0f;\n    ResultPoint result1 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);\n    ResultPoint result3 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);\n\n    return new ResultPoint[]{result0, result1, result2, result3};\n  }\n\n  private boolean isValid(int x, int y) {\n    return x >= 0 && x < image.getWidth() && y > 0 && y < image.getHeight();\n  }\n\n  private boolean isValid(ResultPoint point) {\n    int x = MathUtils.round(point.getX());\n    int y = MathUtils.round(point.getY());\n    return isValid(x, y);\n  }\n\n  private static float distance(Point a, Point b) {\n    return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());\n  }\n\n  private static float distance(ResultPoint a, ResultPoint b) {\n    return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());\n  }\n\n  private int getDimension() {\n    if (compact) {\n      return 4 * nbLayers + 11;\n    }\n    if (nbLayers <= 4) {\n      return 4 * nbLayers + 15;\n    }\n    return 4 * nbLayers + 2 * ((nbLayers - 4) / 8 + 1) + 15;\n  }\n\n  static final class Point {\n    private final int x;\n    private final int y;\n\n    ResultPoint toResultPoint() {\n      return new ResultPoint(getX(), getY());\n    }\n\n    Point(int x, int y) {\n      this.x = x;\n      this.y = y;\n    }\n\n    int getX() {\n      return x;\n    }\n\n    int getY() {\n      return y;\n    }\n\n    @Override\n    public String toString() {\n      return \"<\" + x + ' ' + y + '>';\n    }\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/encoder/AztecCode.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.encoder;\n\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * Aztec 2D code representation\n *\n * @author Rustam Abdullaev\n */\npublic final class AztecCode {\n\n  private boolean compact;\n  private int size;\n  private int layers;\n  private int codeWords;\n  private BitMatrix matrix;\n\n  /**\n   * @return {@code true} if compact instead of full mode\n   */\n  public boolean isCompact() {\n    return compact;\n  }\n\n  public void setCompact(boolean compact) {\n    this.compact = compact;\n  }\n\n  /**\n   * @return size in pixels (width and height)\n   */\n  public int getSize() {\n    return size;\n  }\n\n  public void setSize(int size) {\n    this.size = size;\n  }\n\n  /**\n   * @return number of levels\n   */\n  public int getLayers() {\n    return layers;\n  }\n\n  public void setLayers(int layers) {\n    this.layers = layers;\n  }\n\n  /**\n   * @return number of data codewords\n   */\n  public int getCodeWords() {\n    return codeWords;\n  }\n\n  public void setCodeWords(int codeWords) {\n    this.codeWords = codeWords;\n  }\n\n  /**\n   * @return the symbol image\n   */\n  public BitMatrix getMatrix() {\n    return matrix;\n  }\n\n  public void setMatrix(BitMatrix matrix) {\n    this.matrix = matrix;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/encoder/BinaryShiftToken.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.encoder;\n\nimport com.google.zxing.common.BitArray;\n\nfinal class BinaryShiftToken extends Token {\n\n  private final short binaryShiftStart;\n  private final short binaryShiftByteCount;\n\n  BinaryShiftToken(Token previous,\n                   int binaryShiftStart,\n                   int binaryShiftByteCount) {\n    super(previous);\n    this.binaryShiftStart = (short) binaryShiftStart;\n    this.binaryShiftByteCount = (short) binaryShiftByteCount;\n  }\n\n  @Override\n  public void appendTo(BitArray bitArray, byte[] text) {\n    for (int i = 0; i < binaryShiftByteCount; i++) {\n      if (i == 0 || (i == 31 && binaryShiftByteCount <= 62)) {\n        // We need a header before the first character, and before\n        // character 31 when the total byte code is <= 62\n        bitArray.appendBits(31, 5);  // BINARY_SHIFT\n        if (binaryShiftByteCount > 62) {\n          bitArray.appendBits(binaryShiftByteCount - 31, 16);\n        } else if (i == 0) {\n          // 1 <= binaryShiftByteCode <= 62\n          bitArray.appendBits(Math.min(binaryShiftByteCount, 31), 5);\n        } else {\n          // 32 <= binaryShiftCount <= 62 and i == 31\n          bitArray.appendBits(binaryShiftByteCount - 31, 5);\n        }\n      }\n      bitArray.appendBits(text[binaryShiftStart + i], 8);\n    }\n  }\n\n  @Override\n  public String toString() {\n    return \"<\" + binaryShiftStart + \"::\" + (binaryShiftStart + binaryShiftByteCount - 1) + '>';\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/encoder/Encoder.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.encoder;\n\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.reedsolomon.GenericGF;\nimport com.google.zxing.common.reedsolomon.ReedSolomonEncoder;\n\n/**\n * Generates Aztec 2D barcodes.\n *\n * @author Rustam Abdullaev\n */\npublic final class Encoder {\n\n  public static final int DEFAULT_EC_PERCENT = 33; // default minimal percentage of error check words\n  public static final int DEFAULT_AZTEC_LAYERS = 0;\n  private static final int MAX_NB_BITS = 32;\n  private static final int MAX_NB_BITS_COMPACT = 4;\n\n  private static final int[] WORD_SIZE = {\n    4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,\n    12, 12, 12, 12, 12, 12, 12, 12, 12, 12\n  };\n\n  private Encoder() {\n  }\n\n  /**\n   * Encodes the given binary content as an Aztec symbol\n   *\n   * @param data input data string\n   * @return Aztec symbol matrix with metadata\n   */\n  public static AztecCode encode(byte[] data) {\n    return encode(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS);\n  }\n\n  /**\n   * Encodes the given binary content as an Aztec symbol\n   *\n   * @param data input data string\n   * @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,\n   *                      a minimum of 23% + 3 words is recommended)\n   * @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers\n   * @return Aztec symbol matrix with metadata\n   */\n  public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers) {\n    // High-level encode\n    BitArray bits = new HighLevelEncoder(data).encode();\n\n    // stuff bits and choose symbol size\n    int eccBits = bits.getSize() * minECCPercent / 100 + 11;\n    int totalSizeBits = bits.getSize() + eccBits;\n    boolean compact;\n    int layers;\n    int totalBitsInLayer;\n    int wordSize;\n    BitArray stuffedBits;\n    if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS) {\n      compact = userSpecifiedLayers < 0;\n      layers = Math.abs(userSpecifiedLayers);\n      if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS)) {\n        throw new IllegalArgumentException(\n            String.format(\"Illegal value %s for layers\", userSpecifiedLayers));\n      }\n      totalBitsInLayer = totalBitsInLayer(layers, compact);\n      wordSize = WORD_SIZE[layers];\n      int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);\n      stuffedBits = stuffBits(bits, wordSize);\n      if (stuffedBits.getSize() + eccBits > usableBitsInLayers) {\n        throw new IllegalArgumentException(\"Data to large for user specified layer\");\n      }\n      if (compact && stuffedBits.getSize() > wordSize * 64) {\n        // Compact format only allows 64 data words, though C4 can hold more words than that\n        throw new IllegalArgumentException(\"Data to large for user specified layer\");\n      }\n    } else {\n      wordSize = 0;\n      stuffedBits = null;\n      // We look at the possible table sizes in the order Compact1, Compact2, Compact3,\n      // Compact4, Normal4,...  Normal(i) for i < 4 isn't typically used since Compact(i+1)\n      // is the same size, but has more data.\n      for (int i = 0; ; i++) {\n        if (i > MAX_NB_BITS) {\n          throw new IllegalArgumentException(\"Data too large for an Aztec code\");\n        }\n        compact = i <= 3;\n        layers = compact ? i + 1 : i;\n        totalBitsInLayer = totalBitsInLayer(layers, compact);\n        if (totalSizeBits > totalBitsInLayer) {\n          continue;\n        }\n        // [Re]stuff the bits if this is the first opportunity, or if the\n        // wordSize has changed\n        if (stuffedBits == null || wordSize != WORD_SIZE[layers]) {\n          wordSize = WORD_SIZE[layers];\n          stuffedBits = stuffBits(bits, wordSize);\n        }\n        int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);\n        if (compact && stuffedBits.getSize() > wordSize * 64) {\n          // Compact format only allows 64 data words, though C4 can hold more words than that\n          continue;\n        }\n        if (stuffedBits.getSize() + eccBits <= usableBitsInLayers) {\n          break;\n        }\n      }\n    }\n    BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize);\n\n    // generate mode message\n    int messageSizeInWords = stuffedBits.getSize() / wordSize;\n    BitArray modeMessage = generateModeMessage(compact, layers, messageSizeInWords);\n\n    // allocate symbol\n    int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines\n    int[] alignmentMap = new int[baseMatrixSize];\n    int matrixSize;\n    if (compact) {\n      // no alignment marks in compact mode, alignmentMap is a no-op\n      matrixSize = baseMatrixSize;\n      for (int i = 0; i < alignmentMap.length; i++) {\n        alignmentMap[i] = i;\n      }\n    } else {\n      matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);\n      int origCenter = baseMatrixSize / 2;\n      int center = matrixSize / 2;\n      for (int i = 0; i < origCenter; i++) {\n        int newOffset = i + i / 15;\n        alignmentMap[origCenter - i - 1] = center - newOffset - 1;\n        alignmentMap[origCenter + i] = center + newOffset + 1;\n      }\n    }\n    BitMatrix matrix = new BitMatrix(matrixSize);\n\n    // draw data bits\n    for (int i = 0, rowOffset = 0; i < layers; i++) {\n      int rowSize = (layers - i) * 4 + (compact ? 9 : 12);\n      for (int j = 0; j < rowSize; j++) {\n        int columnOffset = j * 2;\n        for (int k = 0; k < 2; k++) {\n          if (messageBits.get(rowOffset + columnOffset + k)) {\n            matrix.set(alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]);\n          }\n          if (messageBits.get(rowOffset + rowSize * 2 + columnOffset + k)) {\n            matrix.set(alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]);\n          }\n          if (messageBits.get(rowOffset + rowSize * 4 + columnOffset + k)) {\n            matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]);\n          }\n          if (messageBits.get(rowOffset + rowSize * 6 + columnOffset + k)) {\n            matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]);\n          }\n        }\n      }\n      rowOffset += rowSize * 8;\n    }\n\n    // draw mode message\n    drawModeMessage(matrix, compact, matrixSize, modeMessage);\n\n    // draw alignment marks\n    if (compact) {\n      drawBullsEye(matrix, matrixSize / 2, 5);\n    } else {\n      drawBullsEye(matrix, matrixSize / 2, 7);\n      for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {\n        for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {\n          matrix.set(matrixSize / 2 - j, k);\n          matrix.set(matrixSize / 2 + j, k);\n          matrix.set(k, matrixSize / 2 - j);\n          matrix.set(k, matrixSize / 2 + j);\n        }\n      }\n    }\n\n    AztecCode aztec = new AztecCode();\n    aztec.setCompact(compact);\n    aztec.setSize(matrixSize);\n    aztec.setLayers(layers);\n    aztec.setCodeWords(messageSizeInWords);\n    aztec.setMatrix(matrix);\n    return aztec;\n  }\n\n  private static void drawBullsEye(BitMatrix matrix, int center, int size) {\n    for (int i = 0; i < size; i += 2) {\n      for (int j = center - i; j <= center + i; j++) {\n        matrix.set(j, center - i);\n        matrix.set(j, center + i);\n        matrix.set(center - i, j);\n        matrix.set(center + i, j);\n      }\n    }\n    matrix.set(center - size, center - size);\n    matrix.set(center - size + 1, center - size);\n    matrix.set(center - size, center - size + 1);\n    matrix.set(center + size, center - size);\n    matrix.set(center + size, center - size + 1);\n    matrix.set(center + size, center + size - 1);\n  }\n\n  static BitArray generateModeMessage(boolean compact, int layers, int messageSizeInWords) {\n    BitArray modeMessage = new BitArray();\n    if (compact) {\n      modeMessage.appendBits(layers - 1, 2);\n      modeMessage.appendBits(messageSizeInWords - 1, 6);\n      modeMessage = generateCheckWords(modeMessage, 28, 4);\n    } else {\n      modeMessage.appendBits(layers - 1, 5);\n      modeMessage.appendBits(messageSizeInWords - 1, 11);\n      modeMessage = generateCheckWords(modeMessage, 40, 4);\n    }\n    return modeMessage;\n  }\n\n  private static void drawModeMessage(BitMatrix matrix, boolean compact, int matrixSize, BitArray modeMessage) {\n    int center = matrixSize / 2;\n    if (compact) {\n      for (int i = 0; i < 7; i++) {\n        int offset = center - 3 + i;\n        if (modeMessage.get(i)) {\n          matrix.set(offset, center - 5);\n        }\n        if (modeMessage.get(i + 7)) {\n          matrix.set(center + 5, offset);\n        }\n        if (modeMessage.get(20 - i)) {\n          matrix.set(offset, center + 5);\n        }\n        if (modeMessage.get(27 - i)) {\n          matrix.set(center - 5, offset);\n        }\n      }\n    } else {\n      for (int i = 0; i < 10; i++) {\n        int offset = center - 5 + i + i / 5;\n        if (modeMessage.get(i)) {\n          matrix.set(offset, center - 7);\n        }\n        if (modeMessage.get(i + 10)) {\n          matrix.set(center + 7, offset);\n        }\n        if (modeMessage.get(29 - i)) {\n          matrix.set(offset, center + 7);\n        }\n        if (modeMessage.get(39 - i)) {\n          matrix.set(center - 7, offset);\n        }\n      }\n    }\n  }\n\n  private static BitArray generateCheckWords(BitArray bitArray, int totalBits, int wordSize) {\n    // bitArray is guaranteed to be a multiple of the wordSize, so no padding needed\n    int messageSizeInWords = bitArray.getSize() / wordSize;\n    ReedSolomonEncoder rs = new ReedSolomonEncoder(getGF(wordSize));\n    int totalWords = totalBits / wordSize;\n    int[] messageWords = bitsToWords(bitArray, wordSize, totalWords);\n    rs.encode(messageWords, totalWords - messageSizeInWords);\n    int startPad = totalBits % wordSize;\n    BitArray messageBits = new BitArray();\n    messageBits.appendBits(0, startPad);\n    for (int messageWord : messageWords) {\n      messageBits.appendBits(messageWord, wordSize);\n    }\n    return messageBits;\n  }\n\n  private static int[] bitsToWords(BitArray stuffedBits, int wordSize, int totalWords) {\n    int[] message = new int[totalWords];\n    int i;\n    int n;\n    for (i = 0, n = stuffedBits.getSize() / wordSize; i < n; i++) {\n      int value = 0;\n      for (int j = 0; j < wordSize; j++) {\n        value |= stuffedBits.get(i * wordSize + j) ? (1 << wordSize - j - 1) : 0;\n      }\n      message[i] = value;\n    }\n    return message;\n  }\n\n  private static GenericGF getGF(int wordSize) {\n    switch (wordSize) {\n      case 4:\n        return GenericGF.AZTEC_PARAM;\n      case 6:\n        return GenericGF.AZTEC_DATA_6;\n      case 8:\n        return GenericGF.AZTEC_DATA_8;\n      case 10:\n        return GenericGF.AZTEC_DATA_10;\n      case 12:\n        return GenericGF.AZTEC_DATA_12;\n      default:\n        throw new IllegalArgumentException(\"Unsupported word size \" + wordSize);\n    }\n  }\n\n  static BitArray stuffBits(BitArray bits, int wordSize) {\n    BitArray out = new BitArray();\n\n    int n = bits.getSize();\n    int mask = (1 << wordSize) - 2;\n    for (int i = 0; i < n; i += wordSize) {\n      int word = 0;\n      for (int j = 0; j < wordSize; j++) {\n        if (i + j >= n || bits.get(i + j)) {\n          word |= 1 << (wordSize - 1 - j);\n        }\n      }\n      if ((word & mask) == mask) {\n        out.appendBits(word & mask, wordSize);\n        i--;\n      } else if ((word & mask) == 0) {\n        out.appendBits(word | 1, wordSize);\n        i--;\n      } else {\n        out.appendBits(word, wordSize);\n      }\n    }\n    return out;\n  }\n\n  private static int totalBitsInLayer(int layers, boolean compact) {\n    return ((compact ? 88 : 112) + 16 * layers) * layers;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/encoder/HighLevelEncoder.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.encoder;\n\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.LinkedList;\n\n/**\n * This produces nearly optimal encodings of text into the first-level of\n * encoding used by Aztec code.\n *\n * It uses a dynamic algorithm.  For each prefix of the string, it determines\n * a set of encodings that could lead to this prefix.  We repeatedly add a\n * character and generate a new set of optimal encodings until we have read\n * through the entire input.\n *\n * @author Frank Yellin\n * @author Rustam Abdullaev\n */\npublic final class HighLevelEncoder {\n\n  static final String[] MODE_NAMES = {\"UPPER\", \"LOWER\", \"DIGIT\", \"MIXED\", \"PUNCT\"};\n\n  static final int MODE_UPPER = 0; // 5 bits\n  static final int MODE_LOWER = 1; // 5 bits\n  static final int MODE_DIGIT = 2; // 4 bits\n  static final int MODE_MIXED = 3; // 5 bits\n  static final int MODE_PUNCT = 4; // 5 bits\n\n  // The Latch Table shows, for each pair of Modes, the optimal method for\n  // getting from one mode to another.  In the worst possible case, this can\n  // be up to 14 bits.  In the best possible case, we are already there!\n  // The high half-word of each entry gives the number of bits.\n  // The low half-word of each entry are the actual bits necessary to change\n  static final int[][] LATCH_TABLE = {\n    {\n      0,\n      (5 << 16) + 28,              // UPPER -> LOWER\n      (5 << 16) + 30,              // UPPER -> DIGIT\n      (5 << 16) + 29,              // UPPER -> MIXED\n      (10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT\n    },\n    {\n      (9 << 16) + (30 << 4) + 14,  // LOWER -> DIGIT -> UPPER\n      0,\n      (5 << 16) + 30,              // LOWER -> DIGIT\n      (5 << 16) + 29,              // LOWER -> MIXED\n      (10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT\n    },\n    {\n      (4 << 16) + 14,              // DIGIT -> UPPER\n      (9 << 16) + (14 << 5) + 28,  // DIGIT -> UPPER -> LOWER\n      0,\n      (9 << 16) + (14 << 5) + 29,  // DIGIT -> UPPER -> MIXED\n      (14 << 16) + (14 << 10) + (29 << 5) + 30,\n                                   // DIGIT -> UPPER -> MIXED -> PUNCT\n    },\n    {\n      (5 << 16) + 29,              // MIXED -> UPPER\n      (5 << 16) + 28,              // MIXED -> LOWER\n      (10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT\n      0,\n      (5 << 16) + 30,              // MIXED -> PUNCT\n    },\n    {\n      (5 << 16) + 31,              // PUNCT -> UPPER\n      (10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER\n      (10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT\n      (10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED\n      0,\n    },\n  };\n\n  // A reverse mapping from [mode][char] to the encoding for that character\n  // in that mode.  An entry of 0 indicates no mapping exists.\n  private static final int[][] CHAR_MAP = new int[5][256];\n  static {\n    CHAR_MAP[MODE_UPPER][' '] = 1;\n    for (int c = 'A'; c <= 'Z'; c++) {\n      CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;\n    }\n    CHAR_MAP[MODE_LOWER][' '] = 1;\n    for (int c = 'a'; c <= 'z'; c++) {\n      CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;\n    }\n    CHAR_MAP[MODE_DIGIT][' '] = 1;\n    for (int c = '0'; c <= '9'; c++) {\n      CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;\n    }\n    CHAR_MAP[MODE_DIGIT][','] = 12;\n    CHAR_MAP[MODE_DIGIT]['.'] = 13;\n    int[] mixedTable = {\n        '\\0', ' ', '\\1', '\\2', '\\3', '\\4', '\\5', '\\6', '\\7', '\\b', '\\t', '\\n',\n        '\\13', '\\f', '\\r', '\\33', '\\34', '\\35', '\\36', '\\37', '@', '\\\\', '^',\n        '_', '`', '|', '~', '\\177'\n    };\n    for (int i = 0; i < mixedTable.length; i++) {\n      CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;\n    }\n    int[] punctTable = {\n        '\\0', '\\r', '\\0', '\\0', '\\0', '\\0', '!', '\\'', '#', '$', '%', '&', '\\'',\n        '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',\n        '[', ']', '{', '}'\n    };\n    for (int i = 0; i < punctTable.length; i++) {\n      if (punctTable[i] > 0) {\n        CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;\n      }\n    }\n  }\n\n  // A map showing the available shift codes.  (The shifts to BINARY are not\n  // shown\n  static final int[][] SHIFT_TABLE = new int[6][6]; // mode shift codes, per table\n  static {\n    for (int[] table : SHIFT_TABLE) {\n      Arrays.fill(table, -1);\n    }\n    SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;\n\n    SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;\n    SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;\n\n    SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;\n\n    SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;\n    SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;\n  }\n\n  private final byte[] text;\n\n  public HighLevelEncoder(byte[] text) {\n    this.text = text;\n  }\n\n  /**\n   * @return text represented by this encoder encoded as a {@link BitArray}\n   */\n  public BitArray encode() {\n    Collection<State> states = Collections.singletonList(State.INITIAL_STATE);\n    for (int index = 0; index < text.length; index++) {\n      int pairCode;\n      int nextChar = index + 1 < text.length ? text[index + 1] : 0;\n      switch (text[index]) {\n        case '\\r':\n          pairCode = nextChar == '\\n' ? 2 : 0;\n          break;\n        case '.' :\n          pairCode = nextChar == ' ' ? 3 : 0;\n          break;\n        case ',' :\n          pairCode = nextChar == ' ' ? 4 : 0;\n          break;\n        case ':' :\n          pairCode = nextChar == ' ' ? 5 : 0;\n          break;\n        default:\n          pairCode = 0;\n      }\n      if (pairCode > 0) {\n        // We have one of the four special PUNCT pairs.  Treat them specially.\n        // Get a new set of states for the two new characters.\n        states = updateStateListForPair(states, index, pairCode);\n        index++;\n      } else {\n        // Get a new set of states for the new character.\n        states = updateStateListForChar(states, index);\n      }\n    }\n    // We are left with a set of states.  Find the shortest one.\n    State minState = Collections.min(states, new Comparator<State>() {\n      @Override\n      public int compare(State a, State b) {\n        return a.getBitCount() - b.getBitCount();\n      }\n    });\n    // Convert it to a bit array, and return.\n    return minState.toBitArray(text);\n  }\n\n  // We update a set of states for a new character by updating each state\n  // for the new character, merging the results, and then removing the\n  // non-optimal states.\n  private Collection<State> updateStateListForChar(Iterable<State> states, int index) {\n    Collection<State> result = new LinkedList<>();\n    for (State state : states) {\n      updateStateForChar(state, index, result);\n    }\n    return simplifyStates(result);\n  }\n\n  // Return a set of states that represent the possible ways of updating this\n  // state for the next character.  The resulting set of states are added to\n  // the \"result\" list.\n  private void updateStateForChar(State state, int index, Collection<State> result) {\n    char ch = (char) (text[index] & 0xFF);\n    boolean charInCurrentTable = CHAR_MAP[state.getMode()][ch] > 0;\n    State stateNoBinary = null;\n    for (int mode = 0; mode <= MODE_PUNCT; mode++) {\n      int charInMode = CHAR_MAP[mode][ch];\n      if (charInMode > 0) {\n        if (stateNoBinary == null) {\n          // Only create stateNoBinary the first time it's required.\n          stateNoBinary = state.endBinaryShift(index);\n        }\n        // Try generating the character by latching to its mode\n        if (!charInCurrentTable || mode == state.getMode() || mode == MODE_DIGIT) {\n          // If the character is in the current table, we don't want to latch to\n          // any other mode except possibly digit (which uses only 4 bits).  Any\n          // other latch would be equally successful *after* this character, and\n          // so wouldn't save any bits.\n          State latchState = stateNoBinary.latchAndAppend(mode, charInMode);\n          result.add(latchState);\n        }\n        // Try generating the character by switching to its mode.\n        if (!charInCurrentTable && SHIFT_TABLE[state.getMode()][mode] >= 0) {\n          // It never makes sense to temporarily shift to another mode if the\n          // character exists in the current mode.  That can never save bits.\n          State shiftState = stateNoBinary.shiftAndAppend(mode, charInMode);\n          result.add(shiftState);\n        }\n      }\n    }\n    if (state.getBinaryShiftByteCount() > 0 || CHAR_MAP[state.getMode()][ch] == 0) {\n      // It's never worthwhile to go into binary shift mode if you're not already\n      // in binary shift mode, and the character exists in your current mode.\n      // That can never save bits over just outputting the char in the current mode.\n      State binaryState = state.addBinaryShiftChar(index);\n      result.add(binaryState);\n    }\n  }\n\n  private static Collection<State> updateStateListForPair(Iterable<State> states, int index, int pairCode) {\n    Collection<State> result = new LinkedList<>();\n    for (State state : states) {\n      updateStateForPair(state, index, pairCode, result);\n    }\n    return simplifyStates(result);\n  }\n\n  private static void updateStateForPair(State state, int index, int pairCode, Collection<State> result) {\n    State stateNoBinary = state.endBinaryShift(index);\n    // Possibility 1.  Latch to MODE_PUNCT, and then append this code\n    result.add(stateNoBinary.latchAndAppend(MODE_PUNCT, pairCode));\n    if (state.getMode() != MODE_PUNCT) {\n      // Possibility 2.  Shift to MODE_PUNCT, and then append this code.\n      // Every state except MODE_PUNCT (handled above) can shift\n      result.add(stateNoBinary.shiftAndAppend(MODE_PUNCT, pairCode));\n    }\n    if (pairCode == 3 || pairCode == 4) {\n      // both characters are in DIGITS.  Sometimes better to just add two digits\n      State digitState = stateNoBinary\n          .latchAndAppend(MODE_DIGIT, 16 - pairCode)  // period or comma in DIGIT\n          .latchAndAppend(MODE_DIGIT, 1);             // space in DIGIT\n      result.add(digitState);\n    }\n    if (state.getBinaryShiftByteCount() > 0) {\n      // It only makes sense to do the characters as binary if we're already\n      // in binary mode.\n      State binaryState = state.addBinaryShiftChar(index).addBinaryShiftChar(index + 1);\n      result.add(binaryState);\n    }\n  }\n\n  private static Collection<State> simplifyStates(Iterable<State> states) {\n    Collection<State> result = new LinkedList<>();\n    for (State newState : states) {\n      boolean add = true;\n      for (Iterator<State> iterator = result.iterator(); iterator.hasNext();) {\n        State oldState = iterator.next();\n        if (oldState.isBetterThanOrEqualTo(newState)) {\n          add = false;\n          break;\n        }\n        if (newState.isBetterThanOrEqualTo(oldState)) {\n          iterator.remove();\n        }\n      }\n      if (add) {\n        result.add(newState);\n      }\n    }\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/encoder/SimpleToken.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.encoder;\n\nimport com.google.zxing.common.BitArray;\n\nfinal class SimpleToken extends Token {\n\n  // For normal words, indicates value and bitCount\n  private final short value;\n  private final short bitCount;\n\n  SimpleToken(Token previous, int value, int bitCount) {\n    super(previous);\n    this.value = (short) value;\n    this.bitCount = (short) bitCount;\n  }\n\n  @Override\n  void appendTo(BitArray bitArray, byte[] text) {\n    bitArray.appendBits(value, bitCount);\n  }\n\n  @Override\n  public String toString() {\n    int value = this.value & ((1 << bitCount) - 1);\n    value |= 1 << bitCount;\n    return '<' + Integer.toBinaryString(value | (1 << bitCount)).substring(1) + '>';\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/encoder/State.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.encoder;\n\nimport java.util.Deque;\nimport java.util.LinkedList;\n\nimport com.google.zxing.common.BitArray;\n\n/**\n * State represents all information about a sequence necessary to generate the current output.\n * Note that a state is immutable.\n */\nfinal class State {\n\n  static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);\n\n  // The current mode of the encoding (or the mode to which we'll return if\n  // we're in Binary Shift mode.\n  private final int mode;\n  // The list of tokens that we output.  If we are in Binary Shift mode, this\n  // token list does *not* yet included the token for those bytes\n  private final Token token;\n  // If non-zero, the number of most recent bytes that should be output\n  // in Binary Shift mode.\n  private final int binaryShiftByteCount;\n  // The total number of bits generated (including Binary Shift).\n  private final int bitCount;\n\n  private State(Token token, int mode, int binaryBytes, int bitCount) {\n    this.token = token;\n    this.mode = mode;\n    this.binaryShiftByteCount = binaryBytes;\n    this.bitCount = bitCount;\n    // Make sure we match the token\n    //int binaryShiftBitCount = (binaryShiftByteCount * 8) +\n    //    (binaryShiftByteCount == 0 ? 0 :\n    //     binaryShiftByteCount <= 31 ? 10 :\n    //     binaryShiftByteCount <= 62 ? 20 : 21);\n    //assert this.bitCount == token.getTotalBitCount() + binaryShiftBitCount;\n  }\n\n  int getMode() {\n    return mode;\n  }\n\n  Token getToken() {\n    return token;\n  }\n\n  int getBinaryShiftByteCount() {\n    return binaryShiftByteCount;\n  }\n\n  int getBitCount() {\n    return bitCount;\n  }\n\n  // Create a new state representing this state with a latch to a (not\n  // necessary different) mode, and then a code.\n  State latchAndAppend(int mode, int value) {\n    //assert binaryShiftByteCount == 0;\n    int bitCount = this.bitCount;\n    Token token = this.token;\n    if (mode != this.mode) {\n      int latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode];\n      token = token.add(latch & 0xFFFF, latch >> 16);\n      bitCount += latch >> 16;\n    }\n    int latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;\n    token = token.add(value, latchModeBitCount);\n    return new State(token, mode, 0, bitCount + latchModeBitCount);\n  }\n\n  // Create a new state representing this state, with a temporary shift\n  // to a different mode to output a single value.\n  State shiftAndAppend(int mode, int value) {\n    //assert binaryShiftByteCount == 0 && this.mode != mode;\n    Token token = this.token;\n    int thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;\n    // Shifts exist only to UPPER and PUNCT, both with tokens size 5.\n    token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount);\n    token = token.add(value, 5);\n    return new State(token, this.mode, 0, this.bitCount + thisModeBitCount + 5);\n  }\n\n  // Create a new state representing this state, but an additional character\n  // output in Binary Shift mode.\n  State addBinaryShiftChar(int index) {\n    Token token = this.token;\n    int mode = this.mode;\n    int bitCount = this.bitCount;\n    if (this.mode == HighLevelEncoder.MODE_PUNCT || this.mode == HighLevelEncoder.MODE_DIGIT) {\n      //assert binaryShiftByteCount == 0;\n      int latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER];\n      token = token.add(latch & 0xFFFF, latch >> 16);\n      bitCount += latch >> 16;\n      mode = HighLevelEncoder.MODE_UPPER;\n    }\n    int deltaBitCount =\n      (binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 :\n      (binaryShiftByteCount == 62) ? 9 : 8;\n    State result = new State(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount);\n    if (result.binaryShiftByteCount == 2047 + 31) {\n      // The string is as long as it's allowed to be.  We should end it.\n      result = result.endBinaryShift(index + 1);\n    }\n    return result;\n  }\n\n  // Create the state identical to this one, but we are no longer in\n  // Binary Shift mode.\n  State endBinaryShift(int index) {\n    if (binaryShiftByteCount == 0) {\n      return this;\n    }\n    Token token = this.token;\n    token = token.addBinaryShift(index - binaryShiftByteCount, binaryShiftByteCount);\n    //assert token.getTotalBitCount() == this.bitCount;\n    return new State(token, mode, 0, this.bitCount);\n  }\n\n  // Returns true if \"this\" state is better (or equal) to be in than \"that\"\n  // state under all possible circumstances.\n  boolean isBetterThanOrEqualTo(State other) {\n    int newModeBitCount = this.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16);\n    if (this.binaryShiftByteCount < other.binaryShiftByteCount) {\n      // add additional B/S encoding cost of other, if any\n      newModeBitCount += calculateBinaryShiftCost(other) - calculateBinaryShiftCost(this);\n    } else if (this.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0) {\n      // maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)\n      newModeBitCount += 10; \n    }\n    return newModeBitCount <= other.bitCount;\n  }\n\n  BitArray toBitArray(byte[] text) {\n    // Reverse the tokens, so that they are in the order that they should\n    // be output\n    Deque<Token> symbols = new LinkedList<>();\n    for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {\n      symbols.addFirst(token);\n    }\n    BitArray bitArray = new BitArray();\n    // Add each token to the result.\n    for (Token symbol : symbols) {\n      symbol.appendTo(bitArray, text);\n    }\n    //assert bitArray.getSize() == this.bitCount;\n    return bitArray;\n  }\n\n  @Override\n  public String toString() {\n    return String.format(\"%s bits=%d bytes=%d\", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);\n  }\n  \n  private static int calculateBinaryShiftCost(State state) {\n    if (state.binaryShiftByteCount > 62) {\n      return 21; // B/S with extended length\n    }\n    if (state.binaryShiftByteCount > 31) {\n      return 20; // two B/S\n    }\n    if (state.binaryShiftByteCount > 0) {\n      return 10; // one B/S\n    }\n    return 0;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/aztec/encoder/Token.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.aztec.encoder;\n\nimport com.google.zxing.common.BitArray;\n\nabstract class Token {\n\n  static final Token EMPTY = new SimpleToken(null, 0, 0);\n\n  private final Token previous;\n\n  Token(Token previous) {\n    this.previous = previous;\n  }\n\n  final Token getPrevious() {\n    return previous;\n  }\n\n  final Token add(int value, int bitCount) {\n    return new SimpleToken(this, value, bitCount);\n   }\n\n  final Token addBinaryShift(int start, int byteCount) {\n    //int bitCount = (byteCount * 8) + (byteCount <= 31 ? 10 : byteCount <= 62 ? 20 : 21);\n    return new BinaryShiftToken(this, start, byteCount);\n  }\n\n  abstract void appendTo(BitArray bitArray, byte[] text);\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/AbstractDoCoMoResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * <p>See\n * <a href=\"http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html\">\n * DoCoMo's documentation</a> about the result types represented by subclasses of this class.</p>\n *\n * <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less\n * on exception-based mechanisms during parsing.</p>\n *\n * @author Sean Owen\n */\nabstract class AbstractDoCoMoResultParser extends ResultParser {\n\n  static String[] matchDoCoMoPrefixedField(String prefix, String rawText, boolean trim) {\n    return matchPrefixedField(prefix, rawText, ';', trim);\n  }\n\n  static String matchSingleDoCoMoPrefixedField(String prefix, String rawText, boolean trim) {\n    return matchSinglePrefixedField(prefix, rawText, ';', trim);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/AddressBookAUResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Implements KDDI AU's address book format. See\n * <a href=\"http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html\">\n * http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html</a>.\n * (Thanks to Yuzo for translating!)\n *\n * @author Sean Owen\n */\npublic final class AddressBookAUResultParser extends ResultParser {\n\n  @Override\n  public AddressBookParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    // MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF\n    if (!rawText.contains(\"MEMORY\") || !rawText.contains(\"\\r\\n\")) {\n      return null;\n    }\n\n    // NAME1 and NAME2 have specific uses, namely written name and pronunciation, respectively.\n    // Therefore we treat them specially instead of as an array of names.\n    String name = matchSinglePrefixedField(\"NAME1:\", rawText, '\\r', true);\n    String pronunciation = matchSinglePrefixedField(\"NAME2:\", rawText, '\\r', true);\n\n    String[] phoneNumbers = matchMultipleValuePrefix(\"TEL\", 3, rawText, true);\n    String[] emails = matchMultipleValuePrefix(\"MAIL\", 3, rawText, true);\n    String note = matchSinglePrefixedField(\"MEMORY:\", rawText, '\\r', false);\n    String address = matchSinglePrefixedField(\"ADD:\", rawText, '\\r', true);\n    String[] addresses = address == null ? null : new String[] {address};\n    return new AddressBookParsedResult(maybeWrap(name),\n                                       null,\n                                       pronunciation,\n                                       phoneNumbers,\n                                       null,\n                                       emails,\n                                       null,\n                                       null,\n                                       note,\n                                       addresses,\n                                       null,\n                                       null,\n                                       null,\n                                       null,\n                                       null,\n                                       null);\n  }\n\n  private static String[] matchMultipleValuePrefix(String prefix,\n                                                   int max,\n                                                   String rawText,\n                                                   boolean trim) {\n    List<String> values = null;\n    for (int i = 1; i <= max; i++) {\n      String value = matchSinglePrefixedField(prefix + i + ':', rawText, '\\r', trim);\n      if (value == null) {\n        break;\n      }\n      if (values == null) {\n        values = new ArrayList<>(max); // lazy init\n      }\n      values.add(value);\n    }\n    if (values == null) {\n      return null;\n    }\n    return values.toArray(EMPTY_STR_ARRAY);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/AddressBookDoCoMoResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\n/**\n * Implements the \"MECARD\" address book entry format.\n *\n * Supported keys: N, SOUND, TEL, EMAIL, NOTE, ADR, BDAY, URL, plus ORG\n * Unsupported keys: TEL-AV, NICKNAME\n *\n * Except for TEL, multiple values for keys are also not supported;\n * the first one found takes precedence.\n *\n * Our understanding of the MECARD format is based on this document:\n *\n * http://www.mobicode.org.tw/files/OMIA%20Mobile%20Bar%20Code%20Standard%20v3.2.1.doc \n *\n * @author Sean Owen\n */\npublic final class AddressBookDoCoMoResultParser extends AbstractDoCoMoResultParser {\n\n  @Override\n  public AddressBookParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!rawText.startsWith(\"MECARD:\")) {\n      return null;\n    }\n    String[] rawName = matchDoCoMoPrefixedField(\"N:\", rawText, true);\n    if (rawName == null) {\n      return null;\n    }\n    String name = parseName(rawName[0]);\n    String pronunciation = matchSingleDoCoMoPrefixedField(\"SOUND:\", rawText, true);\n    String[] phoneNumbers = matchDoCoMoPrefixedField(\"TEL:\", rawText, true);\n    String[] emails = matchDoCoMoPrefixedField(\"EMAIL:\", rawText, true);\n    String note = matchSingleDoCoMoPrefixedField(\"NOTE:\", rawText, false);\n    String[] addresses = matchDoCoMoPrefixedField(\"ADR:\", rawText, true);\n    String birthday = matchSingleDoCoMoPrefixedField(\"BDAY:\", rawText, true);\n    if (!isStringOfDigits(birthday, 8)) {\n      // No reason to throw out the whole card because the birthday is formatted wrong.\n      birthday = null;\n    }\n    String[] urls = matchDoCoMoPrefixedField(\"URL:\", rawText, true);\n\n    // Although ORG may not be strictly legal in MECARD, it does exist in VCARD and we might as well\n    // honor it when found in the wild.\n    String org = matchSingleDoCoMoPrefixedField(\"ORG:\", rawText, true);\n\n    return new AddressBookParsedResult(maybeWrap(name),\n                                       null,\n                                       pronunciation,\n                                       phoneNumbers,\n                                       null,\n                                       emails,\n                                       null,\n                                       null,\n                                       note,\n                                       addresses,\n                                       null,\n                                       org,\n                                       birthday,\n                                       null,\n                                       urls,\n                                       null);\n  }\n\n  private static String parseName(String name) {\n    int comma = name.indexOf(',');\n    if (comma >= 0) {\n      // Format may be last,first; switch it around\n      return name.substring(comma + 1) + ' ' + name.substring(0, comma);\n    }\n    return name;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/AddressBookParsedResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes contact information, like that in an address book\n * entry.\n *\n * @author Sean Owen\n */\npublic final class AddressBookParsedResult extends ParsedResult {\n\n  private final String[] names;\n  private final String[] nicknames;\n  private final String pronunciation;\n  private final String[] phoneNumbers;\n  private final String[] phoneTypes;\n  private final String[] emails;\n  private final String[] emailTypes;\n  private final String instantMessenger;\n  private final String note;\n  private final String[] addresses;\n  private final String[] addressTypes;\n  private final String org;\n  private final String birthday;\n  private final String title;\n  private final String[] urls;\n  private final String[] geo;\n\n  public AddressBookParsedResult(String[] names,\n                                 String[] phoneNumbers,\n                                 String[] phoneTypes,\n                                 String[] emails,\n                                 String[] emailTypes,\n                                 String[] addresses,\n                                 String[] addressTypes) {\n    this(names,\n         null,\n         null,\n         phoneNumbers,\n         phoneTypes,\n         emails,\n         emailTypes,\n         null,\n         null,\n         addresses,\n         addressTypes,\n         null,\n         null,\n         null,\n         null,\n         null);\n  }\n\n  public AddressBookParsedResult(String[] names,\n                                 String[] nicknames,\n                                 String pronunciation,\n                                 String[] phoneNumbers,\n                                 String[] phoneTypes,\n                                 String[] emails,\n                                 String[] emailTypes,\n                                 String instantMessenger,\n                                 String note,\n                                 String[] addresses,\n                                 String[] addressTypes,\n                                 String org,\n                                 String birthday,\n                                 String title,\n                                 String[] urls,\n                                 String[] geo) {\n    super(ParsedResultType.ADDRESSBOOK);\n    if (phoneNumbers != null && phoneTypes != null && phoneNumbers.length != phoneTypes.length) {\n      throw new IllegalArgumentException(\"Phone numbers and types lengths differ\");\n    }\n    if (emails != null && emailTypes != null && emails.length != emailTypes.length) {\n      throw new IllegalArgumentException(\"Emails and types lengths differ\");\n    }\n    if (addresses != null && addressTypes != null && addresses.length != addressTypes.length) {\n      throw new IllegalArgumentException(\"Addresses and types lengths differ\");\n    }\n    this.names = names;\n    this.nicknames = nicknames;\n    this.pronunciation = pronunciation;\n    this.phoneNumbers = phoneNumbers;\n    this.phoneTypes = phoneTypes;\n    this.emails = emails;\n    this.emailTypes = emailTypes;\n    this.instantMessenger = instantMessenger;\n    this.note = note;\n    this.addresses = addresses;\n    this.addressTypes = addressTypes;\n    this.org = org;\n    this.birthday = birthday;\n    this.title = title;\n    this.urls = urls;\n    this.geo = geo;\n  }\n\n  public String[] getNames() {\n    return names;\n  }\n\n  public String[] getNicknames() {\n    return nicknames;\n  }\n\n  /**\n   * In Japanese, the name is written in kanji, which can have multiple readings. Therefore a hint\n   * is often provided, called furigana, which spells the name phonetically.\n   *\n   * @return The pronunciation of the getNames() field, often in hiragana or katakana.\n   */\n  public String getPronunciation() {\n    return pronunciation;\n  }\n\n  public String[] getPhoneNumbers() {\n    return phoneNumbers;\n  }\n\n  /**\n   * @return optional descriptions of the type of each phone number. It could be like \"HOME\", but,\n   *  there is no guaranteed or standard format.\n   */\n  public String[] getPhoneTypes() {\n    return phoneTypes;\n  }\n\n  public String[] getEmails() {\n    return emails;\n  }\n\n  /**\n   * @return optional descriptions of the type of each e-mail. It could be like \"WORK\", but,\n   *  there is no guaranteed or standard format.\n   */\n  public String[] getEmailTypes() {\n    return emailTypes;\n  }\n  \n  public String getInstantMessenger() {\n    return instantMessenger;\n  }\n\n  public String getNote() {\n    return note;\n  }\n\n  public String[] getAddresses() {\n    return addresses;\n  }\n\n  /**\n   * @return optional descriptions of the type of each e-mail. It could be like \"WORK\", but,\n   *  there is no guaranteed or standard format.\n   */\n  public String[] getAddressTypes() {\n    return addressTypes;\n  }\n\n  public String getTitle() {\n    return title;\n  }\n\n  public String getOrg() {\n    return org;\n  }\n\n  public String[] getURLs() {\n    return urls;\n  }\n\n  /**\n   * @return birthday formatted as yyyyMMdd (e.g. 19780917)\n   */\n  public String getBirthday() {\n    return birthday;\n  }\n\n  /**\n   * @return a location as a latitude/longitude pair\n   */\n  public String[] getGeo() {\n    return geo;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(100);\n    maybeAppend(names, result);\n    maybeAppend(nicknames, result);\n    maybeAppend(pronunciation, result);\n    maybeAppend(title, result);\n    maybeAppend(org, result);\n    maybeAppend(addresses, result);\n    maybeAppend(phoneNumbers, result);\n    maybeAppend(emails, result);\n    maybeAppend(instantMessenger, result);\n    maybeAppend(urls, result);\n    maybeAppend(birthday, result);\n    maybeAppend(geo, result);\n    maybeAppend(note, result);\n    return result.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/BizcardResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Implements the \"BIZCARD\" address book entry format, though this has been\n * largely reverse-engineered from examples observed in the wild -- still\n * looking for a definitive reference.\n *\n * @author Sean Owen\n */\npublic final class BizcardResultParser extends AbstractDoCoMoResultParser {\n\n  // Yes, we extend AbstractDoCoMoResultParser since the format is very much\n  // like the DoCoMo MECARD format, but this is not technically one of \n  // DoCoMo's proposed formats\n\n  @Override\n  public AddressBookParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!rawText.startsWith(\"BIZCARD:\")) {\n      return null;\n    }\n    String firstName = matchSingleDoCoMoPrefixedField(\"N:\", rawText, true);\n    String lastName = matchSingleDoCoMoPrefixedField(\"X:\", rawText, true);\n    String fullName = buildName(firstName, lastName);\n    String title = matchSingleDoCoMoPrefixedField(\"T:\", rawText, true);\n    String org = matchSingleDoCoMoPrefixedField(\"C:\", rawText, true);\n    String[] addresses = matchDoCoMoPrefixedField(\"A:\", rawText, true);\n    String phoneNumber1 = matchSingleDoCoMoPrefixedField(\"B:\", rawText, true);\n    String phoneNumber2 = matchSingleDoCoMoPrefixedField(\"M:\", rawText, true);\n    String phoneNumber3 = matchSingleDoCoMoPrefixedField(\"F:\", rawText, true);\n    String email = matchSingleDoCoMoPrefixedField(\"E:\", rawText, true);\n\n    return new AddressBookParsedResult(maybeWrap(fullName),\n                                       null,\n                                       null,\n                                       buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3),\n                                       null,\n                                       maybeWrap(email),\n                                       null,\n                                       null,\n                                       null,\n                                       addresses,\n                                       null,\n                                       org,\n                                       null,\n                                       title,\n                                       null,\n                                       null);\n  }\n\n  private static String[] buildPhoneNumbers(String number1,\n                                            String number2,\n                                            String number3) {\n    List<String> numbers = new ArrayList<>(3);\n    if (number1 != null) {\n      numbers.add(number1);\n    }\n    if (number2 != null) {\n      numbers.add(number2);\n    }\n    if (number3 != null) {\n      numbers.add(number3);\n    }\n    int size = numbers.size();\n    if (size == 0) {\n      return null;\n    }\n    return numbers.toArray(new String[size]);\n  }\n\n  private static String buildName(String firstName, String lastName) {\n    if (firstName == null) {\n      return lastName;\n    } else {\n      return lastName == null ? firstName : firstName + ' ' + lastName;\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/BookmarkDoCoMoResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\n/**\n * @author Sean Owen\n */\npublic final class BookmarkDoCoMoResultParser extends AbstractDoCoMoResultParser {\n\n  @Override\n  public URIParsedResult parse(Result result) {\n    String rawText = result.getText();\n    if (!rawText.startsWith(\"MEBKM:\")) {\n      return null;\n    }\n    String title = matchSingleDoCoMoPrefixedField(\"TITLE:\", rawText, true);\n    String[] rawUri = matchDoCoMoPrefixedField(\"URL:\", rawText, true);\n    if (rawUri == null) {\n      return null;\n    }\n    String uri = rawUri[0];\n    return URIResultParser.isBasicallyValidURI(uri) ? new URIParsedResult(uri, title) : null;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/CalendarParsedResult.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.Locale;\nimport java.util.TimeZone;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Represents a parsed result that encodes a calendar event at a certain time, optionally\n * with attendees and a location.\n *\n * @author Sean Owen\n */\npublic final class CalendarParsedResult extends ParsedResult {\n\n  private static final Pattern RFC2445_DURATION =\n      Pattern.compile(\"P(?:(\\\\d+)W)?(?:(\\\\d+)D)?(?:T(?:(\\\\d+)H)?(?:(\\\\d+)M)?(?:(\\\\d+)S)?)?\");\n  private static final long[] RFC2445_DURATION_FIELD_UNITS = {\n      7 * 24 * 60 * 60 * 1000L, // 1 week\n      24 * 60 * 60 * 1000L, // 1 day\n      60 * 60 * 1000L, // 1 hour\n      60 * 1000L, // 1 minute\n      1000L, // 1 second\n  };\n\n  private static final Pattern DATE_TIME = Pattern.compile(\"[0-9]{8}(T[0-9]{6}Z?)?\");\n\n  private final String summary;\n  private final long start;\n  private final boolean startAllDay;\n  private final long end;\n  private final boolean endAllDay;\n  private final String location;\n  private final String organizer;\n  private final String[] attendees;\n  private final String description;\n  private final double latitude;\n  private final double longitude;\n\n  public CalendarParsedResult(String summary,\n                              String startString,\n                              String endString,\n                              String durationString,\n                              String location,\n                              String organizer,\n                              String[] attendees,\n                              String description,\n                              double latitude,\n                              double longitude) {\n    super(ParsedResultType.CALENDAR);\n    this.summary = summary;\n\n    try {\n      this.start = parseDate(startString);\n    } catch (ParseException pe) {\n      throw new IllegalArgumentException(pe.toString());\n    }\n\n    if (endString == null) {\n      long durationMS = parseDurationMS(durationString);\n      end = durationMS < 0L ? -1L : start + durationMS;\n    } else {\n      try {\n        this.end = parseDate(endString);\n      } catch (ParseException pe) {\n        throw new IllegalArgumentException(pe.toString());\n      }\n    }\n\n    this.startAllDay = startString.length() == 8;\n    this.endAllDay = endString != null && endString.length() == 8;\n\n    this.location = location;\n    this.organizer = organizer;\n    this.attendees = attendees;\n    this.description = description;\n    this.latitude = latitude;\n    this.longitude = longitude;\n  }\n\n  public String getSummary() {\n    return summary;\n  }\n\n  /**\n   * @return start time\n   * @deprecated use {@link #getStartTimestamp()}\n   */\n  @Deprecated\n  public Date getStart() {\n    return new Date(start);\n  }\n\n  /**\n   * @return start time\n   * @see #getEndTimestamp()\n   */\n  public long getStartTimestamp() {\n    return start;\n  }\n\n  /**\n   * @return true if start time was specified as a whole day\n   */\n  public boolean isStartAllDay() {\n    return startAllDay;\n  }\n\n  /**\n   * @return event end {@link Date}, or {@code null} if event has no duration\n   * @deprecated use {@link #getEndTimestamp()}\n   */\n  @Deprecated\n  public Date getEnd() {\n    return end < 0L ? null : new Date(end);\n  }\n\n  /**\n   * @return event end {@link Date}, or -1 if event has no duration\n   * @see #getStartTimestamp()\n   */\n  public long getEndTimestamp() {\n    return end;\n  }\n\n  /**\n   * @return true if end time was specified as a whole day\n   */\n  public boolean isEndAllDay() {\n    return endAllDay;\n  }\n\n  public String getLocation() {\n    return location;\n  }\n\n  public String getOrganizer() {\n    return organizer;\n  }\n\n  public String[] getAttendees() {\n    return attendees;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public double getLatitude() {\n    return latitude;\n  }\n\n  public double getLongitude() {\n    return longitude;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(100);\n    maybeAppend(summary, result);\n    maybeAppend(format(startAllDay, start), result);\n    maybeAppend(format(endAllDay, end), result);\n    maybeAppend(location, result);\n    maybeAppend(organizer, result);\n    maybeAppend(attendees, result);\n    maybeAppend(description, result);\n    return result.toString();\n  }\n\n  /**\n   * Parses a string as a date. RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021)\n   * or DATE-TIME (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC).\n   *\n   * @param when The string to parse\n   * @throws ParseException if not able to parse as a date\n   */\n  private static long parseDate(String when) throws ParseException {\n    if (!DATE_TIME.matcher(when).matches()) {\n      throw new ParseException(when, 0);\n    }\n    if (when.length() == 8) {\n      // Show only year/month/day\n      DateFormat format = new SimpleDateFormat(\"yyyyMMdd\", Locale.ENGLISH);\n      // For dates without a time, for purposes of interacting with Android, the resulting timestamp\n      // needs to be midnight of that day in GMT. See:\n      // http://code.google.com/p/android/issues/detail?id=8330\n      format.setTimeZone(TimeZone.getTimeZone(\"GMT\"));\n      return format.parse(when).getTime();\n    }\n    // The when string can be local time, or UTC if it ends with a Z\n    if (when.length() == 16 && when.charAt(15) == 'Z') {\n      long milliseconds = parseDateTimeString(when.substring(0, 15));\n      Calendar calendar = new GregorianCalendar();\n      // Account for time zone difference\n      milliseconds += calendar.get(Calendar.ZONE_OFFSET);\n      // Might need to correct for daylight savings time, but use target time since\n      // now might be in DST but not then, or vice versa\n      calendar.setTime(new Date(milliseconds));\n      return milliseconds + calendar.get(Calendar.DST_OFFSET);\n    }\n    return parseDateTimeString(when);\n  }\n\n  private static String format(boolean allDay, long date) {\n    if (date < 0L) {\n      return null;\n    }\n    DateFormat format = allDay\n        ? DateFormat.getDateInstance(DateFormat.MEDIUM)\n        : DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);\n    return format.format(date);\n  }\n\n  private static long parseDurationMS(CharSequence durationString) {\n    if (durationString == null) {\n      return -1L;\n    }\n    Matcher m = RFC2445_DURATION.matcher(durationString);\n    if (!m.matches()) {\n      return -1L;\n    }\n    long durationMS = 0L;\n    for (int i = 0; i < RFC2445_DURATION_FIELD_UNITS.length; i++) {\n      String fieldValue = m.group(i + 1);\n      if (fieldValue != null) {\n        durationMS += RFC2445_DURATION_FIELD_UNITS[i] * Integer.parseInt(fieldValue);\n      }\n    }\n    return durationMS;\n  }\n\n  private static long parseDateTimeString(String dateTimeString) throws ParseException {\n    DateFormat format = new SimpleDateFormat(\"yyyyMMdd'T'HHmmss\", Locale.ENGLISH);\n    return format.parse(dateTimeString).getTime();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/EmailAddressParsedResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes an email message including recipients, subject\n * and body text.\n *\n * @author Sean Owen\n */\npublic final class EmailAddressParsedResult extends ParsedResult {\n\n  private final String[] tos;\n  private final String[] ccs;\n  private final String[] bccs;\n  private final String subject;\n  private final String body;\n\n  EmailAddressParsedResult(String to) {\n    this(new String[] {to}, null, null, null, null);\n  }\n\n  EmailAddressParsedResult(String[] tos,\n                           String[] ccs,\n                           String[] bccs,\n                           String subject,\n                           String body) {\n    super(ParsedResultType.EMAIL_ADDRESS);\n    this.tos = tos;\n    this.ccs = ccs;\n    this.bccs = bccs;\n    this.subject = subject;\n    this.body = body;\n  }\n\n  /**\n   * @return first elements of {@link #getTos()} or {@code null} if none\n   * @deprecated use {@link #getTos()}\n   */\n  @Deprecated\n  public String getEmailAddress() {\n    return tos == null || tos.length == 0 ? null : tos[0];\n  }\n\n  public String[] getTos() {\n    return tos;\n  }\n\n  public String[] getCCs() {\n    return ccs;\n  }\n\n  public String[] getBCCs() {\n    return bccs;\n  }\n\n  public String getSubject() {\n    return subject;\n  }\n\n  public String getBody() {\n    return body;\n  }\n\n  /**\n   * @return \"mailto:\"\n   * @deprecated without replacement\n   */\n  @Deprecated\n  public String getMailtoURI() {\n    return \"mailto:\";\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(30);\n    maybeAppend(tos, result);\n    maybeAppend(ccs, result);\n    maybeAppend(bccs, result);\n    maybeAppend(subject, result);\n    maybeAppend(body, result);\n    return result.toString();\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/EmailAddressResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/**\n * Represents a result that encodes an e-mail address, either as a plain address\n * like \"joe@example.org\" or a mailto: URL like \"mailto:joe@example.org\".\n *\n * @author Sean Owen\n */\npublic final class EmailAddressResultParser extends ResultParser {\n\n  private static final Pattern COMMA = Pattern.compile(\",\");\n\n  @Override\n  public EmailAddressParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (rawText.startsWith(\"mailto:\") || rawText.startsWith(\"MAILTO:\")) {\n      // If it starts with mailto:, assume it is definitely trying to be an email address\n      String hostEmail = rawText.substring(7);\n      int queryStart = hostEmail.indexOf('?');\n      if (queryStart >= 0) {\n        hostEmail = hostEmail.substring(0, queryStart);\n      }\n      try {\n        hostEmail = urlDecode(hostEmail);\n      } catch (IllegalArgumentException iae) {\n        return null;\n      }\n      String[] tos = null;\n      if (!hostEmail.isEmpty()) {\n        tos = COMMA.split(hostEmail);\n      }\n      Map<String,String> nameValues = parseNameValuePairs(rawText);\n      String[] ccs = null;\n      String[] bccs = null;\n      String subject = null;\n      String body = null;\n      if (nameValues != null) {\n        if (tos == null) {\n          String tosString = nameValues.get(\"to\");\n          if (tosString != null) {\n            tos = COMMA.split(tosString);\n          }\n        }\n        String ccString = nameValues.get(\"cc\");\n        if (ccString != null) {\n          ccs = COMMA.split(ccString);\n        }\n        String bccString = nameValues.get(\"bcc\");\n        if (bccString != null) {\n          bccs = COMMA.split(bccString);\n        }\n        subject = nameValues.get(\"subject\");\n        body = nameValues.get(\"body\");\n      }\n      return new EmailAddressParsedResult(tos, ccs, bccs, subject, body);\n    } else {\n      if (!EmailDoCoMoResultParser.isBasicallyValidEmailAddress(rawText)) {\n        return null;\n      }\n      return new EmailAddressParsedResult(rawText);\n    }\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/EmailDoCoMoResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.regex.Pattern;\n\n/**\n * Implements the \"MATMSG\" email message entry format.\n *\n * Supported keys: TO, SUB, BODY\n *\n * @author Sean Owen\n */\npublic final class EmailDoCoMoResultParser extends AbstractDoCoMoResultParser {\n\n  private static final Pattern ATEXT_ALPHANUMERIC = Pattern.compile(\"[a-zA-Z0-9@.!#$%&'*+\\\\-/=?^_`{|}~]+\");\n\n  @Override\n  public EmailAddressParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!rawText.startsWith(\"MATMSG:\")) {\n      return null;\n    }\n    String[] tos = matchDoCoMoPrefixedField(\"TO:\", rawText, true);\n    if (tos == null) {\n      return null;\n    }\n    for (String to : tos) {\n      if (!isBasicallyValidEmailAddress(to)) {\n        return null;\n      }\n    }\n    String subject = matchSingleDoCoMoPrefixedField(\"SUB:\", rawText, false);\n    String body = matchSingleDoCoMoPrefixedField(\"BODY:\", rawText, false);\n    return new EmailAddressParsedResult(tos, null, null, subject, body);\n  }\n\n  /**\n   * This implements only the most basic checking for an email address's validity -- that it contains\n   * an '@' and contains no characters disallowed by RFC 2822. This is an overly lenient definition of\n   * validity. We want to generally be lenient here since this class is only intended to encapsulate what's\n   * in a barcode, not \"judge\" it.\n   */\n  static boolean isBasicallyValidEmailAddress(String email) {\n    return email != null && ATEXT_ALPHANUMERIC.matcher(email).matches() && email.indexOf('@') >= 0;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ExpandedProductParsedResult.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.client.result;\n\nimport java.util.Map;\n\n/**\n * Represents a parsed result that encodes extended product information as encoded\n * by the RSS format, like weight, price, dates, etc.\n *\n * @author Antonio Manuel Benjumea Conde, Servinform, S.A.\n * @author Agustín Delgado, Servinform, S.A.\n */\npublic final class ExpandedProductParsedResult extends ParsedResult {\n\n  public static final String KILOGRAM = \"KG\";\n  public static final String POUND = \"LB\";\n\n  private final String rawText;\n  private final String productID;\n  private final String sscc;\n  private final String lotNumber;\n  private final String productionDate;\n  private final String packagingDate;\n  private final String bestBeforeDate;\n  private final String expirationDate;\n  private final String weight;\n  private final String weightType;\n  private final String weightIncrement;\n  private final String price;\n  private final String priceIncrement;\n  private final String priceCurrency;\n  // For AIS that not exist in this object\n  private final Map<String,String> uncommonAIs;\n\n  public ExpandedProductParsedResult(String rawText,\n                                     String productID,\n                                     String sscc,\n                                     String lotNumber,\n                                     String productionDate,\n                                     String packagingDate,\n                                     String bestBeforeDate,\n                                     String expirationDate,\n                                     String weight,\n                                     String weightType,\n                                     String weightIncrement,\n                                     String price,\n                                     String priceIncrement,\n                                     String priceCurrency,\n                                     Map<String,String> uncommonAIs) {\n    super(ParsedResultType.PRODUCT);\n    this.rawText = rawText;\n    this.productID = productID;\n    this.sscc = sscc;\n    this.lotNumber = lotNumber;\n    this.productionDate = productionDate;\n    this.packagingDate = packagingDate;\n    this.bestBeforeDate = bestBeforeDate;\n    this.expirationDate = expirationDate;\n    this.weight = weight;\n    this.weightType = weightType;\n    this.weightIncrement = weightIncrement;\n    this.price = price;\n    this.priceIncrement = priceIncrement;\n    this.priceCurrency = priceCurrency;\n    this.uncommonAIs = uncommonAIs;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (!(o instanceof ExpandedProductParsedResult)) {\n      return false;\n    }\n\n    ExpandedProductParsedResult other = (ExpandedProductParsedResult) o;\n\n    return equalsOrNull(productID, other.productID)\n        && equalsOrNull(sscc, other.sscc)\n        && equalsOrNull(lotNumber, other.lotNumber)\n        && equalsOrNull(productionDate, other.productionDate)\n        && equalsOrNull(bestBeforeDate, other.bestBeforeDate)\n        && equalsOrNull(expirationDate, other.expirationDate)\n        && equalsOrNull(weight, other.weight)\n        && equalsOrNull(weightType, other.weightType)\n        && equalsOrNull(weightIncrement, other.weightIncrement)\n        && equalsOrNull(price, other.price)\n        && equalsOrNull(priceIncrement, other.priceIncrement)\n        && equalsOrNull(priceCurrency, other.priceCurrency)\n        && equalsOrNull(uncommonAIs, other.uncommonAIs);\n  }\n\n  private static boolean equalsOrNull(Object o1, Object o2) {\n    return o1 == null ? o2 == null : o1.equals(o2);\n  }\n\n  @Override\n  public int hashCode() {\n    int hash = 0;\n    hash ^= hashNotNull(productID);\n    hash ^= hashNotNull(sscc);\n    hash ^= hashNotNull(lotNumber);\n    hash ^= hashNotNull(productionDate);\n    hash ^= hashNotNull(bestBeforeDate);\n    hash ^= hashNotNull(expirationDate);\n    hash ^= hashNotNull(weight);\n    hash ^= hashNotNull(weightType);\n    hash ^= hashNotNull(weightIncrement);\n    hash ^= hashNotNull(price);\n    hash ^= hashNotNull(priceIncrement);\n    hash ^= hashNotNull(priceCurrency);\n    hash ^= hashNotNull(uncommonAIs);\n    return hash;\n  }\n\n  private static int hashNotNull(Object o) {\n    return o == null ? 0 : o.hashCode();\n  }\n\n  public String getRawText() {\n    return rawText;\n  }\n\n  public String getProductID() {\n    return productID;\n  }\n\n  public String getSscc() {\n    return sscc;\n  }\n\n  public String getLotNumber() {\n    return lotNumber;\n  }\n\n  public String getProductionDate() {\n    return productionDate;\n  }\n\n  public String getPackagingDate() {\n    return packagingDate;\n  }\n\n  public String getBestBeforeDate() {\n    return bestBeforeDate;\n  }\n\n  public String getExpirationDate() {\n    return expirationDate;\n  }\n\n  public String getWeight() {\n    return weight;\n  }\n\n  public String getWeightType() {\n    return weightType;\n  }\n\n  public String getWeightIncrement() {\n    return weightIncrement;\n  }\n\n  public String getPrice() {\n    return price;\n  }\n\n  public String getPriceIncrement() {\n    return priceIncrement;\n  }\n\n  public String getPriceCurrency() {\n    return priceCurrency;\n  }\n\n  public Map<String,String> getUncommonAIs() {\n    return uncommonAIs;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    return String.valueOf(rawText);\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ExpandedProductResultParser.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.client.result;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.Result;\n\n/**\n * Parses strings of digits that represent a RSS Extended code.\n * \n * @author Antonio Manuel Benjumea Conde, Servinform, S.A.\n * @author Agustín Delgado, Servinform, S.A.\n */\npublic final class ExpandedProductResultParser extends ResultParser {\n\n  @Override\n  public ExpandedProductParsedResult parse(Result result) {\n    BarcodeFormat format = result.getBarcodeFormat();\n    if (format != BarcodeFormat.RSS_EXPANDED) {\n      // ExtendedProductParsedResult NOT created. Not a RSS Expanded barcode\n      return null;\n    }\n    String rawText = getMassagedText(result);\n\n    String productID = null;\n    String sscc = null;\n    String lotNumber = null;\n    String productionDate = null;\n    String packagingDate = null;\n    String bestBeforeDate = null;\n    String expirationDate = null;\n    String weight = null;\n    String weightType = null;\n    String weightIncrement = null;\n    String price = null;\n    String priceIncrement = null;\n    String priceCurrency = null;\n    Map<String,String> uncommonAIs = new HashMap<>();\n\n    int i = 0;\n\n    while (i < rawText.length()) {\n      String ai = findAIvalue(i, rawText);\n      if (ai == null) {\n        // Error. Code doesn't match with RSS expanded pattern\n        // ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern\n        return null;\n      }\n      i += ai.length() + 2;\n      String value = findValue(i, rawText);\n      i += value.length();\n\n      switch (ai) {\n        case \"00\":\n          sscc = value;\n          break;\n        case \"01\":\n          productID = value;\n          break;\n        case \"10\":\n          lotNumber = value;\n          break;\n        case \"11\":\n          productionDate = value;\n          break;\n        case \"13\":\n          packagingDate = value;\n          break;\n        case \"15\":\n          bestBeforeDate = value;\n          break;\n        case \"17\":\n          expirationDate = value;\n          break;\n        case \"3100\":\n        case \"3101\":\n        case \"3102\":\n        case \"3103\":\n        case \"3104\":\n        case \"3105\":\n        case \"3106\":\n        case \"3107\":\n        case \"3108\":\n        case \"3109\":\n          weight = value;\n          weightType = ExpandedProductParsedResult.KILOGRAM;\n          weightIncrement = ai.substring(3);\n          break;\n        case \"3200\":\n        case \"3201\":\n        case \"3202\":\n        case \"3203\":\n        case \"3204\":\n        case \"3205\":\n        case \"3206\":\n        case \"3207\":\n        case \"3208\":\n        case \"3209\":\n          weight = value;\n          weightType = ExpandedProductParsedResult.POUND;\n          weightIncrement = ai.substring(3);\n          break;\n        case \"3920\":\n        case \"3921\":\n        case \"3922\":\n        case \"3923\":\n          price = value;\n          priceIncrement = ai.substring(3);\n          break;\n        case \"3930\":\n        case \"3931\":\n        case \"3932\":\n        case \"3933\":\n          if (value.length() < 4) {\n            // The value must have more of 3 symbols (3 for currency and\n            // 1 at least for the price)\n            // ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern\n            return null;\n          }\n          price = value.substring(3);\n          priceCurrency = value.substring(0, 3);\n          priceIncrement = ai.substring(3);\n          break;\n        default:\n          // No match with common AIs\n          uncommonAIs.put(ai, value);\n          break;\n      }\n    }\n\n    return new ExpandedProductParsedResult(rawText,\n                                           productID,\n                                           sscc,\n                                           lotNumber,\n                                           productionDate,\n                                           packagingDate,\n                                           bestBeforeDate,\n                                           expirationDate,\n                                           weight,\n                                           weightType,\n                                           weightIncrement,\n                                           price,\n                                           priceIncrement,\n                                           priceCurrency,\n                                           uncommonAIs);\n  }\n\n  private static String findAIvalue(int i, String rawText) {\n    char c = rawText.charAt(i);\n    // First character must be a open parenthesis.If not, ERROR\n    if (c != '(') {\n      return null;\n    }\n\n    CharSequence rawTextAux = rawText.substring(i + 1);\n\n    StringBuilder buf = new StringBuilder();\n    for (int index = 0; index < rawTextAux.length(); index++) {\n      char currentChar = rawTextAux.charAt(index);\n      if (currentChar == ')') {\n        return buf.toString();\n      }\n      if (currentChar < '0' || currentChar > '9') {\n        return null;\n      }\n      buf.append(currentChar);\n    }\n    return buf.toString();\n  }\n\n  private static String findValue(int i, String rawText) {\n    StringBuilder buf = new StringBuilder();\n    String rawTextAux = rawText.substring(i);\n\n    for (int index = 0; index < rawTextAux.length(); index++) {\n      char c = rawTextAux.charAt(index);\n      if (c == '(') {\n        // We look for a new AI. If it doesn't exist (ERROR), we continue\n        // with the iteration\n        if (findAIvalue(index, rawTextAux) != null) {\n          break;\n        }\n        buf.append('(');\n      } else {\n        buf.append(c);\n      }\n    }\n    return buf.toString();\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/GeoParsedResult.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes a geographic coordinate, with latitude,\n * longitude and altitude.\n *\n * @author Sean Owen\n */\npublic final class GeoParsedResult extends ParsedResult {\n\n  private final double latitude;\n  private final double longitude;\n  private final double altitude;\n  private final String query;\n\n  GeoParsedResult(double latitude, double longitude, double altitude, String query) {\n    super(ParsedResultType.GEO);\n    this.latitude = latitude;\n    this.longitude = longitude;\n    this.altitude = altitude;\n    this.query = query;\n  }\n\n  public String getGeoURI() {\n    StringBuilder result = new StringBuilder();\n    result.append(\"geo:\");\n    result.append(latitude);\n    result.append(',');\n    result.append(longitude);\n    if (altitude > 0) {\n      result.append(',');\n      result.append(altitude);\n    }\n    if (query != null) {\n      result.append('?');\n      result.append(query);\n    }\n    return result.toString();\n  }\n\n  /**\n   * @return latitude in degrees\n   */\n  public double getLatitude() {\n    return latitude;\n  }\n\n  /**\n   * @return longitude in degrees\n   */\n  public double getLongitude() {\n    return longitude;\n  }\n\n  /**\n   * @return altitude in meters. If not specified, in the geo URI, returns 0.0\n   */\n  public double getAltitude() {\n    return altitude;\n  }\n\n  /**\n   * @return query string associated with geo URI or null if none exists\n   */\n  public String getQuery() {\n    return query;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(20);\n    result.append(latitude);\n    result.append(\", \");\n    result.append(longitude);\n    if (altitude > 0.0) {\n      result.append(\", \");\n      result.append(altitude);\n      result.append('m');\n    }\n    if (query != null) {\n      result.append(\" (\");\n      result.append(query);\n      result.append(')');\n    }\n    return result.toString();\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/GeoResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Parses a \"geo:\" URI result, which specifies a location on the surface of\n * the Earth as well as an optional altitude above the surface. See\n * <a href=\"http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00\">\n * http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00</a>.\n *\n * @author Sean Owen\n */\npublic final class GeoResultParser extends ResultParser {\n\n  private static final Pattern GEO_URL_PATTERN = \n      Pattern.compile(\"geo:([\\\\-0-9.]+),([\\\\-0-9.]+)(?:,([\\\\-0-9.]+))?(?:\\\\?(.*))?\", Pattern.CASE_INSENSITIVE);\n  \n  @Override\n  public GeoParsedResult parse(Result result) {\n    CharSequence rawText = getMassagedText(result);\n    Matcher matcher = GEO_URL_PATTERN.matcher(rawText);\n    if (!matcher.matches()) {\n      return null;\n    }\n\n    String query = matcher.group(4);\n\n    double latitude;\n    double longitude;\n    double altitude;\n    try {\n      latitude = Double.parseDouble(matcher.group(1));\n      if (latitude > 90.0 || latitude < -90.0) {\n        return null;\n      }\n      longitude = Double.parseDouble(matcher.group(2));\n      if (longitude > 180.0 || longitude < -180.0) {\n        return null;\n      }\n      if (matcher.group(3) == null) {\n        altitude = 0.0;\n      } else {\n        altitude = Double.parseDouble(matcher.group(3));\n        if (altitude < 0.0) {\n          return null;\n        }\n      }\n    } catch (NumberFormatException ignored) {\n      return null;\n    }\n    return new GeoParsedResult(latitude, longitude, altitude, query);\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ISBNParsedResult.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes a product ISBN number.\n *\n * @author jbreiden@google.com (Jeff Breidenbach)\n */\npublic final class ISBNParsedResult extends ParsedResult {\n\n  private final String isbn;\n\n  ISBNParsedResult(String isbn) {\n    super(ParsedResultType.ISBN);\n    this.isbn = isbn;\n  }\n\n  public String getISBN() {\n    return isbn;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    return isbn;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ISBNResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.Result;\n\n/**\n * Parses strings of digits that represent a ISBN.\n * \n * @author jbreiden@google.com (Jeff Breidenbach)\n */\npublic final class ISBNResultParser extends ResultParser {\n\n  /**\n   * See <a href=\"http://www.bisg.org/isbn-13/for.dummies.html\">ISBN-13 For Dummies</a>\n   */\n  @Override\n  public ISBNParsedResult parse(Result result) {\n    BarcodeFormat format = result.getBarcodeFormat();\n    if (format != BarcodeFormat.EAN_13) {\n      return null;\n    }\n    String rawText = getMassagedText(result);\n    int length = rawText.length();\n    if (length != 13) {\n      return null;\n    }\n    if (!rawText.startsWith(\"978\") && !rawText.startsWith(\"979\")) {\n      return null;\n    }\n   \n    return new ISBNParsedResult(rawText);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ParsedResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * <p>Abstract class representing the result of decoding a barcode, as more than\n * a String -- as some type of structured data. This might be a subclass which represents\n * a URL, or an e-mail address. {@link ResultParser#parseResult(com.google.zxing.Result)} will turn a raw\n * decoded string into the most appropriate type of structured representation.</p>\n *\n * <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less\n * on exception-based mechanisms during parsing.</p>\n *\n * @author Sean Owen\n */\npublic abstract class ParsedResult {\n\n  private final ParsedResultType type;\n\n  protected ParsedResult(ParsedResultType type) {\n    this.type = type;\n  }\n\n  public final ParsedResultType getType() {\n    return type;\n  }\n\n  public abstract String getDisplayResult();\n\n  @Override\n  public final String toString() {\n    return getDisplayResult();\n  }\n\n  public static void maybeAppend(String value, StringBuilder result) {\n    if (value != null && !value.isEmpty()) {\n      // Don't add a newline before the first value\n      if (result.length() > 0) {\n        result.append('\\n');\n      }\n      result.append(value);\n    }\n  }\n\n  public static void maybeAppend(String[] values, StringBuilder result) {\n    if (values != null) {\n      for (String value : values) {\n        maybeAppend(value, result);\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ParsedResultType.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents the type of data encoded by a barcode -- from plain text, to a\n * URI, to an e-mail address, etc.\n *\n * @author Sean Owen\n */\npublic enum ParsedResultType {\n\n  ADDRESSBOOK,\n  EMAIL_ADDRESS,\n  PRODUCT,\n  URI,\n  TEXT,\n  GEO,\n  TEL,\n  SMS,\n  CALENDAR,\n  WIFI,\n  ISBN,\n  VIN,\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ProductParsedResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes a product by an identifier of some kind.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class ProductParsedResult extends ParsedResult {\n\n  private final String productID;\n  private final String normalizedProductID;\n\n  ProductParsedResult(String productID) {\n    this(productID, productID);\n  }\n\n  ProductParsedResult(String productID, String normalizedProductID) {\n    super(ParsedResultType.PRODUCT);\n    this.productID = productID;\n    this.normalizedProductID = normalizedProductID;\n  }\n\n  public String getProductID() {\n    return productID;\n  }\n\n  public String getNormalizedProductID() {\n    return normalizedProductID;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    return productID;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ProductResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.Result;\nimport com.google.zxing.oned.UPCEReader;\n\n/**\n * Parses strings of digits that represent a UPC code.\n * \n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class ProductResultParser extends ResultParser {\n\n  // Treat all UPC and EAN variants as UPCs, in the sense that they are all product barcodes.\n  @Override\n  public ProductParsedResult parse(Result result) {\n    BarcodeFormat format = result.getBarcodeFormat();\n    if (!(format == BarcodeFormat.UPC_A || format == BarcodeFormat.UPC_E ||\n          format == BarcodeFormat.EAN_8 || format == BarcodeFormat.EAN_13)) {\n      return null;\n    }\n    String rawText = getMassagedText(result);\n    if (!isStringOfDigits(rawText, rawText.length())) {\n      return null;\n    }\n    // Not actually checking the checksum again here    \n\n    String normalizedProductID;\n    // Expand UPC-E for purposes of searching\n    if (format == BarcodeFormat.UPC_E && rawText.length() == 8) {\n      normalizedProductID = UPCEReader.convertUPCEtoUPCA(rawText);\n    } else {\n      normalizedProductID = rawText;\n    }\n\n    return new ProductParsedResult(rawText, normalizedProductID);\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/ResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/**\n * <p>Abstract class representing the result of decoding a barcode, as more than\n * a String -- as some type of structured data. This might be a subclass which represents\n * a URL, or an e-mail address. {@link #parseResult(Result)} will turn a raw\n * decoded string into the most appropriate type of structured representation.</p>\n *\n * <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less\n * on exception-based mechanisms during parsing.</p>\n *\n * @author Sean Owen\n */\npublic abstract class ResultParser {\n\n  private static final ResultParser[] PARSERS = {\n      new BookmarkDoCoMoResultParser(),\n      new AddressBookDoCoMoResultParser(),\n      new EmailDoCoMoResultParser(),\n      new AddressBookAUResultParser(),\n      new VCardResultParser(),\n      new BizcardResultParser(),\n      new VEventResultParser(),\n      new EmailAddressResultParser(),\n      new SMTPResultParser(),\n      new TelResultParser(),\n      new SMSMMSResultParser(),\n      new SMSTOMMSTOResultParser(),\n      new GeoResultParser(),\n      new WifiResultParser(),\n      new URLTOResultParser(),\n      new URIResultParser(),\n      new ISBNResultParser(),\n      new ProductResultParser(),\n      new ExpandedProductResultParser(),\n      new VINResultParser(),\n  };\n\n  private static final Pattern DIGITS = Pattern.compile(\"\\\\d+\");\n  private static final Pattern AMPERSAND = Pattern.compile(\"&\");\n  private static final Pattern EQUALS = Pattern.compile(\"=\");\n  private static final String BYTE_ORDER_MARK = \"\\ufeff\";\n\n  static final String[] EMPTY_STR_ARRAY = new String[0];\n\n  /**\n   * Attempts to parse the raw {@link Result}'s contents as a particular type\n   * of information (email, URL, etc.) and return a {@link ParsedResult} encapsulating\n   * the result of parsing.\n   *\n   * @param theResult the raw {@link Result} to parse\n   * @return {@link ParsedResult} encapsulating the parsing result\n   */\n  public abstract ParsedResult parse(Result theResult);\n\n  protected static String getMassagedText(Result result) {\n    String text = result.getText();\n    if (text.startsWith(BYTE_ORDER_MARK)) {\n      text = text.substring(1);\n    }\n    return text;\n  }\n\n  public static ParsedResult parseResult(Result theResult) {\n    for (ResultParser parser : PARSERS) {\n      ParsedResult result = parser.parse(theResult);\n      if (result != null) {\n        return result;\n      }\n    }\n    return new TextParsedResult(theResult.getText(), null);\n  }\n\n  protected static void maybeAppend(String value, StringBuilder result) {\n    if (value != null) {\n      result.append('\\n');\n      result.append(value);\n    }\n  }\n\n  protected static void maybeAppend(String[] value, StringBuilder result) {\n    if (value != null) {\n      for (String s : value) {\n        result.append('\\n');\n        result.append(s);\n      }\n    }\n  }\n\n  protected static String[] maybeWrap(String value) {\n    return value == null ? null : new String[] { value };\n  }\n\n  protected static String unescapeBackslash(String escaped) {\n    int backslash = escaped.indexOf('\\\\');\n    if (backslash < 0) {\n      return escaped;\n    }\n    int max = escaped.length();\n    StringBuilder unescaped = new StringBuilder(max - 1);\n    unescaped.append(escaped.toCharArray(), 0, backslash);\n    boolean nextIsEscaped = false;\n    for (int i = backslash; i < max; i++) {\n      char c = escaped.charAt(i);\n      if (nextIsEscaped || c != '\\\\') {\n        unescaped.append(c);\n        nextIsEscaped = false;\n      } else {\n        nextIsEscaped = true;\n      }\n    }\n    return unescaped.toString();\n  }\n\n  protected static int parseHexDigit(char c) {\n    if (c >= '0' && c <= '9') {\n      return c - '0';\n    }\n    if (c >= 'a' && c <= 'f') {\n      return 10 + (c - 'a');\n    }\n    if (c >= 'A' && c <= 'F') {\n      return 10 + (c - 'A');\n    }\n    return -1;\n  }\n\n  protected static boolean isStringOfDigits(CharSequence value, int length) {\n    return value != null && length > 0 && length == value.length() && DIGITS.matcher(value).matches();\n  }\n\n  protected static boolean isSubstringOfDigits(CharSequence value, int offset, int length) {\n    if (value == null || length <= 0) {\n      return false;\n    }\n    int max = offset + length;\n    return value.length() >= max && DIGITS.matcher(value.subSequence(offset, max)).matches();\n  }\n\n  static Map<String,String> parseNameValuePairs(String uri) {\n    int paramStart = uri.indexOf('?');\n    if (paramStart < 0) {\n      return null;\n    }\n    Map<String,String> result = new HashMap<>(3);\n    for (String keyValue : AMPERSAND.split(uri.substring(paramStart + 1))) {\n      appendKeyValue(keyValue, result);\n    }\n    return result;\n  }\n\n  private static void appendKeyValue(CharSequence keyValue, Map<String,String> result) {\n    String[] keyValueTokens = EQUALS.split(keyValue, 2);\n    if (keyValueTokens.length == 2) {\n      String key = keyValueTokens[0];\n      String value = keyValueTokens[1];\n      try {\n        value = urlDecode(value);\n        result.put(key, value);\n      } catch (IllegalArgumentException iae) {\n        // continue; invalid data such as an escape like %0t\n      }\n    }\n  }\n\n  static String urlDecode(String encoded) {\n    try {\n      return URLDecoder.decode(encoded, \"UTF-8\");\n    } catch (UnsupportedEncodingException uee) {\n      throw new IllegalStateException(uee); // can't happen\n    }\n  }\n\n  static String[] matchPrefixedField(String prefix, String rawText, char endChar, boolean trim) {\n    List<String> matches = null;\n    int i = 0;\n    int max = rawText.length();\n    while (i < max) {\n      i = rawText.indexOf(prefix, i);\n      if (i < 0) {\n        break;\n      }\n      i += prefix.length(); // Skip past this prefix we found to start\n      int start = i; // Found the start of a match here\n      boolean more = true;\n      while (more) {\n        i = rawText.indexOf(endChar, i);\n        if (i < 0) {\n          // No terminating end character? uh, done. Set i such that loop terminates and break\n          i = rawText.length();\n          more = false;\n        } else if (countPrecedingBackslashes(rawText, i) % 2 != 0) {\n          // semicolon was escaped (odd count of preceding backslashes) so continue\n          i++;\n        } else {\n          // found a match\n          if (matches == null) {\n            matches = new ArrayList<>(3); // lazy init\n          }\n          String element = unescapeBackslash(rawText.substring(start, i));\n          if (trim) {\n            element = element.trim();\n          }\n          if (!element.isEmpty()) {\n            matches.add(element);\n          }\n          i++;\n          more = false;\n        }\n      }\n    }\n    if (matches == null || matches.isEmpty()) {\n      return null;\n    }\n    return matches.toArray(EMPTY_STR_ARRAY);\n  }\n\n  private static int countPrecedingBackslashes(CharSequence s, int pos) {\n    int count = 0;\n    for (int i = pos - 1; i >= 0; i--) {\n      if (s.charAt(i) == '\\\\') {\n        count++;\n      } else {\n        break;\n      }\n    }\n    return count;\n  }\n\n  static String matchSinglePrefixedField(String prefix, String rawText, char endChar, boolean trim) {\n    String[] matches = matchPrefixedField(prefix, rawText, endChar, trim);\n    return matches == null ? null : matches[0];\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/SMSMMSResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>Parses an \"sms:\" URI result, which specifies a number to SMS.\n * See <a href=\"http://tools.ietf.org/html/rfc5724\"> RFC 5724</a> on this.</p>\n *\n * <p>This class supports \"via\" syntax for numbers, which is not part of the spec.\n * For example \"+12125551212;via=+12124440101\" may appear as a number.\n * It also supports a \"subject\" query parameter, which is not mentioned in the spec.\n * These are included since they were mentioned in earlier IETF drafts and might be\n * used.</p>\n *\n * <p>This actually also parses URIs starting with \"mms:\" and treats them all the same way,\n * and effectively converts them to an \"sms:\" URI for purposes of forwarding to the platform.</p>\n *\n * @author Sean Owen\n */\npublic final class SMSMMSResultParser extends ResultParser {\n\n  @Override\n  public SMSParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!(rawText.startsWith(\"sms:\") || rawText.startsWith(\"SMS:\") ||\n          rawText.startsWith(\"mms:\") || rawText.startsWith(\"MMS:\"))) {\n      return null;\n    }\n\n    // Check up front if this is a URI syntax string with query arguments\n    Map<String,String> nameValuePairs = parseNameValuePairs(rawText);\n    String subject = null;\n    String body = null;\n    boolean querySyntax = false;\n    if (nameValuePairs != null && !nameValuePairs.isEmpty()) {\n      subject = nameValuePairs.get(\"subject\");\n      body = nameValuePairs.get(\"body\");\n      querySyntax = true;\n    }\n\n    // Drop sms, query portion\n    int queryStart = rawText.indexOf('?', 4);\n    String smsURIWithoutQuery;\n    // If it's not query syntax, the question mark is part of the subject or message\n    if (queryStart < 0 || !querySyntax) {\n      smsURIWithoutQuery = rawText.substring(4);\n    } else {\n      smsURIWithoutQuery = rawText.substring(4, queryStart);\n    }\n\n    int lastComma = -1;\n    int comma;\n    List<String> numbers = new ArrayList<>(1);\n    List<String> vias = new ArrayList<>(1);\n    while ((comma = smsURIWithoutQuery.indexOf(',', lastComma + 1)) > lastComma) {\n      String numberPart = smsURIWithoutQuery.substring(lastComma + 1, comma);\n      addNumberVia(numbers, vias, numberPart);\n      lastComma = comma;\n    }\n    addNumberVia(numbers, vias, smsURIWithoutQuery.substring(lastComma + 1));    \n\n    return new SMSParsedResult(numbers.toArray(EMPTY_STR_ARRAY),\n                               vias.toArray(EMPTY_STR_ARRAY),\n                               subject,\n                               body);\n  }\n\n  private static void addNumberVia(Collection<String> numbers,\n                                   Collection<String> vias,\n                                   String numberPart) {\n    int numberEnd = numberPart.indexOf(';');\n    if (numberEnd < 0) {\n      numbers.add(numberPart);\n      vias.add(null);\n    } else {\n      numbers.add(numberPart.substring(0, numberEnd));\n      String maybeVia = numberPart.substring(numberEnd + 1);\n      String via;\n      if (maybeVia.startsWith(\"via=\")) {\n        via = maybeVia.substring(4);\n      } else {\n        via = null;\n      }\n      vias.add(via);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/SMSParsedResult.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes an SMS message, including recipients, subject\n * and body text.\n *\n * @author Sean Owen\n */\npublic final class SMSParsedResult extends ParsedResult {\n\n  private final String[] numbers;\n  private final String[] vias;\n  private final String subject;\n  private final String body;\n\n  public SMSParsedResult(String number,\n                         String via,\n                         String subject,\n                         String body) {\n    super(ParsedResultType.SMS);\n    this.numbers = new String[] {number};\n    this.vias = new String[] {via};\n    this.subject = subject;\n    this.body = body;\n  }\n\n  public SMSParsedResult(String[] numbers,\n                         String[] vias,\n                         String subject,\n                         String body) {\n    super(ParsedResultType.SMS);\n    this.numbers = numbers;\n    this.vias = vias;\n    this.subject = subject;\n    this.body = body;\n  }\n\n  public String getSMSURI() {\n    StringBuilder result = new StringBuilder();\n    result.append(\"sms:\");\n    boolean first = true;\n    for (int i = 0; i < numbers.length; i++) {\n      if (first) {\n        first = false;\n      } else {\n        result.append(',');\n      }\n      result.append(numbers[i]);\n      if (vias != null && vias[i] != null) {\n        result.append(\";via=\");\n        result.append(vias[i]);\n      }\n    }\n    boolean hasBody = body != null;\n    boolean hasSubject = subject != null;\n    if (hasBody || hasSubject) {\n      result.append('?');\n      if (hasBody) {\n        result.append(\"body=\");\n        result.append(body);\n      }\n      if (hasSubject) {\n        if (hasBody) {\n          result.append('&');\n        }\n        result.append(\"subject=\");\n        result.append(subject);\n      }\n    }\n    return result.toString();\n  }\n\n  public String[] getNumbers() {\n    return numbers;\n  }\n\n  public String[] getVias() {\n    return vias;\n  }\n\n  public String getSubject() {\n    return subject;\n  }\n\n  public String getBody() {\n    return body;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(100);\n    maybeAppend(numbers, result);\n    maybeAppend(subject, result);\n    maybeAppend(body, result);\n    return result.toString();\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/SMSTOMMSTOResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\n/**\n * <p>Parses an \"smsto:\" URI result, whose format is not standardized but appears to be like:\n * {@code smsto:number(:body)}.</p>\n *\n * <p>This actually also parses URIs starting with \"smsto:\", \"mmsto:\", \"SMSTO:\", and\n * \"MMSTO:\", and treats them all the same way, and effectively converts them to an \"sms:\" URI\n * for purposes of forwarding to the platform.</p>\n *\n * @author Sean Owen\n */\npublic final class SMSTOMMSTOResultParser extends ResultParser {\n\n  @Override\n  public SMSParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!(rawText.startsWith(\"smsto:\") || rawText.startsWith(\"SMSTO:\") ||\n          rawText.startsWith(\"mmsto:\") || rawText.startsWith(\"MMSTO:\"))) {\n      return null;\n    }\n    // Thanks to dominik.wild for suggesting this enhancement to support\n    // smsto:number:body URIs\n    String number = rawText.substring(6);\n    String body = null;\n    int bodyStart = number.indexOf(':');\n    if (bodyStart >= 0) {\n      body = number.substring(bodyStart + 1);\n      number = number.substring(0, bodyStart);\n    }\n    return new SMSParsedResult(number, null, null, body);\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/SMTPResultParser.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\n/**\n * <p>Parses an \"smtp:\" URI result, whose format is not standardized but appears to be like:\n * {@code smtp[:subject[:body]]}.</p>\n *\n * @author Sean Owen\n */\npublic final class SMTPResultParser extends ResultParser {\n\n  @Override\n  public EmailAddressParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!(rawText.startsWith(\"smtp:\") || rawText.startsWith(\"SMTP:\"))) {\n      return null;\n    }\n    String emailAddress = rawText.substring(5);\n    String subject = null;\n    String body = null;\n    int colon = emailAddress.indexOf(':');\n    if (colon >= 0) {\n      subject = emailAddress.substring(colon + 1);\n      emailAddress = emailAddress.substring(0, colon);\n      colon = subject.indexOf(':');\n      if (colon >= 0) {\n        body = subject.substring(colon + 1);\n        subject = subject.substring(0, colon);\n      }\n    }\n    return new EmailAddressParsedResult(new String[] {emailAddress},\n                                        null,\n                                        null,\n                                        subject,\n                                        body);\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/TelParsedResult.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes a telephone number.\n *\n * @author Sean Owen\n */\npublic final class TelParsedResult extends ParsedResult {\n\n  private final String number;\n  private final String telURI;\n  private final String title;\n\n  public TelParsedResult(String number, String telURI, String title) {\n    super(ParsedResultType.TEL);\n    this.number = number;\n    this.telURI = telURI;\n    this.title = title;\n  }\n\n  public String getNumber() {\n    return number;\n  }\n\n  public String getTelURI() {\n    return telURI;\n  }\n\n  public String getTitle() {\n    return title;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(20);\n    maybeAppend(number, result);\n    maybeAppend(title, result);\n    return result.toString();\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/TelResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\n/**\n * Parses a \"tel:\" URI result, which specifies a phone number.\n *\n * @author Sean Owen\n */\npublic final class TelResultParser extends ResultParser {\n\n  @Override\n  public TelParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!rawText.startsWith(\"tel:\") && !rawText.startsWith(\"TEL:\")) {\n      return null;\n    }\n    // Normalize \"TEL:\" to \"tel:\"\n    String telURI = rawText.startsWith(\"TEL:\") ? \"tel:\" + rawText.substring(4) : rawText;\n    // Drop tel, query portion\n    int queryStart = rawText.indexOf('?', 4);\n    String number = queryStart < 0 ? rawText.substring(4) : rawText.substring(4, queryStart);\n    return new TelParsedResult(number, telURI, null);\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/TextParsedResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * A simple result type encapsulating a string that has no further\n * interpretation.\n * \n * @author Sean Owen\n */\npublic final class TextParsedResult extends ParsedResult {\n\n  private final String text;\n  private final String language;\n\n  public TextParsedResult(String text, String language) {\n    super(ParsedResultType.TEXT);\n    this.text = text;\n    this.language = language;\n  }\n\n  public String getText() {\n    return text;\n  }\n\n  public String getLanguage() {\n    return language;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    return text;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/URIParsedResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * A simple result type encapsulating a URI that has no further interpretation.\n *\n * @author Sean Owen\n */\npublic final class URIParsedResult extends ParsedResult {\n\n  private final String uri;\n  private final String title;\n\n  public URIParsedResult(String uri, String title) {\n    super(ParsedResultType.URI);\n    this.uri = massageURI(uri);\n    this.title = title;\n  }\n\n  public String getURI() {\n    return uri;\n  }\n\n  public String getTitle() {\n    return title;\n  }\n\n  /**\n   * @return true if the URI contains suspicious patterns that may suggest it intends to\n   *  mislead the user about its true nature\n   * @deprecated see {@link URIResultParser#isPossiblyMaliciousURI(String)}\n   */\n  @Deprecated\n  public boolean isPossiblyMaliciousURI() {\n    return URIResultParser.isPossiblyMaliciousURI(uri);\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(30);\n    maybeAppend(title, result);\n    maybeAppend(uri, result);\n    return result.toString();\n  }\n\n  /**\n   * Transforms a string that represents a URI into something more proper, by adding or canonicalizing\n   * the protocol.\n   */\n  private static String massageURI(String uri) {\n    uri = uri.trim();\n    int protocolEnd = uri.indexOf(':');\n    if (protocolEnd < 0 || isColonFollowedByPortNumber(uri, protocolEnd)) {\n      // No protocol, or found a colon, but it looks like it is after the host, so the protocol is still missing,\n      // so assume http\n      uri = \"http://\" + uri;\n    }\n    return uri;\n  }\n\n  private static boolean isColonFollowedByPortNumber(String uri, int protocolEnd) {\n    int start = protocolEnd + 1;\n    int nextSlash = uri.indexOf('/', start);\n    if (nextSlash < 0) {\n      nextSlash = uri.length();\n    }\n    return ResultParser.isSubstringOfDigits(uri, start, nextSlash - start);\n  }\n\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/URIResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Tries to parse results that are a URI of some kind.\n * \n * @author Sean Owen\n */\npublic final class URIResultParser extends ResultParser {\n\n  private static final Pattern ALLOWED_URI_CHARS_PATTERN =\n      Pattern.compile(\"[-._~:/?#\\\\[\\\\]@!$&'()*+,;=%A-Za-z0-9]+\");\n  private static final Pattern USER_IN_HOST = Pattern.compile(\":/*([^/@]+)@[^/]+\");\n  // See http://www.ietf.org/rfc/rfc2396.txt\n  private static final Pattern URL_WITH_PROTOCOL_PATTERN = Pattern.compile(\"[a-zA-Z][a-zA-Z0-9+-.]+:\");\n  private static final Pattern URL_WITHOUT_PROTOCOL_PATTERN = Pattern.compile(\n      \"([a-zA-Z0-9\\\\-]+\\\\.){1,6}[a-zA-Z]{2,}\" + // host name elements; allow up to say 6 domain elements\n      \"(:\\\\d{1,5})?\" + // maybe port\n      \"(/|\\\\?|$)\"); // query, path or nothing\n\n  @Override\n  public URIParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    // We specifically handle the odd \"URL\" scheme here for simplicity and add \"URI\" for fun\n    // Assume anything starting this way really means to be a URI\n    if (rawText.startsWith(\"URL:\") || rawText.startsWith(\"URI:\")) {\n      return new URIParsedResult(rawText.substring(4).trim(), null);\n    }\n    rawText = rawText.trim();\n    if (!isBasicallyValidURI(rawText) || isPossiblyMaliciousURI(rawText)) {\n      return null;\n    }\n    return new URIParsedResult(rawText, null);\n  }\n\n  /**\n   * @return true if the URI contains suspicious patterns that may suggest it intends to\n   *  mislead the user about its true nature. At the moment this looks for the presence\n   *  of user/password syntax in the host/authority portion of a URI which may be used\n   *  in attempts to make the URI's host appear to be other than it is. Example:\n   *  http://yourbank.com@phisher.com  This URI connects to phisher.com but may appear\n   *  to connect to yourbank.com at first glance.\n   */\n  static boolean isPossiblyMaliciousURI(String uri) {\n    return !ALLOWED_URI_CHARS_PATTERN.matcher(uri).matches() || USER_IN_HOST.matcher(uri).find();\n  }\n\n  static boolean isBasicallyValidURI(String uri) {\n    if (uri.contains(\" \")) {\n      // Quick hack check for a common case\n      return false;\n    }\n    Matcher m = URL_WITH_PROTOCOL_PATTERN.matcher(uri);\n    if (m.find() && m.start() == 0) { // match at start only\n      return true;\n    }\n    m = URL_WITHOUT_PROTOCOL_PATTERN.matcher(uri);\n    return m.find() && m.start() == 0;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/URLTOResultParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\n/**\n * Parses the \"URLTO\" result format, which is of the form \"URLTO:[title]:[url]\".\n * This seems to be used sometimes, but I am not able to find documentation\n * on its origin or official format?\n *\n * @author Sean Owen\n */\npublic final class URLTOResultParser extends ResultParser {\n\n  @Override\n  public URIParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!rawText.startsWith(\"urlto:\") && !rawText.startsWith(\"URLTO:\")) {\n      return null;\n    }\n    int titleEnd = rawText.indexOf(':', 6);\n    if (titleEnd < 0) {\n      return null;\n    }\n    String title = titleEnd <= 6 ? null : rawText.substring(6, titleEnd);\n    String uri = rawText.substring(titleEnd + 1);\n    return new URIParsedResult(uri, title);\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/VCardResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Parses contact information formatted according to the VCard (2.1) format. This is not a complete\n * implementation but should parse information as commonly encoded in 2D barcodes.\n *\n * @author Sean Owen\n */\npublic final class VCardResultParser extends ResultParser {\n\n  private static final Pattern BEGIN_VCARD = Pattern.compile(\"BEGIN:VCARD\", Pattern.CASE_INSENSITIVE);\n  private static final Pattern VCARD_LIKE_DATE = Pattern.compile(\"\\\\d{4}-?\\\\d{2}-?\\\\d{2}\");\n  private static final Pattern CR_LF_SPACE_TAB = Pattern.compile(\"\\r\\n[ \\t]\");\n  private static final Pattern NEWLINE_ESCAPE = Pattern.compile(\"\\\\\\\\[nN]\");\n  private static final Pattern VCARD_ESCAPES = Pattern.compile(\"\\\\\\\\([,;\\\\\\\\])\");\n  private static final Pattern EQUALS = Pattern.compile(\"=\");\n  private static final Pattern SEMICOLON = Pattern.compile(\";\");\n  private static final Pattern UNESCAPED_SEMICOLONS = Pattern.compile(\"(?<!\\\\\\\\);+\");\n  private static final Pattern COMMA = Pattern.compile(\",\");\n  private static final Pattern SEMICOLON_OR_COMMA = Pattern.compile(\"[;,]\");\n\n  @Override\n  public AddressBookParsedResult parse(Result result) {\n    // Although we should insist on the raw text ending with \"END:VCARD\", there's no reason\n    // to throw out everything else we parsed just because this was omitted. In fact, Eclair\n    // is doing just that, and we can't parse its contacts without this leniency.\n    String rawText = getMassagedText(result);\n    Matcher m = BEGIN_VCARD.matcher(rawText);\n    if (!m.find() || m.start() != 0) {\n      return null;\n    }\n    List<List<String>> names = matchVCardPrefixedField(\"FN\", rawText, true, false);\n    if (names == null) {\n      // If no display names found, look for regular name fields and format them\n      names = matchVCardPrefixedField(\"N\", rawText, true, false);\n      formatNames(names);\n    }\n    List<String> nicknameString = matchSingleVCardPrefixedField(\"NICKNAME\", rawText, true, false);\n    String[] nicknames = nicknameString == null ? null : COMMA.split(nicknameString.get(0));\n    List<List<String>> phoneNumbers = matchVCardPrefixedField(\"TEL\", rawText, true, false);\n    List<List<String>> emails = matchVCardPrefixedField(\"EMAIL\", rawText, true, false);\n    List<String> note = matchSingleVCardPrefixedField(\"NOTE\", rawText, false, false);\n    List<List<String>> addresses = matchVCardPrefixedField(\"ADR\", rawText, true, true);\n    List<String> org = matchSingleVCardPrefixedField(\"ORG\", rawText, true, true);\n    List<String> birthday = matchSingleVCardPrefixedField(\"BDAY\", rawText, true, false);\n    if (birthday != null && !isLikeVCardDate(birthday.get(0))) {\n      birthday = null;\n    }\n    List<String> title = matchSingleVCardPrefixedField(\"TITLE\", rawText, true, false);\n    List<List<String>> urls = matchVCardPrefixedField(\"URL\", rawText, true, false);\n    List<String> instantMessenger = matchSingleVCardPrefixedField(\"IMPP\", rawText, true, false);\n    List<String> geoString = matchSingleVCardPrefixedField(\"GEO\", rawText, true, false);\n    String[] geo = geoString == null ? null : SEMICOLON_OR_COMMA.split(geoString.get(0));\n    if (geo != null && geo.length != 2) {\n      geo = null;\n    }\n    return new AddressBookParsedResult(toPrimaryValues(names),\n                                       nicknames,\n                                       null, \n                                       toPrimaryValues(phoneNumbers), \n                                       toTypes(phoneNumbers),\n                                       toPrimaryValues(emails),\n                                       toTypes(emails),\n                                       toPrimaryValue(instantMessenger),\n                                       toPrimaryValue(note),\n                                       toPrimaryValues(addresses),\n                                       toTypes(addresses),\n                                       toPrimaryValue(org),\n                                       toPrimaryValue(birthday),\n                                       toPrimaryValue(title),\n                                       toPrimaryValues(urls),\n                                       geo);\n  }\n\n  static List<List<String>> matchVCardPrefixedField(CharSequence prefix,\n                                                    String rawText,\n                                                    boolean trim,\n                                                    boolean parseFieldDivider) {\n    List<List<String>> matches = null;\n    int i = 0;\n    int max = rawText.length();\n\n    while (i < max) {\n\n      // At start or after newline, match prefix, followed by optional metadata \n      // (led by ;) ultimately ending in colon\n      Matcher matcher = Pattern.compile(\"(?:^|\\n)\" + prefix + \"(?:;([^:]*))?:\",\n                                        Pattern.CASE_INSENSITIVE).matcher(rawText);\n      if (i > 0) {\n        i--; // Find from i-1 not i since looking at the preceding character\n      }\n      if (!matcher.find(i)) {\n        break;\n      }\n      i = matcher.end(0); // group 0 = whole pattern; end(0) is past final colon\n\n      String metadataString = matcher.group(1); // group 1 = metadata substring\n      List<String> metadata = null;\n      boolean quotedPrintable = false;\n      String quotedPrintableCharset = null;\n      String valueType = null;\n      if (metadataString != null) {\n        for (String metadatum : SEMICOLON.split(metadataString)) {\n          if (metadata == null) {\n            metadata = new ArrayList<>(1);\n          }\n          metadata.add(metadatum);\n          String[] metadatumTokens = EQUALS.split(metadatum, 2);\n          if (metadatumTokens.length > 1) {\n            String key = metadatumTokens[0];\n            String value = metadatumTokens[1];\n            if (\"ENCODING\".equalsIgnoreCase(key) && \"QUOTED-PRINTABLE\".equalsIgnoreCase(value)) {\n              quotedPrintable = true;\n            } else if (\"CHARSET\".equalsIgnoreCase(key)) {\n              quotedPrintableCharset = value;\n            } else if (\"VALUE\".equalsIgnoreCase(key)) {\n              valueType = value;\n            }\n          }\n        }\n      }\n\n      int matchStart = i; // Found the start of a match here\n\n      while ((i = rawText.indexOf('\\n', i)) >= 0) { // Really, end in \\r\\n\n        if (i < rawText.length() - 1 &&           // But if followed by tab or space,\n            (rawText.charAt(i + 1) == ' ' ||        // this is only a continuation\n             rawText.charAt(i + 1) == '\\t')) {\n          i += 2; // Skip \\n and continutation whitespace\n        } else if (quotedPrintable &&             // If preceded by = in quoted printable\n                   ((i >= 1 && rawText.charAt(i - 1) == '=') || // this is a continuation\n                    (i >= 2 && rawText.charAt(i - 2) == '='))) {\n          i++; // Skip \\n\n        } else {\n          break;\n        }\n      }\n\n      if (i < 0) {\n        // No terminating end character? uh, done. Set i such that loop terminates and break\n        i = max;\n      } else if (i > matchStart) {\n        // found a match\n        if (matches == null) {\n          matches = new ArrayList<>(1); // lazy init\n        }\n        if (i >= 1 && rawText.charAt(i - 1) == '\\r') {\n          i--; // Back up over \\r, which really should be there\n        }\n        String element = rawText.substring(matchStart, i);\n        if (trim) {\n          element = element.trim();\n        }\n        if (quotedPrintable) {\n          element = decodeQuotedPrintable(element, quotedPrintableCharset);\n          if (parseFieldDivider) {\n            element = UNESCAPED_SEMICOLONS.matcher(element).replaceAll(\"\\n\").trim();\n          }\n        } else {\n          if (parseFieldDivider) {\n            element = UNESCAPED_SEMICOLONS.matcher(element).replaceAll(\"\\n\").trim();\n          }\n          element = CR_LF_SPACE_TAB.matcher(element).replaceAll(\"\");\n          element = NEWLINE_ESCAPE.matcher(element).replaceAll(\"\\n\");\n          element = VCARD_ESCAPES.matcher(element).replaceAll(\"$1\");\n        }\n        // Only handle VALUE=uri specially\n        if (\"uri\".equals(valueType)) {\n          // Don't actually support dereferencing URIs, but use scheme-specific part not URI\n          // as value, to support tel: and mailto:\n          try {\n            element = URI.create(element).getSchemeSpecificPart();\n          } catch (IllegalArgumentException iae) {\n            // ignore\n          }\n        }\n        if (metadata == null) {\n          List<String> match = new ArrayList<>(1);\n          match.add(element);\n          matches.add(match);\n        } else {\n          metadata.add(0, element);\n          matches.add(metadata);\n        }\n        i++;\n      } else {\n        i++;\n      }\n\n    }\n\n    return matches;\n  }\n\n  private static String decodeQuotedPrintable(CharSequence value, String charset) {\n    int length = value.length();\n    StringBuilder result = new StringBuilder(length);\n    ByteArrayOutputStream fragmentBuffer = new ByteArrayOutputStream();\n    for (int i = 0; i < length; i++) {\n      char c = value.charAt(i);\n      switch (c) {\n        case '\\r':\n        case '\\n':\n          break;\n        case '=':\n          if (i < length - 2) {\n            char nextChar = value.charAt(i + 1);\n            if (nextChar != '\\r' && nextChar != '\\n') {\n              char nextNextChar = value.charAt(i + 2);\n              int firstDigit = parseHexDigit(nextChar);\n              int secondDigit = parseHexDigit(nextNextChar);\n              if (firstDigit >= 0 && secondDigit >= 0) {\n                fragmentBuffer.write((firstDigit << 4) + secondDigit);\n              } // else ignore it, assume it was incorrectly encoded\n              i += 2;\n            }\n          }\n          break;\n        default:\n          maybeAppendFragment(fragmentBuffer, charset, result);\n          result.append(c);\n      }\n    }\n    maybeAppendFragment(fragmentBuffer, charset, result);\n    return result.toString();\n  }\n\n  private static void maybeAppendFragment(ByteArrayOutputStream fragmentBuffer,\n                                          String charset,\n                                          StringBuilder result) {\n    if (fragmentBuffer.size() > 0) {\n      byte[] fragmentBytes = fragmentBuffer.toByteArray();\n      String fragment;\n      if (charset == null) {\n        fragment = new String(fragmentBytes, StandardCharsets.UTF_8);\n      } else {\n        try {\n          fragment = new String(fragmentBytes, charset);\n        } catch (UnsupportedEncodingException e) {\n          fragment = new String(fragmentBytes, StandardCharsets.UTF_8);\n        }\n      }\n      fragmentBuffer.reset();\n      result.append(fragment);\n    }\n  }\n\n  static List<String> matchSingleVCardPrefixedField(CharSequence prefix,\n                                                    String rawText,\n                                                    boolean trim,\n                                                    boolean parseFieldDivider) {\n    List<List<String>> values = matchVCardPrefixedField(prefix, rawText, trim, parseFieldDivider);\n    return values == null || values.isEmpty() ? null : values.get(0);\n  }\n  \n  private static String toPrimaryValue(List<String> list) {\n    return list == null || list.isEmpty() ? null : list.get(0);\n  }\n  \n  private static String[] toPrimaryValues(Collection<List<String>> lists) {\n    if (lists == null || lists.isEmpty()) {\n      return null;\n    }\n    List<String> result = new ArrayList<>(lists.size());\n    for (List<String> list : lists) {\n      String value = list.get(0);\n      if (value != null && !value.isEmpty()) {\n        result.add(value);\n      }\n    }\n    return result.toArray(EMPTY_STR_ARRAY);\n  }\n  \n  private static String[] toTypes(Collection<List<String>> lists) {\n    if (lists == null || lists.isEmpty()) {\n      return null;\n    }\n    List<String> result = new ArrayList<>(lists.size());\n    for (List<String> list : lists) {\n      String value = list.get(0);\n      if (value != null && !value.isEmpty()) {\n        String type = null;\n        for (int i = 1; i < list.size(); i++) {\n          String metadatum = list.get(i);\n          int equals = metadatum.indexOf('=');\n          if (equals < 0) {\n            // take the whole thing as a usable label\n            type = metadatum;\n            break;\n          }\n          if (\"TYPE\".equalsIgnoreCase(metadatum.substring(0, equals))) {\n            type = metadatum.substring(equals + 1);\n            break;\n          }\n        }\n        result.add(type);\n      }\n    }\n    return result.toArray(EMPTY_STR_ARRAY);\n  }\n\n  private static boolean isLikeVCardDate(CharSequence value) {\n    return value == null || VCARD_LIKE_DATE.matcher(value).matches();\n  }\n\n  /**\n   * Formats name fields of the form \"Public;John;Q.;Reverend;III\" into a form like\n   * \"Reverend John Q. Public III\".\n   *\n   * @param names name values to format, in place\n   */\n  private static void formatNames(Iterable<List<String>> names) {\n    if (names != null) {\n      for (List<String> list : names) {\n        String name = list.get(0);\n        String[] components = new String[5];\n        int start = 0;\n        int end;\n        int componentIndex = 0;\n        while (componentIndex < components.length - 1 && (end = name.indexOf(';', start)) >= 0) {\n          components[componentIndex] = name.substring(start, end);\n          componentIndex++;\n          start = end + 1;\n        }\n        components[componentIndex] = name.substring(start);\n        StringBuilder newName = new StringBuilder(100);\n        maybeAppendComponent(components, 3, newName);\n        maybeAppendComponent(components, 1, newName);\n        maybeAppendComponent(components, 2, newName);\n        maybeAppendComponent(components, 0, newName);\n        maybeAppendComponent(components, 4, newName);\n        list.set(0, newName.toString().trim());\n      }\n    }\n  }\n\n  private static void maybeAppendComponent(String[] components, int i, StringBuilder newName) {\n    if (components[i] != null && !components[i].isEmpty()) {\n      if (newName.length() > 0) {\n        newName.append(' ');\n      }\n      newName.append(components[i]);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/VEventResultParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\nimport java.util.List;\n\n/**\n * Partially implements the iCalendar format's \"VEVENT\" format for specifying a\n * calendar event. See RFC 2445. This supports SUMMARY, LOCATION, GEO, DTSTART and DTEND fields.\n *\n * @author Sean Owen\n */\npublic final class VEventResultParser extends ResultParser {\n\n  @Override\n  public CalendarParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    int vEventStart = rawText.indexOf(\"BEGIN:VEVENT\");\n    if (vEventStart < 0) {\n      return null;\n    }\n\n    String summary = matchSingleVCardPrefixedField(\"SUMMARY\", rawText, true);\n    String start = matchSingleVCardPrefixedField(\"DTSTART\", rawText, true);\n    if (start == null) {\n      return null;\n    }\n    String end = matchSingleVCardPrefixedField(\"DTEND\", rawText, true);\n    String duration = matchSingleVCardPrefixedField(\"DURATION\", rawText, true);\n    String location = matchSingleVCardPrefixedField(\"LOCATION\", rawText, true);\n    String organizer = stripMailto(matchSingleVCardPrefixedField(\"ORGANIZER\", rawText, true));\n\n    String[] attendees = matchVCardPrefixedField(\"ATTENDEE\", rawText, true);\n    if (attendees != null) {\n      for (int i = 0; i < attendees.length; i++) {\n        attendees[i] = stripMailto(attendees[i]);\n      }\n    }\n    String description = matchSingleVCardPrefixedField(\"DESCRIPTION\", rawText, true);\n\n    String geoString = matchSingleVCardPrefixedField(\"GEO\", rawText, true);\n    double latitude;\n    double longitude;\n    if (geoString == null) {\n      latitude = Double.NaN;\n      longitude = Double.NaN;\n    } else {\n      int semicolon = geoString.indexOf(';');\n      if (semicolon < 0) {\n        return null;\n      }\n      try {\n        latitude = Double.parseDouble(geoString.substring(0, semicolon));\n        longitude = Double.parseDouble(geoString.substring(semicolon + 1));\n      } catch (NumberFormatException ignored) {\n        return null;\n      }\n    }\n\n    try {\n      return new CalendarParsedResult(summary,\n                                      start,\n                                      end,\n                                      duration,\n                                      location,\n                                      organizer,\n                                      attendees,\n                                      description,\n                                      latitude,\n                                      longitude);\n    } catch (IllegalArgumentException ignored) {\n      return null;\n    }\n  }\n\n  private static String matchSingleVCardPrefixedField(CharSequence prefix,\n                                                      String rawText,\n                                                      boolean trim) {\n    List<String> values = VCardResultParser.matchSingleVCardPrefixedField(prefix, rawText, trim, false);\n    return values == null || values.isEmpty() ? null : values.get(0);\n  }\n\n  private static String[] matchVCardPrefixedField(CharSequence prefix, String rawText, boolean trim) {\n    List<List<String>> values = VCardResultParser.matchVCardPrefixedField(prefix, rawText, trim, false);\n    if (values == null || values.isEmpty()) {\n      return null;\n    }\n    int size = values.size();\n    String[] result = new String[size];\n    for (int i = 0; i < size; i++) {\n      result[i] = values.get(i).get(0);\n    }\n    return result;\n  }\n\n  private static String stripMailto(String s) {\n    if (s != null && (s.startsWith(\"mailto:\") || s.startsWith(\"MAILTO:\"))) {\n      s = s.substring(7);\n    }\n    return s;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/VINParsedResult.java",
    "content": "/*\n * Copyright 2014 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes a Vehicle Identification Number (VIN).\n */\npublic final class VINParsedResult extends ParsedResult {\n\n  private final String vin;\n  private final String worldManufacturerID;\n  private final String vehicleDescriptorSection;\n  private final String vehicleIdentifierSection;\n  private final String countryCode;\n  private final String vehicleAttributes;\n  private final int modelYear;\n  private final char plantCode;\n  private final String sequentialNumber;\n\n  public VINParsedResult(String vin,\n                         String worldManufacturerID,\n                         String vehicleDescriptorSection,\n                         String vehicleIdentifierSection,\n                         String countryCode,\n                         String vehicleAttributes,\n                         int modelYear,\n                         char plantCode,\n                         String sequentialNumber) {\n    super(ParsedResultType.VIN);\n    this.vin = vin;\n    this.worldManufacturerID = worldManufacturerID;\n    this.vehicleDescriptorSection = vehicleDescriptorSection;\n    this.vehicleIdentifierSection = vehicleIdentifierSection;\n    this.countryCode = countryCode;\n    this.vehicleAttributes = vehicleAttributes;\n    this.modelYear = modelYear;\n    this.plantCode = plantCode;\n    this.sequentialNumber = sequentialNumber;\n  }\n  \n  public String getVIN() {\n    return vin;\n  }\n\n  public String getWorldManufacturerID() {\n    return worldManufacturerID;\n  }\n\n  public String getVehicleDescriptorSection() {\n    return vehicleDescriptorSection;\n  }\n\n  public String getVehicleIdentifierSection() {\n    return vehicleIdentifierSection;\n  }\n\n  public String getCountryCode() {\n    return countryCode;\n  }\n\n  public String getVehicleAttributes() {\n    return vehicleAttributes;\n  }\n\n  public int getModelYear() {\n    return modelYear;\n  }\n\n  public char getPlantCode() {\n    return plantCode;\n  }\n\n  public String getSequentialNumber() {\n    return sequentialNumber;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(50);\n    result.append(worldManufacturerID).append(' ');\n    result.append(vehicleDescriptorSection).append(' ');\n    result.append(vehicleIdentifierSection).append('\\n');\n    if (countryCode != null) {\n      result.append(countryCode).append(' ');\n    }\n    result.append(modelYear).append(' ');\n    result.append(plantCode).append(' ');\n    result.append(sequentialNumber).append('\\n');\n    return result.toString();\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/VINResultParser.java",
    "content": "/*\n * Copyright 2014 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.Result;\n\nimport java.util.regex.Pattern;\n\n/**\n * Detects a result that is likely a vehicle identification number.\n *\n * @author Sean Owen\n */\npublic final class VINResultParser extends ResultParser {\n\n  private static final Pattern IOQ = Pattern.compile(\"[IOQ]\");\n  private static final Pattern AZ09 = Pattern.compile(\"[A-Z0-9]{17}\");\n\n  @Override\n  public VINParsedResult parse(Result result) {\n    if (result.getBarcodeFormat() != BarcodeFormat.CODE_39) {\n      return null;\n    }\n    String rawText = result.getText();\n    rawText = IOQ.matcher(rawText).replaceAll(\"\").trim();\n    if (!AZ09.matcher(rawText).matches()) {\n      return null;\n    }\n    try {\n      if (!checkChecksum(rawText)) {\n        return null;\n      }\n      String wmi = rawText.substring(0, 3);\n      return new VINParsedResult(rawText,\n          wmi,\n          rawText.substring(3, 9),\n          rawText.substring(9, 17),\n          countryCode(wmi),\n          rawText.substring(3, 8),\n          modelYear(rawText.charAt(9)),\n          rawText.charAt(10),\n          rawText.substring(11));\n    } catch (IllegalArgumentException iae) {\n      return null;\n    }\n  }\n\n  private static boolean checkChecksum(CharSequence vin) {\n    int sum = 0;\n    for (int i = 0; i < vin.length(); i++) {\n      sum += vinPositionWeight(i + 1) * vinCharValue(vin.charAt(i));\n    }\n    char checkChar = vin.charAt(8);\n    char expectedCheckChar = checkChar(sum % 11);\n    return checkChar == expectedCheckChar;\n  }\n  \n  private static int vinCharValue(char c) {\n    if (c >= 'A' && c <= 'I') {\n      return (c - 'A') + 1;\n    }\n    if (c >= 'J' && c <= 'R') {\n      return (c - 'J') + 1;\n    }\n    if (c >= 'S' && c <= 'Z') {\n      return (c - 'S') + 2;\n    }\n    if (c >= '0' && c <= '9') {\n      return c - '0';\n    }\n    throw new IllegalArgumentException();\n  }\n  \n  private static int vinPositionWeight(int position) {\n    if (position >= 1 && position <= 7) {\n      return 9 - position;\n    }\n    if (position == 8) {\n      return 10;\n    }\n    if (position == 9) {\n      return 0;\n    }\n    if (position >= 10 && position <= 17) {\n      return 19 - position;\n    }\n    throw new IllegalArgumentException();\n  }\n\n  private static char checkChar(int remainder) {\n    if (remainder < 10) {\n      return (char) ('0' + remainder);\n    }\n    if (remainder == 10) {\n      return 'X';\n    }\n    throw new IllegalArgumentException();\n  }\n  \n  private static int modelYear(char c) {\n    if (c >= 'E' && c <= 'H') {\n      return (c - 'E') + 1984;\n    }\n    if (c >= 'J' && c <= 'N') {\n      return (c - 'J') + 1988;\n    }\n    if (c == 'P') {\n      return 1993;\n    }\n    if (c >= 'R' && c <= 'T') {\n      return (c - 'R') + 1994;\n    }\n    if (c >= 'V' && c <= 'Y') {\n      return (c - 'V') + 1997;\n    }\n    if (c >= '1' && c <= '9') {\n      return (c - '1') + 2001;\n    }\n    if (c >= 'A' && c <= 'D') {\n      return (c - 'A') + 2010;\n    }\n    throw new IllegalArgumentException();\n  }\n\n  private static String countryCode(CharSequence wmi) {\n    char c1 = wmi.charAt(0);\n    char c2 = wmi.charAt(1);\n    switch (c1) {\n      case '1':\n      case '4':\n      case '5':\n        return \"US\";\n      case '2':\n        return \"CA\";\n      case '3':\n        if (c2 >= 'A' && c2 <= 'W') {\n          return \"MX\";\n        }\n        break;\n      case '9':\n        if ((c2 >= 'A' && c2 <= 'E') || (c2 >= '3' && c2 <= '9')) {\n          return \"BR\";\n        }\n        break;\n      case 'J':\n        if (c2 >= 'A' && c2 <= 'T') {\n          return \"JP\";\n        }\n        break;\n      case 'K':\n        if (c2 >= 'L' && c2 <= 'R') {\n          return \"KO\";\n        }\n        break;\n      case 'L':\n        return \"CN\";\n      case 'M':\n        if (c2 >= 'A' && c2 <= 'E') {\n          return \"IN\";\n        }\n        break;\n      case 'S':\n        if (c2 >= 'A' && c2 <= 'M') {\n          return \"UK\";\n        }\n        if (c2 >= 'N' && c2 <= 'T') {\n          return \"DE\";\n        }\n        break;\n      case 'V':\n        if (c2 >= 'F' && c2 <= 'R') {\n          return \"FR\";\n        }\n        if (c2 >= 'S' && c2 <= 'W') {\n          return \"ES\";\n        }\n        break;\n      case 'W':\n        return \"DE\";\n      case 'X':\n        if (c2 == '0' || (c2 >= '3' && c2 <= '9')) {\n          return \"RU\";\n        }\n        break;\n      case 'Z':\n        if (c2 >= 'A' && c2 <= 'R') {\n          return \"IT\";\n        }\n        break;\n    }\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/WifiParsedResult.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\n/**\n * Represents a parsed result that encodes wifi network information, like SSID and password.\n *\n * @author Vikram Aggarwal\n */\npublic final class WifiParsedResult extends ParsedResult {\n\n  private final String ssid;\n  private final String networkEncryption;\n  private final String password;\n  private final boolean hidden;\n  private final String identity;\n  private final String anonymousIdentity;\n  private final String eapMethod;\n  private final String phase2Method;\n\n  public WifiParsedResult(String networkEncryption, String ssid, String password) {\n    this(networkEncryption, ssid, password, false);\n  }\n\n  public WifiParsedResult(String networkEncryption, String ssid, String password, boolean hidden) {\n    this(networkEncryption, ssid, password, hidden, null, null, null, null);\n  }\n\n  public WifiParsedResult(String networkEncryption, String ssid, String password, boolean hidden, String identity, String anonymousIdentity, String eapMethod, String phase2Method) {\n    super(ParsedResultType.WIFI);\n    this.ssid = ssid;\n    this.networkEncryption = networkEncryption;\n    this.password = password;\n    this.hidden = hidden;\n    this.identity = identity;\n    this.anonymousIdentity = anonymousIdentity;\n    this.eapMethod = eapMethod;\n    this.phase2Method = phase2Method;\n  }\n\n  public String getSsid() {\n    return ssid;\n  }\n\n  public String getNetworkEncryption() {\n    return networkEncryption;\n  }\n\n  public String getPassword() {\n    return password;\n  }\n\n  public boolean isHidden() {\n    return hidden;\n  }\n\n  public String getIdentity() {\n    return identity;\n  }\n\n  public String getAnonymousIdentity() {\n    return anonymousIdentity;\n  }\n\n  public String getEapMethod() {\n    return eapMethod;\n  }\n\n  public String getPhase2Method() {\n    return phase2Method;\n  }\n\n  @Override\n  public String getDisplayResult() {\n    StringBuilder result = new StringBuilder(80);\n    maybeAppend(ssid, result);\n    maybeAppend(networkEncryption, result);\n    maybeAppend(password, result);\n    maybeAppend(Boolean.toString(hidden), result);\n    return result.toString();\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/client/result/WifiResultParser.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.client.result;\n\nimport com.google.zxing.Result;\n\n/**\n * <p>Parses a WIFI configuration string. Strings will be of the form:</p>\n *\n * <p>{@code WIFI:T:[network type];S:[network SSID];P:[network password];H:[hidden?];;}</p>\n *\n * <p>For WPA2 enterprise (EAP), strings will be of the form:</p>\n *\n * <p>{@code WIFI:T:WPA2-EAP;S:[network SSID];H:[hidden?];E:[EAP method];H:[Phase 2 method];A:[anonymous identity];I:[username];P:[password];;}</p>\n *\n * <p>\"EAP method\" can e.g. be \"TTLS\" or \"PWD\" or one of the other fields in <a href=\"https://developer.android.com/reference/android/net/wifi/WifiEnterpriseConfig.Eap.html\">WifiEnterpriseConfig.Eap</a> and \"Phase 2 method\" can e.g. be \"MSCHAPV2\" or any of the other fields in <a href=\"https://developer.android.com/reference/android/net/wifi/WifiEnterpriseConfig.Phase2.html\">WifiEnterpriseConfig.Phase2</a></p>\n *\n * <p>The fields can appear in any order. Only \"S:\" is required.</p>\n *\n * @author Vikram Aggarwal\n * @author Sean Owen\n * @author Steffen Kieß\n */\npublic final class WifiResultParser extends ResultParser {\n\n  @Override\n  public WifiParsedResult parse(Result result) {\n    String rawText = getMassagedText(result);\n    if (!rawText.startsWith(\"WIFI:\")) {\n      return null;\n    }\n    rawText = rawText.substring(\"WIFI:\".length());\n    String ssid = matchSinglePrefixedField(\"S:\", rawText, ';', false);\n    if (ssid == null || ssid.isEmpty()) {\n      return null;\n    }\n    String pass = matchSinglePrefixedField(\"P:\", rawText, ';', false);\n    String type = matchSinglePrefixedField(\"T:\", rawText, ';', false);\n    if (type == null) {\n      type = \"nopass\";\n    }\n    boolean hidden = Boolean.parseBoolean(matchSinglePrefixedField(\"H:\", rawText, ';', false));\n    String identity = matchSinglePrefixedField(\"I:\", rawText, ';', false);\n    String anonymousIdentity = matchSinglePrefixedField(\"A:\", rawText, ';', false);\n    String eapMethod = matchSinglePrefixedField(\"E:\", rawText, ';', false);\n    String phase2Method = matchSinglePrefixedField(\"H:\", rawText, ';', false);\n    return new WifiParsedResult(type, ssid, pass, hidden, identity, anonymousIdentity, eapMethod, phase2Method);\n  }\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/BitArray.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport java.util.Arrays;\n\n/**\n * <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>\n *\n * @author Sean Owen\n */\npublic final class BitArray implements Cloneable {\n\n  private int[] bits;\n  private int size;\n\n  public BitArray() {\n    this.size = 0;\n    this.bits = new int[1];\n  }\n\n  public BitArray(int size) {\n    this.size = size;\n    this.bits = makeArray(size);\n  }\n\n  // For testing only\n  BitArray(int[] bits, int size) {\n    this.bits = bits;\n    this.size = size;\n  }\n\n  public int getSize() {\n    return size;\n  }\n\n  public int getSizeInBytes() {\n    return (size + 7) / 8;\n  }\n\n  private void ensureCapacity(int size) {\n    if (size > bits.length * 32) {\n      int[] newBits = makeArray(size);\n      System.arraycopy(bits, 0, newBits, 0, bits.length);\n      this.bits = newBits;\n    }\n  }\n\n  /**\n   * @param i bit to get\n   * @return true iff bit i is set\n   */\n  public boolean get(int i) {\n    return (bits[i / 32] & (1 << (i & 0x1F))) != 0;\n  }\n\n  /**\n   * Sets bit i.\n   *\n   * @param i bit to set\n   */\n  public void set(int i) {\n    bits[i / 32] |= 1 << (i & 0x1F);\n  }\n\n  /**\n   * Flips bit i.\n   *\n   * @param i bit to set\n   */\n  public void flip(int i) {\n    bits[i / 32] ^= 1 << (i & 0x1F);\n  }\n\n  /**\n   * @param from first bit to check\n   * @return index of first bit that is set, starting from the given index, or size if none are set\n   *  at or beyond this given index\n   * @see #getNextUnset(int)\n   */\n  public int getNextSet(int from) {\n    if (from >= size) {\n      return size;\n    }\n    int bitsOffset = from / 32;\n    int currentBits = bits[bitsOffset];\n    // mask off lesser bits first\n    currentBits &= -(1 << (from & 0x1F));\n    while (currentBits == 0) {\n      if (++bitsOffset == bits.length) {\n        return size;\n      }\n      currentBits = bits[bitsOffset];\n    }\n    int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);\n    return result > size ? size : result;\n  }\n\n  /**\n   * @param from index to start looking for unset bit\n   * @return index of next unset bit, or {@code size} if none are unset until the end\n   * @see #getNextSet(int)\n   */\n  public int getNextUnset(int from) {\n    if (from >= size) {\n      return size;\n    }\n    int bitsOffset = from / 32;\n    int currentBits = ~bits[bitsOffset];\n    // mask off lesser bits first\n    currentBits &= -(1 << (from & 0x1F));\n    while (currentBits == 0) {\n      if (++bitsOffset == bits.length) {\n        return size;\n      }\n      currentBits = ~bits[bitsOffset];\n    }\n    int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);\n    return result > size ? size : result;\n  }\n\n  /**\n   * Sets a block of 32 bits, starting at bit i.\n   *\n   * @param i first bit to set\n   * @param newBits the new value of the next 32 bits. Note again that the least-significant bit\n   * corresponds to bit i, the next-least-significant to i+1, and so on.\n   */\n  public void setBulk(int i, int newBits) {\n    bits[i / 32] = newBits;\n  }\n\n  /**\n   * Sets a range of bits.\n   *\n   * @param start start of range, inclusive.\n   * @param end end of range, exclusive\n   */\n  public void setRange(int start, int end) {\n    if (end < start || start < 0 || end > size) {\n      throw new IllegalArgumentException();\n    }\n    if (end == start) {\n      return;\n    }\n    end--; // will be easier to treat this as the last actually set bit -- inclusive\n    int firstInt = start / 32;\n    int lastInt = end / 32;\n    for (int i = firstInt; i <= lastInt; i++) {\n      int firstBit = i > firstInt ? 0 : start & 0x1F;\n      int lastBit = i < lastInt ? 31 : end & 0x1F;\n      // Ones from firstBit to lastBit, inclusive\n      int mask = (2 << lastBit) - (1 << firstBit);\n      bits[i] |= mask;\n    }\n  }\n\n  /**\n   * Clears all bits (sets to false).\n   */\n  public void clear() {\n    int max = bits.length;\n    for (int i = 0; i < max; i++) {\n      bits[i] = 0;\n    }\n  }\n\n  /**\n   * Efficient method to check if a range of bits is set, or not set.\n   *\n   * @param start start of range, inclusive.\n   * @param end end of range, exclusive\n   * @param value if true, checks that bits in range are set, otherwise checks that they are not set\n   * @return true iff all bits are set or not set in range, according to value argument\n   * @throws IllegalArgumentException if end is less than start or the range is not contained in the array\n   */\n  public boolean isRange(int start, int end, boolean value) {\n    if (end < start || start < 0 || end > size) {\n      throw new IllegalArgumentException();\n    }\n    if (end == start) {\n      return true; // empty range matches\n    }\n    end--; // will be easier to treat this as the last actually set bit -- inclusive\n    int firstInt = start / 32;\n    int lastInt = end / 32;\n    for (int i = firstInt; i <= lastInt; i++) {\n      int firstBit = i > firstInt ? 0 : start & 0x1F;\n      int lastBit = i < lastInt ? 31 : end & 0x1F;\n      // Ones from firstBit to lastBit, inclusive\n      int mask = (2 << lastBit) - (1 << firstBit);\n\n      // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,\n      // equals the mask, or we're looking for 0s and the masked portion is not all 0s\n      if ((bits[i] & mask) != (value ? mask : 0)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  public void appendBit(boolean bit) {\n    ensureCapacity(size + 1);\n    if (bit) {\n      bits[size / 32] |= 1 << (size & 0x1F);\n    }\n    size++;\n  }\n\n  /**\n   * Appends the least-significant bits, from value, in order from most-significant to\n   * least-significant. For example, appending 6 bits from 0x000001E will append the bits\n   * 0, 1, 1, 1, 1, 0 in that order.\n   *\n   * @param value {@code int} containing bits to append\n   * @param numBits bits from value to append\n   */\n  public void appendBits(int value, int numBits) {\n    if (numBits < 0 || numBits > 32) {\n      throw new IllegalArgumentException(\"Num bits must be between 0 and 32\");\n    }\n    ensureCapacity(size + numBits);\n    for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {\n      appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);\n    }\n  }\n\n  public void appendBitArray(BitArray other) {\n    int otherSize = other.size;\n    ensureCapacity(size + otherSize);\n    for (int i = 0; i < otherSize; i++) {\n      appendBit(other.get(i));\n    }\n  }\n\n  public void xor(BitArray other) {\n    if (size != other.size) {\n      throw new IllegalArgumentException(\"Sizes don't match\");\n    }\n    for (int i = 0; i < bits.length; i++) {\n      // The last int could be incomplete (i.e. not have 32 bits in\n      // it) but there is no problem since 0 XOR 0 == 0.\n      bits[i] ^= other.bits[i];\n    }\n  }\n\n  /**\n   *\n   * @param bitOffset first bit to start writing\n   * @param array array to write into. Bytes are written most-significant byte first. This is the opposite\n   *  of the internal representation, which is exposed by {@link #getBitArray()}\n   * @param offset position in array to start writing\n   * @param numBytes how many bytes to write\n   */\n  public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {\n    for (int i = 0; i < numBytes; i++) {\n      int theByte = 0;\n      for (int j = 0; j < 8; j++) {\n        if (get(bitOffset)) {\n          theByte |= 1 << (7 - j);\n        }\n        bitOffset++;\n      }\n      array[offset + i] = (byte) theByte;\n    }\n  }\n\n  /**\n   * @return underlying array of ints. The first element holds the first 32 bits, and the least\n   *         significant bit is bit 0.\n   */\n  public int[] getBitArray() {\n    return bits;\n  }\n\n  /**\n   * Reverses all bits in the array.\n   */\n  public void reverse() {\n    int[] newBits = new int[bits.length];\n    // reverse all int's first\n    int len = (size - 1) / 32;\n    int oldBitsLen = len + 1;\n    for (int i = 0; i < oldBitsLen; i++) {\n      long x = bits[i];\n      x = ((x >>  1) & 0x55555555L) | ((x & 0x55555555L) <<  1);\n      x = ((x >>  2) & 0x33333333L) | ((x & 0x33333333L) <<  2);\n      x = ((x >>  4) & 0x0f0f0f0fL) | ((x & 0x0f0f0f0fL) <<  4);\n      x = ((x >>  8) & 0x00ff00ffL) | ((x & 0x00ff00ffL) <<  8);\n      x = ((x >> 16) & 0x0000ffffL) | ((x & 0x0000ffffL) << 16);\n      newBits[len - i] = (int) x;\n    }\n    // now correct the int's if the bit size isn't a multiple of 32\n    if (size != oldBitsLen * 32) {\n      int leftOffset = oldBitsLen * 32 - size;\n      int currentInt = newBits[0] >>> leftOffset;\n      for (int i = 1; i < oldBitsLen; i++) {\n        int nextInt = newBits[i];\n        currentInt |= nextInt << (32 - leftOffset);\n        newBits[i - 1] = currentInt;\n        currentInt = nextInt >>> leftOffset;\n      }\n      newBits[oldBitsLen - 1] = currentInt;\n    }\n    bits = newBits;\n  }\n\n  private static int[] makeArray(int size) {\n    return new int[(size + 31) / 32];\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (!(o instanceof BitArray)) {\n      return false;\n    }\n    BitArray other = (BitArray) o;\n    return size == other.size && Arrays.equals(bits, other.bits);\n  }\n\n  @Override\n  public int hashCode() {\n    return 31 * size + Arrays.hashCode(bits);\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder result = new StringBuilder(size + (size / 8) + 1);\n    for (int i = 0; i < size; i++) {\n      if ((i & 0x07) == 0) {\n        result.append(' ');\n      }\n      result.append(get(i) ? 'X' : '.');\n    }\n    return result.toString();\n  }\n\n  @Override\n  public BitArray clone() {\n    return new BitArray(bits.clone(), size);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/BitMatrix.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport java.util.Arrays;\n\n/**\n * <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common\n * module, x is the column position, and y is the row position. The ordering is always x, y.\n * The origin is at the top-left.</p>\n *\n * <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins\n * with a new int. This is done intentionally so that we can copy out a row into a BitArray very\n * efficiently.</p>\n *\n * <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,\n * meaning they represent lower x values. This is compatible with BitArray's implementation.</p>\n *\n * @author Sean Owen\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class BitMatrix implements Cloneable {\n\n  private final int width;\n  private final int height;\n  private final int rowSize;\n  private final int[] bits;\n\n  /**\n   * Creates an empty square {@code BitMatrix}.\n   *\n   * @param dimension height and width\n   */\n  public BitMatrix(int dimension) {\n    this(dimension, dimension);\n  }\n\n  /**\n   * Creates an empty {@code BitMatrix}.\n   *\n   * @param width bit matrix width\n   * @param height bit matrix height\n   */\n  public BitMatrix(int width, int height) {\n    if (width < 1 || height < 1) {\n      throw new IllegalArgumentException(\"Both dimensions must be greater than 0\");\n    }\n    this.width = width;\n    this.height = height;\n    this.rowSize = (width + 31) / 32;\n    bits = new int[rowSize * height];\n  }\n\n  private BitMatrix(int width, int height, int rowSize, int[] bits) {\n    this.width = width;\n    this.height = height;\n    this.rowSize = rowSize;\n    this.bits = bits;\n  }\n\n  /**\n   * Interprets a 2D array of booleans as a {@code BitMatrix}, where \"true\" means an \"on\" bit.\n   *\n   * @param image bits of the image, as a row-major 2D array. Elements are arrays representing rows\n   * @return {@code BitMatrix} representation of image\n   */\n  public static BitMatrix parse(boolean[][] image) {\n    int height = image.length;\n    int width = image[0].length;\n    BitMatrix bits = new BitMatrix(width, height);\n    for (int i = 0; i < height; i++) {\n      boolean[] imageI = image[i];\n      for (int j = 0; j < width; j++) {\n        if (imageI[j]) {\n          bits.set(j, i);\n        }\n      }\n    }\n    return bits;\n  }\n\n  public static BitMatrix parse(String stringRepresentation, String setString, String unsetString) {\n    if (stringRepresentation == null) {\n      throw new IllegalArgumentException();\n    }\n\n    boolean[] bits = new boolean[stringRepresentation.length()];\n    int bitsPos = 0;\n    int rowStartPos = 0;\n    int rowLength = -1;\n    int nRows = 0;\n    int pos = 0;\n    while (pos < stringRepresentation.length()) {\n      if (stringRepresentation.charAt(pos) == '\\n' ||\n          stringRepresentation.charAt(pos) == '\\r') {\n        if (bitsPos > rowStartPos) {\n          if (rowLength == -1) {\n            rowLength = bitsPos - rowStartPos;\n          } else if (bitsPos - rowStartPos != rowLength) {\n            throw new IllegalArgumentException(\"row lengths do not match\");\n          }\n          rowStartPos = bitsPos;\n          nRows++;\n        }\n        pos++;\n      }  else if (stringRepresentation.substring(pos, pos + setString.length()).equals(setString)) {\n        pos += setString.length();\n        bits[bitsPos] = true;\n        bitsPos++;\n      } else if (stringRepresentation.substring(pos, pos + unsetString.length()).equals(unsetString)) {\n        pos += unsetString.length();\n        bits[bitsPos] = false;\n        bitsPos++;\n      } else {\n        throw new IllegalArgumentException(\n            \"illegal character encountered: \" + stringRepresentation.substring(pos));\n      }\n    }\n\n    // no EOL at end?\n    if (bitsPos > rowStartPos) {\n      if (rowLength == -1) {\n        rowLength = bitsPos - rowStartPos;\n      } else if (bitsPos - rowStartPos != rowLength) {\n        throw new IllegalArgumentException(\"row lengths do not match\");\n      }\n      nRows++;\n    }\n\n    BitMatrix matrix = new BitMatrix(rowLength, nRows);\n    for (int i = 0; i < bitsPos; i++) {\n      if (bits[i]) {\n        matrix.set(i % rowLength, i / rowLength);\n      }\n    }\n    return matrix;\n  }\n\n  /**\n   * <p>Gets the requested bit, where true means black.</p>\n   *\n   * @param x The horizontal component (i.e. which column)\n   * @param y The vertical component (i.e. which row)\n   * @return value of given bit in matrix\n   */\n  public boolean get(int x, int y) {\n    int offset = y * rowSize + (x / 32);\n    return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;\n  }\n\n  /**\n   * <p>Sets the given bit to true.</p>\n   *\n   * @param x The horizontal component (i.e. which column)\n   * @param y The vertical component (i.e. which row)\n   */\n  public void set(int x, int y) {\n    int offset = y * rowSize + (x / 32);\n    bits[offset] |= 1 << (x & 0x1f);\n  }\n\n  public void unset(int x, int y) {\n    int offset = y * rowSize + (x / 32);\n    bits[offset] &= ~(1 << (x & 0x1f));\n  }\n\n  /**\n   * <p>Flips the given bit.</p>\n   *\n   * @param x The horizontal component (i.e. which column)\n   * @param y The vertical component (i.e. which row)\n   */\n  public void flip(int x, int y) {\n    int offset = y * rowSize + (x / 32);\n    bits[offset] ^= 1 << (x & 0x1f);\n  }\n\n  /**\n   * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding\n   * mask bit is set.\n   *\n   * @param mask XOR mask\n   */\n  public void xor(BitMatrix mask) {\n    if (width != mask.getWidth() || height != mask.getHeight()\n        || rowSize != mask.getRowSize()) {\n      throw new IllegalArgumentException(\"input matrix dimensions do not match\");\n    }\n    BitArray rowArray = new BitArray(width);\n    for (int y = 0; y < height; y++) {\n      int offset = y * rowSize;\n      int[] row = mask.getRow(y, rowArray).getBitArray();\n      for (int x = 0; x < rowSize; x++) {\n        bits[offset + x] ^= row[x];\n      }\n    }\n  }\n\n  /**\n   * Clears all bits (sets to false).\n   */\n  public void clear() {\n    int max = bits.length;\n    for (int i = 0; i < max; i++) {\n      bits[i] = 0;\n    }\n  }\n\n  /**\n   * <p>Sets a square region of the bit matrix to true.</p>\n   *\n   * @param left The horizontal position to begin at (inclusive)\n   * @param top The vertical position to begin at (inclusive)\n   * @param width The width of the region\n   * @param height The height of the region\n   */\n  public void setRegion(int left, int top, int width, int height) {\n    if (top < 0 || left < 0) {\n      throw new IllegalArgumentException(\"Left and top must be nonnegative\");\n    }\n    if (height < 1 || width < 1) {\n      throw new IllegalArgumentException(\"Height and width must be at least 1\");\n    }\n    int right = left + width;\n    int bottom = top + height;\n    if (bottom > this.height || right > this.width) {\n      throw new IllegalArgumentException(\"The region must fit inside the matrix\");\n    }\n    for (int y = top; y < bottom; y++) {\n      int offset = y * rowSize;\n      for (int x = left; x < right; x++) {\n        bits[offset + (x / 32)] |= 1 << (x & 0x1f);\n      }\n    }\n  }\n\n  /**\n   * A fast method to retrieve one row of data from the matrix as a BitArray.\n   *\n   * @param y The row to retrieve\n   * @param row An optional caller-allocated BitArray, will be allocated if null or too small\n   * @return The resulting BitArray - this reference should always be used even when passing\n   *         your own row\n   */\n  public BitArray getRow(int y, BitArray row) {\n    if (row == null || row.getSize() < width) {\n      row = new BitArray(width);\n    } else {\n      row.clear();\n    }\n    int offset = y * rowSize;\n    for (int x = 0; x < rowSize; x++) {\n      row.setBulk(x * 32, bits[offset + x]);\n    }\n    return row;\n  }\n\n  /**\n   * @param y row to set\n   * @param row {@link BitArray} to copy from\n   */\n  public void setRow(int y, BitArray row) {\n    System.arraycopy(row.getBitArray(), 0, bits, y * rowSize, rowSize);\n  }\n\n  /**\n   * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees\n   */\n  public void rotate180() {\n    int width = getWidth();\n    int height = getHeight();\n    BitArray topRow = new BitArray(width);\n    BitArray bottomRow = new BitArray(width);\n    for (int i = 0; i < (height + 1) / 2; i++) {\n      topRow = getRow(i, topRow);\n      bottomRow = getRow(height - 1 - i, bottomRow);\n      topRow.reverse();\n      bottomRow.reverse();\n      setRow(i, bottomRow);\n      setRow(height - 1 - i, topRow);\n    }\n  }\n\n  /**\n   * This is useful in detecting the enclosing rectangle of a 'pure' barcode.\n   *\n   * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white\n   */\n  public int[] getEnclosingRectangle() {\n    int left = width;\n    int top = height;\n    int right = -1;\n    int bottom = -1;\n\n    for (int y = 0; y < height; y++) {\n      for (int x32 = 0; x32 < rowSize; x32++) {\n        int theBits = bits[y * rowSize + x32];\n        if (theBits != 0) {\n          if (y < top) {\n            top = y;\n          }\n          if (y > bottom) {\n            bottom = y;\n          }\n          if (x32 * 32 < left) {\n            int bit = 0;\n            while ((theBits << (31 - bit)) == 0) {\n              bit++;\n            }\n            if ((x32 * 32 + bit) < left) {\n              left = x32 * 32 + bit;\n            }\n          }\n          if (x32 * 32 + 31 > right) {\n            int bit = 31;\n            while ((theBits >>> bit) == 0) {\n              bit--;\n            }\n            if ((x32 * 32 + bit) > right) {\n              right = x32 * 32 + bit;\n            }\n          }\n        }\n      }\n    }\n\n    if (right < left || bottom < top) {\n      return null;\n    }\n\n    return new int[] {left, top, right - left + 1, bottom - top + 1};\n  }\n\n  /**\n   * This is useful in detecting a corner of a 'pure' barcode.\n   *\n   * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white\n   */\n  public int[] getTopLeftOnBit() {\n    int bitsOffset = 0;\n    while (bitsOffset < bits.length && bits[bitsOffset] == 0) {\n      bitsOffset++;\n    }\n    if (bitsOffset == bits.length) {\n      return null;\n    }\n    int y = bitsOffset / rowSize;\n    int x = (bitsOffset % rowSize) * 32;\n\n    int theBits = bits[bitsOffset];\n    int bit = 0;\n    while ((theBits << (31 - bit)) == 0) {\n      bit++;\n    }\n    x += bit;\n    return new int[] {x, y};\n  }\n\n  public int[] getBottomRightOnBit() {\n    int bitsOffset = bits.length - 1;\n    while (bitsOffset >= 0 && bits[bitsOffset] == 0) {\n      bitsOffset--;\n    }\n    if (bitsOffset < 0) {\n      return null;\n    }\n\n    int y = bitsOffset / rowSize;\n    int x = (bitsOffset % rowSize) * 32;\n\n    int theBits = bits[bitsOffset];\n    int bit = 31;\n    while ((theBits >>> bit) == 0) {\n      bit--;\n    }\n    x += bit;\n\n    return new int[] {x, y};\n  }\n\n  /**\n   * @return The width of the matrix\n   */\n  public int getWidth() {\n    return width;\n  }\n\n  /**\n   * @return The height of the matrix\n   */\n  public int getHeight() {\n    return height;\n  }\n\n  /**\n   * @return The row size of the matrix\n   */\n  public int getRowSize() {\n    return rowSize;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (!(o instanceof BitMatrix)) {\n      return false;\n    }\n    BitMatrix other = (BitMatrix) o;\n    return width == other.width && height == other.height && rowSize == other.rowSize &&\n    Arrays.equals(bits, other.bits);\n  }\n\n  @Override\n  public int hashCode() {\n    int hash = width;\n    hash = 31 * hash + width;\n    hash = 31 * hash + height;\n    hash = 31 * hash + rowSize;\n     hash = 31 * hash + Arrays.hashCode(bits);\n    return hash;\n  }\n\n  /**\n   * @return string representation using \"X\" for set and \" \" for unset bits\n   */\n  @Override\n  public String toString() {\n    return toString(\"X \", \"  \");\n  }\n\n  /**\n   * @param setString representation of a set bit\n   * @param unsetString representation of an unset bit\n   * @return string representation of entire matrix utilizing given strings\n   */\n  public String toString(String setString, String unsetString) {\n    return buildToString(setString, unsetString, \"\\n\");\n  }\n\n  /**\n   * @param setString representation of a set bit\n   * @param unsetString representation of an unset bit\n   * @param lineSeparator newline character in string representation\n   * @return string representation of entire matrix utilizing given strings and line separator\n   * @deprecated call {@link #toString(String,String)} only, which uses \\n line separator always\n   */\n  @Deprecated\n  public String toString(String setString, String unsetString, String lineSeparator) {\n    return buildToString(setString, unsetString, lineSeparator);\n  }\n\n  private String buildToString(String setString, String unsetString, String lineSeparator) {\n    StringBuilder result = new StringBuilder(height * (width + 1));\n    for (int y = 0; y < height; y++) {\n      for (int x = 0; x < width; x++) {\n        result.append(get(x, y) ? setString : unsetString);\n      }\n      result.append(lineSeparator);\n    }\n    return result.toString();\n  }\n\n  @Override\n  public BitMatrix clone() {\n    return new BitMatrix(width, height, rowSize, bits.clone());\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/BitSource.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\n/**\n * <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the\n * number of bits read is not often a multiple of 8.</p>\n *\n * <p>This class is thread-safe but not reentrant -- unless the caller modifies the bytes array\n * it passed in, in which case all bets are off.</p>\n *\n * @author Sean Owen\n */\npublic final class BitSource {\n\n  private final byte[] bytes;\n  private int byteOffset;\n  private int bitOffset;\n\n  /**\n   * @param bytes bytes from which this will read bits. Bits will be read from the first byte first.\n   * Bits are read within a byte from most-significant to least-significant bit.\n   */\n  public BitSource(byte[] bytes) {\n    this.bytes = bytes;\n  }\n\n  /**\n   * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.\n   */\n  public int getBitOffset() {\n    return bitOffset;\n  }\n\n  /**\n   * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.\n   */\n  public int getByteOffset() {\n    return byteOffset;\n  }\n\n  /**\n   * @param numBits number of bits to read\n   * @return int representing the bits read. The bits will appear as the least-significant\n   *         bits of the int\n   * @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available\n   */\n  public int readBits(int numBits) {\n    if (numBits < 1 || numBits > 32 || numBits > available()) {\n      throw new IllegalArgumentException(String.valueOf(numBits));\n    }\n\n    int result = 0;\n\n    // First, read remainder from current byte\n    if (bitOffset > 0) {\n      int bitsLeft = 8 - bitOffset;\n      int toRead = numBits < bitsLeft ? numBits : bitsLeft;\n      int bitsToNotRead = bitsLeft - toRead;\n      int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;\n      result = (bytes[byteOffset] & mask) >> bitsToNotRead;\n      numBits -= toRead;\n      bitOffset += toRead;\n      if (bitOffset == 8) {\n        bitOffset = 0;\n        byteOffset++;\n      }\n    }\n\n    // Next read whole bytes\n    if (numBits > 0) {\n      while (numBits >= 8) {\n        result = (result << 8) | (bytes[byteOffset] & 0xFF);\n        byteOffset++;\n        numBits -= 8;\n      }\n\n      // Finally read a partial byte\n      if (numBits > 0) {\n        int bitsToNotRead = 8 - numBits;\n        int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;\n        result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead);\n        bitOffset += numBits;\n      }\n    }\n\n    return result;\n  }\n\n  /**\n   * @return number of bits that can be read successfully\n   */\n  public int available() {\n    return 8 * (bytes.length - byteOffset) - bitOffset;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/CharacterSetECI.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport com.google.zxing.FormatException;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Encapsulates a Character Set ECI, according to \"Extended Channel Interpretations\" 5.3.1.1\n * of ISO 18004.\n *\n * @author Sean Owen\n */\npublic enum CharacterSetECI {\n\n  // Enum name is a Java encoding valid for java.lang and java.io\n  Cp437(new int[]{0,2}),\n  ISO8859_1(new int[]{1,3}, \"ISO-8859-1\"),\n  ISO8859_2(4, \"ISO-8859-2\"),\n  ISO8859_3(5, \"ISO-8859-3\"),\n  ISO8859_4(6, \"ISO-8859-4\"),\n  ISO8859_5(7, \"ISO-8859-5\"),\n  ISO8859_6(8, \"ISO-8859-6\"),\n  ISO8859_7(9, \"ISO-8859-7\"),\n  ISO8859_8(10, \"ISO-8859-8\"),\n  ISO8859_9(11, \"ISO-8859-9\"),\n  ISO8859_10(12, \"ISO-8859-10\"),\n  ISO8859_11(13, \"ISO-8859-11\"),\n  ISO8859_13(15, \"ISO-8859-13\"),\n  ISO8859_14(16, \"ISO-8859-14\"),\n  ISO8859_15(17, \"ISO-8859-15\"),\n  ISO8859_16(18, \"ISO-8859-16\"),\n  SJIS(20, \"Shift_JIS\"),\n  Cp1250(21, \"windows-1250\"),\n  Cp1251(22, \"windows-1251\"),\n  Cp1252(23, \"windows-1252\"),\n  Cp1256(24, \"windows-1256\"),\n  UnicodeBigUnmarked(25, \"UTF-16BE\", \"UnicodeBig\"),\n  UTF8(26, \"UTF-8\"),\n  ASCII(new int[] {27, 170}, \"US-ASCII\"),\n  Big5(28),\n  GB18030(29, \"GB2312\", \"EUC_CN\", \"GBK\"),\n  EUC_KR(30, \"EUC-KR\");\n\n  private static final Map<Integer,CharacterSetECI> VALUE_TO_ECI = new HashMap<>();\n  private static final Map<String,CharacterSetECI> NAME_TO_ECI = new HashMap<>();\n  static {\n    for (CharacterSetECI eci : values()) {\n      for (int value : eci.values) {\n        VALUE_TO_ECI.put(value, eci);\n      }\n      NAME_TO_ECI.put(eci.name(), eci);\n      for (String name : eci.otherEncodingNames) {\n        NAME_TO_ECI.put(name, eci);\n      }\n    }\n  }\n\n  private final int[] values;\n  private final String[] otherEncodingNames;\n\n  CharacterSetECI(int value) {\n    this(new int[] {value});\n  }\n\n  CharacterSetECI(int value, String... otherEncodingNames) {\n    this.values = new int[] {value};\n    this.otherEncodingNames = otherEncodingNames;\n  }\n\n  CharacterSetECI(int[] values, String... otherEncodingNames) {\n    this.values = values;\n    this.otherEncodingNames = otherEncodingNames;\n  }\n\n  public int getValue() {\n    return values[0];\n  }\n\n  /**\n   * @param value character set ECI value\n   * @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but\n   *   unsupported\n   * @throws FormatException if ECI value is invalid\n   */\n  public static CharacterSetECI getCharacterSetECIByValue(int value) throws FormatException {\n    if (value < 0 || value >= 900) {\n      throw FormatException.getFormatInstance();\n    }\n    return VALUE_TO_ECI.get(value);\n  }\n\n  /**\n   * @param name character set ECI encoding name\n   * @return CharacterSetECI representing ECI for character encoding, or null if it is legal\n   *   but unsupported\n   */\n  public static CharacterSetECI getCharacterSetECIByName(String name) {\n    return NAME_TO_ECI.get(name);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/DecoderResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport java.util.List;\n\n/**\n * <p>Encapsulates the result of decoding a matrix of bits. This typically\n * applies to 2D barcode formats. For now it contains the raw bytes obtained,\n * as well as a String interpretation of those bytes, if applicable.</p>\n *\n * @author Sean Owen\n */\npublic final class DecoderResult {\n\n  private final byte[] rawBytes;\n  private int numBits;\n  private final String text;\n  private final List<byte[]> byteSegments;\n  private final String ecLevel;\n  private Integer errorsCorrected;\n  private Integer erasures;\n  private Object other;\n  private final int structuredAppendParity;\n  private final int structuredAppendSequenceNumber;\n\n  public DecoderResult(byte[] rawBytes,\n                       String text,\n                       List<byte[]> byteSegments,\n                       String ecLevel) {\n    this(rawBytes, text, byteSegments, ecLevel, -1, -1);\n  }\n\n  public DecoderResult(byte[] rawBytes,\n                       String text,\n                       List<byte[]> byteSegments,\n                       String ecLevel,\n                       int saSequence,\n                       int saParity) {\n    this.rawBytes = rawBytes;\n    this.numBits = rawBytes == null ? 0 : 8 * rawBytes.length;\n    this.text = text;\n    this.byteSegments = byteSegments;\n    this.ecLevel = ecLevel;\n    this.structuredAppendParity = saParity;\n    this.structuredAppendSequenceNumber = saSequence;\n  }\n\n  /**\n   * @return raw bytes representing the result, or {@code null} if not applicable\n   */\n  public byte[] getRawBytes() {\n    return rawBytes;\n  }\n\n  /**\n   * @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length\n   * @since 3.3.0\n   */\n  public int getNumBits() {\n    return numBits;\n  }\n\n  /**\n   * @param numBits overrides the number of bits that are valid in {@link #getRawBytes()}\n   * @since 3.3.0\n   */\n  public void setNumBits(int numBits) {\n    this.numBits = numBits;\n  }\n\n  /**\n   * @return text representation of the result\n   */\n  public String getText() {\n    return text;\n  }\n\n  /**\n   * @return list of byte segments in the result, or {@code null} if not applicable\n   */\n  public List<byte[]> getByteSegments() {\n    return byteSegments;\n  }\n\n  /**\n   * @return name of error correction level used, or {@code null} if not applicable\n   */\n  public String getECLevel() {\n    return ecLevel;\n  }\n\n  /**\n   * @return number of errors corrected, or {@code null} if not applicable\n   */\n  public Integer getErrorsCorrected() {\n    return errorsCorrected;\n  }\n\n  public void setErrorsCorrected(Integer errorsCorrected) {\n    this.errorsCorrected = errorsCorrected;\n  }\n\n  /**\n   * @return number of erasures corrected, or {@code null} if not applicable\n   */\n  public Integer getErasures() {\n    return erasures;\n  }\n\n  public void setErasures(Integer erasures) {\n    this.erasures = erasures;\n  }\n\n  /**\n   * @return arbitrary additional metadata\n   */\n  public Object getOther() {\n    return other;\n  }\n\n  public void setOther(Object other) {\n    this.other = other;\n  }\n\n  public boolean hasStructuredAppend() {\n    return structuredAppendParity >= 0 && structuredAppendSequenceNumber >= 0;\n  }\n\n  public int getStructuredAppendParity() {\n    return structuredAppendParity;\n  }\n\n  public int getStructuredAppendSequenceNumber() {\n    return structuredAppendSequenceNumber;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/DefaultGridSampler.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport com.google.zxing.NotFoundException;\n\n/**\n * @author Sean Owen\n */\npublic final class DefaultGridSampler extends GridSampler {\n\n  @Override\n  public BitMatrix sampleGrid(BitMatrix image,\n                              int dimensionX,\n                              int dimensionY,\n                              float p1ToX, float p1ToY,\n                              float p2ToX, float p2ToY,\n                              float p3ToX, float p3ToY,\n                              float p4ToX, float p4ToY,\n                              float p1FromX, float p1FromY,\n                              float p2FromX, float p2FromY,\n                              float p3FromX, float p3FromY,\n                              float p4FromX, float p4FromY) throws NotFoundException {\n\n    PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral(\n        p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,\n        p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);\n\n    return sampleGrid(image, dimensionX, dimensionY, transform);\n  }\n\n  @Override\n  public BitMatrix sampleGrid(BitMatrix image,\n                              int dimensionX,\n                              int dimensionY,\n                              PerspectiveTransform transform) throws NotFoundException {\n    if (dimensionX <= 0 || dimensionY <= 0) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    BitMatrix bits = new BitMatrix(dimensionX, dimensionY);\n    float[] points = new float[2 * dimensionX];\n    for (int y = 0; y < dimensionY; y++) {\n      int max = points.length;\n      float iValue = y + 0.5f;\n      for (int x = 0; x < max; x += 2) {\n        points[x] = (float) (x / 2) + 0.5f;\n        points[x + 1] = iValue;\n      }\n      transform.transformPoints(points);\n      // Quick check to see if points transformed to something inside the image;\n      // sufficient to check the endpoints\n      checkAndNudgePoints(image, points);\n      try {\n        for (int x = 0; x < max; x += 2) {\n          if (image.get((int) points[x], (int) points[x + 1])) {\n            // Black(-ish) pixel\n            bits.set(x / 2, y);\n          }\n        }\n      } catch (ArrayIndexOutOfBoundsException aioobe) {\n        // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting\n        // transform gets \"twisted\" such that it maps a straight line of points to a set of points\n        // whose endpoints are in bounds, but others are not. There is probably some mathematical\n        // way to detect this about the transformation that I don't know yet.\n        // This results in an ugly runtime exception despite our clever checks above -- can't have\n        // that. We could check each point's coordinates but that feels duplicative. We settle for\n        // catching and wrapping ArrayIndexOutOfBoundsException.\n        throw NotFoundException.getNotFoundInstance();\n      }\n    }\n    return bits;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/DetectorResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport com.google.zxing.ResultPoint;\n\n/**\n * <p>Encapsulates the result of detecting a barcode in an image. This includes the raw\n * matrix of black/white pixels corresponding to the barcode, and possibly points of interest\n * in the image, like the location of finder patterns or corners of the barcode in the image.</p>\n *\n * @author Sean Owen\n */\npublic class DetectorResult {\n\n  private final BitMatrix bits;\n  private final ResultPoint[] points;\n\n  public DetectorResult(BitMatrix bits, ResultPoint[] points) {\n    this.bits = bits;\n    this.points = points;\n  }\n\n  public final BitMatrix getBits() {\n    return bits;\n  }\n\n  public final ResultPoint[] getPoints() {\n    return points;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/GlobalHistogramBinarizer.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport com.google.zxing.Binarizer;\nimport com.google.zxing.LuminanceSource;\nimport com.google.zxing.NotFoundException;\n\n/**\n * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable\n * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding\n * algorithm. However, because it picks a global black point, it cannot handle difficult shadows\n * and gradients.\n *\n * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Sean Owen\n */\npublic class GlobalHistogramBinarizer extends Binarizer {\n\n  private static final int LUMINANCE_BITS = 5;\n  private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;\n  private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;\n  private static final byte[] EMPTY = new byte[0];\n\n  private byte[] luminances;\n  private final int[] buckets;\n\n  public GlobalHistogramBinarizer(LuminanceSource source) {\n    super(source);\n    luminances = EMPTY;\n    buckets = new int[LUMINANCE_BUCKETS];\n  }\n\n  // Applies simple sharpening to the row data to improve performance of the 1D Readers.\n  @Override\n  public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {\n    LuminanceSource source = getLuminanceSource();\n    int width = source.getWidth();\n    if (row == null || row.getSize() < width) {\n      row = new BitArray(width);\n    } else {\n      row.clear();\n    }\n\n    initArrays(width);\n    byte[] localLuminances = source.getRow(y, luminances);\n    int[] localBuckets = buckets;\n    for (int x = 0; x < width; x++) {\n      localBuckets[(localLuminances[x] & 0xff) >> LUMINANCE_SHIFT]++;\n    }\n    int blackPoint = estimateBlackPoint(localBuckets);\n\n    if (width < 3) {\n      // Special case for very small images\n      for (int x = 0; x < width; x++) {\n        if ((localLuminances[x] & 0xff) < blackPoint) {\n          row.set(x);\n        }\n      }\n    } else {\n      int left = localLuminances[0] & 0xff;\n      int center = localLuminances[1] & 0xff;\n      for (int x = 1; x < width - 1; x++) {\n        int right = localLuminances[x + 1] & 0xff;\n        // A simple -1 4 -1 box filter with a weight of 2.\n        if (((center * 4) - left - right) / 2 < blackPoint) {\n          row.set(x);\n        }\n        left = center;\n        center = right;\n      }\n    }\n    return row;\n  }\n\n  // Does not sharpen the data, as this call is intended to only be used by 2D Readers.\n  @Override\n  public BitMatrix getBlackMatrix() throws NotFoundException {\n    LuminanceSource source = getLuminanceSource();\n    int width = source.getWidth();\n    int height = source.getHeight();\n    BitMatrix matrix = new BitMatrix(width, height);\n\n    // Quickly calculates the histogram by sampling four rows from the image. This proved to be\n    // more robust on the blackbox tests than sampling a diagonal as we used to do.\n    initArrays(width);\n    int[] localBuckets = buckets;\n    for (int y = 1; y < 5; y++) {\n      int row = height * y / 5;\n      byte[] localLuminances = source.getRow(row, luminances);\n      int right = (width * 4) / 5;\n      for (int x = width / 5; x < right; x++) {\n        int pixel = localLuminances[x] & 0xff;\n        localBuckets[pixel >> LUMINANCE_SHIFT]++;\n      }\n    }\n    int blackPoint = estimateBlackPoint(localBuckets);\n\n    // We delay reading the entire image luminance until the black point estimation succeeds.\n    // Although we end up reading four rows twice, it is consistent with our motto of\n    // \"fail quickly\" which is necessary for continuous scanning.\n    byte[] localLuminances = source.getMatrix();\n    for (int y = 0; y < height; y++) {\n      int offset = y * width;\n      for (int x = 0; x < width; x++) {\n        int pixel = localLuminances[offset + x] & 0xff;\n        if (pixel < blackPoint) {\n          matrix.set(x, y);\n        }\n      }\n    }\n\n    return matrix;\n  }\n\n  @Override\n  public Binarizer createBinarizer(LuminanceSource source) {\n    return new GlobalHistogramBinarizer(source);\n  }\n\n  private void initArrays(int luminanceSize) {\n    if (luminances.length < luminanceSize) {\n      luminances = new byte[luminanceSize];\n    }\n    for (int x = 0; x < LUMINANCE_BUCKETS; x++) {\n      buckets[x] = 0;\n    }\n  }\n\n  private static int estimateBlackPoint(int[] buckets) throws NotFoundException {\n    // Find the tallest peak in the histogram.\n    int numBuckets = buckets.length;\n    int maxBucketCount = 0;\n    int firstPeak = 0;\n    int firstPeakSize = 0;\n    for (int x = 0; x < numBuckets; x++) {\n      if (buckets[x] > firstPeakSize) {\n        firstPeak = x;\n        firstPeakSize = buckets[x];\n      }\n      if (buckets[x] > maxBucketCount) {\n        maxBucketCount = buckets[x];\n      }\n    }\n\n    // Find the second-tallest peak which is somewhat far from the tallest peak.\n    int secondPeak = 0;\n    int secondPeakScore = 0;\n    for (int x = 0; x < numBuckets; x++) {\n      int distanceToBiggest = x - firstPeak;\n      // Encourage more distant second peaks by multiplying by square of distance.\n      int score = buckets[x] * distanceToBiggest * distanceToBiggest;\n      if (score > secondPeakScore) {\n        secondPeak = x;\n        secondPeakScore = score;\n      }\n    }\n\n    // Make sure firstPeak corresponds to the black peak.\n    if (firstPeak > secondPeak) {\n      int temp = firstPeak;\n      firstPeak = secondPeak;\n      secondPeak = temp;\n    }\n\n    // If there is too little contrast in the image to pick a meaningful black point, throw rather\n    // than waste time trying to decode the image, and risk false positives.\n    if (secondPeak - firstPeak <= numBuckets / 16) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    // Find a valley between them that is low and closer to the white peak.\n    int bestValley = secondPeak - 1;\n    int bestValleyScore = -1;\n    for (int x = secondPeak - 1; x > firstPeak; x--) {\n      int fromFirst = x - firstPeak;\n      int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);\n      if (score > bestValleyScore) {\n        bestValley = x;\n        bestValleyScore = score;\n      }\n    }\n\n    return bestValley << LUMINANCE_SHIFT;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/GridSampler.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport com.google.zxing.NotFoundException;\n\n/**\n * Implementations of this class can, given locations of finder patterns for a QR code in an\n * image, sample the right points in the image to reconstruct the QR code, accounting for\n * perspective distortion. It is abstracted since it is relatively expensive and should be allowed\n * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced\n * Imaging library, but which may not be available in other environments such as J2ME, and vice\n * versa.\n *\n * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}\n * with an instance of a class which implements this interface.\n *\n * @author Sean Owen\n */\npublic abstract class GridSampler {\n\n  private static GridSampler gridSampler = new DefaultGridSampler();\n\n  /**\n   * Sets the implementation of GridSampler used by the library. One global\n   * instance is stored, which may sound problematic. But, the implementation provided\n   * ought to be appropriate for the entire platform, and all uses of this library\n   * in the whole lifetime of the JVM. For instance, an Android activity can swap in\n   * an implementation that takes advantage of native platform libraries.\n   *\n   * @param newGridSampler The platform-specific object to install.\n   */\n  public static void setGridSampler(GridSampler newGridSampler) {\n    gridSampler = newGridSampler;\n  }\n\n  /**\n   * @return the current implementation of GridSampler\n   */\n  public static GridSampler getInstance() {\n    return gridSampler;\n  }\n\n  /**\n   * Samples an image for a rectangular matrix of bits of the given dimension. The sampling\n   * transformation is determined by the coordinates of 4 points, in the original and transformed\n   * image space.\n   *\n   * @param image image to sample\n   * @param dimensionX width of {@link BitMatrix} to sample from image\n   * @param dimensionY height of {@link BitMatrix} to sample from image\n   * @param p1ToX point 1 preimage X\n   * @param p1ToY point 1 preimage Y\n   * @param p2ToX point 2 preimage X\n   * @param p2ToY point 2 preimage Y\n   * @param p3ToX point 3 preimage X\n   * @param p3ToY point 3 preimage Y\n   * @param p4ToX point 4 preimage X\n   * @param p4ToY point 4 preimage Y\n   * @param p1FromX point 1 image X\n   * @param p1FromY point 1 image Y\n   * @param p2FromX point 2 image X\n   * @param p2FromY point 2 image Y\n   * @param p3FromX point 3 image X\n   * @param p3FromY point 3 image Y\n   * @param p4FromX point 4 image X\n   * @param p4FromY point 4 image Y\n   * @return {@link BitMatrix} representing a grid of points sampled from the image within a region\n   *   defined by the \"from\" parameters\n   * @throws NotFoundException if image can't be sampled, for example, if the transformation defined\n   *   by the given points is invalid or results in sampling outside the image boundaries\n   */\n  public abstract BitMatrix sampleGrid(BitMatrix image,\n                                       int dimensionX,\n                                       int dimensionY,\n                                       float p1ToX, float p1ToY,\n                                       float p2ToX, float p2ToY,\n                                       float p3ToX, float p3ToY,\n                                       float p4ToX, float p4ToY,\n                                       float p1FromX, float p1FromY,\n                                       float p2FromX, float p2FromY,\n                                       float p3FromX, float p3FromY,\n                                       float p4FromX, float p4FromY) throws NotFoundException;\n\n  public abstract BitMatrix sampleGrid(BitMatrix image,\n                                       int dimensionX,\n                                       int dimensionY,\n                                       PerspectiveTransform transform) throws NotFoundException;\n\n  /**\n   * <p>Checks a set of points that have been transformed to sample points on an image against\n   * the image's dimensions to see if the point are even within the image.</p>\n   *\n   * <p>This method will actually \"nudge\" the endpoints back onto the image if they are found to be\n   * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder\n   * patterns in an image where the QR Code runs all the way to the image border.</p>\n   *\n   * <p>For efficiency, the method will check points from either end of the line until one is found\n   * to be within the image. Because the set of points are assumed to be linear, this is valid.</p>\n   *\n   * @param image image into which the points should map\n   * @param points actual points in x1,y1,...,xn,yn form\n   * @throws NotFoundException if an endpoint is lies outside the image boundaries\n   */\n  protected static void checkAndNudgePoints(BitMatrix image,\n                                            float[] points) throws NotFoundException {\n    int width = image.getWidth();\n    int height = image.getHeight();\n    // Check and nudge points from start until we see some that are OK:\n    boolean nudged = true;\n    int maxOffset = points.length - 1; // points.length must be even\n    for (int offset = 0; offset < maxOffset && nudged; offset += 2) {\n      int x = (int) points[offset];\n      int y = (int) points[offset + 1];\n      if (x < -1 || x > width || y < -1 || y > height) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      nudged = false;\n      if (x == -1) {\n        points[offset] = 0.0f;\n        nudged = true;\n      } else if (x == width) {\n        points[offset] = width - 1;\n        nudged = true;\n      }\n      if (y == -1) {\n        points[offset + 1] = 0.0f;\n        nudged = true;\n      } else if (y == height) {\n        points[offset + 1] = height - 1;\n        nudged = true;\n      }\n    }\n    // Check and nudge points from end:\n    nudged = true;\n    for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {\n      int x = (int) points[offset];\n      int y = (int) points[offset + 1];\n      if (x < -1 || x > width || y < -1 || y > height) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      nudged = false;\n      if (x == -1) {\n        points[offset] = 0.0f;\n        nudged = true;\n      } else if (x == width) {\n        points[offset] = width - 1;\n        nudged = true;\n      }\n      if (y == -1) {\n        points[offset + 1] = 0.0f;\n        nudged = true;\n      } else if (y == height) {\n        points[offset + 1] = height - 1;\n        nudged = true;\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/HybridBinarizer.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport com.google.zxing.Binarizer;\nimport com.google.zxing.LuminanceSource;\nimport com.google.zxing.NotFoundException;\n\n/**\n * This class implements a local thresholding algorithm, which while slower than the\n * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for\n * high frequency images of barcodes with black data on white backgrounds. For this application,\n * it does a much better job than a global blackpoint with severe shadows and gradients.\n * However it tends to produce artifacts on lower frequency images and is therefore not\n * a good general purpose binarizer for uses outside ZXing.\n *\n * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,\n * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already\n * inherently local, and only fails for horizontal gradients. We can revisit that problem later,\n * but for now it was not a win to use local blocks for 1D.\n *\n * This Binarizer is the default for the unit tests and the recommended class for library users.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class HybridBinarizer extends GlobalHistogramBinarizer {\n\n  // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.\n  // So this is the smallest dimension in each axis we can accept.\n  private static final int BLOCK_SIZE_POWER = 3;\n  private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00\n  private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;   // ...0011...11\n  private static final int MINIMUM_DIMENSION = BLOCK_SIZE * 5;\n  private static final int MIN_DYNAMIC_RANGE = 24;\n\n  private BitMatrix matrix;\n\n  public HybridBinarizer(LuminanceSource source) {\n    super(source);\n  }\n\n  /**\n   * Calculates the final BitMatrix once for all requests. This could be called once from the\n   * constructor instead, but there are some advantages to doing it lazily, such as making\n   * profiling easier, and not doing heavy lifting when callers don't expect it.\n   */\n  @Override\n  public BitMatrix getBlackMatrix() throws NotFoundException {\n    if (matrix != null) {\n      return matrix;\n    }\n    LuminanceSource source = getLuminanceSource();\n    int width = source.getWidth();\n    int height = source.getHeight();\n    if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) {\n      byte[] luminances = source.getMatrix();\n      int subWidth = width >> BLOCK_SIZE_POWER;\n      if ((width & BLOCK_SIZE_MASK) != 0) {\n        subWidth++;\n      }\n      int subHeight = height >> BLOCK_SIZE_POWER;\n      if ((height & BLOCK_SIZE_MASK) != 0) {\n        subHeight++;\n      }\n      int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height);\n\n      BitMatrix newMatrix = new BitMatrix(width, height);\n      calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, newMatrix);\n      matrix = newMatrix;\n    } else {\n      // If the image is too small, fall back to the global histogram approach.\n      matrix = super.getBlackMatrix();\n    }\n    return matrix;\n  }\n\n  @Override\n  public Binarizer createBinarizer(LuminanceSource source) {\n    return new HybridBinarizer(source);\n  }\n\n  /**\n   * For each block in the image, calculate the average black point using a 5x5 grid\n   * of the blocks around it. Also handles the corner cases (fractional blocks are computed based\n   * on the last pixels in the row/column which are also used in the previous block).\n   */\n  private static void calculateThresholdForBlock(byte[] luminances,\n                                                 int subWidth,\n                                                 int subHeight,\n                                                 int width,\n                                                 int height,\n                                                 int[][] blackPoints,\n                                                 BitMatrix matrix) {\n    int maxYOffset = height - BLOCK_SIZE;\n    int maxXOffset = width - BLOCK_SIZE;\n    for (int y = 0; y < subHeight; y++) {\n      int yoffset = y << BLOCK_SIZE_POWER;\n      if (yoffset > maxYOffset) {\n        yoffset = maxYOffset;\n      }\n      int top = cap(y, 2, subHeight - 3);\n      for (int x = 0; x < subWidth; x++) {\n        int xoffset = x << BLOCK_SIZE_POWER;\n        if (xoffset > maxXOffset) {\n          xoffset = maxXOffset;\n        }\n        int left = cap(x, 2, subWidth - 3);\n        int sum = 0;\n        for (int z = -2; z <= 2; z++) {\n          int[] blackRow = blackPoints[top + z];\n          sum += blackRow[left - 2] + blackRow[left - 1] + blackRow[left] + blackRow[left + 1] + blackRow[left + 2];\n        }\n        int average = sum / 25;\n        thresholdBlock(luminances, xoffset, yoffset, average, width, matrix);\n      }\n    }\n  }\n\n  private static int cap(int value, int min, int max) {\n    return value < min ? min : value > max ? max : value;\n  }\n\n  /**\n   * Applies a single threshold to a block of pixels.\n   */\n  private static void thresholdBlock(byte[] luminances,\n                                     int xoffset,\n                                     int yoffset,\n                                     int threshold,\n                                     int stride,\n                                     BitMatrix matrix) {\n    for (int y = 0, offset = yoffset * stride + xoffset; y < BLOCK_SIZE; y++, offset += stride) {\n      for (int x = 0; x < BLOCK_SIZE; x++) {\n        // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.\n        if ((luminances[offset + x] & 0xFF) <= threshold) {\n          matrix.set(xoffset + x, yoffset + y);\n        }\n      }\n    }\n  }\n\n  /**\n   * Calculates a single black point for each block of pixels and saves it away.\n   * See the following thread for a discussion of this algorithm:\n   *  http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0\n   */\n  private static int[][] calculateBlackPoints(byte[] luminances,\n                                              int subWidth,\n                                              int subHeight,\n                                              int width,\n                                              int height) {\n    int maxYOffset = height - BLOCK_SIZE;\n    int maxXOffset = width - BLOCK_SIZE;\n    int[][] blackPoints = new int[subHeight][subWidth];\n    for (int y = 0; y < subHeight; y++) {\n      int yoffset = y << BLOCK_SIZE_POWER;\n      if (yoffset > maxYOffset) {\n        yoffset = maxYOffset;\n      }\n      for (int x = 0; x < subWidth; x++) {\n        int xoffset = x << BLOCK_SIZE_POWER;\n        if (xoffset > maxXOffset) {\n          xoffset = maxXOffset;\n        }\n        int sum = 0;\n        int min = 0xFF;\n        int max = 0;\n        for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE; yy++, offset += width) {\n          for (int xx = 0; xx < BLOCK_SIZE; xx++) {\n            int pixel = luminances[offset + xx] & 0xFF;\n            sum += pixel;\n            // still looking for good contrast\n            if (pixel < min) {\n              min = pixel;\n            }\n            if (pixel > max) {\n              max = pixel;\n            }\n          }\n          // short-circuit min/max tests once dynamic range is met\n          if (max - min > MIN_DYNAMIC_RANGE) {\n            // finish the rest of the rows quickly\n            for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width) {\n              for (int xx = 0; xx < BLOCK_SIZE; xx++) {\n                sum += luminances[offset + xx] & 0xFF;\n              }\n            }\n          }\n        }\n\n        // The default estimate is the average of the values in the block.\n        int average = sum >> (BLOCK_SIZE_POWER * 2);\n        if (max - min <= MIN_DYNAMIC_RANGE) {\n          // If variation within the block is low, assume this is a block with only light or only\n          // dark pixels. In that case we do not want to use the average, as it would divide this\n          // low contrast area into black and white pixels, essentially creating data out of noise.\n          //\n          // The default assumption is that the block is light/background. Since no estimate for\n          // the level of dark pixels exists locally, use half the min for the block.\n          average = min / 2;\n\n          if (y > 0 && x > 0) {\n            // Correct the \"white background\" assumption for blocks that have neighbors by comparing\n            // the pixels in this block to the previously calculated black points. This is based on\n            // the fact that dark barcode symbology is always surrounded by some amount of light\n            // background for which reasonable black point estimates were made. The bp estimated at\n            // the boundaries is used for the interior.\n\n            // The (min < bp) is arbitrary but works better than other heuristics that were tried.\n            int averageNeighborBlackPoint =\n                (blackPoints[y - 1][x] + (2 * blackPoints[y][x - 1]) + blackPoints[y - 1][x - 1]) / 4;\n            if (min < averageNeighborBlackPoint) {\n              average = averageNeighborBlackPoint;\n            }\n          }\n        }\n        blackPoints[y][x] = average;\n      }\n    }\n    return blackPoints;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/PerspectiveTransform.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\n/**\n * <p>This class implements a perspective transform in two dimensions. Given four source and four\n * destination points, it will compute the transformation implied between them. The code is based\n * directly upon section 3.4.2 of George Wolberg's \"Digital Image Warping\"; see pages 54-56.</p>\n *\n * @author Sean Owen\n */\npublic final class PerspectiveTransform {\n\n  private final float a11;\n  private final float a12;\n  private final float a13;\n  private final float a21;\n  private final float a22;\n  private final float a23;\n  private final float a31;\n  private final float a32;\n  private final float a33;\n\n  private PerspectiveTransform(float a11, float a21, float a31,\n                               float a12, float a22, float a32,\n                               float a13, float a23, float a33) {\n    this.a11 = a11;\n    this.a12 = a12;\n    this.a13 = a13;\n    this.a21 = a21;\n    this.a22 = a22;\n    this.a23 = a23;\n    this.a31 = a31;\n    this.a32 = a32;\n    this.a33 = a33;\n  }\n\n  public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0,\n                                                                  float x1, float y1,\n                                                                  float x2, float y2,\n                                                                  float x3, float y3,\n                                                                  float x0p, float y0p,\n                                                                  float x1p, float y1p,\n                                                                  float x2p, float y2p,\n                                                                  float x3p, float y3p) {\n\n    PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);\n    PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);\n    return sToQ.times(qToS);\n  }\n\n  public void transformPoints(float[] points) {\n    float a11 = this.a11;\n    float a12 = this.a12;\n    float a13 = this.a13;\n    float a21 = this.a21;\n    float a22 = this.a22;\n    float a23 = this.a23;\n    float a31 = this.a31;\n    float a32 = this.a32;\n    float a33 = this.a33;\n    int maxI = points.length - 1; // points.length must be even\n    for (int i = 0; i < maxI; i += 2) {\n      float x = points[i];\n      float y = points[i + 1];\n      float denominator = a13 * x + a23 * y + a33;\n      points[i] = (a11 * x + a21 * y + a31) / denominator;\n      points[i + 1] = (a12 * x + a22 * y + a32) / denominator;\n    }\n  }\n\n  public void transformPoints(float[] xValues, float[] yValues) {\n    int n = xValues.length;\n    for (int i = 0; i < n; i++) {\n      float x = xValues[i];\n      float y = yValues[i];\n      float denominator = a13 * x + a23 * y + a33;\n      xValues[i] = (a11 * x + a21 * y + a31) / denominator;\n      yValues[i] = (a12 * x + a22 * y + a32) / denominator;\n    }\n  }\n\n  public static PerspectiveTransform squareToQuadrilateral(float x0, float y0,\n                                                           float x1, float y1,\n                                                           float x2, float y2,\n                                                           float x3, float y3) {\n    float dx3 = x0 - x1 + x2 - x3;\n    float dy3 = y0 - y1 + y2 - y3;\n    if (dx3 == 0.0f && dy3 == 0.0f) {\n      // Affine\n      return new PerspectiveTransform(x1 - x0, x2 - x1, x0,\n                                      y1 - y0, y2 - y1, y0,\n                                      0.0f,    0.0f,    1.0f);\n    } else {\n      float dx1 = x1 - x2;\n      float dx2 = x3 - x2;\n      float dy1 = y1 - y2;\n      float dy2 = y3 - y2;\n      float denominator = dx1 * dy2 - dx2 * dy1;\n      float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;\n      float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;\n      return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0,\n                                      y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0,\n                                      a13,                a23,                1.0f);\n    }\n  }\n\n  public static PerspectiveTransform quadrilateralToSquare(float x0, float y0,\n                                                           float x1, float y1,\n                                                           float x2, float y2,\n                                                           float x3, float y3) {\n    // Here, the adjoint serves as the inverse:\n    return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();\n  }\n\n  PerspectiveTransform buildAdjoint() {\n    // Adjoint is the transpose of the cofactor matrix:\n    return new PerspectiveTransform(a22 * a33 - a23 * a32,\n        a23 * a31 - a21 * a33,\n        a21 * a32 - a22 * a31,\n        a13 * a32 - a12 * a33,\n        a11 * a33 - a13 * a31,\n        a12 * a31 - a11 * a32,\n        a12 * a23 - a13 * a22,\n        a13 * a21 - a11 * a23,\n        a11 * a22 - a12 * a21);\n  }\n\n  PerspectiveTransform times(PerspectiveTransform other) {\n    return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13,\n        a11 * other.a21 + a21 * other.a22 + a31 * other.a23,\n        a11 * other.a31 + a21 * other.a32 + a31 * other.a33,\n        a12 * other.a11 + a22 * other.a12 + a32 * other.a13,\n        a12 * other.a21 + a22 * other.a22 + a32 * other.a23,\n        a12 * other.a31 + a22 * other.a32 + a32 * other.a33,\n        a13 * other.a11 + a23 * other.a12 + a33 * other.a13,\n        a13 * other.a21 + a23 * other.a22 + a33 * other.a23,\n        a13 * other.a31 + a23 * other.a32 + a33 * other.a33);\n\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/StringUtils.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common;\n\nimport java.nio.charset.Charset;\nimport java.util.Map;\n\nimport com.google.zxing.DecodeHintType;\n\n/**\n * Common string-related functions.\n *\n * @author Sean Owen\n * @author Alex Dupre\n */\npublic final class StringUtils {\n\n  private static final String PLATFORM_DEFAULT_ENCODING = Charset.defaultCharset().name();\n  public static final String SHIFT_JIS = \"SJIS\";\n  public static final String GB2312 = \"GB2312\";\n  private static final String EUC_JP = \"EUC_JP\";\n  private static final String UTF8 = \"UTF8\";\n  private static final String ISO88591 = \"ISO8859_1\";\n  private static final boolean ASSUME_SHIFT_JIS =\n      SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) ||\n      EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING);\n\n  private StringUtils() { }\n\n  /**\n   * @param bytes bytes encoding a string, whose encoding should be guessed\n   * @param hints decode hints if applicable\n   * @return name of guessed encoding; at the moment will only guess one of:\n   *  {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform\n   *  default encoding if none of these can possibly be correct\n   */\n  public static String guessEncoding(byte[] bytes, Map<DecodeHintType,?> hints) {\n    if (hints != null && hints.containsKey(DecodeHintType.CHARACTER_SET)) {\n      return hints.get(DecodeHintType.CHARACTER_SET).toString();\n    }\n    // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,\n    // which should be by far the most common encodings.\n    int length = bytes.length;\n    boolean canBeISO88591 = true;\n    boolean canBeShiftJIS = true;\n    boolean canBeUTF8 = true;\n    int utf8BytesLeft = 0;\n    int utf2BytesChars = 0;\n    int utf3BytesChars = 0;\n    int utf4BytesChars = 0;\n    int sjisBytesLeft = 0;\n    int sjisKatakanaChars = 0;\n    int sjisCurKatakanaWordLength = 0;\n    int sjisCurDoubleBytesWordLength = 0;\n    int sjisMaxKatakanaWordLength = 0;\n    int sjisMaxDoubleBytesWordLength = 0;\n    int isoHighOther = 0;\n\n    boolean utf8bom = bytes.length > 3 &&\n        bytes[0] == (byte) 0xEF &&\n        bytes[1] == (byte) 0xBB &&\n        bytes[2] == (byte) 0xBF;\n\n    for (int i = 0;\n         i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);\n         i++) {\n\n      int value = bytes[i] & 0xFF;\n\n      // UTF-8 stuff\n      if (canBeUTF8) {\n        if (utf8BytesLeft > 0) {\n          if ((value & 0x80) == 0) {\n            canBeUTF8 = false;\n          } else {\n            utf8BytesLeft--;\n          }\n        } else if ((value & 0x80) != 0) {\n          if ((value & 0x40) == 0) {\n            canBeUTF8 = false;\n          } else {\n            utf8BytesLeft++;\n            if ((value & 0x20) == 0) {\n              utf2BytesChars++;\n            } else {\n              utf8BytesLeft++;\n              if ((value & 0x10) == 0) {\n                utf3BytesChars++;\n              } else {\n                utf8BytesLeft++;\n                if ((value & 0x08) == 0) {\n                  utf4BytesChars++;\n                } else {\n                  canBeUTF8 = false;\n                }\n              }\n            }\n          }\n        }\n      }\n\n      // ISO-8859-1 stuff\n      if (canBeISO88591) {\n        if (value > 0x7F && value < 0xA0) {\n          canBeISO88591 = false;\n        } else if (value > 0x9F && (value < 0xC0 || value == 0xD7 || value == 0xF7)) {\n          isoHighOther++;\n        }\n      }\n\n      // Shift_JIS stuff\n      if (canBeShiftJIS) {\n        if (sjisBytesLeft > 0) {\n          if (value < 0x40 || value == 0x7F || value > 0xFC) {\n            canBeShiftJIS = false;\n          } else {\n            sjisBytesLeft--;\n          }\n        } else if (value == 0x80 || value == 0xA0 || value > 0xEF) {\n          canBeShiftJIS = false;\n        } else if (value > 0xA0 && value < 0xE0) {\n          sjisKatakanaChars++;\n          sjisCurDoubleBytesWordLength = 0;\n          sjisCurKatakanaWordLength++;\n          if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) {\n            sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength;\n          }\n        } else if (value > 0x7F) {\n          sjisBytesLeft++;\n          //sjisDoubleBytesChars++;\n          sjisCurKatakanaWordLength = 0;\n          sjisCurDoubleBytesWordLength++;\n          if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) {\n            sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength;\n          }\n        } else {\n          //sjisLowChars++;\n          sjisCurKatakanaWordLength = 0;\n          sjisCurDoubleBytesWordLength = 0;\n        }\n      }\n    }\n\n    if (canBeUTF8 && utf8BytesLeft > 0) {\n      canBeUTF8 = false;\n    }\n    if (canBeShiftJIS && sjisBytesLeft > 0) {\n      canBeShiftJIS = false;\n    }\n\n    // Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done\n    if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) {\n      return UTF8;\n    }\n    // Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii characters (and no evidence it can't be), done\n    if (canBeShiftJIS && (ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) {\n      return SHIFT_JIS;\n    }\n    // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is:\n    // - If we saw\n    //   - only two consecutive katakana chars in the whole text, or\n    //   - at least 10% of bytes that could be \"upper\" not-alphanumeric Latin1,\n    // - then we conclude Shift_JIS, else ISO-8859-1\n    if (canBeISO88591 && canBeShiftJIS) {\n      return (sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) || isoHighOther * 10 >= length\n          ? SHIFT_JIS : ISO88591;\n    }\n\n    // Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding\n    if (canBeISO88591) {\n      return ISO88591;\n    }\n    if (canBeShiftJIS) {\n      return SHIFT_JIS;\n    }\n    if (canBeUTF8) {\n      return UTF8;\n    }\n    // Otherwise, we take a wild guess with platform encoding\n    return PLATFORM_DEFAULT_ENCODING;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/detector/MathUtils.java",
    "content": "/*\n * Copyright 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.detector;\n\n/**\n * General math-related and numeric utility functions.\n */\npublic final class MathUtils {\n\n  private MathUtils() {\n  }\n\n  /**\n   * Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its\n   * argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut\n   * differ slightly from {@link Math#round(float)} in that half rounds down for negative\n   * values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.\n   *\n   * @param d real value to round\n   * @return nearest {@code int}\n   */\n  public static int round(float d) {\n    return (int) (d + (d < 0.0f ? -0.5f : 0.5f));\n  }\n\n  /**\n   * @param aX point A x coordinate\n   * @param aY point A y coordinate\n   * @param bX point B x coordinate\n   * @param bY point B y coordinate\n   * @return Euclidean distance between points A and B\n   */\n  public static float distance(float aX, float aY, float bX, float bY) {\n    double xDiff = aX - bX;\n    double yDiff = aY - bY;\n    return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);\n  }\n\n  /**\n   * @param aX point A x coordinate\n   * @param aY point A y coordinate\n   * @param bX point B x coordinate\n   * @param bY point B y coordinate\n   * @return Euclidean distance between points A and B\n   */\n  public static float distance(int aX, int aY, int bX, int bY) {\n    double xDiff = aX - bX;\n    double yDiff = aY - bY;\n    return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);\n  }\n\n  /**\n   * @param array values to sum\n   * @return sum of values in array\n   */\n  public static int sum(int[] array) {\n    int count = 0;\n    for (int a : array) {\n      count += a;\n    }\n    return count;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/detector/MonochromeRectangleDetector.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.detector;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.\n * It looks within a mostly white region of an image for a region of black and white, but mostly\n * black. It returns the four corners of the region, as best it can determine.</p>\n *\n * @author Sean Owen\n * @deprecated without replacement since 3.3.0\n */\n@Deprecated\npublic final class MonochromeRectangleDetector {\n\n  private static final int MAX_MODULES = 32;\n\n  private final BitMatrix image;\n\n  public MonochromeRectangleDetector(BitMatrix image) {\n    this.image = image;\n  }\n\n  /**\n   * <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly\n   * white, in an image.</p>\n   *\n   * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and\n   *  last points are opposed on the diagonal, as are the second and third. The first point will be\n   *  the topmost point and the last, the bottommost. The second point will be leftmost and the\n   *  third, the rightmost\n   * @throws NotFoundException if no Data Matrix Code can be found\n   */\n  public ResultPoint[] detect() throws NotFoundException {\n    int height = image.getHeight();\n    int width = image.getWidth();\n    int halfHeight = height / 2;\n    int halfWidth = width / 2;\n    int deltaY = Math.max(1, height / (MAX_MODULES * 8));\n    int deltaX = Math.max(1, width / (MAX_MODULES * 8));\n\n    int top = 0;\n    int bottom = height;\n    int left = 0;\n    int right = width;\n    ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right,\n        halfHeight, -deltaY, top, bottom, halfWidth / 2);\n    top = (int) pointA.getY() - 1;\n    ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right,\n        halfHeight, 0, top, bottom, halfHeight / 2);\n    left = (int) pointB.getX() - 1;\n    ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right,\n        halfHeight, 0, top, bottom, halfHeight / 2);\n    right = (int) pointC.getX() + 1;\n    ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right,\n        halfHeight, deltaY, top, bottom, halfWidth / 2);\n    bottom = (int) pointD.getY() + 1;\n\n    // Go try to find point A again with better information -- might have been off at first.\n    pointA = findCornerFromCenter(halfWidth, 0, left, right,\n        halfHeight, -deltaY, top, bottom, halfWidth / 4);\n\n    return new ResultPoint[] { pointA, pointB, pointC, pointD };\n  }\n\n  /**\n   * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center\n   * point which should be within the barcode.\n   *\n   * @param centerX center's x component (horizontal)\n   * @param deltaX same as deltaY but change in x per step instead\n   * @param left minimum value of x\n   * @param right maximum value of x\n   * @param centerY center's y component (vertical)\n   * @param deltaY change in y per step. If scanning up this is negative; down, positive;\n   *  left or right, 0\n   * @param top minimum value of y to search through (meaningless when di == 0)\n   * @param bottom maximum value of y\n   * @param maxWhiteRun maximum run of white pixels that can still be considered to be within\n   *  the barcode\n   * @return a {@link ResultPoint} encapsulating the corner that was found\n   * @throws NotFoundException if such a point cannot be found\n   */\n  private ResultPoint findCornerFromCenter(int centerX,\n                                           int deltaX,\n                                           int left,\n                                           int right,\n                                           int centerY,\n                                           int deltaY,\n                                           int top,\n                                           int bottom,\n                                           int maxWhiteRun) throws NotFoundException {\n    int[] lastRange = null;\n    for (int y = centerY, x = centerX;\n         y < bottom && y >= top && x < right && x >= left;\n         y += deltaY, x += deltaX) {\n      int[] range;\n      if (deltaX == 0) {\n        // horizontal slices, up and down\n        range = blackWhiteRange(y, maxWhiteRun, left, right, true);\n      } else {\n        // vertical slices, left and right\n        range = blackWhiteRange(x, maxWhiteRun, top, bottom, false);\n      }\n      if (range == null) {\n        if (lastRange == null) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        // lastRange was found\n        if (deltaX == 0) {\n          int lastY = y - deltaY;\n          if (lastRange[0] < centerX) {\n            if (lastRange[1] > centerX) {\n              // straddle, choose one or the other based on direction\n              return new ResultPoint(lastRange[deltaY > 0 ? 0 : 1], lastY);\n            }\n            return new ResultPoint(lastRange[0], lastY);\n          } else {\n            return new ResultPoint(lastRange[1], lastY);\n          }\n        } else {\n          int lastX = x - deltaX;\n          if (lastRange[0] < centerY) {\n            if (lastRange[1] > centerY) {\n              return new ResultPoint(lastX, lastRange[deltaX < 0 ? 0 : 1]);\n            }\n            return new ResultPoint(lastX, lastRange[0]);\n          } else {\n            return new ResultPoint(lastX, lastRange[1]);\n          }\n        }\n      }\n      lastRange = range;\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * Computes the start and end of a region of pixels, either horizontally or vertically, that could\n   * be part of a Data Matrix barcode.\n   *\n   * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)\n   *  where we are scanning. If scanning vertically it's the column, the fixed horizontal location\n   * @param maxWhiteRun largest run of white pixels that can still be considered part of the\n   *  barcode region\n   * @param minDim minimum pixel location, horizontally or vertically, to consider\n   * @param maxDim maximum pixel location, horizontally or vertically, to consider\n   * @param horizontal if true, we're scanning left-right, instead of up-down\n   * @return int[] with start and end of found range, or null if no such range is found\n   *  (e.g. only white was found)\n   */\n  private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, boolean horizontal) {\n\n    int center = (minDim + maxDim) / 2;\n\n    // Scan left/up first\n    int start = center;\n    while (start >= minDim) {\n      if (horizontal ? image.get(start, fixedDimension) : image.get(fixedDimension, start)) {\n        start--;\n      } else {\n        int whiteRunStart = start;\n        do {\n          start--;\n        } while (start >= minDim && !(horizontal ? image.get(start, fixedDimension) :\n            image.get(fixedDimension, start)));\n        int whiteRunSize = whiteRunStart - start;\n        if (start < minDim || whiteRunSize > maxWhiteRun) {\n          start = whiteRunStart;\n          break;\n        }\n      }\n    }\n    start++;\n\n    // Then try right/down\n    int end = center;\n    while (end < maxDim) {\n      if (horizontal ? image.get(end, fixedDimension) : image.get(fixedDimension, end)) {\n        end++;\n      } else {\n        int whiteRunStart = end;\n        do {\n          end++;\n        } while (end < maxDim && !(horizontal ? image.get(end, fixedDimension) :\n            image.get(fixedDimension, end)));\n        int whiteRunSize = end - whiteRunStart;\n        if (end >= maxDim || whiteRunSize > maxWhiteRun) {\n          end = whiteRunStart;\n          break;\n        }\n      }\n    }\n    end--;\n\n    return end > start ? new int[]{start, end} : null;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/detector/WhiteRectangleDetector.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.detector;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * <p>\n * Detects a candidate barcode-like rectangular region within an image. It\n * starts around the center of the image, increases the size of the candidate\n * region until it finds a white rectangular region. By keeping track of the\n * last black points it encountered, it determines the corners of the barcode.\n * </p>\n *\n * @author David Olivier\n */\npublic final class WhiteRectangleDetector {\n\n  private static final int INIT_SIZE = 10;\n  private static final int CORR = 1;\n\n  private final BitMatrix image;\n  private final int height;\n  private final int width;\n  private final int leftInit;\n  private final int rightInit;\n  private final int downInit;\n  private final int upInit;\n\n  public WhiteRectangleDetector(BitMatrix image) throws NotFoundException {\n    this(image, INIT_SIZE, image.getWidth() / 2, image.getHeight() / 2);\n  }\n\n  /**\n   * @param image barcode image to find a rectangle in\n   * @param initSize initial size of search area around center\n   * @param x x position of search center\n   * @param y y position of search center\n   * @throws NotFoundException if image is too small to accommodate {@code initSize}\n   */\n  public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y) throws NotFoundException {\n    this.image = image;\n    height = image.getHeight();\n    width = image.getWidth();\n    int halfsize = initSize / 2;\n    leftInit = x - halfsize;\n    rightInit = x + halfsize;\n    upInit = y - halfsize;\n    downInit = y + halfsize;\n    if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n  /**\n   * <p>\n   * Detects a candidate barcode-like rectangular region within an image. It\n   * starts around the center of the image, increases the size of the candidate\n   * region until it finds a white rectangular region.\n   * </p>\n   *\n   * @return {@link ResultPoint}[] describing the corners of the rectangular\n   *         region. The first and last points are opposed on the diagonal, as\n   *         are the second and third. The first point will be the topmost\n   *         point and the last, the bottommost. The second point will be\n   *         leftmost and the third, the rightmost\n   * @throws NotFoundException if no Data Matrix Code can be found\n   */\n  public ResultPoint[] detect() throws NotFoundException {\n\n    int left = leftInit;\n    int right = rightInit;\n    int up = upInit;\n    int down = downInit;\n    boolean sizeExceeded = false;\n    boolean aBlackPointFoundOnBorder = true;\n\n    boolean atLeastOneBlackPointFoundOnRight = false;\n    boolean atLeastOneBlackPointFoundOnBottom = false;\n    boolean atLeastOneBlackPointFoundOnLeft = false;\n    boolean atLeastOneBlackPointFoundOnTop = false;\n\n    while (aBlackPointFoundOnBorder) {\n\n      aBlackPointFoundOnBorder = false;\n\n      // .....\n      // .   |\n      // .....\n      boolean rightBorderNotWhite = true;\n      while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < width) {\n        rightBorderNotWhite = containsBlackPoint(up, down, right, false);\n        if (rightBorderNotWhite) {\n          right++;\n          aBlackPointFoundOnBorder = true;\n          atLeastOneBlackPointFoundOnRight = true;\n        } else if (!atLeastOneBlackPointFoundOnRight) {\n          right++;\n        }\n      }\n\n      if (right >= width) {\n        sizeExceeded = true;\n        break;\n      }\n\n      // .....\n      // .   .\n      // .___.\n      boolean bottomBorderNotWhite = true;\n      while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < height) {\n        bottomBorderNotWhite = containsBlackPoint(left, right, down, true);\n        if (bottomBorderNotWhite) {\n          down++;\n          aBlackPointFoundOnBorder = true;\n          atLeastOneBlackPointFoundOnBottom = true;\n        } else if (!atLeastOneBlackPointFoundOnBottom) {\n          down++;\n        }\n      }\n\n      if (down >= height) {\n        sizeExceeded = true;\n        break;\n      }\n\n      // .....\n      // |   .\n      // .....\n      boolean leftBorderNotWhite = true;\n      while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) {\n        leftBorderNotWhite = containsBlackPoint(up, down, left, false);\n        if (leftBorderNotWhite) {\n          left--;\n          aBlackPointFoundOnBorder = true;\n          atLeastOneBlackPointFoundOnLeft = true;\n        } else if (!atLeastOneBlackPointFoundOnLeft) {\n          left--;\n        }\n      }\n\n      if (left < 0) {\n        sizeExceeded = true;\n        break;\n      }\n\n      // .___.\n      // .   .\n      // .....\n      boolean topBorderNotWhite = true;\n      while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) {\n        topBorderNotWhite = containsBlackPoint(left, right, up, true);\n        if (topBorderNotWhite) {\n          up--;\n          aBlackPointFoundOnBorder = true;\n          atLeastOneBlackPointFoundOnTop = true;\n        } else if (!atLeastOneBlackPointFoundOnTop) {\n          up--;\n        }\n      }\n\n      if (up < 0) {\n        sizeExceeded = true;\n        break;\n      }\n\n    }\n\n    if (!sizeExceeded) {\n\n      int maxSize = right - left;\n\n      ResultPoint z = null;\n      for (int i = 1; z == null && i < maxSize; i++) {\n        z = getBlackPointOnSegment(left, down - i, left + i, down);\n      }\n\n      if (z == null) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n\n      ResultPoint t = null;\n      //go down right\n      for (int i = 1; t == null && i < maxSize; i++) {\n        t = getBlackPointOnSegment(left, up + i, left + i, up);\n      }\n\n      if (t == null) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n\n      ResultPoint x = null;\n      //go down left\n      for (int i = 1; x == null && i < maxSize; i++) {\n        x = getBlackPointOnSegment(right, up + i, right - i, up);\n      }\n\n      if (x == null) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n\n      ResultPoint y = null;\n      //go up left\n      for (int i = 1; y == null && i < maxSize; i++) {\n        y = getBlackPointOnSegment(right, down - i, right - i, down);\n      }\n\n      if (y == null) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n\n      return centerEdges(y, z, x, t);\n\n    } else {\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n  private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) {\n    int dist = MathUtils.round(MathUtils.distance(aX, aY, bX, bY));\n    float xStep = (bX - aX) / dist;\n    float yStep = (bY - aY) / dist;\n\n    for (int i = 0; i < dist; i++) {\n      int x = MathUtils.round(aX + i * xStep);\n      int y = MathUtils.round(aY + i * yStep);\n      if (image.get(x, y)) {\n        return new ResultPoint(x, y);\n      }\n    }\n    return null;\n  }\n\n  /**\n   * recenters the points of a constant distance towards the center\n   *\n   * @param y bottom most point\n   * @param z left most point\n   * @param x right most point\n   * @param t top most point\n   * @return {@link ResultPoint}[] describing the corners of the rectangular\n   *         region. The first and last points are opposed on the diagonal, as\n   *         are the second and third. The first point will be the topmost\n   *         point and the last, the bottommost. The second point will be\n   *         leftmost and the third, the rightmost\n   */\n  private ResultPoint[] centerEdges(ResultPoint y, ResultPoint z,\n                                    ResultPoint x, ResultPoint t) {\n\n    //\n    //       t            t\n    //  z                      x\n    //        x    OR    z\n    //   y                    y\n    //\n\n    float yi = y.getX();\n    float yj = y.getY();\n    float zi = z.getX();\n    float zj = z.getY();\n    float xi = x.getX();\n    float xj = x.getY();\n    float ti = t.getX();\n    float tj = t.getY();\n\n    if (yi < width / 2.0f) {\n      return new ResultPoint[]{\n          new ResultPoint(ti - CORR, tj + CORR),\n          new ResultPoint(zi + CORR, zj + CORR),\n          new ResultPoint(xi - CORR, xj - CORR),\n          new ResultPoint(yi + CORR, yj - CORR)};\n    } else {\n      return new ResultPoint[]{\n          new ResultPoint(ti + CORR, tj + CORR),\n          new ResultPoint(zi + CORR, zj - CORR),\n          new ResultPoint(xi - CORR, xj + CORR),\n          new ResultPoint(yi - CORR, yj - CORR)};\n    }\n  }\n\n  /**\n   * Determines whether a segment contains a black point\n   *\n   * @param a          min value of the scanned coordinate\n   * @param b          max value of the scanned coordinate\n   * @param fixed      value of fixed coordinate\n   * @param horizontal set to true if scan must be horizontal, false if vertical\n   * @return true if a black point has been found, else false.\n   */\n  private boolean containsBlackPoint(int a, int b, int fixed, boolean horizontal) {\n\n    if (horizontal) {\n      for (int x = a; x <= b; x++) {\n        if (image.get(x, fixed)) {\n          return true;\n        }\n      }\n    } else {\n      for (int y = a; y <= b; y++) {\n        if (image.get(fixed, y)) {\n          return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.reedsolomon;\n\n/**\n * <p>This class contains utility methods for performing mathematical operations over\n * the Galois Fields. Operations use a given primitive polynomial in calculations.</p>\n *\n * <p>Throughout this package, elements of the GF are represented as an {@code int}\n * for convenience and speed (but at the cost of memory).\n * </p>\n *\n * @author Sean Owen\n * @author David Olivier\n */\npublic final class GenericGF {\n\n  public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1\n  public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1\n  public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1\n  public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1\n  public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1\n  public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1\n  public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256;\n  public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6;\n\n  private final int[] expTable;\n  private final int[] logTable;\n  private final GenericGFPoly zero;\n  private final GenericGFPoly one;\n  private final int size;\n  private final int primitive;\n  private final int generatorBase;\n\n  /**\n   * Create a representation of GF(size) using the given primitive polynomial.\n   *\n   * @param primitive irreducible polynomial whose coefficients are represented by\n   *  the bits of an int, where the least-significant bit represents the constant\n   *  coefficient\n   * @param size the size of the field\n   * @param b the factor b in the generator polynomial can be 0- or 1-based\n   *  (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).\n   *  In most cases it should be 1, but for QR code it is 0.\n   */\n  public GenericGF(int primitive, int size, int b) {\n    this.primitive = primitive;\n    this.size = size;\n    this.generatorBase = b;\n\n    expTable = new int[size];\n    logTable = new int[size];\n    int x = 1;\n    for (int i = 0; i < size; i++) {\n      expTable[i] = x;\n      x *= 2; // we're assuming the generator alpha is 2\n      if (x >= size) {\n        x ^= primitive;\n        x &= size - 1;\n      }\n    }\n    for (int i = 0; i < size - 1; i++) {\n      logTable[expTable[i]] = i;\n    }\n    // logTable[0] == 0 but this should never be used\n    zero = new GenericGFPoly(this, new int[]{0});\n    one = new GenericGFPoly(this, new int[]{1});\n  }\n\n  GenericGFPoly getZero() {\n    return zero;\n  }\n\n  GenericGFPoly getOne() {\n    return one;\n  }\n\n  /**\n   * @return the monomial representing coefficient * x^degree\n   */\n  GenericGFPoly buildMonomial(int degree, int coefficient) {\n    if (degree < 0) {\n      throw new IllegalArgumentException();\n    }\n    if (coefficient == 0) {\n      return zero;\n    }\n    int[] coefficients = new int[degree + 1];\n    coefficients[0] = coefficient;\n    return new GenericGFPoly(this, coefficients);\n  }\n\n  /**\n   * Implements both addition and subtraction -- they are the same in GF(size).\n   *\n   * @return sum/difference of a and b\n   */\n  static int addOrSubtract(int a, int b) {\n    return a ^ b;\n  }\n\n  /**\n   * @return 2 to the power of a in GF(size)\n   */\n  int exp(int a) {\n    return expTable[a];\n  }\n\n  /**\n   * @return base 2 log of a in GF(size)\n   */\n  int log(int a) {\n    if (a == 0) {\n      throw new IllegalArgumentException();\n    }\n    return logTable[a];\n  }\n\n  /**\n   * @return multiplicative inverse of a\n   */\n  int inverse(int a) {\n    if (a == 0) {\n      throw new ArithmeticException();\n    }\n    return expTable[size - logTable[a] - 1];\n  }\n\n  /**\n   * @return product of a and b in GF(size)\n   */\n  int multiply(int a, int b) {\n    if (a == 0 || b == 0) {\n      return 0;\n    }\n    return expTable[(logTable[a] + logTable[b]) % (size - 1)];\n  }\n\n  public int getSize() {\n    return size;\n  }\n\n  public int getGeneratorBase() {\n    return generatorBase;\n  }\n\n  @Override\n  public String toString() {\n    return \"GF(0x\" + Integer.toHexString(primitive) + ',' + size + ')';\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.reedsolomon;\n\n/**\n * <p>Represents a polynomial whose coefficients are elements of a GF.\n * Instances of this class are immutable.</p>\n *\n * <p>Much credit is due to William Rucklidge since portions of this code are an indirect\n * port of his C++ Reed-Solomon implementation.</p>\n *\n * @author Sean Owen\n */\nfinal class GenericGFPoly {\n\n  private final GenericGF field;\n  private final int[] coefficients;\n\n  /**\n   * @param field the {@link GenericGF} instance representing the field to use\n   * to perform computations\n   * @param coefficients coefficients as ints representing elements of GF(size), arranged\n   * from most significant (highest-power term) coefficient to least significant\n   * @throws IllegalArgumentException if argument is null or empty,\n   * or if leading coefficient is 0 and this is not a\n   * constant polynomial (that is, it is not the monomial \"0\")\n   */\n  GenericGFPoly(GenericGF field, int[] coefficients) {\n    if (coefficients.length == 0) {\n      throw new IllegalArgumentException();\n    }\n    this.field = field;\n    int coefficientsLength = coefficients.length;\n    if (coefficientsLength > 1 && coefficients[0] == 0) {\n      // Leading term must be non-zero for anything except the constant polynomial \"0\"\n      int firstNonZero = 1;\n      while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {\n        firstNonZero++;\n      }\n      if (firstNonZero == coefficientsLength) {\n        this.coefficients = new int[]{0};\n      } else {\n        this.coefficients = new int[coefficientsLength - firstNonZero];\n        System.arraycopy(coefficients,\n            firstNonZero,\n            this.coefficients,\n            0,\n            this.coefficients.length);\n      }\n    } else {\n      this.coefficients = coefficients;\n    }\n  }\n\n  int[] getCoefficients() {\n    return coefficients;\n  }\n\n  /**\n   * @return degree of this polynomial\n   */\n  int getDegree() {\n    return coefficients.length - 1;\n  }\n\n  /**\n   * @return true iff this polynomial is the monomial \"0\"\n   */\n  boolean isZero() {\n    return coefficients[0] == 0;\n  }\n\n  /**\n   * @return coefficient of x^degree term in this polynomial\n   */\n  int getCoefficient(int degree) {\n    return coefficients[coefficients.length - 1 - degree];\n  }\n\n  /**\n   * @return evaluation of this polynomial at a given point\n   */\n  int evaluateAt(int a) {\n    if (a == 0) {\n      // Just return the x^0 coefficient\n      return getCoefficient(0);\n    }\n    if (a == 1) {\n      // Just the sum of the coefficients\n      int result = 0;\n      for (int coefficient : coefficients) {\n        result = GenericGF.addOrSubtract(result, coefficient);\n      }\n      return result;\n    }\n    int result = coefficients[0];\n    int size = coefficients.length;\n    for (int i = 1; i < size; i++) {\n      result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]);\n    }\n    return result;\n  }\n\n  GenericGFPoly addOrSubtract(GenericGFPoly other) {\n    if (!field.equals(other.field)) {\n      throw new IllegalArgumentException(\"GenericGFPolys do not have same GenericGF field\");\n    }\n    if (isZero()) {\n      return other;\n    }\n    if (other.isZero()) {\n      return this;\n    }\n\n    int[] smallerCoefficients = this.coefficients;\n    int[] largerCoefficients = other.coefficients;\n    if (smallerCoefficients.length > largerCoefficients.length) {\n      int[] temp = smallerCoefficients;\n      smallerCoefficients = largerCoefficients;\n      largerCoefficients = temp;\n    }\n    int[] sumDiff = new int[largerCoefficients.length];\n    int lengthDiff = largerCoefficients.length - smallerCoefficients.length;\n    // Copy high-order terms only found in higher-degree polynomial's coefficients\n    System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);\n\n    for (int i = lengthDiff; i < largerCoefficients.length; i++) {\n      sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);\n    }\n\n    return new GenericGFPoly(field, sumDiff);\n  }\n\n  GenericGFPoly multiply(GenericGFPoly other) {\n    if (!field.equals(other.field)) {\n      throw new IllegalArgumentException(\"GenericGFPolys do not have same GenericGF field\");\n    }\n    if (isZero() || other.isZero()) {\n      return field.getZero();\n    }\n    int[] aCoefficients = this.coefficients;\n    int aLength = aCoefficients.length;\n    int[] bCoefficients = other.coefficients;\n    int bLength = bCoefficients.length;\n    int[] product = new int[aLength + bLength - 1];\n    for (int i = 0; i < aLength; i++) {\n      int aCoeff = aCoefficients[i];\n      for (int j = 0; j < bLength; j++) {\n        product[i + j] = GenericGF.addOrSubtract(product[i + j],\n            field.multiply(aCoeff, bCoefficients[j]));\n      }\n    }\n    return new GenericGFPoly(field, product);\n  }\n\n  GenericGFPoly multiply(int scalar) {\n    if (scalar == 0) {\n      return field.getZero();\n    }\n    if (scalar == 1) {\n      return this;\n    }\n    int size = coefficients.length;\n    int[] product = new int[size];\n    for (int i = 0; i < size; i++) {\n      product[i] = field.multiply(coefficients[i], scalar);\n    }\n    return new GenericGFPoly(field, product);\n  }\n\n  GenericGFPoly multiplyByMonomial(int degree, int coefficient) {\n    if (degree < 0) {\n      throw new IllegalArgumentException();\n    }\n    if (coefficient == 0) {\n      return field.getZero();\n    }\n    int size = coefficients.length;\n    int[] product = new int[size + degree];\n    for (int i = 0; i < size; i++) {\n      product[i] = field.multiply(coefficients[i], coefficient);\n    }\n    return new GenericGFPoly(field, product);\n  }\n\n  GenericGFPoly[] divide(GenericGFPoly other) {\n    if (!field.equals(other.field)) {\n      throw new IllegalArgumentException(\"GenericGFPolys do not have same GenericGF field\");\n    }\n    if (other.isZero()) {\n      throw new IllegalArgumentException(\"Divide by 0\");\n    }\n\n    GenericGFPoly quotient = field.getZero();\n    GenericGFPoly remainder = this;\n\n    int denominatorLeadingTerm = other.getCoefficient(other.getDegree());\n    int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);\n\n    while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {\n      int degreeDifference = remainder.getDegree() - other.getDegree();\n      int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);\n      GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale);\n      GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);\n      quotient = quotient.addOrSubtract(iterationQuotient);\n      remainder = remainder.addOrSubtract(term);\n    }\n\n    return new GenericGFPoly[] { quotient, remainder };\n  }\n\n  @Override\n  public String toString() {\n    if (isZero()) {\n      return \"0\";\n    }\n    StringBuilder result = new StringBuilder(8 * getDegree());\n    for (int degree = getDegree(); degree >= 0; degree--) {\n      int coefficient = getCoefficient(degree);\n      if (coefficient != 0) {\n        if (coefficient < 0) {\n          if (degree == getDegree()) {\n            result.append(\"-\");\n          } else {\n            result.append(\" - \");\n          }\n          coefficient = -coefficient;\n        } else {\n          if (result.length() > 0) {\n            result.append(\" + \");\n          }\n        }\n        if (degree == 0 || coefficient != 1) {\n          int alphaPower = field.log(coefficient);\n          if (alphaPower == 0) {\n            result.append('1');\n          } else if (alphaPower == 1) {\n            result.append('a');\n          } else {\n            result.append(\"a^\");\n            result.append(alphaPower);\n          }\n        }\n        if (degree != 0) {\n          if (degree == 1) {\n            result.append('x');\n          } else {\n            result.append(\"x^\");\n            result.append(degree);\n          }\n        }\n      }\n    }\n    return result.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.reedsolomon;\n\n/**\n * <p>Implements Reed-Solomon decoding, as the name implies.</p>\n *\n * <p>The algorithm will not be explained here, but the following references were helpful\n * in creating this implementation:</p>\n *\n * <ul>\n * <li>Bruce Maggs.\n * <a href=\"http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps\">\n * \"Decoding Reed-Solomon Codes\"</a> (see discussion of Forney's Formula)</li>\n * <li>J.I. Hall. <a href=\"www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf\">\n * \"Chapter 5. Generalized Reed-Solomon Codes\"</a>\n * (see discussion of Euclidean algorithm)</li>\n * </ul>\n *\n * <p>Much credit is due to William Rucklidge since portions of this code are an indirect\n * port of his C++ Reed-Solomon implementation.</p>\n *\n * @author Sean Owen\n * @author William Rucklidge\n * @author sanfordsquires\n */\npublic final class ReedSolomonDecoder {\n\n  private final GenericGF field;\n\n  public ReedSolomonDecoder(GenericGF field) {\n    this.field = field;\n  }\n\n  /**\n   * <p>Decodes given set of received codewords, which include both data and error-correction\n   * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,\n   * in the input.</p>\n   *\n   * @param received data and error-correction codewords\n   * @param twoS number of error-correction codewords available\n   * @throws ReedSolomonException if decoding fails for any reason\n   */\n  public void decode(int[] received, int twoS) throws ReedSolomonException {\n    GenericGFPoly poly = new GenericGFPoly(field, received);\n    int[] syndromeCoefficients = new int[twoS];\n    boolean noError = true;\n    for (int i = 0; i < twoS; i++) {\n      int eval = poly.evaluateAt(field.exp(i + field.getGeneratorBase()));\n      syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;\n      if (eval != 0) {\n        noError = false;\n      }\n    }\n    if (noError) {\n      return;\n    }\n    GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients);\n    GenericGFPoly[] sigmaOmega =\n        runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);\n    GenericGFPoly sigma = sigmaOmega[0];\n    GenericGFPoly omega = sigmaOmega[1];\n    int[] errorLocations = findErrorLocations(sigma);\n    int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations);\n    for (int i = 0; i < errorLocations.length; i++) {\n      int position = received.length - 1 - field.log(errorLocations[i]);\n      if (position < 0) {\n        throw new ReedSolomonException(\"Bad error location\");\n      }\n      received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]);\n    }\n  }\n\n  private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R)\n      throws ReedSolomonException {\n    // Assume a's degree is >= b's\n    if (a.getDegree() < b.getDegree()) {\n      GenericGFPoly temp = a;\n      a = b;\n      b = temp;\n    }\n\n    GenericGFPoly rLast = a;\n    GenericGFPoly r = b;\n    GenericGFPoly tLast = field.getZero();\n    GenericGFPoly t = field.getOne();\n\n    // Run Euclidean algorithm until r's degree is less than R/2\n    while (r.getDegree() >= R / 2) {\n      GenericGFPoly rLastLast = rLast;\n      GenericGFPoly tLastLast = tLast;\n      rLast = r;\n      tLast = t;\n\n      // Divide rLastLast by rLast, with quotient in q and remainder in r\n      if (rLast.isZero()) {\n        // Oops, Euclidean algorithm already terminated?\n        throw new ReedSolomonException(\"r_{i-1} was zero\");\n      }\n      r = rLastLast;\n      GenericGFPoly q = field.getZero();\n      int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());\n      int dltInverse = field.inverse(denominatorLeadingTerm);\n      while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {\n        int degreeDiff = r.getDegree() - rLast.getDegree();\n        int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);\n        q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));\n        r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));\n      }\n\n      t = q.multiply(tLast).addOrSubtract(tLastLast);\n\n      if (r.getDegree() >= rLast.getDegree()) {\n        throw new IllegalStateException(\"Division algorithm failed to reduce polynomial?\");\n      }\n    }\n\n    int sigmaTildeAtZero = t.getCoefficient(0);\n    if (sigmaTildeAtZero == 0) {\n      throw new ReedSolomonException(\"sigmaTilde(0) was zero\");\n    }\n\n    int inverse = field.inverse(sigmaTildeAtZero);\n    GenericGFPoly sigma = t.multiply(inverse);\n    GenericGFPoly omega = r.multiply(inverse);\n    return new GenericGFPoly[]{sigma, omega};\n  }\n\n  private int[] findErrorLocations(GenericGFPoly errorLocator) throws ReedSolomonException {\n    // This is a direct application of Chien's search\n    int numErrors = errorLocator.getDegree();\n    if (numErrors == 1) { // shortcut\n      return new int[] { errorLocator.getCoefficient(1) };\n    }\n    int[] result = new int[numErrors];\n    int e = 0;\n    for (int i = 1; i < field.getSize() && e < numErrors; i++) {\n      if (errorLocator.evaluateAt(i) == 0) {\n        result[e] = field.inverse(i);\n        e++;\n      }\n    }\n    if (e != numErrors) {\n      throw new ReedSolomonException(\"Error locator degree does not match number of roots\");\n    }\n    return result;\n  }\n\n  private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations) {\n    // This is directly applying Forney's Formula\n    int s = errorLocations.length;\n    int[] result = new int[s];\n    for (int i = 0; i < s; i++) {\n      int xiInverse = field.inverse(errorLocations[i]);\n      int denominator = 1;\n      for (int j = 0; j < s; j++) {\n        if (i != j) {\n          //denominator = field.multiply(denominator,\n          //    GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));\n          // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.\n          // Below is a funny-looking workaround from Steven Parkes\n          int term = field.multiply(errorLocations[j], xiInverse);\n          int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1;\n          denominator = field.multiply(denominator, termPlus1);\n        }\n      }\n      result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),\n          field.inverse(denominator));\n      if (field.getGeneratorBase() != 0) {\n        result[i] = field.multiply(result[i], xiInverse);\n      }\n    }\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.reedsolomon;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <p>Implements Reed-Solomon encoding, as the name implies.</p>\n *\n * @author Sean Owen\n * @author William Rucklidge\n */\npublic final class ReedSolomonEncoder {\n\n  private final GenericGF field;\n  private final List<GenericGFPoly> cachedGenerators;\n\n  public ReedSolomonEncoder(GenericGF field) {\n    this.field = field;\n    this.cachedGenerators = new ArrayList<>();\n    cachedGenerators.add(new GenericGFPoly(field, new int[]{1}));\n  }\n\n  private GenericGFPoly buildGenerator(int degree) {\n    if (degree >= cachedGenerators.size()) {\n      GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1);\n      for (int d = cachedGenerators.size(); d <= degree; d++) {\n        GenericGFPoly nextGenerator = lastGenerator.multiply(\n            new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) }));\n        cachedGenerators.add(nextGenerator);\n        lastGenerator = nextGenerator;\n      }\n    }\n    return cachedGenerators.get(degree);\n  }\n\n  public void encode(int[] toEncode, int ecBytes) {\n    if (ecBytes == 0) {\n      throw new IllegalArgumentException(\"No error correction bytes\");\n    }\n    int dataBytes = toEncode.length - ecBytes;\n    if (dataBytes <= 0) {\n      throw new IllegalArgumentException(\"No data bytes provided\");\n    }\n    GenericGFPoly generator = buildGenerator(ecBytes);\n    int[] infoCoefficients = new int[dataBytes];\n    System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);\n    GenericGFPoly info = new GenericGFPoly(field, infoCoefficients);\n    info = info.multiplyByMonomial(ecBytes, 1);\n    GenericGFPoly remainder = info.divide(generator)[1];\n    int[] coefficients = remainder.getCoefficients();\n    int numZeroCoefficients = ecBytes - coefficients.length;\n    for (int i = 0; i < numZeroCoefficients; i++) {\n      toEncode[dataBytes + i] = 0;\n    }\n    System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonException.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.common.reedsolomon;\n\n/**\n * <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when\n * there are too many errors to correct.</p>\n *\n * @author Sean Owen\n */\npublic final class ReedSolomonException extends Exception {\n\n  public ReedSolomonException(String message) {\n    super(message);\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/DataMatrixReader.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.DetectorResult;\nimport com.google.zxing.datamatrix.decoder.Decoder;\nimport com.google.zxing.datamatrix.detector.Detector;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * This implementation can detect and decode Data Matrix codes in an image.\n *\n * @author bbrown@google.com (Brian Brown)\n */\npublic final class DataMatrixReader implements Reader {\n\n  private static final ResultPoint[] NO_POINTS = new ResultPoint[0];\n\n  private final Decoder decoder = new Decoder();\n\n  /**\n   * Locates and decodes a Data Matrix code in an image.\n   *\n   * @return a String representing the content encoded by the Data Matrix code\n   * @throws NotFoundException if a Data Matrix code cannot be found\n   * @throws FormatException if a Data Matrix code cannot be decoded\n   * @throws ChecksumException if error correction fails\n   */\n  @Override\n  public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {\n    return decode(image, null);\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException {\n    DecoderResult decoderResult;\n    ResultPoint[] points;\n    if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {\n      BitMatrix bits = extractPureBits(image.getBlackMatrix());\n      decoderResult = decoder.decode(bits);\n      points = NO_POINTS;\n    } else {\n      DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect();\n      decoderResult = decoder.decode(detectorResult.getBits());\n      points = detectorResult.getPoints();\n    }\n    Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,\n        BarcodeFormat.DATA_MATRIX);\n    List<byte[]> byteSegments = decoderResult.getByteSegments();\n    if (byteSegments != null) {\n      result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);\n    }\n    String ecLevel = decoderResult.getECLevel();\n    if (ecLevel != null) {\n      result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);\n    }\n    return result;\n  }\n\n  @Override\n  public void reset() {\n    // do nothing\n  }\n\n  /**\n   * This method detects a code in a \"pure\" image -- that is, pure monochrome image\n   * which contains only an unrotated, unskewed, image of a code, with some white border\n   * around it. This is a specialized method that works exceptionally fast in this special\n   * case.\n   *\n   * @see com.google.zxing.qrcode.QRCodeReader#extractPureBits(BitMatrix)\n   */\n  private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {\n\n    int[] leftTopBlack = image.getTopLeftOnBit();\n    int[] rightBottomBlack = image.getBottomRightOnBit();\n    if (leftTopBlack == null || rightBottomBlack == null) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    int moduleSize = moduleSize(leftTopBlack, image);\n\n    int top = leftTopBlack[1];\n    int bottom = rightBottomBlack[1];\n    int left = leftTopBlack[0];\n    int right = rightBottomBlack[0];\n\n    int matrixWidth = (right - left + 1) / moduleSize;\n    int matrixHeight = (bottom - top + 1) / moduleSize;\n    if (matrixWidth <= 0 || matrixHeight <= 0) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    // Push in the \"border\" by half the module width so that we start\n    // sampling in the middle of the module. Just in case the image is a\n    // little off, this will help recover.\n    int nudge = moduleSize / 2;\n    top += nudge;\n    left += nudge;\n\n    // Now just read off the bits\n    BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);\n    for (int y = 0; y < matrixHeight; y++) {\n      int iOffset = top + y * moduleSize;\n      for (int x = 0; x < matrixWidth; x++) {\n        if (image.get(left + x * moduleSize, iOffset)) {\n          bits.set(x, y);\n        }\n      }\n    }\n    return bits;\n  }\n\n  private static int moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException {\n    int width = image.getWidth();\n    int x = leftTopBlack[0];\n    int y = leftTopBlack[1];\n    while (x < width && image.get(x, y)) {\n      x++;\n    }\n    if (x == width) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    int moduleSize = x - leftTopBlack[0];\n    if (moduleSize == 0) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    return moduleSize;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/DataMatrixWriter.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.Writer;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.datamatrix.encoder.DefaultPlacement;\nimport com.google.zxing.Dimension;\nimport com.google.zxing.datamatrix.encoder.ErrorCorrection;\nimport com.google.zxing.datamatrix.encoder.HighLevelEncoder;\nimport com.google.zxing.datamatrix.encoder.SymbolInfo;\nimport com.google.zxing.datamatrix.encoder.SymbolShapeHint;\nimport com.google.zxing.qrcode.encoder.ByteMatrix;\n\nimport java.util.Map;\n\n/**\n * This object renders a Data Matrix code as a BitMatrix 2D array of greyscale values.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Guillaume Le Biller Added to zxing lib.\n */\npublic final class DataMatrixWriter implements Writer {\n\n  @Override\n  public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {\n    return encode(contents, format, width, height, null);\n  }\n\n  @Override\n  public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) {\n\n    if (contents.isEmpty()) {\n      throw new IllegalArgumentException(\"Found empty contents\");\n    }\n\n    if (format != BarcodeFormat.DATA_MATRIX) {\n      throw new IllegalArgumentException(\"Can only encode DATA_MATRIX, but got \" + format);\n    }\n\n    if (width < 0 || height < 0) {\n      throw new IllegalArgumentException(\"Requested dimensions can't be negative: \" + width + 'x' + height);\n    }\n\n    // Try to get force shape & min / max size\n    SymbolShapeHint shape = SymbolShapeHint.FORCE_NONE;\n    Dimension minSize = null;\n    Dimension maxSize = null;\n    if (hints != null) {\n      SymbolShapeHint requestedShape = (SymbolShapeHint) hints.get(EncodeHintType.DATA_MATRIX_SHAPE);\n      if (requestedShape != null) {\n        shape = requestedShape;\n      }\n      @SuppressWarnings(\"deprecation\")\n      Dimension requestedMinSize = (Dimension) hints.get(EncodeHintType.MIN_SIZE);\n      if (requestedMinSize != null) {\n        minSize = requestedMinSize;\n      }\n      @SuppressWarnings(\"deprecation\")\n      Dimension requestedMaxSize = (Dimension) hints.get(EncodeHintType.MAX_SIZE);\n      if (requestedMaxSize != null) {\n        maxSize = requestedMaxSize;\n      }\n    }\n\n\n    //1. step: Data encodation\n    String encoded = HighLevelEncoder.encodeHighLevel(contents, shape, minSize, maxSize);\n\n    SymbolInfo symbolInfo = SymbolInfo.lookup(encoded.length(), shape, minSize, maxSize, true);\n\n    //2. step: ECC generation\n    String codewords = ErrorCorrection.encodeECC200(encoded, symbolInfo);\n\n    //3. step: Module placement in Matrix\n    DefaultPlacement placement = new DefaultPlacement(codewords, symbolInfo.getSymbolDataWidth(), symbolInfo.getSymbolDataHeight());\n    placement.place();\n\n    //4. step: low-level encoding\n    return encodeLowLevel(placement, symbolInfo, width, height);\n  }\n\n  /**\n   * Encode the given symbol info to a bit matrix.\n   *\n   * @param placement  The DataMatrix placement.\n   * @param symbolInfo The symbol info to encode.\n   * @return The bit matrix generated.\n   */\n  private static BitMatrix encodeLowLevel(DefaultPlacement placement, SymbolInfo symbolInfo, int width, int height) {\n    int symbolWidth = symbolInfo.getSymbolDataWidth();\n    int symbolHeight = symbolInfo.getSymbolDataHeight();\n\n    ByteMatrix matrix = new ByteMatrix(symbolInfo.getSymbolWidth(), symbolInfo.getSymbolHeight());\n\n    int matrixY = 0;\n\n    for (int y = 0; y < symbolHeight; y++) {\n      // Fill the top edge with alternate 0 / 1\n      int matrixX;\n      if ((y % symbolInfo.matrixHeight) == 0) {\n        matrixX = 0;\n        for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {\n          matrix.set(matrixX, matrixY, (x % 2) == 0);\n          matrixX++;\n        }\n        matrixY++;\n      }\n      matrixX = 0;\n      for (int x = 0; x < symbolWidth; x++) {\n        // Fill the right edge with full 1\n        if ((x % symbolInfo.matrixWidth) == 0) {\n          matrix.set(matrixX, matrixY, true);\n          matrixX++;\n        }\n        matrix.set(matrixX, matrixY, placement.getBit(x, y));\n        matrixX++;\n        // Fill the right edge with alternate 0 / 1\n        if ((x % symbolInfo.matrixWidth) == symbolInfo.matrixWidth - 1) {\n          matrix.set(matrixX, matrixY, (y % 2) == 0);\n          matrixX++;\n        }\n      }\n      matrixY++;\n      // Fill the bottom edge with full 1\n      if ((y % symbolInfo.matrixHeight) == symbolInfo.matrixHeight - 1) {\n        matrixX = 0;\n        for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {\n          matrix.set(matrixX, matrixY, true);\n          matrixX++;\n        }\n        matrixY++;\n      }\n    }\n\n    return convertByteMatrixToBitMatrix(matrix, width, height);\n  }\n\n  /**\n   * Convert the ByteMatrix to BitMatrix.\n   *\n   * @param reqHeight The requested height of the image (in pixels) with the Datamatrix code\n   * @param reqWidth The requested width of the image (in pixels) with the Datamatrix code\n   * @param matrix The input matrix.\n   * @return The output matrix.\n   */\n  private static BitMatrix convertByteMatrixToBitMatrix(ByteMatrix matrix, int reqWidth, int reqHeight) {\n    int matrixWidth = matrix.getWidth();\n    int matrixHeight = matrix.getHeight();\n    int outputWidth = Math.max(reqWidth, matrixWidth);\n    int outputHeight = Math.max(reqHeight, matrixHeight);\n\n    int multiple = Math.min(outputWidth / matrixWidth, outputHeight / matrixHeight);\n\n    int leftPadding = (outputWidth - (matrixWidth * multiple)) / 2 ;\n    int topPadding = (outputHeight - (matrixHeight * multiple)) / 2 ;\n\n    BitMatrix output;\n\n    // remove padding if requested width and height are too small\n    if (reqHeight < matrixHeight || reqWidth < matrixWidth) {\n      leftPadding = 0;\n      topPadding = 0;\n      output = new BitMatrix(matrixWidth, matrixHeight);\n    } else {\n      output = new BitMatrix(reqWidth, reqHeight);\n    }\n\n    output.clear();\n    for (int inputY = 0, outputY = topPadding; inputY < matrixHeight; inputY++, outputY += multiple) {\n      // Write the contents of this row of the bytematrix\n      for (int inputX = 0, outputX = leftPadding; inputX < matrixWidth; inputX++, outputX += multiple) {\n        if (matrix.get(inputX, inputY) == 1) {\n          output.setRegion(outputX, outputY, multiple, multiple);\n        }\n      }\n    }\n\n    return output;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/decoder/BitMatrixParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.decoder;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * @author bbrown@google.com (Brian Brown)\n */\nfinal class BitMatrixParser {\n\n  private final BitMatrix mappingBitMatrix;\n  private final BitMatrix readMappingMatrix;\n  private final Version version;\n\n  /**\n   * @param bitMatrix {@link BitMatrix} to parse\n   * @throws FormatException if dimension is < 8 or > 144 or not 0 mod 2\n   */\n  BitMatrixParser(BitMatrix bitMatrix) throws FormatException {\n    int dimension = bitMatrix.getHeight();\n    if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0) {\n      throw FormatException.getFormatInstance();\n    }\n\n    version = readVersion(bitMatrix);\n    this.mappingBitMatrix = extractDataRegion(bitMatrix);\n    this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getWidth(), this.mappingBitMatrix.getHeight());\n  }\n\n  Version getVersion() {\n    return version;\n  }\n\n  /**\n   * <p>Creates the version object based on the dimension of the original bit matrix from \n   * the datamatrix code.</p>\n   *\n   * <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>\n   *\n   * @param bitMatrix Original {@link BitMatrix} including alignment patterns\n   * @return {@link Version} encapsulating the Data Matrix Code's \"version\"\n   * @throws FormatException if the dimensions of the mapping matrix are not valid\n   * Data Matrix dimensions.\n   */\n  private static Version readVersion(BitMatrix bitMatrix) throws FormatException {\n    int numRows = bitMatrix.getHeight();\n    int numColumns = bitMatrix.getWidth();\n    return Version.getVersionForDimensions(numRows, numColumns);\n  }\n\n  /**\n   * <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns)\n   * in the correct order in order to reconstitute the codewords bytes contained within the\n   * Data Matrix Code.</p>\n   *\n   * @return bytes encoded within the Data Matrix Code\n   * @throws FormatException if the exact number of bytes expected is not read\n   */\n  byte[] readCodewords() throws FormatException {\n\n    byte[] result = new byte[version.getTotalCodewords()];\n    int resultOffset = 0;\n\n    int row = 4;\n    int column = 0;\n\n    int numRows = mappingBitMatrix.getHeight();\n    int numColumns = mappingBitMatrix.getWidth();\n\n    boolean corner1Read = false;\n    boolean corner2Read = false;\n    boolean corner3Read = false;\n    boolean corner4Read = false;\n\n    // Read all of the codewords\n    do {\n      // Check the four corner cases\n      if ((row == numRows) && (column == 0) && !corner1Read) {\n        result[resultOffset++] = (byte) readCorner1(numRows, numColumns);\n        row -= 2;\n        column += 2;\n        corner1Read = true;\n      } else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {\n        result[resultOffset++] = (byte) readCorner2(numRows, numColumns);\n        row -= 2;\n        column += 2;\n        corner2Read = true;\n      } else if ((row == numRows + 4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {\n        result[resultOffset++] = (byte) readCorner3(numRows, numColumns);\n        row -= 2;\n        column += 2;\n        corner3Read = true;\n      } else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {\n        result[resultOffset++] = (byte) readCorner4(numRows, numColumns);\n        row -= 2;\n        column += 2;\n        corner4Read = true;\n      } else {\n        // Sweep upward diagonally to the right\n        do {\n          if ((row < numRows) && (column >= 0) && !readMappingMatrix.get(column, row)) {\n            result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);\n          }\n          row -= 2;\n          column += 2;\n        } while ((row >= 0) && (column < numColumns));\n        row += 1;\n        column += 3;\n\n        // Sweep downward diagonally to the left\n        do {\n          if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get(column, row)) {\n             result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);\n          }\n          row += 2;\n          column -= 2;\n        } while ((row < numRows) && (column >= 0));\n        row += 3;\n        column += 1;\n      }\n    } while ((row < numRows) || (column < numColumns));\n\n    if (resultOffset != version.getTotalCodewords()) {\n      throw FormatException.getFormatInstance();\n    }\n    return result;\n  }\n\n  /**\n   * <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>\n   *\n   * @param row Row to read in the mapping matrix\n   * @param column Column to read in the mapping matrix\n   * @param numRows Number of rows in the mapping matrix\n   * @param numColumns Number of columns in the mapping matrix\n   * @return value of the given bit in the mapping matrix\n   */\n  private boolean readModule(int row, int column, int numRows, int numColumns) {\n    // Adjust the row and column indices based on boundary wrapping\n    if (row < 0) {\n      row += numRows;\n      column += 4 - ((numRows + 4) & 0x07);\n    }\n    if (column < 0) {\n      column += numColumns;\n      row += 4 - ((numColumns + 4) & 0x07);\n    }\n    readMappingMatrix.set(column, row);\n    return mappingBitMatrix.get(column, row);\n  }\n\n  /**\n   * <p>Reads the 8 bits of the standard Utah-shaped pattern.</p>\n   *\n   * <p>See ISO 16022:2006, 5.8.1 Figure 6</p>\n   *\n   * @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern\n   * @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern\n   * @param numRows Number of rows in the mapping matrix\n   * @param numColumns Number of columns in the mapping matrix\n   * @return byte from the utah shape\n   */\n  private int readUtah(int row, int column, int numRows, int numColumns) {\n    int currentByte = 0;\n    if (readModule(row - 2, column - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(row - 2, column - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(row - 1, column - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(row - 1, column - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(row - 1, column, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(row, column - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(row, column - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(row, column, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    return currentByte;\n  }\n\n  /**\n   * <p>Reads the 8 bits of the special corner condition 1.</p>\n   *\n   * <p>See ISO 16022:2006, Figure F.3</p>\n   *\n   * @param numRows Number of rows in the mapping matrix\n   * @param numColumns Number of columns in the mapping matrix\n   * @return byte from the Corner condition 1\n   */\n  private int readCorner1(int numRows, int numColumns) {\n    int currentByte = 0;\n    if (readModule(numRows - 1, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(numRows - 1, 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(numRows - 1, 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(1, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(2, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(3, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    return currentByte;\n  }\n\n  /**\n   * <p>Reads the 8 bits of the special corner condition 2.</p>\n   *\n   * <p>See ISO 16022:2006, Figure F.4</p>\n   *\n   * @param numRows Number of rows in the mapping matrix\n   * @param numColumns Number of columns in the mapping matrix\n   * @return byte from the Corner condition 2\n   */\n  private int readCorner2(int numRows, int numColumns) {\n    int currentByte = 0;\n    if (readModule(numRows - 3, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(numRows - 2, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(numRows - 1, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 4, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 3, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(1, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    return currentByte;\n  }\n\n  /**\n   * <p>Reads the 8 bits of the special corner condition 3.</p>\n   *\n   * <p>See ISO 16022:2006, Figure F.5</p>\n   *\n   * @param numRows Number of rows in the mapping matrix\n   * @param numColumns Number of columns in the mapping matrix\n   * @return byte from the Corner condition 3\n   */\n  private int readCorner3(int numRows, int numColumns) {\n    int currentByte = 0;\n    if (readModule(numRows - 1, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 3, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(1, numColumns - 3, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(1, numColumns - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(1, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    return currentByte;\n  }\n\n  /**\n   * <p>Reads the 8 bits of the special corner condition 4.</p>\n   *\n   * <p>See ISO 16022:2006, Figure F.6</p>\n   *\n   * @param numRows Number of rows in the mapping matrix\n   * @param numColumns Number of columns in the mapping matrix\n   * @return byte from the Corner condition 4\n   */\n  private int readCorner4(int numRows, int numColumns) {\n    int currentByte = 0;\n    if (readModule(numRows - 3, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(numRows - 2, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(numRows - 1, 0, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 2, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(0, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(1, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(2, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    currentByte <<= 1;\n    if (readModule(3, numColumns - 1, numRows, numColumns)) {\n      currentByte |= 1;\n    }\n    return currentByte;\n  }\n\n  /**\n   * <p>Extracts the data region from a {@link BitMatrix} that contains\n   * alignment patterns.</p>\n   *\n   * @param bitMatrix Original {@link BitMatrix} with alignment patterns\n   * @return BitMatrix that has the alignment patterns removed\n   */\n  private BitMatrix extractDataRegion(BitMatrix bitMatrix) {\n    int symbolSizeRows = version.getSymbolSizeRows();\n    int symbolSizeColumns = version.getSymbolSizeColumns();\n\n    if (bitMatrix.getHeight() != symbolSizeRows) {\n      throw new IllegalArgumentException(\"Dimension of bitMatrix must match the version size\");\n    }\n\n    int dataRegionSizeRows = version.getDataRegionSizeRows();\n    int dataRegionSizeColumns = version.getDataRegionSizeColumns();\n\n    int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;\n    int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;\n\n    int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;\n    int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;\n\n    BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow);\n    for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {\n      int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;\n      for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {\n        int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;\n        for (int i = 0; i < dataRegionSizeRows; ++i) {\n          int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;\n          int writeRowOffset = dataRegionRowOffset + i;\n          for (int j = 0; j < dataRegionSizeColumns; ++j) {\n            int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;\n            if (bitMatrix.get(readColumnOffset, readRowOffset)) {\n              int writeColumnOffset = dataRegionColumnOffset + j;\n              bitMatrixWithoutAlignment.set(writeColumnOffset, writeRowOffset);\n            }\n          }\n        }\n      }\n    }\n    return bitMatrixWithoutAlignment;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/decoder/DataBlock.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.decoder;\n\n/**\n * <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into\n * multiple blocks, each of which is a unit of data and error-correction codewords. Each\n * is represented by an instance of this class.</p>\n *\n * @author bbrown@google.com (Brian Brown)\n */\nfinal class DataBlock {\n\n  private final int numDataCodewords;\n  private final byte[] codewords;\n\n  private DataBlock(int numDataCodewords, byte[] codewords) {\n    this.numDataCodewords = numDataCodewords;\n    this.codewords = codewords;\n  }\n\n  /**\n   * <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them.\n   * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This\n   * method will separate the data into original blocks.</p>\n   *\n   * @param rawCodewords bytes as read directly from the Data Matrix Code\n   * @param version version of the Data Matrix Code\n   * @return DataBlocks containing original bytes, \"de-interleaved\" from representation in the\n   *         Data Matrix Code\n   */\n  static DataBlock[] getDataBlocks(byte[] rawCodewords,\n                                   Version version) {\n    // Figure out the number and size of data blocks used by this version\n    Version.ECBlocks ecBlocks = version.getECBlocks();\n\n    // First count the total number of data blocks\n    int totalBlocks = 0;\n    Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();\n    for (Version.ECB ecBlock : ecBlockArray) {\n       totalBlocks += ecBlock.getCount();\n    }\n\n    // Now establish DataBlocks of the appropriate size and number of data codewords\n    DataBlock[] result = new DataBlock[totalBlocks];\n    int numResultBlocks = 0;\n    for (Version.ECB ecBlock : ecBlockArray) {\n      for (int i = 0; i < ecBlock.getCount(); i++) {\n        int numDataCodewords = ecBlock.getDataCodewords();\n        int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;\n        result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);\n      }\n    }\n\n    // All blocks have the same amount of data, except that the last n\n    // (where n may be 0) have 1 less byte. Figure out where these start.\n    // TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144\n    int longerBlocksTotalCodewords = result[0].codewords.length;\n    //int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1;\n\n    int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.getECCodewords();\n    int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1;\n    // The last elements of result may be 1 element shorter for 144 matrix\n    // first fill out as many elements as all of them have minus 1\n    int rawCodewordsOffset = 0;\n    for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {\n      for (int j = 0; j < numResultBlocks; j++) {\n        result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];\n      }\n    }\n\n    // Fill out the last data block in the longer ones\n    boolean specialVersion = version.getVersionNumber() == 24;\n    int numLongerBlocks = specialVersion ? 8 : numResultBlocks;\n    for (int j = 0; j < numLongerBlocks; j++) {\n      result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++];\n    }\n\n    // Now add in error correction blocks\n    int max = result[0].codewords.length;\n    for (int i = longerBlocksNumDataCodewords; i < max; i++) {\n      for (int j = 0; j < numResultBlocks; j++) {\n        int jOffset = specialVersion ? (j + 8) % numResultBlocks : j;\n        int iOffset = specialVersion && jOffset > 7 ? i - 1 : i;\n        result[jOffset].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];\n      }\n    }\n\n    if (rawCodewordsOffset != rawCodewords.length) {\n      throw new IllegalArgumentException();\n    }\n\n    return result;\n  }\n\n  int getNumDataCodewords() {\n    return numDataCodewords;\n  }\n\n  byte[] getCodewords() {\n    return codewords;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/decoder/DecodedBitStreamParser.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.decoder;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitSource;\nimport com.google.zxing.common.DecoderResult;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes\n * in one Data Matrix Code. This class decodes the bits back into text.</p>\n *\n * <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p>\n *\n * @author bbrown@google.com (Brian Brown)\n * @author Sean Owen\n */\nfinal class DecodedBitStreamParser {\n\n  private enum Mode {\n    PAD_ENCODE, // Not really a mode\n    ASCII_ENCODE,\n    C40_ENCODE,\n    TEXT_ENCODE,\n    ANSIX12_ENCODE,\n    EDIFACT_ENCODE,\n    BASE256_ENCODE\n  }\n\n  /**\n   * See ISO 16022:2006, Annex C Table C.1\n   * The C40 Basic Character Set (*'s used for placeholders for the shift values)\n   */\n  private static final char[] C40_BASIC_SET_CHARS = {\n    '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'\n  };\n\n  private static final char[] C40_SHIFT2_SET_CHARS = {\n    '!', '\"', '#', '$', '%', '&', '\\'', '(', ')', '*',  '+', ',', '-', '.',\n    '/', ':', ';', '<', '=', '>', '?',  '@', '[', '\\\\', ']', '^', '_'\n  };\n\n  /**\n   * See ISO 16022:2006, Annex C Table C.2\n   * The Text Basic Character Set (*'s used for placeholders for the shift values)\n   */\n  private static final char[] TEXT_BASIC_SET_CHARS = {\n    '*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',\n    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'\n  };\n\n  // Shift 2 for Text is the same encoding as C40\n  private static final char[] TEXT_SHIFT2_SET_CHARS = C40_SHIFT2_SET_CHARS;\n\n  private static final char[] TEXT_SHIFT3_SET_CHARS = {\n    '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',\n    'O',  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', (char) 127\n  };\n\n  private DecodedBitStreamParser() {\n  }\n\n  static DecoderResult decode(byte[] bytes) throws FormatException {\n    BitSource bits = new BitSource(bytes);\n    StringBuilder result = new StringBuilder(100);\n    StringBuilder resultTrailer = new StringBuilder(0);\n    List<byte[]> byteSegments = new ArrayList<>(1);\n    Mode mode = Mode.ASCII_ENCODE;\n    do {\n      if (mode == Mode.ASCII_ENCODE) {\n        mode = decodeAsciiSegment(bits, result, resultTrailer);\n      } else {\n        switch (mode) {\n          case C40_ENCODE:\n            decodeC40Segment(bits, result);\n            break;\n          case TEXT_ENCODE:\n            decodeTextSegment(bits, result);\n            break;\n          case ANSIX12_ENCODE:\n            decodeAnsiX12Segment(bits, result);\n            break;\n          case EDIFACT_ENCODE:\n            decodeEdifactSegment(bits, result);\n            break;\n          case BASE256_ENCODE:\n            decodeBase256Segment(bits, result, byteSegments);\n            break;\n          default:\n            throw FormatException.getFormatInstance();\n        }\n        mode = Mode.ASCII_ENCODE;\n      }\n    } while (mode != Mode.PAD_ENCODE && bits.available() > 0);\n    if (resultTrailer.length() > 0) {\n      result.append(resultTrailer);\n    }\n    return new DecoderResult(bytes, result.toString(), byteSegments.isEmpty() ? null : byteSegments, null);\n  }\n\n  /**\n   * See ISO 16022:2006, 5.2.3 and Annex C, Table C.2\n   */\n  private static Mode decodeAsciiSegment(BitSource bits,\n                                         StringBuilder result,\n                                         StringBuilder resultTrailer) throws FormatException {\n    boolean upperShift = false;\n    do {\n      int oneByte = bits.readBits(8);\n      if (oneByte == 0) {\n        throw FormatException.getFormatInstance();\n      } else if (oneByte <= 128) {  // ASCII data (ASCII value + 1)\n        if (upperShift) {\n          oneByte += 128;\n          //upperShift = false;\n        }\n        result.append((char) (oneByte - 1));\n        return Mode.ASCII_ENCODE;\n      } else if (oneByte == 129) {  // Pad\n        return Mode.PAD_ENCODE;\n      } else if (oneByte <= 229) {  // 2-digit data 00-99 (Numeric Value + 130)\n        int value = oneByte - 130;\n        if (value < 10) { // pad with '0' for single digit values\n          result.append('0');\n        }\n        result.append(value);\n      } else {\n        switch (oneByte) {\n          case 230: // Latch to C40 encodation\n            return Mode.C40_ENCODE;\n          case 231: // Latch to Base 256 encodation\n            return Mode.BASE256_ENCODE;\n          case 232: // FNC1\n            result.append((char) 29); // translate as ASCII 29\n            break;\n          case 233: // Structured Append\n          case 234: // Reader Programming\n            // Ignore these symbols for now\n            //throw ReaderException.getInstance();\n            break;\n          case 235: // Upper Shift (shift to Extended ASCII)\n            upperShift = true;\n            break;\n          case 236: // 05 Macro\n            result.append(\"[)>\\u001E05\\u001D\");\n            resultTrailer.insert(0, \"\\u001E\\u0004\");\n            break;\n          case 237: // 06 Macro\n            result.append(\"[)>\\u001E06\\u001D\");\n            resultTrailer.insert(0, \"\\u001E\\u0004\");\n            break;\n          case 238: // Latch to ANSI X12 encodation\n            return Mode.ANSIX12_ENCODE;\n          case 239: // Latch to Text encodation\n            return Mode.TEXT_ENCODE;\n          case 240: // Latch to EDIFACT encodation\n            return Mode.EDIFACT_ENCODE;\n          case 241: // ECI Character\n            // TODO(bbrown): I think we need to support ECI\n            //throw ReaderException.getInstance();\n            // Ignore this symbol for now\n            break;\n          default:\n            // Not to be used in ASCII encodation\n            // but work around encoders that end with 254, latch back to ASCII\n            if (oneByte != 254 || bits.available() != 0) {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n        }\n      }\n    } while (bits.available() > 0);\n    return Mode.ASCII_ENCODE;\n  }\n\n  /**\n   * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1\n   */\n  private static void decodeC40Segment(BitSource bits, StringBuilder result) throws FormatException {\n    // Three C40 values are encoded in a 16-bit value as\n    // (1600 * C1) + (40 * C2) + C3 + 1\n    // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time\n    boolean upperShift = false;\n\n    int[] cValues = new int[3];\n    int shift = 0;\n\n    do {\n      // If there is only one byte left then it will be encoded as ASCII\n      if (bits.available() == 8) {\n        return;\n      }\n      int firstByte = bits.readBits(8);\n      if (firstByte == 254) {  // Unlatch codeword\n        return;\n      }\n\n      parseTwoBytes(firstByte, bits.readBits(8), cValues);\n\n      for (int i = 0; i < 3; i++) {\n        int cValue = cValues[i];\n        switch (shift) {\n          case 0:\n            if (cValue < 3) {\n              shift = cValue + 1;\n            } else if (cValue < C40_BASIC_SET_CHARS.length) {\n              char c40char = C40_BASIC_SET_CHARS[cValue];\n              if (upperShift) {\n                result.append((char) (c40char + 128));\n                upperShift = false;\n              } else {\n                result.append(c40char);\n              }\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case 1:\n            if (upperShift) {\n              result.append((char) (cValue + 128));\n              upperShift = false;\n            } else {\n              result.append((char) cValue);\n            }\n            shift = 0;\n            break;\n          case 2:\n            if (cValue < C40_SHIFT2_SET_CHARS.length) {\n              char c40char = C40_SHIFT2_SET_CHARS[cValue];\n              if (upperShift) {\n                result.append((char) (c40char + 128));\n                upperShift = false;\n              } else {\n                result.append(c40char);\n              }\n            } else {\n              switch (cValue) {\n                case 27: // FNC1\n                  result.append((char) 29); // translate as ASCII 29\n                  break;\n                case 30: // Upper Shift\n                  upperShift = true;\n                  break;\n                default:\n                  throw FormatException.getFormatInstance();\n              }\n            }\n            shift = 0;\n            break;\n          case 3:\n            if (upperShift) {\n              result.append((char) (cValue + 224));\n              upperShift = false;\n            } else {\n              result.append((char) (cValue + 96));\n            }\n            shift = 0;\n            break;\n          default:\n            throw FormatException.getFormatInstance();\n        }\n      }\n    } while (bits.available() > 0);\n  }\n\n  /**\n   * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2\n   */\n  private static void decodeTextSegment(BitSource bits, StringBuilder result) throws FormatException {\n    // Three Text values are encoded in a 16-bit value as\n    // (1600 * C1) + (40 * C2) + C3 + 1\n    // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time\n    boolean upperShift = false;\n\n    int[] cValues = new int[3];\n    int shift = 0;\n    do {\n      // If there is only one byte left then it will be encoded as ASCII\n      if (bits.available() == 8) {\n        return;\n      }\n      int firstByte = bits.readBits(8);\n      if (firstByte == 254) {  // Unlatch codeword\n        return;\n      }\n\n      parseTwoBytes(firstByte, bits.readBits(8), cValues);\n\n      for (int i = 0; i < 3; i++) {\n        int cValue = cValues[i];\n        switch (shift) {\n          case 0:\n            if (cValue < 3) {\n              shift = cValue + 1;\n            } else if (cValue < TEXT_BASIC_SET_CHARS.length) {\n              char textChar = TEXT_BASIC_SET_CHARS[cValue];\n              if (upperShift) {\n                result.append((char) (textChar + 128));\n                upperShift = false;\n              } else {\n                result.append(textChar);\n              }\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case 1:\n            if (upperShift) {\n              result.append((char) (cValue + 128));\n              upperShift = false;\n            } else {\n              result.append((char) cValue);\n            }\n            shift = 0;\n            break;\n          case 2:\n            // Shift 2 for Text is the same encoding as C40\n            if (cValue < TEXT_SHIFT2_SET_CHARS.length) {\n              char textChar = TEXT_SHIFT2_SET_CHARS[cValue];\n              if (upperShift) {\n                result.append((char) (textChar + 128));\n                upperShift = false;\n              } else {\n                result.append(textChar);\n              }\n            } else {\n              switch (cValue) {\n                case 27: // FNC1\n                  result.append((char) 29); // translate as ASCII 29\n                  break;\n                case 30: // Upper Shift\n                  upperShift = true;\n                  break;\n                default:\n                  throw FormatException.getFormatInstance();\n              }\n            }\n            shift = 0;\n            break;\n          case 3:\n            if (cValue < TEXT_SHIFT3_SET_CHARS.length) {\n              char textChar = TEXT_SHIFT3_SET_CHARS[cValue];\n              if (upperShift) {\n                result.append((char) (textChar + 128));\n                upperShift = false;\n              } else {\n                result.append(textChar);\n              }\n              shift = 0;\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          default:\n            throw FormatException.getFormatInstance();\n        }\n      }\n    } while (bits.available() > 0);\n  }\n\n  /**\n   * See ISO 16022:2006, 5.2.7\n   */\n  private static void decodeAnsiX12Segment(BitSource bits,\n                                           StringBuilder result) throws FormatException {\n    // Three ANSI X12 values are encoded in a 16-bit value as\n    // (1600 * C1) + (40 * C2) + C3 + 1\n\n    int[] cValues = new int[3];\n    do {\n      // If there is only one byte left then it will be encoded as ASCII\n      if (bits.available() == 8) {\n        return;\n      }\n      int firstByte = bits.readBits(8);\n      if (firstByte == 254) {  // Unlatch codeword\n        return;\n      }\n\n      parseTwoBytes(firstByte, bits.readBits(8), cValues);\n\n      for (int i = 0; i < 3; i++) {\n        int cValue = cValues[i];\n        switch (cValue) {\n          case 0: // X12 segment terminator <CR>\n            result.append('\\r');\n            break;\n          case 1: // X12 segment separator *\n            result.append('*');\n            break;\n          case 2: // X12 sub-element separator >\n            result.append('>');\n            break;\n          case 3: // space\n            result.append(' ');\n            break;\n          default:\n            if (cValue < 14) {  // 0 - 9\n              result.append((char) (cValue + 44));\n            } else if (cValue < 40) {  // A - Z\n              result.append((char) (cValue + 51));\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n        }\n      }\n    } while (bits.available() > 0);\n  }\n\n  private static void parseTwoBytes(int firstByte, int secondByte, int[] result) {\n    int fullBitValue = (firstByte << 8) + secondByte - 1;\n    int temp = fullBitValue / 1600;\n    result[0] = temp;\n    fullBitValue -= temp * 1600;\n    temp = fullBitValue / 40;\n    result[1] = temp;\n    result[2] = fullBitValue - temp * 40;\n  }\n\n  /**\n   * See ISO 16022:2006, 5.2.8 and Annex C Table C.3\n   */\n  private static void decodeEdifactSegment(BitSource bits, StringBuilder result) {\n    do {\n      // If there is only two or less bytes left then it will be encoded as ASCII\n      if (bits.available() <= 16) {\n        return;\n      }\n\n      for (int i = 0; i < 4; i++) {\n        int edifactValue = bits.readBits(6);\n\n        // Check for the unlatch character\n        if (edifactValue == 0x1F) {  // 011111\n          // Read rest of byte, which should be 0, and stop\n          int bitsLeft = 8 - bits.getBitOffset();\n          if (bitsLeft != 8) {\n            bits.readBits(bitsLeft);\n          }\n          return;\n        }\n\n        if ((edifactValue & 0x20) == 0) {  // no 1 in the leading (6th) bit\n          edifactValue |= 0x40;  // Add a leading 01 to the 6 bit binary value\n        }\n        result.append((char) edifactValue);\n      }\n    } while (bits.available() > 0);\n  }\n\n  /**\n   * See ISO 16022:2006, 5.2.9 and Annex B, B.2\n   */\n  private static void decodeBase256Segment(BitSource bits,\n                                           StringBuilder result,\n                                           Collection<byte[]> byteSegments)\n      throws FormatException {\n    // Figure out how long the Base 256 Segment is.\n    int codewordPosition = 1 + bits.getByteOffset(); // position is 1-indexed\n    int d1 = unrandomize255State(bits.readBits(8), codewordPosition++);\n    int count;\n    if (d1 == 0) {  // Read the remainder of the symbol\n      count = bits.available() / 8;\n    } else if (d1 < 250) {\n      count = d1;\n    } else {\n      count = 250 * (d1 - 249) + unrandomize255State(bits.readBits(8), codewordPosition++);\n    }\n\n    // We're seeing NegativeArraySizeException errors from users.\n    if (count < 0) {\n      throw FormatException.getFormatInstance();\n    }\n\n    byte[] bytes = new byte[count];\n    for (int i = 0; i < count; i++) {\n      // Have seen this particular error in the wild, such as at\n      // http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2\n      if (bits.available() < 8) {\n        throw FormatException.getFormatInstance();\n      }\n      bytes[i] = (byte) unrandomize255State(bits.readBits(8), codewordPosition++);\n    }\n    byteSegments.add(bytes);\n    try {\n      result.append(new String(bytes, \"ISO8859_1\"));\n    } catch (UnsupportedEncodingException uee) {\n      throw new IllegalStateException(\"Platform does not support required encoding: \" + uee);\n    }\n  }\n\n  /**\n   * See ISO 16022:2006, Annex B, B.2\n   */\n  private static int unrandomize255State(int randomizedBase256Codeword,\n                                          int base256CodewordPosition) {\n    int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1;\n    int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;\n    return tempVariable >= 0 ? tempVariable : tempVariable + 256;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/decoder/Decoder.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.decoder;\n\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.reedsolomon.GenericGF;\nimport com.google.zxing.common.reedsolomon.ReedSolomonDecoder;\nimport com.google.zxing.common.reedsolomon.ReedSolomonException;\n\n/**\n * <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting\n * the Data Matrix Code from an image.</p>\n *\n * @author bbrown@google.com (Brian Brown)\n */\npublic final class Decoder {\n\n  private final ReedSolomonDecoder rsDecoder;\n\n  public Decoder() {\n    rsDecoder = new ReedSolomonDecoder(GenericGF.DATA_MATRIX_FIELD_256);\n  }\n\n  /**\n   * <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans.\n   * \"true\" is taken to mean a black module.</p>\n   *\n   * @param image booleans representing white/black Data Matrix Code modules\n   * @return text and bytes encoded within the Data Matrix Code\n   * @throws FormatException if the Data Matrix Code cannot be decoded\n   * @throws ChecksumException if error correction fails\n   */\n  public DecoderResult decode(boolean[][] image) throws FormatException, ChecksumException {\n    return decode(BitMatrix.parse(image));\n  }\n\n  /**\n   * <p>Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or \"true\" is taken\n   * to mean a black module.</p>\n   *\n   * @param bits booleans representing white/black Data Matrix Code modules\n   * @return text and bytes encoded within the Data Matrix Code\n   * @throws FormatException if the Data Matrix Code cannot be decoded\n   * @throws ChecksumException if error correction fails\n   */\n  public DecoderResult decode(BitMatrix bits) throws FormatException, ChecksumException {\n\n    // Construct a parser and read version, error-correction level\n    BitMatrixParser parser = new BitMatrixParser(bits);\n    Version version = parser.getVersion();\n\n    // Read codewords\n    byte[] codewords = parser.readCodewords();\n    // Separate into data blocks\n    DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version);\n\n    // Count total number of data bytes\n    int totalBytes = 0;\n    for (DataBlock db : dataBlocks) {\n      totalBytes += db.getNumDataCodewords();\n    }\n    byte[] resultBytes = new byte[totalBytes];\n\n    int dataBlocksCount = dataBlocks.length;\n    // Error-correct and copy data blocks together into a stream of bytes\n    for (int j = 0; j < dataBlocksCount; j++) {\n      DataBlock dataBlock = dataBlocks[j];\n      byte[] codewordBytes = dataBlock.getCodewords();\n      int numDataCodewords = dataBlock.getNumDataCodewords();\n      correctErrors(codewordBytes, numDataCodewords);\n      for (int i = 0; i < numDataCodewords; i++) {\n        // De-interlace data blocks.\n        resultBytes[i * dataBlocksCount + j] = codewordBytes[i];\n      }\n    }\n\n    // Decode the contents of that stream of bytes\n    return DecodedBitStreamParser.decode(resultBytes);\n  }\n\n  /**\n   * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to\n   * correct the errors in-place using Reed-Solomon error correction.</p>\n   *\n   * @param codewordBytes data and error correction codewords\n   * @param numDataCodewords number of codewords that are data bytes\n   * @throws ChecksumException if error correction fails\n   */\n  private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {\n    int numCodewords = codewordBytes.length;\n    // First read into an array of ints\n    int[] codewordsInts = new int[numCodewords];\n    for (int i = 0; i < numCodewords; i++) {\n      codewordsInts[i] = codewordBytes[i] & 0xFF;\n    }\n    try {\n      rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords);\n    } catch (ReedSolomonException ignored) {\n      throw ChecksumException.getChecksumInstance();\n    }\n    // Copy back into array of bytes -- only need to worry about the bytes that were data\n    // We don't care about errors in the error-correction codewords\n    for (int i = 0; i < numDataCodewords; i++) {\n      codewordBytes[i] = (byte) codewordsInts[i];\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/decoder/Version.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.decoder;\n\nimport com.google.zxing.FormatException;\n\n/**\n * The Version object encapsulates attributes about a particular\n * size Data Matrix Code.\n *\n * @author bbrown@google.com (Brian Brown)\n */\npublic final class Version {\n\n  private static final Version[] VERSIONS = buildVersions();\n\n  private final int versionNumber;\n  private final int symbolSizeRows;\n  private final int symbolSizeColumns;\n  private final int dataRegionSizeRows;\n  private final int dataRegionSizeColumns;\n  private final ECBlocks ecBlocks;\n  private final int totalCodewords;\n\n  private Version(int versionNumber,\n                  int symbolSizeRows,\n                  int symbolSizeColumns,\n                  int dataRegionSizeRows,\n                  int dataRegionSizeColumns,\n                  ECBlocks ecBlocks) {\n    this.versionNumber = versionNumber;\n    this.symbolSizeRows = symbolSizeRows;\n    this.symbolSizeColumns = symbolSizeColumns;\n    this.dataRegionSizeRows = dataRegionSizeRows;\n    this.dataRegionSizeColumns = dataRegionSizeColumns;\n    this.ecBlocks = ecBlocks;\n\n    // Calculate the total number of codewords\n    int total = 0;\n    int ecCodewords = ecBlocks.getECCodewords();\n    ECB[] ecbArray = ecBlocks.getECBlocks();\n    for (ECB ecBlock : ecbArray) {\n      total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);\n    }\n    this.totalCodewords = total;\n  }\n\n  public int getVersionNumber() {\n    return versionNumber;\n  }\n\n  public int getSymbolSizeRows() {\n    return symbolSizeRows;\n  }\n\n  public int getSymbolSizeColumns() {\n    return symbolSizeColumns;\n  }\n\n  public int getDataRegionSizeRows() {\n    return dataRegionSizeRows;\n  }\n\n  public int getDataRegionSizeColumns() {\n    return dataRegionSizeColumns;\n  }\n\n  public int getTotalCodewords() {\n    return totalCodewords;\n  }\n\n  ECBlocks getECBlocks() {\n    return ecBlocks;\n  }\n\n  /**\n   * <p>Deduces version information from Data Matrix dimensions.</p>\n   *\n   * @param numRows Number of rows in modules\n   * @param numColumns Number of columns in modules\n   * @return Version for a Data Matrix Code of those dimensions\n   * @throws FormatException if dimensions do correspond to a valid Data Matrix size\n   */\n  public static Version getVersionForDimensions(int numRows, int numColumns) throws FormatException {\n    if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {\n      throw FormatException.getFormatInstance();\n    }\n\n    for (Version version : VERSIONS) {\n      if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) {\n        return version;\n      }\n    }\n\n    throw FormatException.getFormatInstance();\n  }\n\n  /**\n   * <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will\n   * use blocks of differing sizes within one version, so, this encapsulates the parameters for\n   * each set of blocks. It also holds the number of error-correction codewords per block since it\n   * will be the same across all blocks within one version.</p>\n   */\n  static final class ECBlocks {\n    private final int ecCodewords;\n    private final ECB[] ecBlocks;\n\n    private ECBlocks(int ecCodewords, ECB ecBlocks) {\n      this.ecCodewords = ecCodewords;\n      this.ecBlocks = new ECB[] { ecBlocks };\n    }\n\n    private ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) {\n      this.ecCodewords = ecCodewords;\n      this.ecBlocks = new ECB[] { ecBlocks1, ecBlocks2 };\n    }\n\n    int getECCodewords() {\n      return ecCodewords;\n    }\n\n    ECB[] getECBlocks() {\n      return ecBlocks;\n    }\n  }\n\n  /**\n   * <p>Encapsulates the parameters for one error-correction block in one symbol version.\n   * This includes the number of data codewords, and the number of times a block with these\n   * parameters is used consecutively in the Data Matrix code version's format.</p>\n   */\n  static final class ECB {\n    private final int count;\n    private final int dataCodewords;\n\n    private ECB(int count, int dataCodewords) {\n      this.count = count;\n      this.dataCodewords = dataCodewords;\n    }\n\n    int getCount() {\n      return count;\n    }\n\n    int getDataCodewords() {\n      return dataCodewords;\n    }\n  }\n\n  @Override\n  public String toString() {\n    return String.valueOf(versionNumber);\n  }\n\n  /**\n   * See ISO 16022:2006 5.5.1 Table 7\n   */\n  private static Version[] buildVersions() {\n    return new Version[]{\n        new Version(1, 10, 10, 8, 8,\n            new ECBlocks(5, new ECB(1, 3))),\n        new Version(2, 12, 12, 10, 10,\n            new ECBlocks(7, new ECB(1, 5))),\n        new Version(3, 14, 14, 12, 12,\n            new ECBlocks(10, new ECB(1, 8))),\n        new Version(4, 16, 16, 14, 14,\n            new ECBlocks(12, new ECB(1, 12))),\n        new Version(5, 18, 18, 16, 16,\n            new ECBlocks(14, new ECB(1, 18))),\n        new Version(6, 20, 20, 18, 18,\n            new ECBlocks(18, new ECB(1, 22))),\n        new Version(7, 22, 22, 20, 20,\n            new ECBlocks(20, new ECB(1, 30))),\n        new Version(8, 24, 24, 22, 22,\n            new ECBlocks(24, new ECB(1, 36))),\n        new Version(9, 26, 26, 24, 24,\n            new ECBlocks(28, new ECB(1, 44))),\n        new Version(10, 32, 32, 14, 14,\n            new ECBlocks(36, new ECB(1, 62))),\n        new Version(11, 36, 36, 16, 16,\n            new ECBlocks(42, new ECB(1, 86))),\n        new Version(12, 40, 40, 18, 18,\n            new ECBlocks(48, new ECB(1, 114))),\n        new Version(13, 44, 44, 20, 20,\n            new ECBlocks(56, new ECB(1, 144))),\n        new Version(14, 48, 48, 22, 22,\n            new ECBlocks(68, new ECB(1, 174))),\n        new Version(15, 52, 52, 24, 24,\n            new ECBlocks(42, new ECB(2, 102))),\n        new Version(16, 64, 64, 14, 14,\n            new ECBlocks(56, new ECB(2, 140))),\n        new Version(17, 72, 72, 16, 16,\n            new ECBlocks(36, new ECB(4, 92))),\n        new Version(18, 80, 80, 18, 18,\n            new ECBlocks(48, new ECB(4, 114))),\n        new Version(19, 88, 88, 20, 20,\n            new ECBlocks(56, new ECB(4, 144))),\n        new Version(20, 96, 96, 22, 22,\n            new ECBlocks(68, new ECB(4, 174))),\n        new Version(21, 104, 104, 24, 24,\n            new ECBlocks(56, new ECB(6, 136))),\n        new Version(22, 120, 120, 18, 18,\n            new ECBlocks(68, new ECB(6, 175))),\n        new Version(23, 132, 132, 20, 20,\n            new ECBlocks(62, new ECB(8, 163))),\n        new Version(24, 144, 144, 22, 22,\n            new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))),\n        new Version(25, 8, 18, 6, 16,\n            new ECBlocks(7, new ECB(1, 5))),\n        new Version(26, 8, 32, 6, 14,\n            new ECBlocks(11, new ECB(1, 10))),\n        new Version(27, 12, 26, 10, 24,\n            new ECBlocks(14, new ECB(1, 16))),\n        new Version(28, 12, 36, 10, 16,\n            new ECBlocks(18, new ECB(1, 22))),\n        new Version(29, 16, 36, 14, 16,\n            new ECBlocks(24, new ECB(1, 32))),\n        new Version(30, 16, 48, 14, 22,\n            new ECBlocks(28, new ECB(1, 49)))\n    };\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/detector/Detector.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.detector;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DetectorResult;\nimport com.google.zxing.common.GridSampler;\nimport com.google.zxing.common.detector.WhiteRectangleDetector;\n\n/**\n * <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code\n * is rotated or skewed, or partially obscured.</p>\n *\n * @author Sean Owen\n */\npublic final class Detector {\n\n  private final BitMatrix image;\n  private final WhiteRectangleDetector rectangleDetector;\n\n  public Detector(BitMatrix image) throws NotFoundException {\n    this.image = image;\n    rectangleDetector = new WhiteRectangleDetector(image);\n  }\n\n  /**\n   * <p>Detects a Data Matrix Code in an image.</p>\n   *\n   * @return {@link DetectorResult} encapsulating results of detecting a Data Matrix Code\n   * @throws NotFoundException if no Data Matrix Code can be found\n   */\n  public DetectorResult detect() throws NotFoundException {\n\n    ResultPoint[] cornerPoints = rectangleDetector.detect();\n\n    ResultPoint[] points = detectSolid1(cornerPoints);\n    points = detectSolid2(points);\n    points[3] = correctTopRight(points);\n    if (points[3] == null) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    points = shiftToModuleCenter(points);\n\n    ResultPoint topLeft = points[0];\n    ResultPoint bottomLeft = points[1];\n    ResultPoint bottomRight = points[2];\n    ResultPoint topRight = points[3];\n\n    int dimensionTop = transitionsBetween(topLeft, topRight) + 1;\n    int dimensionRight = transitionsBetween(bottomRight, topRight) + 1;\n    if ((dimensionTop & 0x01) == 1) {\n      dimensionTop += 1;\n    }\n    if ((dimensionRight & 0x01) == 1) {\n      dimensionRight += 1;\n    }\n\n    if (4 * dimensionTop < 7 * dimensionRight && 4 * dimensionRight < 7 * dimensionTop) {\n      // The matrix is square\n      dimensionTop = dimensionRight = Math.max(dimensionTop, dimensionRight);\n    }\n\n    BitMatrix bits = sampleGrid(image, \n                                topLeft,\n                                bottomLeft,\n                                bottomRight,\n                                topRight,\n                                dimensionTop,\n                                dimensionRight);\n\n    return new DetectorResult(bits, new ResultPoint[]{topLeft, bottomLeft, bottomRight, topRight});\n  }\n\n  private ResultPoint shiftPoint(ResultPoint point, ResultPoint to, int div) {\n    float x = (to.getX() - point.getX()) / (div + 1);\n    float y = (to.getY() - point.getY()) / (div + 1);\n    return new ResultPoint(point.getX() + x, point.getY() + y);\n  }\n\n  private ResultPoint moveAway(ResultPoint point, float fromX, float fromY) {\n    float x = point.getX();\n    float y = point.getY();\n\n    if (x < fromX) {\n      x -= 1;\n    } else {\n      x += 1;\n    }\n\n    if (y < fromY) {\n      y -= 1;\n    } else {\n      y += 1;\n    }\n\n    return new ResultPoint(x, y);\n  }\n\n  /**\n   * Detect a solid side which has minimum transition.\n   */\n  private ResultPoint[] detectSolid1(ResultPoint[] cornerPoints) {\n    // 0  2\n    // 1  3\n    ResultPoint pointA = cornerPoints[0];\n    ResultPoint pointB = cornerPoints[1];\n    ResultPoint pointC = cornerPoints[3];\n    ResultPoint pointD = cornerPoints[2];\n\n    int trAB = transitionsBetween(pointA, pointB);\n    int trBC = transitionsBetween(pointB, pointC);\n    int trCD = transitionsBetween(pointC, pointD);\n    int trDA = transitionsBetween(pointD, pointA);\n\n    // 0..3\n    // :  :\n    // 1--2\n    int min = trAB;\n    ResultPoint[] points = {pointD, pointA, pointB, pointC};\n    if (min > trBC) {\n      min = trBC;\n      points[0] = pointA;\n      points[1] = pointB;\n      points[2] = pointC;\n      points[3] = pointD;\n    }\n    if (min > trCD) {\n      min = trCD;\n      points[0] = pointB;\n      points[1] = pointC;\n      points[2] = pointD;\n      points[3] = pointA;\n    }\n    if (min > trDA) {\n      points[0] = pointC;\n      points[1] = pointD;\n      points[2] = pointA;\n      points[3] = pointB;\n    }\n\n    return points;\n  }\n\n  /**\n   * Detect a second solid side next to first solid side.\n   */\n  private ResultPoint[] detectSolid2(ResultPoint[] points) {\n    // A..D\n    // :  :\n    // B--C\n    ResultPoint pointA = points[0];\n    ResultPoint pointB = points[1];\n    ResultPoint pointC = points[2];\n    ResultPoint pointD = points[3];\n\n    // Transition detection on the edge is not stable.\n    // To safely detect, shift the points to the module center.\n    int tr = transitionsBetween(pointA, pointD);\n    ResultPoint pointBs = shiftPoint(pointB, pointC, (tr + 1) * 4);\n    ResultPoint pointCs = shiftPoint(pointC, pointB, (tr + 1) * 4);\n    int trBA = transitionsBetween(pointBs, pointA);\n    int trCD = transitionsBetween(pointCs, pointD);\n\n    // 0..3\n    // |  :\n    // 1--2\n    if (trBA < trCD) {\n      // solid sides: A-B-C\n      points[0] = pointA;\n      points[1] = pointB;\n      points[2] = pointC;\n      points[3] = pointD;\n    } else {\n      // solid sides: B-C-D\n      points[0] = pointB;\n      points[1] = pointC;\n      points[2] = pointD;\n      points[3] = pointA;\n    }\n\n    return points;\n  }\n\n  /**\n   * Calculates the corner position of the white top right module.\n   */\n  private ResultPoint correctTopRight(ResultPoint[] points) {\n    // A..D\n    // |  :\n    // B--C\n    ResultPoint pointA = points[0];\n    ResultPoint pointB = points[1];\n    ResultPoint pointC = points[2];\n    ResultPoint pointD = points[3];\n\n    // shift points for safe transition detection.\n    int trTop = transitionsBetween(pointA, pointD);\n    int trRight = transitionsBetween(pointB, pointD);\n    ResultPoint pointAs = shiftPoint(pointA, pointB, (trRight + 1) * 4);\n    ResultPoint pointCs = shiftPoint(pointC, pointB, (trTop + 1) * 4);\n\n    trTop = transitionsBetween(pointAs, pointD);\n    trRight = transitionsBetween(pointCs, pointD);\n\n    ResultPoint candidate1 = new ResultPoint(\n      pointD.getX() + (pointC.getX() - pointB.getX()) / (trTop + 1),\n      pointD.getY() + (pointC.getY() - pointB.getY()) / (trTop + 1));\n    ResultPoint candidate2 = new ResultPoint(\n      pointD.getX() + (pointA.getX() - pointB.getX()) / (trRight + 1),\n      pointD.getY() + (pointA.getY() - pointB.getY()) / (trRight + 1));\n\n    if (!isValid(candidate1)) {\n      if (isValid(candidate2)) {\n        return candidate2;\n      }\n      return null;\n    }\n    if (!isValid(candidate2)) {\n      return candidate1;\n    }\n\n    int sumc1 = transitionsBetween(pointAs, candidate1) + transitionsBetween(pointCs, candidate1);\n    int sumc2 = transitionsBetween(pointAs, candidate2) + transitionsBetween(pointCs, candidate2);\n\n    if (sumc1 > sumc2) {\n      return candidate1;\n    } else {\n      return candidate2;\n    }\n  }\n\n  /**\n   * Shift the edge points to the module center.\n   */\n  private ResultPoint[] shiftToModuleCenter(ResultPoint[] points) {\n    // A..D\n    // |  :\n    // B--C\n    ResultPoint pointA = points[0];\n    ResultPoint pointB = points[1];\n    ResultPoint pointC = points[2];\n    ResultPoint pointD = points[3];\n\n    // calculate pseudo dimensions\n    int dimH = transitionsBetween(pointA, pointD) + 1;\n    int dimV = transitionsBetween(pointC, pointD) + 1;\n\n    // shift points for safe dimension detection\n    ResultPoint pointAs = shiftPoint(pointA, pointB, dimV * 4);\n    ResultPoint pointCs = shiftPoint(pointC, pointB, dimH * 4);\n\n    //  calculate more precise dimensions\n    dimH = transitionsBetween(pointAs, pointD) + 1;\n    dimV = transitionsBetween(pointCs, pointD) + 1;\n    if ((dimH & 0x01) == 1) {\n      dimH += 1;\n    }\n    if ((dimV & 0x01) == 1) {\n      dimV += 1;\n    }\n\n    // WhiteRectangleDetector returns points inside of the rectangle.\n    // I want points on the edges.\n    float centerX = (pointA.getX() + pointB.getX() + pointC.getX() + pointD.getX()) / 4;\n    float centerY = (pointA.getY() + pointB.getY() + pointC.getY() + pointD.getY()) / 4;\n    pointA = moveAway(pointA, centerX, centerY);\n    pointB = moveAway(pointB, centerX, centerY);\n    pointC = moveAway(pointC, centerX, centerY);\n    pointD = moveAway(pointD, centerX, centerY);\n\n    ResultPoint pointBs;\n    ResultPoint pointDs;\n\n    // shift points to the center of each modules\n    pointAs = shiftPoint(pointA, pointB, dimV * 4);\n    pointAs = shiftPoint(pointAs, pointD, dimH * 4);\n    pointBs = shiftPoint(pointB, pointA, dimV * 4);\n    pointBs = shiftPoint(pointBs, pointC, dimH * 4);\n    pointCs = shiftPoint(pointC, pointD, dimV * 4);\n    pointCs = shiftPoint(pointCs, pointB, dimH * 4);\n    pointDs = shiftPoint(pointD, pointC, dimV * 4);\n    pointDs = shiftPoint(pointDs, pointA, dimH * 4);\n\n    return new ResultPoint[]{pointAs, pointBs, pointCs, pointDs};\n  }\n\n  private boolean isValid(ResultPoint p) {\n    return p.getX() >= 0 && p.getX() < image.getWidth() && p.getY() > 0 && p.getY() < image.getHeight();\n  }\n\n  private static BitMatrix sampleGrid(BitMatrix image,\n                                      ResultPoint topLeft,\n                                      ResultPoint bottomLeft,\n                                      ResultPoint bottomRight,\n                                      ResultPoint topRight,\n                                      int dimensionX,\n                                      int dimensionY) throws NotFoundException {\n\n    GridSampler sampler = GridSampler.getInstance();\n\n    return sampler.sampleGrid(image,\n                              dimensionX,\n                              dimensionY,\n                              0.5f,\n                              0.5f,\n                              dimensionX - 0.5f,\n                              0.5f,\n                              dimensionX - 0.5f,\n                              dimensionY - 0.5f,\n                              0.5f,\n                              dimensionY - 0.5f,\n                              topLeft.getX(),\n                              topLeft.getY(),\n                              topRight.getX(),\n                              topRight.getY(),\n                              bottomRight.getX(),\n                              bottomRight.getY(),\n                              bottomLeft.getX(),\n                              bottomLeft.getY());\n  }\n\n  /**\n   * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.\n   */\n  private int transitionsBetween(ResultPoint from, ResultPoint to) {\n    // See QR Code Detector, sizeOfBlackWhiteBlackRun()\n    int fromX = (int) from.getX();\n    int fromY = (int) from.getY();\n    int toX = (int) to.getX();\n    int toY = (int) to.getY();\n    boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);\n    if (steep) {\n      int temp = fromX;\n      fromX = fromY;\n      fromY = temp;\n      temp = toX;\n      toX = toY;\n      toY = temp;\n    }\n\n    int dx = Math.abs(toX - fromX);\n    int dy = Math.abs(toY - fromY);\n    int error = -dx / 2;\n    int ystep = fromY < toY ? 1 : -1;\n    int xstep = fromX < toX ? 1 : -1;\n    int transitions = 0;\n    boolean inBlack = image.get(steep ? fromY : fromX, steep ? fromX : fromY);\n    for (int x = fromX, y = fromY; x != toX; x += xstep) {\n      boolean isBlack = image.get(steep ? y : x, steep ? x : y);\n      if (isBlack != inBlack) {\n        transitions++;\n        inBlack = isBlack;\n      }\n      error += dy;\n      if (error > 0) {\n        if (y == toY) {\n          break;\n        }\n        y += ystep;\n        error -= dx;\n      }\n    }\n    return transitions;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/ASCIIEncoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nfinal class ASCIIEncoder implements Encoder {\n\n  @Override\n  public int getEncodingMode() {\n    return HighLevelEncoder.ASCII_ENCODATION;\n  }\n\n  @Override\n  public void encode(EncoderContext context) {\n    //step B\n    int n = HighLevelEncoder.determineConsecutiveDigitCount(context.getMessage(), context.pos);\n    if (n >= 2) {\n      context.writeCodeword(encodeASCIIDigits(context.getMessage().charAt(context.pos),\n                                              context.getMessage().charAt(context.pos + 1)));\n      context.pos += 2;\n    } else {\n      char c = context.getCurrentChar();\n      int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());\n      if (newMode != getEncodingMode()) {\n        switch (newMode) {\n          case HighLevelEncoder.BASE256_ENCODATION:\n            context.writeCodeword(HighLevelEncoder.LATCH_TO_BASE256);\n            context.signalEncoderChange(HighLevelEncoder.BASE256_ENCODATION);\n            return;\n          case HighLevelEncoder.C40_ENCODATION:\n            context.writeCodeword(HighLevelEncoder.LATCH_TO_C40);\n            context.signalEncoderChange(HighLevelEncoder.C40_ENCODATION);\n            return;\n          case HighLevelEncoder.X12_ENCODATION:\n            context.writeCodeword(HighLevelEncoder.LATCH_TO_ANSIX12);\n            context.signalEncoderChange(HighLevelEncoder.X12_ENCODATION);\n            break;\n          case HighLevelEncoder.TEXT_ENCODATION:\n            context.writeCodeword(HighLevelEncoder.LATCH_TO_TEXT);\n            context.signalEncoderChange(HighLevelEncoder.TEXT_ENCODATION);\n            break;\n          case HighLevelEncoder.EDIFACT_ENCODATION:\n            context.writeCodeword(HighLevelEncoder.LATCH_TO_EDIFACT);\n            context.signalEncoderChange(HighLevelEncoder.EDIFACT_ENCODATION);\n            break;\n          default:\n            throw new IllegalStateException(\"Illegal mode: \" + newMode);\n        }\n      } else if (HighLevelEncoder.isExtendedASCII(c)) {\n        context.writeCodeword(HighLevelEncoder.UPPER_SHIFT);\n        context.writeCodeword((char) (c - 128 + 1));\n        context.pos++;\n      } else {\n        context.writeCodeword((char) (c + 1));\n        context.pos++;\n      }\n\n    }\n  }\n\n  private static char encodeASCIIDigits(char digit1, char digit2) {\n    if (HighLevelEncoder.isDigit(digit1) && HighLevelEncoder.isDigit(digit2)) {\n      int num = (digit1 - 48) * 10 + (digit2 - 48);\n      return (char) (num + 130);\n    }\n    throw new IllegalArgumentException(\"not digits: \" + digit1 + digit2);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/Base256Encoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nfinal class Base256Encoder implements Encoder {\n\n  @Override\n  public int getEncodingMode() {\n    return HighLevelEncoder.BASE256_ENCODATION;\n  }\n\n  @Override\n  public void encode(EncoderContext context) {\n    StringBuilder buffer = new StringBuilder();\n    buffer.append('\\0'); //Initialize length field\n    while (context.hasMoreCharacters()) {\n      char c = context.getCurrentChar();\n      buffer.append(c);\n\n      context.pos++;\n\n      int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());\n      if (newMode != getEncodingMode()) {\n        // Return to ASCII encodation, which will actually handle latch to new mode\n        context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);\n        break;\n      }\n    }\n    int dataCount = buffer.length() - 1;\n    int lengthFieldSize = 1;\n    int currentSize = context.getCodewordCount() + dataCount + lengthFieldSize;\n    context.updateSymbolInfo(currentSize);\n    boolean mustPad = (context.getSymbolInfo().getDataCapacity() - currentSize) > 0;\n    if (context.hasMoreCharacters() || mustPad) {\n      if (dataCount <= 249) {\n        buffer.setCharAt(0, (char) dataCount);\n      } else if (dataCount <= 1555) {\n        buffer.setCharAt(0, (char) ((dataCount / 250) + 249));\n        buffer.insert(1, (char) (dataCount % 250));\n      } else {\n        throw new IllegalStateException(\n            \"Message length not in valid ranges: \" + dataCount);\n      }\n    }\n    for (int i = 0, c = buffer.length(); i < c; i++) {\n      context.writeCodeword(randomize255State(\n          buffer.charAt(i), context.getCodewordCount() + 1));\n    }\n  }\n\n  private static char randomize255State(char ch, int codewordPosition) {\n    int pseudoRandom = ((149 * codewordPosition) % 255) + 1;\n    int tempVariable = ch + pseudoRandom;\n    if (tempVariable <= 255) {\n      return (char) tempVariable;\n    } else {\n      return (char) (tempVariable - 256);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/C40Encoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nclass C40Encoder implements Encoder {\n\n  @Override\n  public int getEncodingMode() {\n    return HighLevelEncoder.C40_ENCODATION;\n  }\n\n  @Override\n  public void encode(EncoderContext context) {\n    //step C\n    StringBuilder buffer = new StringBuilder();\n    while (context.hasMoreCharacters()) {\n      char c = context.getCurrentChar();\n      context.pos++;\n\n      int lastCharSize = encodeChar(c, buffer);\n\n      int unwritten = (buffer.length() / 3) * 2;\n\n      int curCodewordCount = context.getCodewordCount() + unwritten;\n      context.updateSymbolInfo(curCodewordCount);\n      int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;\n\n      if (!context.hasMoreCharacters()) {\n        //Avoid having a single C40 value in the last triplet\n        StringBuilder removed = new StringBuilder();\n        if ((buffer.length() % 3) == 2 && (available < 2 || available > 2)) {\n          lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);\n        }\n        while ((buffer.length() % 3) == 1 && (lastCharSize > 3 || available != 1)) {\n          lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);\n        }\n        break;\n      }\n\n      int count = buffer.length();\n      if ((count % 3) == 0) {\n        int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());\n        if (newMode != getEncodingMode()) {\n          // Return to ASCII encodation, which will actually handle latch to new mode\n          context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);\n          break;\n        }\n      }\n    }\n    handleEOD(context, buffer);\n  }\n\n  private int backtrackOneCharacter(EncoderContext context,\n                                    StringBuilder buffer, StringBuilder removed, int lastCharSize) {\n    int count = buffer.length();\n    buffer.delete(count - lastCharSize, count);\n    context.pos--;\n    char c = context.getCurrentChar();\n    lastCharSize = encodeChar(c, removed);\n    context.resetSymbolInfo(); //Deal with possible reduction in symbol size\n    return lastCharSize;\n  }\n\n  static void writeNextTriplet(EncoderContext context, StringBuilder buffer) {\n    context.writeCodewords(encodeToCodewords(buffer, 0));\n    buffer.delete(0, 3);\n  }\n\n  /**\n   * Handle \"end of data\" situations\n   *\n   * @param context the encoder context\n   * @param buffer  the buffer with the remaining encoded characters\n   */\n  void handleEOD(EncoderContext context, StringBuilder buffer) {\n    int unwritten = (buffer.length() / 3) * 2;\n    int rest = buffer.length() % 3;\n\n    int curCodewordCount = context.getCodewordCount() + unwritten;\n    context.updateSymbolInfo(curCodewordCount);\n    int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;\n\n    if (rest == 2) {\n      buffer.append('\\0'); //Shift 1\n      while (buffer.length() >= 3) {\n        writeNextTriplet(context, buffer);\n      }\n      if (context.hasMoreCharacters()) {\n        context.writeCodeword(HighLevelEncoder.C40_UNLATCH);\n      }\n    } else if (available == 1 && rest == 1) {\n      while (buffer.length() >= 3) {\n        writeNextTriplet(context, buffer);\n      }\n      if (context.hasMoreCharacters()) {\n        context.writeCodeword(HighLevelEncoder.C40_UNLATCH);\n      }\n      // else no unlatch\n      context.pos--;\n    } else if (rest == 0) {\n      while (buffer.length() >= 3) {\n        writeNextTriplet(context, buffer);\n      }\n      if (available > 0 || context.hasMoreCharacters()) {\n        context.writeCodeword(HighLevelEncoder.C40_UNLATCH);\n      }\n    } else {\n      throw new IllegalStateException(\"Unexpected case. Please report!\");\n    }\n    context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);\n  }\n\n  int encodeChar(char c, StringBuilder sb) {\n    if (c == ' ') {\n      sb.append('\\3');\n      return 1;\n    }\n    if (c >= '0' && c <= '9') {\n      sb.append((char) (c - 48 + 4));\n      return 1;\n    }\n    if (c >= 'A' && c <= 'Z') {\n      sb.append((char) (c - 65 + 14));\n      return 1;\n    }\n    if (c < ' ') {\n      sb.append('\\0'); //Shift 1 Set\n      sb.append(c);\n      return 2;\n    }\n    if (c >= '!' && c <= '/') {\n      sb.append('\\1'); //Shift 2 Set\n      sb.append((char) (c - 33));\n      return 2;\n    }\n    if (c >= ':' && c <= '@') {\n      sb.append('\\1'); //Shift 2 Set\n      sb.append((char) (c - 58 + 15));\n      return 2;\n    }\n    if (c >= '[' && c <= '_') {\n      sb.append('\\1'); //Shift 2 Set\n      sb.append((char) (c - 91 + 22));\n      return 2;\n    }\n    if (c >= '`' && c <= 127) {\n      sb.append('\\2'); //Shift 3 Set\n      sb.append((char) (c - 96));\n      return 2;\n    }\n    sb.append(\"\\1\\u001e\"); //Shift 2, Upper Shift\n    int len = 2;\n    len += encodeChar((char) (c - 128), sb);\n    return len;\n  }\n\n  private static String encodeToCodewords(CharSequence sb, int startPos) {\n    char c1 = sb.charAt(startPos);\n    char c2 = sb.charAt(startPos + 1);\n    char c3 = sb.charAt(startPos + 2);\n    int v = (1600 * c1) + (40 * c2) + c3 + 1;\n    char cw1 = (char) (v / 256);\n    char cw2 = (char) (v % 256);\n    return new String(new char[] {cw1, cw2});\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/DataMatrixSymbolInfo144.java",
    "content": "/*\n * Copyright 2006 Jeremias Maerki\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nfinal class DataMatrixSymbolInfo144 extends SymbolInfo {\n\n  DataMatrixSymbolInfo144() {\n    super(false, 1558, 620, 22, 22, 36, -1, 62);\n  }\n\n  @Override\n  public int getInterleavedBlockCount() {\n    return 10;\n  }\n\n  @Override\n  public int getDataLengthForInterleavedBlock(int index) {\n    return (index <= 8) ? 156 : 155;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/DefaultPlacement.java",
    "content": "/*\n * Copyright 2006 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nimport java.util.Arrays;\n\n/**\n * Symbol Character Placement Program. Adapted from Annex M.1 in ISO/IEC 16022:2000(E).\n */\npublic class DefaultPlacement {\n\n  private final CharSequence codewords;\n  private final int numrows;\n  private final int numcols;\n  private final byte[] bits;\n\n  /**\n   * Main constructor\n   *\n   * @param codewords the codewords to place\n   * @param numcols   the number of columns\n   * @param numrows   the number of rows\n   */\n  public DefaultPlacement(CharSequence codewords, int numcols, int numrows) {\n    this.codewords = codewords;\n    this.numcols = numcols;\n    this.numrows = numrows;\n    this.bits = new byte[numcols * numrows];\n    Arrays.fill(this.bits, (byte) -1); //Initialize with \"not set\" value\n  }\n\n  final int getNumrows() {\n    return numrows;\n  }\n\n  final int getNumcols() {\n    return numcols;\n  }\n\n  final byte[] getBits() {\n    return bits;\n  }\n\n  public final boolean getBit(int col, int row) {\n    return bits[row * numcols + col] == 1;\n  }\n\n  private void setBit(int col, int row, boolean bit) {\n    bits[row * numcols + col] = (byte) (bit ? 1 : 0);\n  }\n\n  private boolean hasBit(int col, int row) {\n    return bits[row * numcols + col] >= 0;\n  }\n\n  public final void place() {\n    int pos = 0;\n    int row = 4;\n    int col = 0;\n\n    do {\n            /* repeatedly first check for one of the special corner cases, then... */\n      if ((row == numrows) && (col == 0)) {\n        corner1(pos++);\n      }\n      if ((row == numrows - 2) && (col == 0) && ((numcols % 4) != 0)) {\n        corner2(pos++);\n      }\n      if ((row == numrows - 2) && (col == 0) && (numcols % 8 == 4)) {\n        corner3(pos++);\n      }\n      if ((row == numrows + 4) && (col == 2) && ((numcols % 8) == 0)) {\n        corner4(pos++);\n      }\n            /* sweep upward diagonally, inserting successive characters... */\n      do {\n        if ((row < numrows) && (col >= 0) && !hasBit(col, row)) {\n          utah(row, col, pos++);\n        }\n        row -= 2;\n        col += 2;\n      } while (row >= 0 && (col < numcols));\n      row++;\n      col += 3;\n\n            /* and then sweep downward diagonally, inserting successive characters, ... */\n      do {\n        if ((row >= 0) && (col < numcols) && !hasBit(col, row)) {\n          utah(row, col, pos++);\n        }\n        row += 2;\n        col -= 2;\n      } while ((row < numrows) && (col >= 0));\n      row += 3;\n      col++;\n\n            /* ...until the entire array is scanned */\n    } while ((row < numrows) || (col < numcols));\n\n        /* Lastly, if the lower righthand corner is untouched, fill in fixed pattern */\n    if (!hasBit(numcols - 1, numrows - 1)) {\n      setBit(numcols - 1, numrows - 1, true);\n      setBit(numcols - 2, numrows - 2, true);\n    }\n  }\n\n  private void module(int row, int col, int pos, int bit) {\n    if (row < 0) {\n      row += numrows;\n      col += 4 - ((numrows + 4) % 8);\n    }\n    if (col < 0) {\n      col += numcols;\n      row += 4 - ((numcols + 4) % 8);\n    }\n    // Note the conversion:\n    int v = codewords.charAt(pos);\n    v &= 1 << (8 - bit);\n    setBit(col, row, v != 0);\n  }\n\n  /**\n   * Places the 8 bits of a utah-shaped symbol character in ECC200.\n   *\n   * @param row the row\n   * @param col the column\n   * @param pos character position\n   */\n  private void utah(int row, int col, int pos) {\n    module(row - 2, col - 2, pos, 1);\n    module(row - 2, col - 1, pos, 2);\n    module(row - 1, col - 2, pos, 3);\n    module(row - 1, col - 1, pos, 4);\n    module(row - 1, col, pos, 5);\n    module(row, col - 2, pos, 6);\n    module(row, col - 1, pos, 7);\n    module(row, col, pos, 8);\n  }\n\n  private void corner1(int pos) {\n    module(numrows - 1, 0, pos, 1);\n    module(numrows - 1, 1, pos, 2);\n    module(numrows - 1, 2, pos, 3);\n    module(0, numcols - 2, pos, 4);\n    module(0, numcols - 1, pos, 5);\n    module(1, numcols - 1, pos, 6);\n    module(2, numcols - 1, pos, 7);\n    module(3, numcols - 1, pos, 8);\n  }\n\n  private void corner2(int pos) {\n    module(numrows - 3, 0, pos, 1);\n    module(numrows - 2, 0, pos, 2);\n    module(numrows - 1, 0, pos, 3);\n    module(0, numcols - 4, pos, 4);\n    module(0, numcols - 3, pos, 5);\n    module(0, numcols - 2, pos, 6);\n    module(0, numcols - 1, pos, 7);\n    module(1, numcols - 1, pos, 8);\n  }\n\n  private void corner3(int pos) {\n    module(numrows - 3, 0, pos, 1);\n    module(numrows - 2, 0, pos, 2);\n    module(numrows - 1, 0, pos, 3);\n    module(0, numcols - 2, pos, 4);\n    module(0, numcols - 1, pos, 5);\n    module(1, numcols - 1, pos, 6);\n    module(2, numcols - 1, pos, 7);\n    module(3, numcols - 1, pos, 8);\n  }\n\n  private void corner4(int pos) {\n    module(numrows - 1, 0, pos, 1);\n    module(numrows - 1, numcols - 1, pos, 2);\n    module(0, numcols - 3, pos, 3);\n    module(0, numcols - 2, pos, 4);\n    module(0, numcols - 1, pos, 5);\n    module(1, numcols - 3, pos, 6);\n    module(1, numcols - 2, pos, 7);\n    module(1, numcols - 1, pos, 8);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/EdifactEncoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nfinal class EdifactEncoder implements Encoder {\n\n  @Override\n  public int getEncodingMode() {\n    return HighLevelEncoder.EDIFACT_ENCODATION;\n  }\n\n  @Override\n  public void encode(EncoderContext context) {\n    //step F\n    StringBuilder buffer = new StringBuilder();\n    while (context.hasMoreCharacters()) {\n      char c = context.getCurrentChar();\n      encodeChar(c, buffer);\n      context.pos++;\n\n      int count = buffer.length();\n      if (count >= 4) {\n        context.writeCodewords(encodeToCodewords(buffer, 0));\n        buffer.delete(0, 4);\n\n        int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());\n        if (newMode != getEncodingMode()) {\n          // Return to ASCII encodation, which will actually handle latch to new mode\n          context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);\n          break;\n        }\n      }\n    }\n    buffer.append((char) 31); //Unlatch\n    handleEOD(context, buffer);\n  }\n\n  /**\n   * Handle \"end of data\" situations\n   *\n   * @param context the encoder context\n   * @param buffer  the buffer with the remaining encoded characters\n   */\n  private static void handleEOD(EncoderContext context, CharSequence buffer) {\n    try {\n      int count = buffer.length();\n      if (count == 0) {\n        return; //Already finished\n      }\n      if (count == 1) {\n        //Only an unlatch at the end\n        context.updateSymbolInfo();\n        int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();\n        int remaining = context.getRemainingCharacters();\n        // The following two lines are a hack inspired by the 'fix' from https://sourceforge.net/p/barcode4j/svn/221/\n        if (remaining > available) {\n          context.updateSymbolInfo(context.getCodewordCount() + 1);\n          available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();\n        }\n        if (remaining <= available && available <= 2) {\n          return; //No unlatch\n        }\n      }\n\n      if (count > 4) {\n        throw new IllegalStateException(\"Count must not exceed 4\");\n      }\n      int restChars = count - 1;\n      String encoded = encodeToCodewords(buffer, 0);\n      boolean endOfSymbolReached = !context.hasMoreCharacters();\n      boolean restInAscii = endOfSymbolReached && restChars <= 2;\n\n      if (restChars <= 2) {\n        context.updateSymbolInfo(context.getCodewordCount() + restChars);\n        int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();\n        if (available >= 3) {\n          restInAscii = false;\n          context.updateSymbolInfo(context.getCodewordCount() + encoded.length());\n          //available = context.symbolInfo.dataCapacity - context.getCodewordCount();\n        }\n      }\n\n      if (restInAscii) {\n        context.resetSymbolInfo();\n        context.pos -= restChars;\n      } else {\n        context.writeCodewords(encoded);\n      }\n    } finally {\n      context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);\n    }\n  }\n\n  private static void encodeChar(char c, StringBuilder sb) {\n    if (c >= ' ' && c <= '?') {\n      sb.append(c);\n    } else if (c >= '@' && c <= '^') {\n      sb.append((char) (c - 64));\n    } else {\n      HighLevelEncoder.illegalCharacter(c);\n    }\n  }\n\n  private static String encodeToCodewords(CharSequence sb, int startPos) {\n    int len = sb.length() - startPos;\n    if (len == 0) {\n      throw new IllegalStateException(\"StringBuilder must not be empty\");\n    }\n    char c1 = sb.charAt(startPos);\n    char c2 = len >= 2 ? sb.charAt(startPos + 1) : 0;\n    char c3 = len >= 3 ? sb.charAt(startPos + 2) : 0;\n    char c4 = len >= 4 ? sb.charAt(startPos + 3) : 0;\n\n    int v = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4;\n    char cw1 = (char) ((v >> 16) & 255);\n    char cw2 = (char) ((v >> 8) & 255);\n    char cw3 = (char) (v & 255);\n    StringBuilder res = new StringBuilder(3);\n    res.append(cw1);\n    if (len >= 2) {\n      res.append(cw2);\n    }\n    if (len >= 3) {\n      res.append(cw3);\n    }\n    return res.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/Encoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\ninterface Encoder {\n\n  int getEncodingMode();\n\n  void encode(EncoderContext context);\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/EncoderContext.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nimport com.google.zxing.Dimension;\n\nimport java.nio.charset.StandardCharsets;\n\nfinal class EncoderContext {\n\n  private final String msg;\n  private SymbolShapeHint shape;\n  private Dimension minSize;\n  private Dimension maxSize;\n  private final StringBuilder codewords;\n  int pos;\n  private int newEncoding;\n  private SymbolInfo symbolInfo;\n  private int skipAtEnd;\n\n  EncoderContext(String msg) {\n    //From this point on Strings are not Unicode anymore!\n    byte[] msgBinary = msg.getBytes(StandardCharsets.ISO_8859_1);\n    StringBuilder sb = new StringBuilder(msgBinary.length);\n    for (int i = 0, c = msgBinary.length; i < c; i++) {\n      char ch = (char) (msgBinary[i] & 0xff);\n      if (ch == '?' && msg.charAt(i) != '?') {\n        throw new IllegalArgumentException(\"Message contains characters outside ISO-8859-1 encoding.\");\n      }\n      sb.append(ch);\n    }\n    this.msg = sb.toString(); //Not Unicode here!\n    shape = SymbolShapeHint.FORCE_NONE;\n    this.codewords = new StringBuilder(msg.length());\n    newEncoding = -1;\n  }\n\n  public void setSymbolShape(SymbolShapeHint shape) {\n    this.shape = shape;\n  }\n\n  public void setSizeConstraints(Dimension minSize, Dimension maxSize) {\n    this.minSize = minSize;\n    this.maxSize = maxSize;\n  }\n\n  public String getMessage() {\n    return this.msg;\n  }\n\n  public void setSkipAtEnd(int count) {\n    this.skipAtEnd = count;\n  }\n\n  public char getCurrentChar() {\n    return msg.charAt(pos);\n  }\n\n  public char getCurrent() {\n    return msg.charAt(pos);\n  }\n\n  public StringBuilder getCodewords() {\n    return codewords;\n  }\n\n  public void writeCodewords(String codewords) {\n    this.codewords.append(codewords);\n  }\n\n  public void writeCodeword(char codeword) {\n    this.codewords.append(codeword);\n  }\n\n  public int getCodewordCount() {\n    return this.codewords.length();\n  }\n\n  public int getNewEncoding() {\n    return newEncoding;\n  }\n\n  public void signalEncoderChange(int encoding) {\n    this.newEncoding = encoding;\n  }\n\n  public void resetEncoderSignal() {\n    this.newEncoding = -1;\n  }\n\n  public boolean hasMoreCharacters() {\n    return pos < getTotalMessageCharCount();\n  }\n\n  private int getTotalMessageCharCount() {\n    return msg.length() - skipAtEnd;\n  }\n\n  public int getRemainingCharacters() {\n    return getTotalMessageCharCount() - pos;\n  }\n\n  public SymbolInfo getSymbolInfo() {\n    return symbolInfo;\n  }\n\n  public void updateSymbolInfo() {\n    updateSymbolInfo(getCodewordCount());\n  }\n\n  public void updateSymbolInfo(int len) {\n    if (this.symbolInfo == null || len > this.symbolInfo.getDataCapacity()) {\n      this.symbolInfo = SymbolInfo.lookup(len, shape, minSize, maxSize, true);\n    }\n  }\n\n  public void resetSymbolInfo() {\n    this.symbolInfo = null;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/ErrorCorrection.java",
    "content": "/*\n * Copyright 2006 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\n/**\n * Error Correction Code for ECC200.\n */\npublic final class ErrorCorrection {\n\n  /**\n   * Lookup table which factors to use for which number of error correction codewords.\n   * See FACTORS.\n   */\n  private static final int[] FACTOR_SETS\n      = {5, 7, 10, 11, 12, 14, 18, 20, 24, 28, 36, 42, 48, 56, 62, 68};\n\n  /**\n   * Precomputed polynomial factors for ECC 200.\n   */\n  private static final int[][] FACTORS = {\n      {228, 48, 15, 111, 62},\n      {23, 68, 144, 134, 240, 92, 254},\n      {28, 24, 185, 166, 223, 248, 116, 255, 110, 61},\n      {175, 138, 205, 12, 194, 168, 39, 245, 60, 97, 120},\n      {41, 153, 158, 91, 61, 42, 142, 213, 97, 178, 100, 242},\n      {156, 97, 192, 252, 95, 9, 157, 119, 138, 45, 18, 186, 83, 185},\n      {83, 195, 100, 39, 188, 75, 66, 61, 241, 213, 109, 129, 94, 254, 225, 48, 90, 188},\n      {15, 195, 244, 9, 233, 71, 168, 2, 188, 160, 153, 145, 253, 79, 108, 82, 27, 174, 186, 172},\n      {52, 190, 88, 205, 109, 39, 176, 21, 155, 197, 251, 223, 155, 21, 5, 172,\n          254, 124, 12, 181, 184, 96, 50, 193},\n      {211, 231, 43, 97, 71, 96, 103, 174, 37, 151, 170, 53, 75, 34, 249, 121,\n          17, 138, 110, 213, 141, 136, 120, 151, 233, 168, 93, 255},\n      {245, 127, 242, 218, 130, 250, 162, 181, 102, 120, 84, 179, 220, 251, 80, 182,\n          229, 18, 2, 4, 68, 33, 101, 137, 95, 119, 115, 44, 175, 184, 59, 25,\n          225, 98, 81, 112},\n      {77, 193, 137, 31, 19, 38, 22, 153, 247, 105, 122, 2, 245, 133, 242, 8,\n          175, 95, 100, 9, 167, 105, 214, 111, 57, 121, 21, 1, 253, 57, 54, 101,\n          248, 202, 69, 50, 150, 177, 226, 5, 9, 5},\n      {245, 132, 172, 223, 96, 32, 117, 22, 238, 133, 238, 231, 205, 188, 237, 87,\n          191, 106, 16, 147, 118, 23, 37, 90, 170, 205, 131, 88, 120, 100, 66, 138,\n          186, 240, 82, 44, 176, 87, 187, 147, 160, 175, 69, 213, 92, 253, 225, 19},\n      {175, 9, 223, 238, 12, 17, 220, 208, 100, 29, 175, 170, 230, 192, 215, 235,\n          150, 159, 36, 223, 38, 200, 132, 54, 228, 146, 218, 234, 117, 203, 29, 232,\n          144, 238, 22, 150, 201, 117, 62, 207, 164, 13, 137, 245, 127, 67, 247, 28,\n          155, 43, 203, 107, 233, 53, 143, 46},\n      {242, 93, 169, 50, 144, 210, 39, 118, 202, 188, 201, 189, 143, 108, 196, 37,\n          185, 112, 134, 230, 245, 63, 197, 190, 250, 106, 185, 221, 175, 64, 114, 71,\n          161, 44, 147, 6, 27, 218, 51, 63, 87, 10, 40, 130, 188, 17, 163, 31,\n          176, 170, 4, 107, 232, 7, 94, 166, 224, 124, 86, 47, 11, 204},\n      {220, 228, 173, 89, 251, 149, 159, 56, 89, 33, 147, 244, 154, 36, 73, 127,\n          213, 136, 248, 180, 234, 197, 158, 177, 68, 122, 93, 213, 15, 160, 227, 236,\n          66, 139, 153, 185, 202, 167, 179, 25, 220, 232, 96, 210, 231, 136, 223, 239,\n          181, 241, 59, 52, 172, 25, 49, 232, 211, 189, 64, 54, 108, 153, 132, 63,\n          96, 103, 82, 186}};\n\n  private static final int MODULO_VALUE = 0x12D;\n\n  private static final int[] LOG;\n  private static final int[] ALOG;\n\n  static {\n    //Create log and antilog table\n    LOG = new int[256];\n    ALOG = new int[255];\n\n    int p = 1;\n    for (int i = 0; i < 255; i++) {\n      ALOG[i] = p;\n      LOG[p] = i;\n      p *= 2;\n      if (p >= 256) {\n        p ^= MODULO_VALUE;\n      }\n    }\n  }\n\n  private ErrorCorrection() {\n  }\n\n  /**\n   * Creates the ECC200 error correction for an encoded message.\n   *\n   * @param codewords  the codewords\n   * @param symbolInfo information about the symbol to be encoded\n   * @return the codewords with interleaved error correction.\n   */\n  public static String encodeECC200(String codewords, SymbolInfo symbolInfo) {\n    if (codewords.length() != symbolInfo.getDataCapacity()) {\n      throw new IllegalArgumentException(\n          \"The number of codewords does not match the selected symbol\");\n    }\n    StringBuilder sb = new StringBuilder(symbolInfo.getDataCapacity() + symbolInfo.getErrorCodewords());\n    sb.append(codewords);\n    int blockCount = symbolInfo.getInterleavedBlockCount();\n    if (blockCount == 1) {\n      String ecc = createECCBlock(codewords, symbolInfo.getErrorCodewords());\n      sb.append(ecc);\n    } else {\n      sb.setLength(sb.capacity());\n      int[] dataSizes = new int[blockCount];\n      int[] errorSizes = new int[blockCount];\n      int[] startPos = new int[blockCount];\n      for (int i = 0; i < blockCount; i++) {\n        dataSizes[i] = symbolInfo.getDataLengthForInterleavedBlock(i + 1);\n        errorSizes[i] = symbolInfo.getErrorLengthForInterleavedBlock(i + 1);\n        startPos[i] = 0;\n        if (i > 0) {\n          startPos[i] = startPos[i - 1] + dataSizes[i];\n        }\n      }\n      for (int block = 0; block < blockCount; block++) {\n        StringBuilder temp = new StringBuilder(dataSizes[block]);\n        for (int d = block; d < symbolInfo.getDataCapacity(); d += blockCount) {\n          temp.append(codewords.charAt(d));\n        }\n        String ecc = createECCBlock(temp.toString(), errorSizes[block]);\n        int pos = 0;\n        for (int e = block; e < errorSizes[block] * blockCount; e += blockCount) {\n          sb.setCharAt(symbolInfo.getDataCapacity() + e, ecc.charAt(pos++));\n        }\n      }\n    }\n    return sb.toString();\n\n  }\n\n  private static String createECCBlock(CharSequence codewords, int numECWords) {\n    return createECCBlock(codewords, 0, codewords.length(), numECWords);\n  }\n\n  private static String createECCBlock(CharSequence codewords, int start, int len, int numECWords) {\n    int table = -1;\n    for (int i = 0; i < FACTOR_SETS.length; i++) {\n      if (FACTOR_SETS[i] == numECWords) {\n        table = i;\n        break;\n      }\n    }\n    if (table < 0) {\n      throw new IllegalArgumentException(\n          \"Illegal number of error correction codewords specified: \" + numECWords);\n    }\n    int[] poly = FACTORS[table];\n    char[] ecc = new char[numECWords];\n    for (int i = 0; i < numECWords; i++) {\n      ecc[i] = 0;\n    }\n    for (int i = start; i < start + len; i++) {\n      int m = ecc[numECWords - 1] ^ codewords.charAt(i);\n      for (int k = numECWords - 1; k > 0; k--) {\n        if (m != 0 && poly[k] != 0) {\n          ecc[k] = (char) (ecc[k - 1] ^ ALOG[(LOG[m] + LOG[poly[k]]) % 255]);\n        } else {\n          ecc[k] = ecc[k - 1];\n        }\n      }\n      if (m != 0 && poly[0] != 0) {\n        ecc[0] = (char) ALOG[(LOG[m] + LOG[poly[0]]) % 255];\n      } else {\n        ecc[0] = 0;\n      }\n    }\n    char[] eccReversed = new char[numECWords];\n    for (int i = 0; i < numECWords; i++) {\n      eccReversed[i] = ecc[numECWords - i - 1];\n    }\n    return String.valueOf(eccReversed);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/HighLevelEncoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nimport com.google.zxing.Dimension;\n\nimport java.util.Arrays;\n\n/**\n * DataMatrix ECC 200 data encoder following the algorithm described in ISO/IEC 16022:200(E) in\n * annex S.\n */\npublic final class HighLevelEncoder {\n\n  /**\n   * Padding character\n   */\n  private static final char PAD = 129;\n  /**\n   * mode latch to C40 encodation mode\n   */\n  static final char LATCH_TO_C40 = 230;\n  /**\n   * mode latch to Base 256 encodation mode\n   */\n  static final char LATCH_TO_BASE256 = 231;\n  /**\n   * FNC1 Codeword\n   */\n  //private static final char FNC1 = 232;\n  /**\n   * Structured Append Codeword\n   */\n  //private static final char STRUCTURED_APPEND = 233;\n  /**\n   * Reader Programming\n   */\n  //private static final char READER_PROGRAMMING = 234;\n  /**\n   * Upper Shift\n   */\n  static final char UPPER_SHIFT = 235;\n  /**\n   * 05 Macro\n   */\n  private static final char MACRO_05 = 236;\n  /**\n   * 06 Macro\n   */\n  private static final char MACRO_06 = 237;\n  /**\n   * mode latch to ANSI X.12 encodation mode\n   */\n  static final char LATCH_TO_ANSIX12 = 238;\n  /**\n   * mode latch to Text encodation mode\n   */\n  static final char LATCH_TO_TEXT = 239;\n  /**\n   * mode latch to EDIFACT encodation mode\n   */\n  static final char LATCH_TO_EDIFACT = 240;\n  /**\n   * ECI character (Extended Channel Interpretation)\n   */\n  //private static final char ECI = 241;\n\n  /**\n   * Unlatch from C40 encodation\n   */\n  static final char C40_UNLATCH = 254;\n  /**\n   * Unlatch from X12 encodation\n   */\n  static final char X12_UNLATCH = 254;\n\n  /**\n   * 05 Macro header\n   */\n  private static final String MACRO_05_HEADER = \"[)>\\u001E05\\u001D\";\n  /**\n   * 06 Macro header\n   */\n  private static final String MACRO_06_HEADER = \"[)>\\u001E06\\u001D\";\n  /**\n   * Macro trailer\n   */\n  private static final String MACRO_TRAILER = \"\\u001E\\u0004\";\n\n  static final int ASCII_ENCODATION = 0;\n  static final int C40_ENCODATION = 1;\n  static final int TEXT_ENCODATION = 2;\n  static final int X12_ENCODATION = 3;\n  static final int EDIFACT_ENCODATION = 4;\n  static final int BASE256_ENCODATION = 5;\n\n  private HighLevelEncoder() {\n  }\n\n  /*\n   * Converts the message to a byte array using the default encoding (cp437) as defined by the\n   * specification\n   *\n   * @param msg the message\n   * @return the byte array of the message\n   */\n\n  /*\n  public static byte[] getBytesForMessage(String msg) {\n    return msg.getBytes(Charset.forName(\"cp437\")); //See 4.4.3 and annex B of ISO/IEC 15438:2001(E)\n  }\n   */\n\n  private static char randomize253State(char ch, int codewordPosition) {\n    int pseudoRandom = ((149 * codewordPosition) % 253) + 1;\n    int tempVariable = ch + pseudoRandom;\n    return (char) (tempVariable <= 254 ? tempVariable : tempVariable - 254);\n  }\n\n  /**\n   * Performs message encoding of a DataMatrix message using the algorithm described in annex P\n   * of ISO/IEC 16022:2000(E).\n   *\n   * @param msg the message\n   * @return the encoded message (the char values range from 0 to 255)\n   */\n  public static String encodeHighLevel(String msg) {\n    return encodeHighLevel(msg, SymbolShapeHint.FORCE_NONE, null, null);\n  }\n\n  /**\n   * Performs message encoding of a DataMatrix message using the algorithm described in annex P\n   * of ISO/IEC 16022:2000(E).\n   *\n   * @param msg     the message\n   * @param shape   requested shape. May be {@code SymbolShapeHint.FORCE_NONE},\n   *                {@code SymbolShapeHint.FORCE_SQUARE} or {@code SymbolShapeHint.FORCE_RECTANGLE}.\n   * @param minSize the minimum symbol size constraint or null for no constraint\n   * @param maxSize the maximum symbol size constraint or null for no constraint\n   * @return the encoded message (the char values range from 0 to 255)\n   */\n  public static String encodeHighLevel(String msg,\n                                       SymbolShapeHint shape,\n                                       Dimension minSize,\n                                       Dimension maxSize) {\n    //the codewords 0..255 are encoded as Unicode characters\n    Encoder[] encoders = {\n        new ASCIIEncoder(), new C40Encoder(), new TextEncoder(),\n        new X12Encoder(), new EdifactEncoder(),  new Base256Encoder()\n    };\n\n    EncoderContext context = new EncoderContext(msg);\n    context.setSymbolShape(shape);\n    context.setSizeConstraints(minSize, maxSize);\n\n    if (msg.startsWith(MACRO_05_HEADER) && msg.endsWith(MACRO_TRAILER)) {\n      context.writeCodeword(MACRO_05);\n      context.setSkipAtEnd(2);\n      context.pos += MACRO_05_HEADER.length();\n    } else if (msg.startsWith(MACRO_06_HEADER) && msg.endsWith(MACRO_TRAILER)) {\n      context.writeCodeword(MACRO_06);\n      context.setSkipAtEnd(2);\n      context.pos += MACRO_06_HEADER.length();\n    }\n\n    int encodingMode = ASCII_ENCODATION; //Default mode\n    while (context.hasMoreCharacters()) {\n      encoders[encodingMode].encode(context);\n      if (context.getNewEncoding() >= 0) {\n        encodingMode = context.getNewEncoding();\n        context.resetEncoderSignal();\n      }\n    }\n    int len = context.getCodewordCount();\n    context.updateSymbolInfo();\n    int capacity = context.getSymbolInfo().getDataCapacity();\n    if (len < capacity &&\n        encodingMode != ASCII_ENCODATION &&\n        encodingMode != BASE256_ENCODATION &&\n        encodingMode != EDIFACT_ENCODATION) {\n      context.writeCodeword('\\u00fe'); //Unlatch (254)\n    }\n    //Padding\n    StringBuilder codewords = context.getCodewords();\n    if (codewords.length() < capacity) {\n      codewords.append(PAD);\n    }\n    while (codewords.length() < capacity) {\n      codewords.append(randomize253State(PAD, codewords.length() + 1));\n    }\n\n    return context.getCodewords().toString();\n  }\n\n  static int lookAheadTest(CharSequence msg, int startpos, int currentMode) {\n    if (startpos >= msg.length()) {\n      return currentMode;\n    }\n    float[] charCounts;\n    //step J\n    if (currentMode == ASCII_ENCODATION) {\n      charCounts = new float[]{0, 1, 1, 1, 1, 1.25f};\n    } else {\n      charCounts = new float[]{1, 2, 2, 2, 2, 2.25f};\n      charCounts[currentMode] = 0;\n    }\n\n    int charsProcessed = 0;\n    while (true) {\n      //step K\n      if ((startpos + charsProcessed) == msg.length()) {\n        int min = Integer.MAX_VALUE;\n        byte[] mins = new byte[6];\n        int[] intCharCounts = new int[6];\n        min = findMinimums(charCounts, intCharCounts, min, mins);\n        int minCount = getMinimumCount(mins);\n\n        if (intCharCounts[ASCII_ENCODATION] == min) {\n          return ASCII_ENCODATION;\n        }\n        if (minCount == 1 && mins[BASE256_ENCODATION] > 0) {\n          return BASE256_ENCODATION;\n        }\n        if (minCount == 1 && mins[EDIFACT_ENCODATION] > 0) {\n          return EDIFACT_ENCODATION;\n        }\n        if (minCount == 1 && mins[TEXT_ENCODATION] > 0) {\n          return TEXT_ENCODATION;\n        }\n        if (minCount == 1 && mins[X12_ENCODATION] > 0) {\n          return X12_ENCODATION;\n        }\n        return C40_ENCODATION;\n      }\n\n      char c = msg.charAt(startpos + charsProcessed);\n      charsProcessed++;\n\n      //step L\n      if (isDigit(c)) {\n        charCounts[ASCII_ENCODATION] += 0.5f;\n      } else if (isExtendedASCII(c)) {\n        charCounts[ASCII_ENCODATION] = (float) Math.ceil(charCounts[ASCII_ENCODATION]);\n        charCounts[ASCII_ENCODATION] += 2.0f;\n      } else {\n        charCounts[ASCII_ENCODATION] = (float) Math.ceil(charCounts[ASCII_ENCODATION]);\n        charCounts[ASCII_ENCODATION]++;\n      }\n\n      //step M\n      if (isNativeC40(c)) {\n        charCounts[C40_ENCODATION] += 2.0f / 3.0f;\n      } else if (isExtendedASCII(c)) {\n        charCounts[C40_ENCODATION] += 8.0f / 3.0f;\n      } else {\n        charCounts[C40_ENCODATION] += 4.0f / 3.0f;\n      }\n\n      //step N\n      if (isNativeText(c)) {\n        charCounts[TEXT_ENCODATION] += 2.0f / 3.0f;\n      } else if (isExtendedASCII(c)) {\n        charCounts[TEXT_ENCODATION] += 8.0f / 3.0f;\n      } else {\n        charCounts[TEXT_ENCODATION] += 4.0f / 3.0f;\n      }\n\n      //step O\n      if (isNativeX12(c)) {\n        charCounts[X12_ENCODATION] += 2.0f / 3.0f;\n      } else if (isExtendedASCII(c)) {\n        charCounts[X12_ENCODATION] += 13.0f / 3.0f;\n      } else {\n        charCounts[X12_ENCODATION] += 10.0f / 3.0f;\n      }\n\n      //step P\n      if (isNativeEDIFACT(c)) {\n        charCounts[EDIFACT_ENCODATION] += 3.0f / 4.0f;\n      } else if (isExtendedASCII(c)) {\n        charCounts[EDIFACT_ENCODATION] += 17.0f / 4.0f;\n      } else {\n        charCounts[EDIFACT_ENCODATION] += 13.0f / 4.0f;\n      }\n\n      // step Q\n      if (isSpecialB256(c)) {\n        charCounts[BASE256_ENCODATION] += 4.0f;\n      } else {\n        charCounts[BASE256_ENCODATION]++;\n      }\n\n      //step R\n      if (charsProcessed >= 4) {\n        int[] intCharCounts = new int[6];\n        byte[] mins = new byte[6];\n        findMinimums(charCounts, intCharCounts, Integer.MAX_VALUE, mins);\n        int minCount = getMinimumCount(mins);\n\n        if (intCharCounts[ASCII_ENCODATION] < intCharCounts[BASE256_ENCODATION]\n            && intCharCounts[ASCII_ENCODATION] < intCharCounts[C40_ENCODATION]\n            && intCharCounts[ASCII_ENCODATION] < intCharCounts[TEXT_ENCODATION]\n            && intCharCounts[ASCII_ENCODATION] < intCharCounts[X12_ENCODATION]\n            && intCharCounts[ASCII_ENCODATION] < intCharCounts[EDIFACT_ENCODATION]) {\n          return ASCII_ENCODATION;\n        }\n        if (intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION]\n            || (mins[C40_ENCODATION] + mins[TEXT_ENCODATION] + mins[X12_ENCODATION] + mins[EDIFACT_ENCODATION]) == 0) {\n          return BASE256_ENCODATION;\n        }\n        if (minCount == 1 && mins[EDIFACT_ENCODATION] > 0) {\n          return EDIFACT_ENCODATION;\n        }\n        if (minCount == 1 && mins[TEXT_ENCODATION] > 0) {\n          return TEXT_ENCODATION;\n        }\n        if (minCount == 1 && mins[X12_ENCODATION] > 0) {\n          return X12_ENCODATION;\n        }\n        if (intCharCounts[C40_ENCODATION] + 1 < intCharCounts[ASCII_ENCODATION]\n            && intCharCounts[C40_ENCODATION] + 1 < intCharCounts[BASE256_ENCODATION]\n            && intCharCounts[C40_ENCODATION] + 1 < intCharCounts[EDIFACT_ENCODATION]\n            && intCharCounts[C40_ENCODATION] + 1 < intCharCounts[TEXT_ENCODATION]) {\n          if (intCharCounts[C40_ENCODATION] < intCharCounts[X12_ENCODATION]) {\n            return C40_ENCODATION;\n          }\n          if (intCharCounts[C40_ENCODATION] == intCharCounts[X12_ENCODATION]) {\n            int p = startpos + charsProcessed + 1;\n            while (p < msg.length()) {\n              char tc = msg.charAt(p);\n              if (isX12TermSep(tc)) {\n                return X12_ENCODATION;\n              }\n              if (!isNativeX12(tc)) {\n                break;\n              }\n              p++;\n            }\n            return C40_ENCODATION;\n          }\n        }\n      }\n    }\n  }\n\n  private static int findMinimums(float[] charCounts, int[] intCharCounts, int min, byte[] mins) {\n    Arrays.fill(mins, (byte) 0);\n    for (int i = 0; i < 6; i++) {\n      intCharCounts[i] = (int) Math.ceil(charCounts[i]);\n      int current = intCharCounts[i];\n      if (min > current) {\n        min = current;\n        Arrays.fill(mins, (byte) 0);\n      }\n      if (min == current) {\n        mins[i]++;\n\n      }\n    }\n    return min;\n  }\n\n  private static int getMinimumCount(byte[] mins) {\n    int minCount = 0;\n    for (int i = 0; i < 6; i++) {\n      minCount += mins[i];\n    }\n    return minCount;\n  }\n\n  static boolean isDigit(char ch) {\n    return ch >= '0' && ch <= '9';\n  }\n\n  static boolean isExtendedASCII(char ch) {\n    return ch >= 128 && ch <= 255;\n  }\n\n  private static boolean isNativeC40(char ch) {\n    return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');\n  }\n\n  private static boolean isNativeText(char ch) {\n    return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');\n  }\n\n  private static boolean isNativeX12(char ch) {\n    return isX12TermSep(ch) || (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');\n  }\n\n  private static boolean isX12TermSep(char ch) {\n    return (ch == '\\r') //CR\n        || (ch == '*')\n        || (ch == '>');\n  }\n\n  private static boolean isNativeEDIFACT(char ch) {\n    return ch >= ' ' && ch <= '^';\n  }\n\n  private static boolean isSpecialB256(char ch) {\n    return false; //TODO NOT IMPLEMENTED YET!!!\n  }\n\n  /**\n   * Determines the number of consecutive characters that are encodable using numeric compaction.\n   *\n   * @param msg      the message\n   * @param startpos the start position within the message\n   * @return the requested character count\n   */\n  public static int determineConsecutiveDigitCount(CharSequence msg, int startpos) {\n    int count = 0;\n    int len = msg.length();\n    int idx = startpos;\n    if (idx < len) {\n      char ch = msg.charAt(idx);\n      while (isDigit(ch) && idx < len) {\n        count++;\n        idx++;\n        if (idx < len) {\n          ch = msg.charAt(idx);\n        }\n      }\n    }\n    return count;\n  }\n\n  static void illegalCharacter(char c) {\n    String hex = Integer.toHexString(c);\n    hex = \"0000\".substring(0, 4 - hex.length()) + hex;\n    throw new IllegalArgumentException(\"Illegal character: \" + c + \" (0x\" + hex + ')');\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/SymbolInfo.java",
    "content": "/*\n * Copyright 2006 Jeremias Maerki\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nimport com.google.zxing.Dimension;\n\n/**\n * Symbol info table for DataMatrix.\n *\n * @version $Id$\n */\npublic class SymbolInfo {\n\n  static final SymbolInfo[] PROD_SYMBOLS = {\n    new SymbolInfo(false, 3, 5, 8, 8, 1),\n    new SymbolInfo(false, 5, 7, 10, 10, 1),\n      /*rect*/new SymbolInfo(true, 5, 7, 16, 6, 1),\n    new SymbolInfo(false, 8, 10, 12, 12, 1),\n      /*rect*/new SymbolInfo(true, 10, 11, 14, 6, 2),\n    new SymbolInfo(false, 12, 12, 14, 14, 1),\n      /*rect*/new SymbolInfo(true, 16, 14, 24, 10, 1),\n\n    new SymbolInfo(false, 18, 14, 16, 16, 1),\n    new SymbolInfo(false, 22, 18, 18, 18, 1),\n      /*rect*/new SymbolInfo(true, 22, 18, 16, 10, 2),\n    new SymbolInfo(false, 30, 20, 20, 20, 1),\n      /*rect*/new SymbolInfo(true, 32, 24, 16, 14, 2),\n    new SymbolInfo(false, 36, 24, 22, 22, 1),\n    new SymbolInfo(false, 44, 28, 24, 24, 1),\n      /*rect*/new SymbolInfo(true, 49, 28, 22, 14, 2),\n\n    new SymbolInfo(false, 62, 36, 14, 14, 4),\n    new SymbolInfo(false, 86, 42, 16, 16, 4),\n    new SymbolInfo(false, 114, 48, 18, 18, 4),\n    new SymbolInfo(false, 144, 56, 20, 20, 4),\n    new SymbolInfo(false, 174, 68, 22, 22, 4),\n\n    new SymbolInfo(false, 204, 84, 24, 24, 4, 102, 42),\n    new SymbolInfo(false, 280, 112, 14, 14, 16, 140, 56),\n    new SymbolInfo(false, 368, 144, 16, 16, 16, 92, 36),\n    new SymbolInfo(false, 456, 192, 18, 18, 16, 114, 48),\n    new SymbolInfo(false, 576, 224, 20, 20, 16, 144, 56),\n    new SymbolInfo(false, 696, 272, 22, 22, 16, 174, 68),\n    new SymbolInfo(false, 816, 336, 24, 24, 16, 136, 56),\n    new SymbolInfo(false, 1050, 408, 18, 18, 36, 175, 68),\n    new SymbolInfo(false, 1304, 496, 20, 20, 36, 163, 62),\n    new DataMatrixSymbolInfo144(),\n  };\n\n  private static SymbolInfo[] symbols = PROD_SYMBOLS;\n\n  private final boolean rectangular;\n  private final int dataCapacity;\n  private final int errorCodewords;\n  public final int matrixWidth;\n  public final int matrixHeight;\n  private final int dataRegions;\n  private final int rsBlockData;\n  private final int rsBlockError;\n\n  /**\n   * Overrides the symbol info set used by this class. Used for testing purposes.\n   *\n   * @param override the symbol info set to use\n   */\n  public static void overrideSymbolSet(SymbolInfo[] override) {\n    symbols = override;\n  }\n\n  public SymbolInfo(boolean rectangular, int dataCapacity, int errorCodewords,\n                    int matrixWidth, int matrixHeight, int dataRegions) {\n    this(rectangular, dataCapacity, errorCodewords, matrixWidth, matrixHeight, dataRegions,\n         dataCapacity, errorCodewords);\n  }\n\n  SymbolInfo(boolean rectangular, int dataCapacity, int errorCodewords,\n             int matrixWidth, int matrixHeight, int dataRegions,\n             int rsBlockData, int rsBlockError) {\n    this.rectangular = rectangular;\n    this.dataCapacity = dataCapacity;\n    this.errorCodewords = errorCodewords;\n    this.matrixWidth = matrixWidth;\n    this.matrixHeight = matrixHeight;\n    this.dataRegions = dataRegions;\n    this.rsBlockData = rsBlockData;\n    this.rsBlockError = rsBlockError;\n  }\n\n  public static SymbolInfo lookup(int dataCodewords) {\n    return lookup(dataCodewords, SymbolShapeHint.FORCE_NONE, true);\n  }\n\n  public static SymbolInfo lookup(int dataCodewords, SymbolShapeHint shape) {\n    return lookup(dataCodewords, shape, true);\n  }\n\n  public static SymbolInfo lookup(int dataCodewords, boolean allowRectangular, boolean fail) {\n    SymbolShapeHint shape = allowRectangular\n        ? SymbolShapeHint.FORCE_NONE : SymbolShapeHint.FORCE_SQUARE;\n    return lookup(dataCodewords, shape, fail);\n  }\n\n  private static SymbolInfo lookup(int dataCodewords, SymbolShapeHint shape, boolean fail) {\n    return lookup(dataCodewords, shape, null, null, fail);\n  }\n\n  public static SymbolInfo lookup(int dataCodewords,\n                                  SymbolShapeHint shape,\n                                  Dimension minSize,\n                                  Dimension maxSize,\n                                  boolean fail) {\n    for (SymbolInfo symbol : symbols) {\n      if (shape == SymbolShapeHint.FORCE_SQUARE && symbol.rectangular) {\n        continue;\n      }\n      if (shape == SymbolShapeHint.FORCE_RECTANGLE && !symbol.rectangular) {\n        continue;\n      }\n      if (minSize != null\n          && (symbol.getSymbolWidth() < minSize.getWidth()\n          || symbol.getSymbolHeight() < minSize.getHeight())) {\n        continue;\n      }\n      if (maxSize != null\n          && (symbol.getSymbolWidth() > maxSize.getWidth()\n          || symbol.getSymbolHeight() > maxSize.getHeight())) {\n        continue;\n      }\n      if (dataCodewords <= symbol.dataCapacity) {\n        return symbol;\n      }\n    }\n    if (fail) {\n      throw new IllegalArgumentException(\n          \"Can't find a symbol arrangement that matches the message. Data codewords: \"\n              + dataCodewords);\n    }\n    return null;\n  }\n\n  private int getHorizontalDataRegions() {\n    switch (dataRegions) {\n      case 1:\n        return 1;\n      case 2:\n      case 4:\n        return 2;\n      case 16:\n        return 4;\n      case 36:\n        return 6;\n      default:\n        throw new IllegalStateException(\"Cannot handle this number of data regions\");\n    }\n  }\n\n  private int getVerticalDataRegions() {\n    switch (dataRegions) {\n      case 1:\n      case 2:\n        return 1;\n      case 4:\n        return 2;\n      case 16:\n        return 4;\n      case 36:\n        return 6;\n      default:\n        throw new IllegalStateException(\"Cannot handle this number of data regions\");\n    }\n  }\n\n  public final int getSymbolDataWidth() {\n    return getHorizontalDataRegions() * matrixWidth;\n  }\n\n  public final int getSymbolDataHeight() {\n    return getVerticalDataRegions() * matrixHeight;\n  }\n\n  public final int getSymbolWidth() {\n    return getSymbolDataWidth() + (getHorizontalDataRegions() * 2);\n  }\n\n  public final int getSymbolHeight() {\n    return getSymbolDataHeight() + (getVerticalDataRegions() * 2);\n  }\n\n  public int getCodewordCount() {\n    return dataCapacity + errorCodewords;\n  }\n\n  public int getInterleavedBlockCount() {\n    return dataCapacity / rsBlockData;\n  }\n\n  public final int getDataCapacity() {\n    return dataCapacity;\n  }\n\n  public final int getErrorCodewords() {\n    return errorCodewords;\n  }\n\n  public int getDataLengthForInterleavedBlock(int index) {\n    return rsBlockData;\n  }\n\n  public final int getErrorLengthForInterleavedBlock(int index) {\n    return rsBlockError;\n  }\n\n  @Override\n  public final String toString() {\n    return (rectangular ? \"Rectangular Symbol:\" : \"Square Symbol:\") +\n        \" data region \" + matrixWidth + 'x' + matrixHeight +\n        \", symbol size \" + getSymbolWidth() + 'x' + getSymbolHeight() +\n        \", symbol data size \" + getSymbolDataWidth() + 'x' + getSymbolDataHeight() +\n        \", codewords \" + dataCapacity + '+' + errorCodewords;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/SymbolShapeHint.java",
    "content": "/*\n * Copyright 2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\n/**\n * Enumeration for DataMatrix symbol shape hint. It can be used to force square or rectangular\n * symbols.\n */\npublic enum SymbolShapeHint {\n\n  FORCE_NONE,\n  FORCE_SQUARE,\n  FORCE_RECTANGLE,\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/TextEncoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nfinal class TextEncoder extends C40Encoder {\n\n  @Override\n  public int getEncodingMode() {\n    return HighLevelEncoder.TEXT_ENCODATION;\n  }\n\n  @Override\n  int encodeChar(char c, StringBuilder sb) {\n    if (c == ' ') {\n      sb.append('\\3');\n      return 1;\n    }\n    if (c >= '0' && c <= '9') {\n      sb.append((char) (c - 48 + 4));\n      return 1;\n    }\n    if (c >= 'a' && c <= 'z') {\n      sb.append((char) (c - 97 + 14));\n      return 1;\n    }\n    if (c < ' ') {\n      sb.append('\\0'); //Shift 1 Set\n      sb.append(c);\n      return 2;\n    }\n    if (c >= '!' && c <= '/') {\n      sb.append('\\1'); //Shift 2 Set\n      sb.append((char) (c - 33));\n      return 2;\n    }\n    if (c >= ':' && c <= '@') {\n      sb.append('\\1'); //Shift 2 Set\n      sb.append((char) (c - 58 + 15));\n      return 2;\n    }\n    if (c >= '[' && c <= '_') {\n      sb.append('\\1'); //Shift 2 Set\n      sb.append((char) (c - 91 + 22));\n      return 2;\n    }\n    if (c == '`') {\n      sb.append('\\2'); //Shift 3 Set\n      sb.append((char) (c - 96));\n      return 2;\n    }\n    if (c >= 'A' && c <= 'Z') {\n      sb.append('\\2'); //Shift 3 Set\n      sb.append((char) (c - 65 + 1));\n      return 2;\n    }\n    if (c >= '{' && c <= 127) {\n      sb.append('\\2'); //Shift 3 Set\n      sb.append((char) (c - 123 + 27));\n      return 2;\n    }\n    sb.append(\"\\1\\u001e\"); //Shift 2, Upper Shift\n    int len = 2;\n    len += encodeChar((char) (c - 128), sb);\n    return len;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/datamatrix/encoder/X12Encoder.java",
    "content": "/*\n * Copyright 2006-2007 Jeremias Maerki.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.datamatrix.encoder;\n\nfinal class X12Encoder extends C40Encoder {\n\n  @Override\n  public int getEncodingMode() {\n    return HighLevelEncoder.X12_ENCODATION;\n  }\n\n  @Override\n  public void encode(EncoderContext context) {\n    //step C\n    StringBuilder buffer = new StringBuilder();\n    while (context.hasMoreCharacters()) {\n      char c = context.getCurrentChar();\n      context.pos++;\n\n      encodeChar(c, buffer);\n\n      int count = buffer.length();\n      if ((count % 3) == 0) {\n        writeNextTriplet(context, buffer);\n\n        int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());\n        if (newMode != getEncodingMode()) {\n          // Return to ASCII encodation, which will actually handle latch to new mode\n          context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);\n          break;\n        }\n      }\n    }\n    handleEOD(context, buffer);\n  }\n\n  @Override\n  int encodeChar(char c, StringBuilder sb) {\n    switch (c) {\n      case '\\r':\n        sb.append('\\0');\n        break;\n      case '*':\n        sb.append('\\1');\n        break;\n      case '>':\n        sb.append('\\2');\n        break;\n      case ' ':\n        sb.append('\\3');\n        break;\n      default:\n        if (c >= '0' && c <= '9') {\n          sb.append((char) (c - 48 + 4));\n        } else if (c >= 'A' && c <= 'Z') {\n          sb.append((char) (c - 65 + 14));\n        } else {\n          HighLevelEncoder.illegalCharacter(c);\n        }\n        break;\n    }\n    return 1;\n  }\n\n  @Override\n  void handleEOD(EncoderContext context, StringBuilder buffer) {\n    context.updateSymbolInfo();\n    int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();\n    int count = buffer.length();\n    context.pos -= count;\n    if (context.getRemainingCharacters() > 1 || available > 1 ||\n        context.getRemainingCharacters() != available) {\n      context.writeCodeword(HighLevelEncoder.X12_UNLATCH);\n    }\n    if (context.getNewEncoding() < 0) {\n      context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);\n    }\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/maxicode/MaxiCodeReader.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.maxicode;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.maxicode.decoder.Decoder;\n\nimport java.util.Map;\n\n/**\n * This implementation can detect and decode a MaxiCode in an image.\n */\npublic final class MaxiCodeReader implements Reader {\n\n  private static final ResultPoint[] NO_POINTS = new ResultPoint[0];\n  private static final int MATRIX_WIDTH = 30;\n  private static final int MATRIX_HEIGHT = 33;\n\n  private final Decoder decoder = new Decoder();\n\n  /**\n   * Locates and decodes a MaxiCode in an image.\n   *\n   * @return a String representing the content encoded by the MaxiCode\n   * @throws NotFoundException if a MaxiCode cannot be found\n   * @throws FormatException if a MaxiCode cannot be decoded\n   * @throws ChecksumException if error correction fails\n   */\n  @Override\n  public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {\n    return decode(image, null);\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException {\n    // Note that MaxiCode reader effectively always assumes PURE_BARCODE mode\n    // and can't detect it in an image\n    BitMatrix bits = extractPureBits(image.getBlackMatrix());\n    DecoderResult decoderResult = decoder.decode(bits, hints);\n    Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), NO_POINTS, BarcodeFormat.MAXICODE);\n\n    String ecLevel = decoderResult.getECLevel();\n    if (ecLevel != null) {\n      result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);\n    }\n    return result;\n  }\n\n  @Override\n  public void reset() {\n    // do nothing\n  }\n\n  /**\n   * This method detects a code in a \"pure\" image -- that is, pure monochrome image\n   * which contains only an unrotated, unskewed, image of a code, with some white border\n   * around it. This is a specialized method that works exceptionally fast in this special\n   * case.\n   *\n   * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)\n   * @see com.google.zxing.qrcode.QRCodeReader#extractPureBits(BitMatrix)\n   */\n  private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {\n\n    int[] enclosingRectangle = image.getEnclosingRectangle();\n    if (enclosingRectangle == null) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    int left = enclosingRectangle[0];\n    int top = enclosingRectangle[1];\n    int width = enclosingRectangle[2];\n    int height = enclosingRectangle[3];\n\n    // Now just read off the bits\n    BitMatrix bits = new BitMatrix(MATRIX_WIDTH, MATRIX_HEIGHT);\n    for (int y = 0; y < MATRIX_HEIGHT; y++) {\n      int iy = top + (y * height + height / 2) / MATRIX_HEIGHT;\n      for (int x = 0; x < MATRIX_WIDTH; x++) {\n        int ix = left + (x * width + width / 2 + (y & 0x01) *  width / 2) / MATRIX_WIDTH;\n        if (image.get(ix, iy)) {\n          bits.set(x, y);\n        }\n      }\n    }\n    return bits;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/maxicode/decoder/BitMatrixParser.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.maxicode.decoder;\n\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * @author mike32767\n * @author Manuel Kasten\n */\nfinal class BitMatrixParser {\n\n  private static final int[][] BITNR = {\n    {121,120,127,126,133,132,139,138,145,144,151,150,157,156,163,162,169,168,175,174,181,180,187,186,193,192,199,198, -2, -2},\n    {123,122,129,128,135,134,141,140,147,146,153,152,159,158,165,164,171,170,177,176,183,182,189,188,195,194,201,200,816, -3},\n    {125,124,131,130,137,136,143,142,149,148,155,154,161,160,167,166,173,172,179,178,185,184,191,190,197,196,203,202,818,817},\n    {283,282,277,276,271,270,265,264,259,258,253,252,247,246,241,240,235,234,229,228,223,222,217,216,211,210,205,204,819, -3},\n    {285,284,279,278,273,272,267,266,261,260,255,254,249,248,243,242,237,236,231,230,225,224,219,218,213,212,207,206,821,820},\n    {287,286,281,280,275,274,269,268,263,262,257,256,251,250,245,244,239,238,233,232,227,226,221,220,215,214,209,208,822, -3},\n    {289,288,295,294,301,300,307,306,313,312,319,318,325,324,331,330,337,336,343,342,349,348,355,354,361,360,367,366,824,823},\n    {291,290,297,296,303,302,309,308,315,314,321,320,327,326,333,332,339,338,345,344,351,350,357,356,363,362,369,368,825, -3},\n    {293,292,299,298,305,304,311,310,317,316,323,322,329,328,335,334,341,340,347,346,353,352,359,358,365,364,371,370,827,826},\n    {409,408,403,402,397,396,391,390, 79, 78, -2, -2, 13, 12, 37, 36,  2, -1, 44, 43,109,108,385,384,379,378,373,372,828, -3},\n    {411,410,405,404,399,398,393,392, 81, 80, 40, -2, 15, 14, 39, 38,  3, -1, -1, 45,111,110,387,386,381,380,375,374,830,829},\n    {413,412,407,406,401,400,395,394, 83, 82, 41, -3, -3, -3, -3, -3,  5,  4, 47, 46,113,112,389,388,383,382,377,376,831, -3},\n    {415,414,421,420,427,426,103,102, 55, 54, 16, -3, -3, -3, -3, -3, -3, -3, 20, 19, 85, 84,433,432,439,438,445,444,833,832},\n    {417,416,423,422,429,428,105,104, 57, 56, -3, -3, -3, -3, -3, -3, -3, -3, 22, 21, 87, 86,435,434,441,440,447,446,834, -3},\n    {419,418,425,424,431,430,107,106, 59, 58, -3, -3, -3, -3, -3, -3, -3, -3, -3, 23, 89, 88,437,436,443,442,449,448,836,835},\n    {481,480,475,474,469,468, 48, -2, 30, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  0, 53, 52,463,462,457,456,451,450,837, -3},\n    {483,482,477,476,471,470, 49, -1, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -1,465,464,459,458,453,452,839,838},\n    {485,484,479,478,473,472, 51, 50, 31, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,  1, -2, 42,467,466,461,460,455,454,840, -3},\n    {487,486,493,492,499,498, 97, 96, 61, 60, -3, -3, -3, -3, -3, -3, -3, -3, -3, 26, 91, 90,505,504,511,510,517,516,842,841},\n    {489,488,495,494,501,500, 99, 98, 63, 62, -3, -3, -3, -3, -3, -3, -3, -3, 28, 27, 93, 92,507,506,513,512,519,518,843, -3},\n    {491,490,497,496,503,502,101,100, 65, 64, 17, -3, -3, -3, -3, -3, -3, -3, 18, 29, 95, 94,509,508,515,514,521,520,845,844},\n    {559,558,553,552,547,546,541,540, 73, 72, 32, -3, -3, -3, -3, -3, -3, 10, 67, 66,115,114,535,534,529,528,523,522,846, -3},\n    {561,560,555,554,549,548,543,542, 75, 74, -2, -1,  7,  6, 35, 34, 11, -2, 69, 68,117,116,537,536,531,530,525,524,848,847},\n    {563,562,557,556,551,550,545,544, 77, 76, -2, 33,  9,  8, 25, 24, -1, -2, 71, 70,119,118,539,538,533,532,527,526,849, -3},\n    {565,564,571,570,577,576,583,582,589,588,595,594,601,600,607,606,613,612,619,618,625,624,631,630,637,636,643,642,851,850},\n    {567,566,573,572,579,578,585,584,591,590,597,596,603,602,609,608,615,614,621,620,627,626,633,632,639,638,645,644,852, -3},\n    {569,568,575,574,581,580,587,586,593,592,599,598,605,604,611,610,617,616,623,622,629,628,635,634,641,640,647,646,854,853},\n    {727,726,721,720,715,714,709,708,703,702,697,696,691,690,685,684,679,678,673,672,667,666,661,660,655,654,649,648,855, -3},\n    {729,728,723,722,717,716,711,710,705,704,699,698,693,692,687,686,681,680,675,674,669,668,663,662,657,656,651,650,857,856},\n    {731,730,725,724,719,718,713,712,707,706,701,700,695,694,689,688,683,682,677,676,671,670,665,664,659,658,653,652,858, -3},\n    {733,732,739,738,745,744,751,750,757,756,763,762,769,768,775,774,781,780,787,786,793,792,799,798,805,804,811,810,860,859},\n    {735,734,741,740,747,746,753,752,759,758,765,764,771,770,777,776,783,782,789,788,795,794,801,800,807,806,813,812,861, -3},\n    {737,736,743,742,749,748,755,754,761,760,767,766,773,772,779,778,785,784,791,790,797,796,803,802,809,808,815,814,863,862}\n  };\n\n  private final BitMatrix bitMatrix;\n\n  /**\n   * @param bitMatrix {@link BitMatrix} to parse\n   */\n  BitMatrixParser(BitMatrix bitMatrix) {\n    this.bitMatrix = bitMatrix;\n  }\n\n  byte[] readCodewords() {\n    byte[] result = new byte[144];\n    int height = bitMatrix.getHeight();\n    int width = bitMatrix.getWidth();\n    for (int y = 0; y < height; y++) {\n      int[] bitnrRow = BITNR[y];\n      for (int x = 0; x < width; x++) {\n        int bit = bitnrRow[x];\n        if (bit >= 0 && bitMatrix.get(x, y)) {\n          result[bit / 6] |= (byte) (1 << (5 - (bit % 6)));\n        }\n      }\n    }\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/maxicode/decoder/DecodedBitStreamParser.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.maxicode.decoder;\n\nimport com.google.zxing.common.DecoderResult;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\n\n/**\n * <p>MaxiCodes can encode text or structured information as bits in one of several modes,\n * with multiple character sets in one code. This class decodes the bits back into text.</p>\n *\n * @author mike32767\n * @author Manuel Kasten\n */\nfinal class DecodedBitStreamParser {\n\n  private static final char SHIFTA = '\\uFFF0';\n  private static final char SHIFTB = '\\uFFF1';\n  private static final char SHIFTC = '\\uFFF2';\n  private static final char SHIFTD = '\\uFFF3';\n  private static final char SHIFTE = '\\uFFF4';\n  private static final char TWOSHIFTA = '\\uFFF5';\n  private static final char THREESHIFTA = '\\uFFF6';\n  private static final char LATCHA = '\\uFFF7';\n  private static final char LATCHB = '\\uFFF8';\n  private static final char LOCK = '\\uFFF9';\n  private static final char ECI = '\\uFFFA';\n  private static final char NS = '\\uFFFB';\n  private static final char PAD = '\\uFFFC';\n  private static final char FS = '\\u001C';\n  private static final char GS = '\\u001D';\n  private static final char RS = '\\u001E';\n\n  private static final String[] SETS = {\n    \"\\nABCDEFGHIJKLMNOPQRSTUVWXYZ\" + ECI + FS + GS + RS + NS + ' ' + PAD +\n        \"\\\"#$%&'()*+,-./0123456789:\" + SHIFTB + SHIFTC + SHIFTD + SHIFTE + LATCHB,\n    \"`abcdefghijklmnopqrstuvwxyz\" + ECI + FS + GS + RS + NS + '{' + PAD +\n        \"}~\\u007F;<=>?[\\\\]^_ ,./:@!|\" + PAD + TWOSHIFTA + THREESHIFTA + PAD +\n        SHIFTA + SHIFTC + SHIFTD + SHIFTE + LATCHA,\n    \"\\u00C0\\u00C1\\u00C2\\u00C3\\u00C4\\u00C5\\u00C6\\u00C7\\u00C8\\u00C9\\u00CA\\u00CB\\u00CC\\u00CD\\u00CE\\u00CF\\u00D0\\u00D1\\u00D2\\u00D3\\u00D4\\u00D5\\u00D6\\u00D7\\u00D8\\u00D9\\u00DA\" +\n        ECI + FS + GS + RS +\n        \"\\u00DB\\u00DC\\u00DD\\u00DE\\u00DF\\u00AA\\u00AC\\u00B1\\u00B2\\u00B3\\u00B5\\u00B9\\u00BA\\u00BC\\u00BD\\u00BE\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087\\u0088\\u0089\" +\n        LATCHA + ' ' + LOCK + SHIFTD + SHIFTE + LATCHB,\n    \"\\u00E0\\u00E1\\u00E2\\u00E3\\u00E4\\u00E5\\u00E6\\u00E7\\u00E8\\u00E9\\u00EA\\u00EB\\u00EC\\u00ED\\u00EE\\u00EF\\u00F0\\u00F1\\u00F2\\u00F3\\u00F4\\u00F5\\u00F6\\u00F7\\u00F8\\u00F9\\u00FA\" +\n        ECI + FS + GS + RS + NS +\n        \"\\u00FB\\u00FC\\u00FD\\u00FE\\u00FF\\u00A1\\u00A8\\u00AB\\u00AF\\u00B0\\u00B4\\u00B7\\u00B8\\u00BB\\u00BF\\u008A\\u008B\\u008C\\u008D\\u008E\\u008F\\u0090\\u0091\\u0092\\u0093\\u0094\" +\n        LATCHA + ' ' + SHIFTC + LOCK + SHIFTE + LATCHB,\n    \"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\u0008\\u0009\\n\\u000B\\u000C\\r\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001A\" +\n        ECI + PAD + PAD + '\\u001B' + NS + FS + GS + RS +\n        \"\\u001F\\u009F\\u00A0\\u00A2\\u00A3\\u00A4\\u00A5\\u00A6\\u00A7\\u00A9\\u00AD\\u00AE\\u00B6\\u0095\\u0096\\u0097\\u0098\\u0099\\u009A\\u009B\\u009C\\u009D\\u009E\" +\n        LATCHA + ' ' + SHIFTC + SHIFTD + LOCK + LATCHB,\n    \"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\u0008\\u0009\\n\\u000B\\u000C\\r\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\\u0020\\u0021\\\"\\u0023\\u0024\\u0025\\u0026\\u0027\\u0028\\u0029\\u002A\\u002B\\u002C\\u002D\\u002E\\u002F\\u0030\\u0031\\u0032\\u0033\\u0034\\u0035\\u0036\\u0037\\u0038\\u0039\\u003A\\u003B\\u003C\\u003D\\u003E\\u003F\"\n  };\n\n  private DecodedBitStreamParser() {\n  }\n\n  static DecoderResult decode(byte[] bytes, int mode) {\n    StringBuilder result = new StringBuilder(144);\n    switch (mode) {\n      case 2:\n      case 3:\n        String postcode;\n        if (mode == 2) {\n          int pc = getPostCode2(bytes);\n          NumberFormat df = new DecimalFormat(\"0000000000\".substring(0, getPostCode2Length(bytes)));\n          postcode = df.format(pc);\n        } else {\n          postcode = getPostCode3(bytes);\n        }\n        NumberFormat threeDigits = new DecimalFormat(\"000\");\n        String country = threeDigits.format(getCountry(bytes));\n        String service = threeDigits.format(getServiceClass(bytes));\n        result.append(getMessage(bytes, 10, 84));\n        if (result.toString().startsWith(\"[)>\" + RS + \"01\" + GS)) {\n          result.insert(9, postcode + GS + country + GS + service + GS);\n        } else {\n          result.insert(0, postcode + GS + country + GS + service + GS);\n        }\n        break;\n      case 4:\n        result.append(getMessage(bytes, 1, 93));\n        break;\n      case 5:\n        result.append(getMessage(bytes, 1, 77));\n        break;\n    }\n    return new DecoderResult(bytes, result.toString(), null, String.valueOf(mode));\n  }\n\n  private static int getBit(int bit, byte[] bytes) {\n    bit--;\n    return (bytes[bit / 6] & (1 << (5 - (bit % 6)))) == 0 ? 0 : 1;\n  }\n\n  private static int getInt(byte[] bytes, byte[] x) {\n    if (x.length == 0) {\n      throw new IllegalArgumentException();\n    }\n    int val = 0;\n    for (int i = 0; i < x.length; i++) {\n      val += getBit(x[i], bytes) << (x.length - i - 1);\n    }\n    return val;\n  }\n\n  private static int getCountry(byte[] bytes) {\n    return getInt(bytes, new byte[] {53, 54, 43, 44, 45, 46, 47, 48, 37, 38});\n  }\n\n  private static int getServiceClass(byte[] bytes) {\n    return getInt(bytes, new byte[] {55, 56, 57, 58, 59, 60, 49, 50, 51, 52});\n  }\n\n  private static int getPostCode2Length(byte[] bytes) {\n    return getInt(bytes, new byte[] {39, 40, 41, 42, 31, 32});\n  }\n\n  private static int getPostCode2(byte[] bytes) {\n    return getInt(bytes, new byte[] {33, 34, 35, 36, 25, 26, 27, 28, 29, 30, 19,\n        20, 21, 22, 23, 24, 13, 14, 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 1, 2});\n  }\n\n  private static String getPostCode3(byte[] bytes) {\n    return String.valueOf(\n       new char[] {\n         SETS[0].charAt(getInt(bytes, new byte[] {39, 40, 41, 42, 31, 32})),\n         SETS[0].charAt(getInt(bytes, new byte[] {33, 34, 35, 36, 25, 26})),\n         SETS[0].charAt(getInt(bytes, new byte[] {27, 28, 29, 30, 19, 20})),\n         SETS[0].charAt(getInt(bytes, new byte[] {21, 22, 23, 24, 13, 14})),\n         SETS[0].charAt(getInt(bytes, new byte[] {15, 16, 17, 18,  7,  8})),\n         SETS[0].charAt(getInt(bytes, new byte[] { 9, 10, 11, 12,  1,  2})),\n       }\n    );\n  }\n\n  private static String getMessage(byte[] bytes, int start, int len) {\n    StringBuilder sb = new StringBuilder();\n    int shift = -1;\n    int set = 0;\n    int lastset = 0;\n    for (int i = start; i < start + len; i++) {\n      char c = SETS[set].charAt(bytes[i]);\n      switch (c) {\n        case LATCHA:\n          set = 0;\n          shift = -1;\n          break;\n        case LATCHB:\n          set = 1;\n          shift = -1;\n          break;\n        case SHIFTA:\n        case SHIFTB:\n        case SHIFTC:\n        case SHIFTD:\n        case SHIFTE:\n          lastset = set;\n          set = c - SHIFTA;\n          shift = 1;\n          break;\n        case TWOSHIFTA:\n          lastset = set;\n          set = 0;\n          shift = 2;\n          break;\n        case THREESHIFTA:\n          lastset = set;\n          set = 0;\n          shift = 3;\n          break;\n        case NS:\n          int nsval = (bytes[++i] << 24) + (bytes[++i] << 18) + (bytes[++i] << 12) + (bytes[++i] << 6) + bytes[++i];\n          sb.append(new DecimalFormat(\"000000000\").format(nsval));\n          break;\n        case LOCK:\n          shift = -1;\n          break;\n        default:\n          sb.append(c);\n      }\n      if (shift-- == 0) {\n        set = lastset;\n      }\n    }\n    while (sb.length() > 0 && sb.charAt(sb.length() - 1) == PAD) {\n      sb.setLength(sb.length() - 1);\n    }\n    return sb.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/maxicode/decoder/Decoder.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.maxicode.decoder;\n\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.reedsolomon.GenericGF;\nimport com.google.zxing.common.reedsolomon.ReedSolomonDecoder;\nimport com.google.zxing.common.reedsolomon.ReedSolomonException;\n\nimport java.util.Map;\n\n/**\n * <p>The main class which implements MaxiCode decoding -- as opposed to locating and extracting\n * the MaxiCode from an image.</p>\n *\n * @author Manuel Kasten\n */\npublic final class Decoder {\n\n  private static final int ALL = 0;\n  private static final int EVEN = 1;\n  private static final int ODD = 2;\n\n  private final ReedSolomonDecoder rsDecoder;\n\n  public Decoder() {\n    rsDecoder = new ReedSolomonDecoder(GenericGF.MAXICODE_FIELD_64);\n  }\n\n  public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException {\n    return decode(bits, null);\n  }\n\n  public DecoderResult decode(BitMatrix bits,\n                              Map<DecodeHintType,?> hints) throws FormatException, ChecksumException {\n    BitMatrixParser parser = new BitMatrixParser(bits);\n    byte[] codewords = parser.readCodewords();\n\n    correctErrors(codewords, 0, 10, 10, ALL);\n    int mode = codewords[0] & 0x0F;\n    byte[] datawords;\n    switch (mode) {\n      case 2:\n      case 3:\n      case 4:\n        correctErrors(codewords, 20, 84, 40, EVEN);\n        correctErrors(codewords, 20, 84, 40, ODD);\n        datawords = new byte[94];\n        break;\n      case 5:\n        correctErrors(codewords, 20, 68, 56, EVEN);\n        correctErrors(codewords, 20, 68, 56, ODD);\n        datawords = new byte[78];\n        break;\n      default:\n        throw FormatException.getFormatInstance();\n    }\n\n    System.arraycopy(codewords, 0, datawords, 0, 10);\n    System.arraycopy(codewords, 20, datawords, 10, datawords.length - 10);\n\n    return DecodedBitStreamParser.decode(datawords, mode);\n  }\n\n  private void correctErrors(byte[] codewordBytes,\n                             int start,\n                             int dataCodewords,\n                             int ecCodewords,\n                             int mode) throws ChecksumException {\n    int codewords = dataCodewords + ecCodewords;\n\n    // in EVEN or ODD mode only half the codewords\n    int divisor = mode == ALL ? 1 : 2;\n\n    // First read into an array of ints\n    int[] codewordsInts = new int[codewords / divisor];\n    for (int i = 0; i < codewords; i++) {\n      if ((mode == ALL) || (i % 2 == (mode - 1))) {\n        codewordsInts[i / divisor] = codewordBytes[i + start] & 0xFF;\n      }\n    }\n    try {\n      rsDecoder.decode(codewordsInts, ecCodewords / divisor);\n    } catch (ReedSolomonException ignored) {\n      throw ChecksumException.getChecksumInstance();\n    }\n    // Copy back into array of bytes -- only need to worry about the bytes that were data\n    // We don't care about errors in the error-correction codewords\n    for (int i = 0; i < dataCodewords; i++) {\n      if ((mode == ALL) || (i % 2 == (mode - 1))) {\n        codewordBytes[i + start] = (byte) codewordsInts[i / divisor];\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/multi/ByQuadrantReader.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.multi;\n\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\n\nimport java.util.Map;\n\n/**\n * This class attempts to decode a barcode from an image, not by scanning the whole image,\n * but by scanning subsets of the image. This is important when there may be multiple barcodes in\n * an image, and detecting a barcode may find parts of multiple barcode and fail to decode\n * (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center\n * 'quadrant' to cover the case where a barcode is found in the center.\n *\n * @see GenericMultipleBarcodeReader\n */\npublic final class ByQuadrantReader implements Reader {\n\n  private final Reader delegate;\n\n  public ByQuadrantReader(Reader delegate) {\n    this.delegate = delegate;\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image)\n      throws NotFoundException, ChecksumException, FormatException {\n    return decode(image, null);\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException {\n\n    int width = image.getWidth();\n    int height = image.getHeight();\n    int halfWidth = width / 2;\n    int halfHeight = height / 2;\n\n    try {\n      // No need to call makeAbsolute as results will be relative to original top left here\n      return delegate.decode(image.crop(0, 0, halfWidth, halfHeight), hints);\n    } catch (NotFoundException re) {\n      // continue\n    }\n\n    try {\n      Result result = delegate.decode(image.crop(halfWidth, 0, halfWidth, halfHeight), hints);\n      makeAbsolute(result.getResultPoints(), halfWidth, 0);\n      return result;\n    } catch (NotFoundException re) {\n      // continue\n    }\n\n    try {\n      Result result = delegate.decode(image.crop(0, halfHeight, halfWidth, halfHeight), hints);\n      makeAbsolute(result.getResultPoints(), 0, halfHeight);\n      return result;\n    } catch (NotFoundException re) {\n      // continue\n    }\n\n    try {\n      Result result = delegate.decode(image.crop(halfWidth, halfHeight, halfWidth, halfHeight), hints);\n      makeAbsolute(result.getResultPoints(), halfWidth, halfHeight);\n      return result;\n    } catch (NotFoundException re) {\n      // continue\n    }\n\n    int quarterWidth = halfWidth / 2;\n    int quarterHeight = halfHeight / 2;\n    BinaryBitmap center = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight);\n    Result result = delegate.decode(center, hints);\n    makeAbsolute(result.getResultPoints(), quarterWidth, quarterHeight);\n    return result;\n  }\n\n  @Override\n  public void reset() {\n    delegate.reset();\n  }\n\n  private static void makeAbsolute(ResultPoint[] points, int leftOffset, int topOffset) {\n    if (points != null) {\n      for (int i = 0; i < points.length; i++) {\n        ResultPoint relative = points[i];\n        if (relative != null) {\n          points[i] = new ResultPoint(relative.getX() + leftOffset, relative.getY() + topOffset);\n        }    \n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/multi/GenericMultipleBarcodeReader.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.multi;\n\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.\n * After one barcode is found, the areas left, above, right and below the barcode's\n * {@link ResultPoint}s are scanned, recursively.</p>\n *\n * <p>A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple\n * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent\n * detecting any one of them.</p>\n *\n * <p>That is, instead of passing a {@link Reader} a caller might pass\n * {@code new ByQuadrantReader(reader)}.</p>\n *\n * @author Sean Owen\n */\npublic final class GenericMultipleBarcodeReader implements MultipleBarcodeReader {\n\n  private static final int MIN_DIMENSION_TO_RECUR = 100;\n  private static final int MAX_DEPTH = 4;\n\n  static final Result[] EMPTY_RESULT_ARRAY = new Result[0];\n\n  private final Reader delegate;\n\n  public GenericMultipleBarcodeReader(Reader delegate) {\n    this.delegate = delegate;\n  }\n\n  @Override\n  public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {\n    return decodeMultiple(image, null);\n  }\n\n  @Override\n  public Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints)\n      throws NotFoundException {\n    List<Result> results = new ArrayList<>();\n    doDecodeMultiple(image, hints, results, 0, 0, 0);\n    if (results.isEmpty()) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    return results.toArray(EMPTY_RESULT_ARRAY);\n  }\n\n  private void doDecodeMultiple(BinaryBitmap image,\n                                Map<DecodeHintType,?> hints,\n                                List<Result> results,\n                                int xOffset,\n                                int yOffset,\n                                int currentDepth) {\n    if (currentDepth > MAX_DEPTH) {\n      return;\n    }\n\n    Result result;\n    try {\n      result = delegate.decode(image, hints);\n    } catch (ReaderException ignored) {\n      return;\n    }\n    boolean alreadyFound = false;\n    for (Result existingResult : results) {\n      if (existingResult.getText().equals(result.getText())) {\n        alreadyFound = true;\n        break;\n      }\n    }\n    if (!alreadyFound) {\n      results.add(translateResultPoints(result, xOffset, yOffset));\n    }\n    ResultPoint[] resultPoints = result.getResultPoints();\n    if (resultPoints == null || resultPoints.length == 0) {\n      return;\n    }\n    int width = image.getWidth();\n    int height = image.getHeight();\n    float minX = width;\n    float minY = height;\n    float maxX = 0.0f;\n    float maxY = 0.0f;\n    for (ResultPoint point : resultPoints) {\n      if (point == null) {\n        continue;\n      }\n      float x = point.getX();\n      float y = point.getY();\n      if (x < minX) {\n        minX = x;\n      }\n      if (y < minY) {\n        minY = y;\n      }\n      if (x > maxX) {\n        maxX = x;\n      }\n      if (y > maxY) {\n        maxY = y;\n      }\n    }\n\n    // Decode left of barcode\n    if (minX > MIN_DIMENSION_TO_RECUR) {\n      doDecodeMultiple(image.crop(0, 0, (int) minX, height),\n                       hints, results,\n                       xOffset, yOffset,\n                       currentDepth + 1);\n    }\n    // Decode above barcode\n    if (minY > MIN_DIMENSION_TO_RECUR) {\n      doDecodeMultiple(image.crop(0, 0, width, (int) minY),\n                       hints, results,\n                       xOffset, yOffset,\n                       currentDepth + 1);\n    }\n    // Decode right of barcode\n    if (maxX < width - MIN_DIMENSION_TO_RECUR) {\n      doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height),\n                       hints, results,\n                       xOffset + (int) maxX, yOffset,\n                       currentDepth + 1);\n    }\n    // Decode below barcode\n    if (maxY < height - MIN_DIMENSION_TO_RECUR) {\n      doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY),\n                       hints, results,\n                       xOffset, yOffset + (int) maxY,\n                       currentDepth + 1);\n    }\n  }\n\n  private static Result translateResultPoints(Result result, int xOffset, int yOffset) {\n    ResultPoint[] oldResultPoints = result.getResultPoints();\n    if (oldResultPoints == null) {\n      return result;\n    }\n    ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.length];\n    for (int i = 0; i < oldResultPoints.length; i++) {\n      ResultPoint oldPoint = oldResultPoints[i];\n      if (oldPoint != null) {\n        newResultPoints[i] = new ResultPoint(oldPoint.getX() + xOffset, oldPoint.getY() + yOffset);\n      }\n    }\n    Result newResult = new Result(result.getText(),\n                                  result.getRawBytes(),\n                                  result.getNumBits(),\n                                  newResultPoints,\n                                  result.getBarcodeFormat(),\n                                  result.getTimestamp());\n    newResult.putAllMetadata(result.getResultMetadata());\n    return newResult;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/multi/MultipleBarcodeReader.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.multi;\n\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\n\nimport java.util.Map;\n\n/**\n * Implementation of this interface attempt to read several barcodes from one image.\n *\n * @see com.google.zxing.Reader\n * @author Sean Owen\n */\npublic interface MultipleBarcodeReader {\n\n  Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException;\n\n  Result[] decodeMultiple(BinaryBitmap image,\n                          Map<DecodeHintType, ?> hints) throws NotFoundException;\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/multi/qrcode/QRCodeMultiReader.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.multi.qrcode;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.DetectorResult;\nimport com.google.zxing.multi.MultipleBarcodeReader;\nimport com.google.zxing.multi.qrcode.detector.MultiDetector;\nimport com.google.zxing.qrcode.QRCodeReader;\nimport com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collections;\nimport java.util.Comparator;\n\n/**\n * This implementation can detect and decode multiple QR Codes in an image.\n *\n * @author Sean Owen\n * @author Hannes Erven\n */\npublic final class QRCodeMultiReader extends QRCodeReader implements MultipleBarcodeReader {\n\n  private static final Result[] EMPTY_RESULT_ARRAY = new Result[0];\n  private static final ResultPoint[] NO_POINTS = new ResultPoint[0];\n\n  @Override\n  public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {\n    return decodeMultiple(image, null);\n  }\n\n  @Override\n  public Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {\n    List<Result> results = new ArrayList<>();\n    DetectorResult[] detectorResults = new MultiDetector(image.getBlackMatrix()).detectMulti(hints);\n    for (DetectorResult detectorResult : detectorResults) {\n      try {\n        DecoderResult decoderResult = getDecoder().decode(detectorResult.getBits(), hints);\n        ResultPoint[] points = detectorResult.getPoints();\n        // If the code was mirrored: swap the bottom-left and the top-right points.\n        if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {\n          ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);\n        }\n        Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points,\n                                   BarcodeFormat.QR_CODE);\n        List<byte[]> byteSegments = decoderResult.getByteSegments();\n        if (byteSegments != null) {\n          result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);\n        }\n        String ecLevel = decoderResult.getECLevel();\n        if (ecLevel != null) {\n          result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);\n        }\n        if (decoderResult.hasStructuredAppend()) {\n          result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,\n                             decoderResult.getStructuredAppendSequenceNumber());\n          result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,\n                             decoderResult.getStructuredAppendParity());\n        }\n        results.add(result);\n      } catch (ReaderException re) {\n        // ignore and continue \n      }\n    }\n    if (results.isEmpty()) {\n      return EMPTY_RESULT_ARRAY;\n    } else {\n      results = processStructuredAppend(results);\n      return results.toArray(EMPTY_RESULT_ARRAY);\n    }\n  }\n\n  private static List<Result> processStructuredAppend(List<Result> results) {\n    boolean hasSA = false;\n\n    // first, check, if there is at least on SA result in the list\n    for (Result result : results) {\n      if (result.getResultMetadata().containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {\n        hasSA = true;\n        break;\n      }\n    }\n    if (!hasSA) {\n      return results;\n    }\n\n    // it is, second, split the lists and built a new result list\n    List<Result> newResults = new ArrayList<>();\n    List<Result> saResults = new ArrayList<>();\n    for (Result result : results) {\n      newResults.add(result);\n      if (result.getResultMetadata().containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {\n        saResults.add(result);\n      }\n    }\n    // sort and concatenate the SA list items\n    Collections.sort(saResults, new SAComparator());\n    StringBuilder concatedText = new StringBuilder();\n    int rawBytesLen = 0;\n    int byteSegmentLength = 0;\n    for (Result saResult : saResults) {\n      concatedText.append(saResult.getText());\n      rawBytesLen += saResult.getRawBytes().length;\n      if (saResult.getResultMetadata().containsKey(ResultMetadataType.BYTE_SEGMENTS)) {\n        @SuppressWarnings(\"unchecked\")\n        Iterable<byte[]> byteSegments =\n            (Iterable<byte[]>) saResult.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS);\n        for (byte[] segment : byteSegments) {\n          byteSegmentLength += segment.length;\n        }\n      }\n    }\n    byte[] newRawBytes = new byte[rawBytesLen];\n    byte[] newByteSegment = new byte[byteSegmentLength];\n    int newRawBytesIndex = 0;\n    int byteSegmentIndex = 0;\n    for (Result saResult : saResults) {\n      System.arraycopy(saResult.getRawBytes(), 0, newRawBytes, newRawBytesIndex, saResult.getRawBytes().length);\n      newRawBytesIndex += saResult.getRawBytes().length;\n      if (saResult.getResultMetadata().containsKey(ResultMetadataType.BYTE_SEGMENTS)) {\n        @SuppressWarnings(\"unchecked\")\n        Iterable<byte[]> byteSegments =\n            (Iterable<byte[]>) saResult.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS);\n        for (byte[] segment : byteSegments) {\n          System.arraycopy(segment, 0, newByteSegment, byteSegmentIndex, segment.length);\n          byteSegmentIndex += segment.length;\n        }\n      }\n    }\n    Result newResult = new Result(concatedText.toString(), newRawBytes, NO_POINTS, BarcodeFormat.QR_CODE);\n    if (byteSegmentLength > 0) {\n      Collection<byte[]> byteSegmentList = new ArrayList<>();\n      byteSegmentList.add(newByteSegment);\n      newResult.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegmentList);\n    }\n    newResults.add(newResult);\n    return newResults;\n  }\n\n  private static final class SAComparator implements Comparator<Result>, Serializable {\n    @Override\n    public int compare(Result a, Result b) {\n      int aNumber = (int) a.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);\n      int bNumber = (int) b.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);\n      return Integer.compare(aNumber, bNumber);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/multi/qrcode/detector/MultiDetector.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.multi.qrcode.detector;\n\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DetectorResult;\nimport com.google.zxing.qrcode.detector.Detector;\nimport com.google.zxing.qrcode.detector.FinderPatternInfo;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code\n * is rotated or skewed, or partially obscured.</p>\n *\n * @author Sean Owen\n * @author Hannes Erven\n */\npublic final class MultiDetector extends Detector {\n\n  private static final DetectorResult[] EMPTY_DETECTOR_RESULTS = new DetectorResult[0];\n\n  public MultiDetector(BitMatrix image) {\n    super(image);\n  }\n\n  public DetectorResult[] detectMulti(Map<DecodeHintType,?> hints) throws NotFoundException {\n    BitMatrix image = getImage();\n    ResultPointCallback resultPointCallback =\n        hints == null ? null : (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);\n    MultiFinderPatternFinder finder = new MultiFinderPatternFinder(image, resultPointCallback);\n    FinderPatternInfo[] infos = finder.findMulti(hints);\n\n    if (infos.length == 0) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    List<DetectorResult> result = new ArrayList<>();\n    for (FinderPatternInfo info : infos) {\n      try {\n        result.add(processFinderPatternInfo(info));\n      } catch (ReaderException e) {\n        // ignore\n      }\n    }\n    if (result.isEmpty()) {\n      return EMPTY_DETECTOR_RESULTS;\n    } else {\n      return result.toArray(EMPTY_DETECTOR_RESULTS);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.multi.qrcode.detector;\n\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.qrcode.detector.FinderPattern;\nimport com.google.zxing.qrcode.detector.FinderPatternFinder;\nimport com.google.zxing.qrcode.detector.FinderPatternInfo;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square\n * markers at three corners of a QR Code.</p>\n *\n * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.\n *\n * <p>In contrast to {@link FinderPatternFinder}, this class will return an array of all possible\n * QR code locations in the image.</p>\n *\n * <p>Use the TRY_HARDER hint to ask for a more thorough detection.</p>\n *\n * @author Sean Owen\n * @author Hannes Erven\n */\nfinal class MultiFinderPatternFinder extends FinderPatternFinder {\n\n  private static final FinderPatternInfo[] EMPTY_RESULT_ARRAY = new FinderPatternInfo[0];\n  private static final FinderPattern[][] EMPTY_FP_2D_ARRAY = new FinderPattern[0][];\n\n  // TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for\n  // since it limits the number of regions to decode\n\n  // max. legal count of modules per QR code edge (177)\n  private static final float MAX_MODULE_COUNT_PER_EDGE = 180;\n  // min. legal count per modules per QR code edge (11)\n  private static final float MIN_MODULE_COUNT_PER_EDGE = 9;\n\n  /**\n   * More or less arbitrary cutoff point for determining if two finder patterns might belong\n   * to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their\n   * estimated modules sizes.\n   */\n  private static final float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f;\n\n  /**\n   * More or less arbitrary cutoff point for determining if two finder patterns might belong\n   * to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their\n   * estimated modules sizes.\n   */\n  private static final float DIFF_MODSIZE_CUTOFF = 0.5f;\n\n\n  /**\n   * A comparator that orders FinderPatterns by their estimated module size.\n   */\n  private static final class ModuleSizeComparator implements Comparator<FinderPattern>, Serializable {\n    @Override\n    public int compare(FinderPattern center1, FinderPattern center2) {\n      float value = center2.getEstimatedModuleSize() - center1.getEstimatedModuleSize();\n      return value < 0.0 ? -1 : value > 0.0 ? 1 : 0;\n    }\n  }\n\n  /**\n   * <p>Creates a finder that will search the image for three finder patterns.</p>\n   *\n   * @param image image to search\n   */\n  MultiFinderPatternFinder(BitMatrix image) {\n    super(image);\n  }\n\n  MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {\n    super(image, resultPointCallback);\n  }\n\n  /**\n   * @return the 3 best {@link FinderPattern}s from our list of candidates. The \"best\" are\n   *         those that have been detected at least {@link #CENTER_QUORUM} times, and whose module\n   *         size differs from the average among those patterns the least\n   * @throws NotFoundException if 3 such finder patterns do not exist\n   */\n  private FinderPattern[][] selectMultipleBestPatterns() throws NotFoundException {\n    List<FinderPattern> possibleCenters = getPossibleCenters();\n    int size = possibleCenters.size();\n\n    if (size < 3) {\n      // Couldn't find enough finder patterns\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    /*\n     * Begin HE modifications to safely detect multiple codes of equal size\n     */\n    if (size == 3) {\n      return new FinderPattern[][]{\n          new FinderPattern[]{\n              possibleCenters.get(0),\n              possibleCenters.get(1),\n              possibleCenters.get(2)\n          }\n      };\n    }\n\n    // Sort by estimated module size to speed up the upcoming checks\n    Collections.sort(possibleCenters, new ModuleSizeComparator());\n\n    /*\n     * Now lets start: build a list of tuples of three finder locations that\n     *  - feature similar module sizes\n     *  - are placed in a distance so the estimated module count is within the QR specification\n     *  - have similar distance between upper left/right and left top/bottom finder patterns\n     *  - form a triangle with 90° angle (checked by comparing top right/bottom left distance\n     *    with pythagoras)\n     *\n     * Note: we allow each point to be used for more than one code region: this might seem\n     * counterintuitive at first, but the performance penalty is not that big. At this point,\n     * we cannot make a good quality decision whether the three finders actually represent\n     * a QR code, or are just by chance layouted so it looks like there might be a QR code there.\n     * So, if the layout seems right, lets have the decoder try to decode.     \n     */\n\n     List<FinderPattern[]> results = new ArrayList<>(); // holder for the results\n\n    for (int i1 = 0; i1 < (size - 2); i1++) {\n      FinderPattern p1 = possibleCenters.get(i1);\n      if (p1 == null) {\n        continue;\n      }\n\n      for (int i2 = i1 + 1; i2 < (size - 1); i2++) {\n        FinderPattern p2 = possibleCenters.get(i2);\n        if (p2 == null) {\n          continue;\n        }\n\n        // Compare the expected module sizes; if they are really off, skip\n        float vModSize12 = (p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize()) /\n            Math.min(p1.getEstimatedModuleSize(), p2.getEstimatedModuleSize());\n        float vModSize12A = Math.abs(p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize());\n        if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) {\n          // break, since elements are ordered by the module size deviation there cannot be\n          // any more interesting elements for the given p1.\n          break;\n        }\n\n        for (int i3 = i2 + 1; i3 < size; i3++) {\n          FinderPattern p3 = possibleCenters.get(i3);\n          if (p3 == null) {\n            continue;\n          }\n\n          // Compare the expected module sizes; if they are really off, skip\n          float vModSize23 = (p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize()) /\n              Math.min(p2.getEstimatedModuleSize(), p3.getEstimatedModuleSize());\n          float vModSize23A = Math.abs(p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize());\n          if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) {\n            // break, since elements are ordered by the module size deviation there cannot be\n            // any more interesting elements for the given p1.\n            break;\n          }\n\n          FinderPattern[] test = {p1, p2, p3};\n          ResultPoint.orderBestPatterns(test);\n\n          // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal\n          FinderPatternInfo info = new FinderPatternInfo(test);\n          float dA = ResultPoint.distance(info.getTopLeft(), info.getBottomLeft());\n          float dC = ResultPoint.distance(info.getTopRight(), info.getBottomLeft());\n          float dB = ResultPoint.distance(info.getTopLeft(), info.getTopRight());\n\n          // Check the sizes\n          float estimatedModuleCount = (dA + dB) / (p1.getEstimatedModuleSize() * 2.0f);\n          if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE ||\n              estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) {\n            continue;\n          }\n\n          // Calculate the difference of the edge lengths in percent\n          float vABBC = Math.abs((dA - dB) / Math.min(dA, dB));\n          if (vABBC >= 0.1f) {\n            continue;\n          }\n\n          // Calculate the diagonal length by assuming a 90° angle at topleft\n          float dCpy = (float) Math.sqrt((double) dA * dA + (double) dB * dB);\n          // Compare to the real distance in %\n          float vPyC = Math.abs((dC - dCpy) / Math.min(dC, dCpy));\n\n          if (vPyC >= 0.1f) {\n            continue;\n          }\n\n          // All tests passed!\n          results.add(test);\n        }\n      }\n    }\n\n    if (!results.isEmpty()) {\n      return results.toArray(EMPTY_FP_2D_ARRAY);\n    }\n\n    // Nothing found!\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  public FinderPatternInfo[] findMulti(Map<DecodeHintType,?> hints) throws NotFoundException {\n    boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);\n    BitMatrix image = getImage();\n    int maxI = image.getHeight();\n    int maxJ = image.getWidth();\n    // We are looking for black/white/black/white/black modules in\n    // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far\n\n    // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the\n    // image, and then account for the center being 3 modules in size. This gives the smallest\n    // number of pixels the center could be, so skip this often. When trying harder, look for all\n    // QR versions regardless of how dense they are.\n    int iSkip = (3 * maxI) / (4 * MAX_MODULES);\n    if (iSkip < MIN_SKIP || tryHarder) {\n      iSkip = MIN_SKIP;\n    }\n\n    int[] stateCount = new int[5];\n    for (int i = iSkip - 1; i < maxI; i += iSkip) {\n      // Get a row of black/white values\n      clearCounts(stateCount);\n      int currentState = 0;\n      for (int j = 0; j < maxJ; j++) {\n        if (image.get(j, i)) {\n          // Black pixel\n          if ((currentState & 1) == 1) { // Counting white pixels\n            currentState++;\n          }\n          stateCount[currentState]++;\n        } else { // White pixel\n          if ((currentState & 1) == 0) { // Counting black pixels\n            if (currentState == 4) { // A winner?\n              if (foundPatternCross(stateCount) && handlePossibleCenter(stateCount, i, j)) { // Yes\n                // Clear state to start looking again\n                currentState = 0;\n                clearCounts(stateCount);\n              } else { // No, shift counts back by two\n                shiftCounts2(stateCount);\n                currentState = 3;\n              }\n            } else {\n              stateCount[++currentState]++;\n            }\n          } else { // Counting white pixels\n            stateCount[currentState]++;\n          }\n        }\n      } // for j=...\n\n      if (foundPatternCross(stateCount)) {\n        handlePossibleCenter(stateCount, i, maxJ);\n      }\n    } // for i=iSkip-1 ...\n    FinderPattern[][] patternInfo = selectMultipleBestPatterns();\n    List<FinderPatternInfo> result = new ArrayList<>();\n    for (FinderPattern[] pattern : patternInfo) {\n      ResultPoint.orderBestPatterns(pattern);\n      result.add(new FinderPatternInfo(pattern));\n    }\n\n    if (result.isEmpty()) {\n      return EMPTY_RESULT_ARRAY;\n    } else {\n      return result.toArray(EMPTY_RESULT_ARRAY);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/CodaBarReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Arrays;\nimport java.util.Map;\n\n/**\n * <p>Decodes Codabar barcodes.</p>\n *\n * @author Bas Vijfwinkel\n * @author David Walker\n */\npublic final class CodaBarReader extends OneDReader {\n\n  // These values are critical for determining how permissive the decoding\n  // will be. All stripe sizes must be within the window these define, as\n  // compared to the average stripe size.\n  private static final float MAX_ACCEPTABLE = 2.0f;\n  private static final float PADDING = 1.5f;\n\n  private static final String ALPHABET_STRING = \"0123456789-$:/.+ABCD\";\n  static final char[] ALPHABET = ALPHABET_STRING.toCharArray();\n\n  /**\n   * These represent the encodings of characters, as patterns of wide and narrow bars. The 7 least-significant bits of\n   * each int correspond to the pattern of wide and narrow, with 1s representing \"wide\" and 0s representing narrow.\n   */\n  static final int[] CHARACTER_ENCODINGS = {\n      0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9\n      0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD\n  };\n\n  // minimal number of characters that should be present (including start and stop characters)\n  // under normal circumstances this should be set to 3, but can be set higher\n  // as a last-ditch attempt to reduce false positives.\n  private static final int MIN_CHARACTER_LENGTH = 3;\n\n  // official start and end patterns\n  private static final char[] STARTEND_ENCODING = {'A', 'B', 'C', 'D'};\n  // some Codabar generator allow the Codabar string to be closed by every\n  // character. This will cause lots of false positives!\n\n  // some industries use a checksum standard but this is not part of the original Codabar standard\n  // for more information see : http://www.mecsw.com/specs/codabar.html\n\n  // Keep some instance variables to avoid reallocations\n  private final StringBuilder decodeRowResult;\n  private int[] counters;\n  private int counterLength;\n\n  public CodaBarReader() {\n    decodeRowResult = new StringBuilder(20);\n    counters = new int[80];\n    counterLength = 0;\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints) throws NotFoundException {\n\n    Arrays.fill(counters, 0);\n    setCounters(row);\n    int startOffset = findStartPattern();\n    int nextStart = startOffset;\n\n    decodeRowResult.setLength(0);\n    do {\n      int charOffset = toNarrowWidePattern(nextStart);\n      if (charOffset == -1) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      // Hack: We store the position in the alphabet table into a\n      // StringBuilder, so that we can access the decoded patterns in\n      // validatePattern. We'll translate to the actual characters later.\n      decodeRowResult.append((char) charOffset);\n      nextStart += 8;\n      // Stop as soon as we see the end character.\n      if (decodeRowResult.length() > 1 &&\n          arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {\n        break;\n      }\n    } while (nextStart < counterLength); // no fixed end pattern so keep on reading while data is available\n\n    // Look for whitespace after pattern:\n    int trailingWhitespace = counters[nextStart - 1];\n    int lastPatternSize = 0;\n    for (int i = -8; i < -1; i++) {\n      lastPatternSize += counters[nextStart + i];\n    }\n\n    // We need to see whitespace equal to 50% of the last pattern size,\n    // otherwise this is probably a false positive. The exception is if we are\n    // at the end of the row. (I.e. the barcode barely fits.)\n    if (nextStart < counterLength && trailingWhitespace < lastPatternSize / 2) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    validatePattern(startOffset);\n\n    // Translate character table offsets to actual characters.\n    for (int i = 0; i < decodeRowResult.length(); i++) {\n      decodeRowResult.setCharAt(i, ALPHABET[decodeRowResult.charAt(i)]);\n    }\n    // Ensure a valid start and end character\n    char startchar = decodeRowResult.charAt(0);\n    if (!arrayContains(STARTEND_ENCODING, startchar)) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    char endchar = decodeRowResult.charAt(decodeRowResult.length() - 1);\n    if (!arrayContains(STARTEND_ENCODING, endchar)) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    // remove stop/start characters character and check if a long enough string is contained\n    if (decodeRowResult.length() <= MIN_CHARACTER_LENGTH) {\n      // Almost surely a false positive ( start + stop + at least 1 character)\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    if (hints == null || !hints.containsKey(DecodeHintType.RETURN_CODABAR_START_END)) {\n      decodeRowResult.deleteCharAt(decodeRowResult.length() - 1);\n      decodeRowResult.deleteCharAt(0);\n    }\n\n    int runningCount = 0;\n    for (int i = 0; i < startOffset; i++) {\n      runningCount += counters[i];\n    }\n    float left = runningCount;\n    for (int i = startOffset; i < nextStart - 1; i++) {\n      runningCount += counters[i];\n    }\n    float right = runningCount;\n    return new Result(\n        decodeRowResult.toString(),\n        null,\n        new ResultPoint[]{\n            new ResultPoint(left, rowNumber),\n            new ResultPoint(right, rowNumber)},\n        BarcodeFormat.CODABAR);\n  }\n\n  private void validatePattern(int start) throws NotFoundException {\n    // First, sum up the total size of our four categories of stripe sizes;\n    int[] sizes = {0, 0, 0, 0};\n    int[] counts = {0, 0, 0, 0};\n    int end = decodeRowResult.length() - 1;\n\n    // We break out of this loop in the middle, in order to handle\n    // inter-character spaces properly.\n    int pos = start;\n    for (int i = 0; true; i++) {\n      int pattern = CHARACTER_ENCODINGS[decodeRowResult.charAt(i)];\n      for (int j = 6; j >= 0; j--) {\n        // Even j = bars, while odd j = spaces. Categories 2 and 3 are for\n        // long stripes, while 0 and 1 are for short stripes.\n        int category = (j & 1) + (pattern & 1) * 2;\n        sizes[category] += counters[pos + j];\n        counts[category]++;\n        pattern >>= 1;\n      }\n      if (i >= end) {\n        break;\n      }\n      // We ignore the inter-character space - it could be of any size.\n      pos += 8;\n    }\n\n    // Calculate our allowable size thresholds using fixed-point math.\n    float[] maxes = new float[4];\n    float[] mins = new float[4];\n    // Define the threshold of acceptability to be the midpoint between the\n    // average small stripe and the average large stripe. No stripe lengths\n    // should be on the \"wrong\" side of that line.\n    for (int i = 0; i < 2; i++) {\n      mins[i] = 0.0f;  // Accept arbitrarily small \"short\" stripes.\n      mins[i + 2] = ((float) sizes[i] / counts[i] + (float) sizes[i + 2] / counts[i + 2]) / 2.0f;\n      maxes[i] = mins[i + 2];\n      maxes[i + 2] = (sizes[i + 2] * MAX_ACCEPTABLE + PADDING) / counts[i + 2];\n    }\n\n    // Now verify that all of the stripes are within the thresholds.\n    pos = start;\n    for (int i = 0; true; i++) {\n      int pattern = CHARACTER_ENCODINGS[decodeRowResult.charAt(i)];\n      for (int j = 6; j >= 0; j--) {\n        // Even j = bars, while odd j = spaces. Categories 2 and 3 are for\n        // long stripes, while 0 and 1 are for short stripes.\n        int category = (j & 1) + (pattern & 1) * 2;\n        int size = counters[pos + j];\n        if (size < mins[category] || size > maxes[category]) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        pattern >>= 1;\n      }\n      if (i >= end) {\n        break;\n      }\n      pos += 8;\n    }\n  }\n\n  /**\n   * Records the size of all runs of white and black pixels, starting with white.\n   * This is just like recordPattern, except it records all the counters, and\n   * uses our builtin \"counters\" member for storage.\n   * @param row row to count from\n   */\n  private void setCounters(BitArray row) throws NotFoundException {\n    counterLength = 0;\n    // Start from the first white bit.\n    int i = row.getNextUnset(0);\n    int end = row.getSize();\n    if (i >= end) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    boolean isWhite = true;\n    int count = 0;\n    while (i < end) {\n      if (row.get(i) != isWhite) {\n        count++;\n      } else {\n        counterAppend(count);\n        count = 1;\n        isWhite = !isWhite;\n      }\n      i++;\n    }\n    counterAppend(count);\n  }\n\n  private void counterAppend(int e) {\n    counters[counterLength] = e;\n    counterLength++;\n    if (counterLength >= counters.length) {\n      int[] temp = new int[counterLength * 2];\n      System.arraycopy(counters, 0, temp, 0, counterLength);\n      counters = temp;\n    }\n  }\n\n  private int findStartPattern() throws NotFoundException {\n    for (int i = 1; i < counterLength; i += 2) {\n      int charOffset = toNarrowWidePattern(i);\n      if (charOffset != -1 && arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {\n        // Look for whitespace before start pattern, >= 50% of width of start pattern\n        // We make an exception if the whitespace is the first element.\n        int patternSize = 0;\n        for (int j = i; j < i + 7; j++) {\n          patternSize += counters[j];\n        }\n        if (i == 1 || counters[i - 1] >= patternSize / 2) {\n          return i;\n        }\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  static boolean arrayContains(char[] array, char key) {\n    if (array != null) {\n      for (char c : array) {\n        if (c == key) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n\n  // Assumes that counters[position] is a bar.\n  private int toNarrowWidePattern(int position) {\n    int end = position + 7;\n    if (end >= counterLength) {\n      return -1;\n    }\n\n    int[] theCounters = counters;\n\n    int maxBar = 0;\n    int minBar = Integer.MAX_VALUE;\n    for (int j = position; j < end; j += 2) {\n      int currentCounter = theCounters[j];\n      if (currentCounter < minBar) {\n        minBar = currentCounter;\n      }\n      if (currentCounter > maxBar) {\n        maxBar = currentCounter;\n      }\n    }\n    int thresholdBar = (minBar + maxBar) / 2;\n\n    int maxSpace = 0;\n    int minSpace = Integer.MAX_VALUE;\n    for (int j = position + 1; j < end; j += 2) {\n      int currentCounter = theCounters[j];\n      if (currentCounter < minSpace) {\n        minSpace = currentCounter;\n      }\n      if (currentCounter > maxSpace) {\n        maxSpace = currentCounter;\n      }\n    }\n    int thresholdSpace = (minSpace + maxSpace) / 2;\n\n    int bitmask = 1 << 7;\n    int pattern = 0;\n    for (int i = 0; i < 7; i++) {\n      int threshold = (i & 1) == 0 ? thresholdBar : thresholdSpace;\n      bitmask >>= 1;\n      if (theCounters[position + i] > threshold) {\n        pattern |= bitmask;\n      }\n    }\n\n    for (int i = 0; i < CHARACTER_ENCODINGS.length; i++) {\n      if (CHARACTER_ENCODINGS[i] == pattern) {\n        return i;\n      }\n    }\n    return -1;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/CodaBarWriter.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\n/**\n * This class renders CodaBar as {@code boolean[]}.\n *\n * @author dsbnatut@gmail.com (Kazuki Nishiura)\n */\npublic final class CodaBarWriter extends OneDimensionalCodeWriter {\n\n  private static final char[] START_END_CHARS = {'A', 'B', 'C', 'D'};\n  private static final char[] ALT_START_END_CHARS = {'T', 'N', '*', 'E'};\n  private static final char[] CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED = {'/', ':', '+', '.'};\n  private static final char DEFAULT_GUARD = START_END_CHARS[0];\n\n  @Override\n  public boolean[] encode(String contents) {\n\n    if (contents.length() < 2) {\n      // Can't have a start/end guard, so tentatively add default guards\n      contents = DEFAULT_GUARD + contents + DEFAULT_GUARD;\n    } else {\n      // Verify input and calculate decoded length.\n      char firstChar = Character.toUpperCase(contents.charAt(0));\n      char lastChar = Character.toUpperCase(contents.charAt(contents.length() - 1));\n      boolean startsNormal = CodaBarReader.arrayContains(START_END_CHARS, firstChar);\n      boolean endsNormal = CodaBarReader.arrayContains(START_END_CHARS, lastChar);\n      boolean startsAlt = CodaBarReader.arrayContains(ALT_START_END_CHARS, firstChar);\n      boolean endsAlt = CodaBarReader.arrayContains(ALT_START_END_CHARS, lastChar);\n      if (startsNormal) {\n        if (!endsNormal) {\n          throw new IllegalArgumentException(\"Invalid start/end guards: \" + contents);\n        }\n        // else already has valid start/end\n      } else if (startsAlt) {\n        if (!endsAlt) {\n          throw new IllegalArgumentException(\"Invalid start/end guards: \" + contents);\n        }\n        // else already has valid start/end\n      } else {\n        // Doesn't start with a guard\n        if (endsNormal || endsAlt) {\n          throw new IllegalArgumentException(\"Invalid start/end guards: \" + contents);\n        }\n        // else doesn't end with guard either, so add a default\n        contents = DEFAULT_GUARD + contents + DEFAULT_GUARD;\n      }\n    }\n\n    // The start character and the end character are decoded to 10 length each.\n    int resultLength = 20;\n    for (int i = 1; i < contents.length() - 1; i++) {\n      if (Character.isDigit(contents.charAt(i)) || contents.charAt(i) == '-' || contents.charAt(i) == '$') {\n        resultLength += 9;\n      } else if (CodaBarReader.arrayContains(CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED, contents.charAt(i))) {\n        resultLength += 10;\n      } else {\n        throw new IllegalArgumentException(\"Cannot encode : '\" + contents.charAt(i) + '\\'');\n      }\n    }\n    // A blank is placed between each character.\n    resultLength += contents.length() - 1;\n\n    boolean[] result = new boolean[resultLength];\n    int position = 0;\n    for (int index = 0; index < contents.length(); index++) {\n      char c = Character.toUpperCase(contents.charAt(index));\n      if (index == 0 || index == contents.length() - 1) {\n        // The start/end chars are not in the CodaBarReader.ALPHABET.\n        switch (c) {\n          case 'T':\n            c = 'A';\n            break;\n          case 'N':\n            c = 'B';\n            break;\n          case '*':\n            c = 'C';\n            break;\n          case 'E':\n            c = 'D';\n            break;\n        }\n      }\n      int code = 0;\n      for (int i = 0; i < CodaBarReader.ALPHABET.length; i++) {\n        // Found any, because I checked above.\n        if (c == CodaBarReader.ALPHABET[i]) {\n          code = CodaBarReader.CHARACTER_ENCODINGS[i];\n          break;\n        }\n      }\n      boolean color = true;\n      int counter = 0;\n      int bit = 0;\n      while (bit < 7) { // A character consists of 7 digit.\n        result[position] = color;\n        position++;\n        if (((code >> (6 - bit)) & 1) == 0 || counter == 1) {\n          color = !color; // Flip the color.\n          bit++;\n          counter = 0;\n        } else {\n          counter++;\n        }\n      }\n      if (index < contents.length() - 1) {\n        result[position] = false;\n        position++;\n      }\n    }\n    return result;\n  }\n}\n\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/Code128Reader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>Decodes Code 128 barcodes.</p>\n *\n * @author Sean Owen\n */\npublic final class Code128Reader extends OneDReader {\n\n  static final int[][] CODE_PATTERNS = {\n      {2, 1, 2, 2, 2, 2}, // 0\n      {2, 2, 2, 1, 2, 2},\n      {2, 2, 2, 2, 2, 1},\n      {1, 2, 1, 2, 2, 3},\n      {1, 2, 1, 3, 2, 2},\n      {1, 3, 1, 2, 2, 2}, // 5\n      {1, 2, 2, 2, 1, 3},\n      {1, 2, 2, 3, 1, 2},\n      {1, 3, 2, 2, 1, 2},\n      {2, 2, 1, 2, 1, 3},\n      {2, 2, 1, 3, 1, 2}, // 10\n      {2, 3, 1, 2, 1, 2},\n      {1, 1, 2, 2, 3, 2},\n      {1, 2, 2, 1, 3, 2},\n      {1, 2, 2, 2, 3, 1},\n      {1, 1, 3, 2, 2, 2}, // 15\n      {1, 2, 3, 1, 2, 2},\n      {1, 2, 3, 2, 2, 1},\n      {2, 2, 3, 2, 1, 1},\n      {2, 2, 1, 1, 3, 2},\n      {2, 2, 1, 2, 3, 1}, // 20\n      {2, 1, 3, 2, 1, 2},\n      {2, 2, 3, 1, 1, 2},\n      {3, 1, 2, 1, 3, 1},\n      {3, 1, 1, 2, 2, 2},\n      {3, 2, 1, 1, 2, 2}, // 25\n      {3, 2, 1, 2, 2, 1},\n      {3, 1, 2, 2, 1, 2},\n      {3, 2, 2, 1, 1, 2},\n      {3, 2, 2, 2, 1, 1},\n      {2, 1, 2, 1, 2, 3}, // 30\n      {2, 1, 2, 3, 2, 1},\n      {2, 3, 2, 1, 2, 1},\n      {1, 1, 1, 3, 2, 3},\n      {1, 3, 1, 1, 2, 3},\n      {1, 3, 1, 3, 2, 1}, // 35\n      {1, 1, 2, 3, 1, 3},\n      {1, 3, 2, 1, 1, 3},\n      {1, 3, 2, 3, 1, 1},\n      {2, 1, 1, 3, 1, 3},\n      {2, 3, 1, 1, 1, 3}, // 40\n      {2, 3, 1, 3, 1, 1},\n      {1, 1, 2, 1, 3, 3},\n      {1, 1, 2, 3, 3, 1},\n      {1, 3, 2, 1, 3, 1},\n      {1, 1, 3, 1, 2, 3}, // 45\n      {1, 1, 3, 3, 2, 1},\n      {1, 3, 3, 1, 2, 1},\n      {3, 1, 3, 1, 2, 1},\n      {2, 1, 1, 3, 3, 1},\n      {2, 3, 1, 1, 3, 1}, // 50\n      {2, 1, 3, 1, 1, 3},\n      {2, 1, 3, 3, 1, 1},\n      {2, 1, 3, 1, 3, 1},\n      {3, 1, 1, 1, 2, 3},\n      {3, 1, 1, 3, 2, 1}, // 55\n      {3, 3, 1, 1, 2, 1},\n      {3, 1, 2, 1, 1, 3},\n      {3, 1, 2, 3, 1, 1},\n      {3, 3, 2, 1, 1, 1},\n      {3, 1, 4, 1, 1, 1}, // 60\n      {2, 2, 1, 4, 1, 1},\n      {4, 3, 1, 1, 1, 1},\n      {1, 1, 1, 2, 2, 4},\n      {1, 1, 1, 4, 2, 2},\n      {1, 2, 1, 1, 2, 4}, // 65\n      {1, 2, 1, 4, 2, 1},\n      {1, 4, 1, 1, 2, 2},\n      {1, 4, 1, 2, 2, 1},\n      {1, 1, 2, 2, 1, 4},\n      {1, 1, 2, 4, 1, 2}, // 70\n      {1, 2, 2, 1, 1, 4},\n      {1, 2, 2, 4, 1, 1},\n      {1, 4, 2, 1, 1, 2},\n      {1, 4, 2, 2, 1, 1},\n      {2, 4, 1, 2, 1, 1}, // 75\n      {2, 2, 1, 1, 1, 4},\n      {4, 1, 3, 1, 1, 1},\n      {2, 4, 1, 1, 1, 2},\n      {1, 3, 4, 1, 1, 1},\n      {1, 1, 1, 2, 4, 2}, // 80\n      {1, 2, 1, 1, 4, 2},\n      {1, 2, 1, 2, 4, 1},\n      {1, 1, 4, 2, 1, 2},\n      {1, 2, 4, 1, 1, 2},\n      {1, 2, 4, 2, 1, 1}, // 85\n      {4, 1, 1, 2, 1, 2},\n      {4, 2, 1, 1, 1, 2},\n      {4, 2, 1, 2, 1, 1},\n      {2, 1, 2, 1, 4, 1},\n      {2, 1, 4, 1, 2, 1}, // 90\n      {4, 1, 2, 1, 2, 1},\n      {1, 1, 1, 1, 4, 3},\n      {1, 1, 1, 3, 4, 1},\n      {1, 3, 1, 1, 4, 1},\n      {1, 1, 4, 1, 1, 3}, // 95\n      {1, 1, 4, 3, 1, 1},\n      {4, 1, 1, 1, 1, 3},\n      {4, 1, 1, 3, 1, 1},\n      {1, 1, 3, 1, 4, 1},\n      {1, 1, 4, 1, 3, 1}, // 100\n      {3, 1, 1, 1, 4, 1},\n      {4, 1, 1, 1, 3, 1},\n      {2, 1, 1, 4, 1, 2},\n      {2, 1, 1, 2, 1, 4},\n      {2, 1, 1, 2, 3, 2}, // 105\n      {2, 3, 3, 1, 1, 1, 2}\n  };\n\n  private static final float MAX_AVG_VARIANCE = 0.25f;\n  private static final float MAX_INDIVIDUAL_VARIANCE = 0.7f;\n\n  private static final int CODE_SHIFT = 98;\n\n  private static final int CODE_CODE_C = 99;\n  private static final int CODE_CODE_B = 100;\n  private static final int CODE_CODE_A = 101;\n\n  private static final int CODE_FNC_1 = 102;\n  private static final int CODE_FNC_2 = 97;\n  private static final int CODE_FNC_3 = 96;\n  private static final int CODE_FNC_4_A = 101;\n  private static final int CODE_FNC_4_B = 100;\n\n  private static final int CODE_START_A = 103;\n  private static final int CODE_START_B = 104;\n  private static final int CODE_START_C = 105;\n  private static final int CODE_STOP = 106;\n\n  private static int[] findStartPattern(BitArray row) throws NotFoundException {\n    int width = row.getSize();\n    int rowOffset = row.getNextSet(0);\n\n    int counterPosition = 0;\n    int[] counters = new int[6];\n    int patternStart = rowOffset;\n    boolean isWhite = false;\n    int patternLength = counters.length;\n\n    for (int i = rowOffset; i < width; i++) {\n      if (row.get(i) != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (counterPosition == patternLength - 1) {\n          float bestVariance = MAX_AVG_VARIANCE;\n          int bestMatch = -1;\n          for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {\n            float variance = patternMatchVariance(counters, CODE_PATTERNS[startCode],\n                MAX_INDIVIDUAL_VARIANCE);\n            if (variance < bestVariance) {\n              bestVariance = variance;\n              bestMatch = startCode;\n            }\n          }\n          // Look for whitespace before start pattern, >= 50% of width of start pattern\n          if (bestMatch >= 0 &&\n              row.isRange(Math.max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {\n            return new int[]{patternStart, i, bestMatch};\n          }\n          patternStart += counters[0] + counters[1];\n          System.arraycopy(counters, 2, counters, 0, counterPosition - 1);\n          counters[counterPosition - 1] = 0;\n          counters[counterPosition] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        counters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private static int decodeCode(BitArray row, int[] counters, int rowOffset)\n      throws NotFoundException {\n    recordPattern(row, rowOffset, counters);\n    float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\n    int bestMatch = -1;\n    for (int d = 0; d < CODE_PATTERNS.length; d++) {\n      int[] pattern = CODE_PATTERNS[d];\n      float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\n      if (variance < bestVariance) {\n        bestVariance = variance;\n        bestMatch = d;\n      }\n    }\n    // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6.\n    if (bestMatch >= 0) {\n      return bestMatch;\n    } else {\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)\n      throws NotFoundException, FormatException, ChecksumException {\n\n    boolean convertFNC1 = hints != null && hints.containsKey(DecodeHintType.ASSUME_GS1);\n\n    int[] startPatternInfo = findStartPattern(row);\n    int startCode = startPatternInfo[2];\n\n    List<Byte> rawCodes = new ArrayList<>(20);\n    rawCodes.add((byte) startCode);\n\n    int codeSet;\n    switch (startCode) {\n      case CODE_START_A:\n        codeSet = CODE_CODE_A;\n        break;\n      case CODE_START_B:\n        codeSet = CODE_CODE_B;\n        break;\n      case CODE_START_C:\n        codeSet = CODE_CODE_C;\n        break;\n      default:\n        throw FormatException.getFormatInstance();\n    }\n\n    boolean done = false;\n    boolean isNextShifted = false;\n\n    StringBuilder result = new StringBuilder(20);\n\n    int lastStart = startPatternInfo[0];\n    int nextStart = startPatternInfo[1];\n    int[] counters = new int[6];\n\n    int lastCode = 0;\n    int code = 0;\n    int checksumTotal = startCode;\n    int multiplier = 0;\n    boolean lastCharacterWasPrintable = true;\n    boolean upperMode = false;\n    boolean shiftUpperMode = false;\n\n    while (!done) {\n\n      boolean unshift = isNextShifted;\n      isNextShifted = false;\n\n      // Save off last code\n      lastCode = code;\n\n      // Decode another code from image\n      code = decodeCode(row, counters, nextStart);\n\n      rawCodes.add((byte) code);\n\n      // Remember whether the last code was printable or not (excluding CODE_STOP)\n      if (code != CODE_STOP) {\n        lastCharacterWasPrintable = true;\n      }\n\n      // Add to checksum computation (if not CODE_STOP of course)\n      if (code != CODE_STOP) {\n        multiplier++;\n        checksumTotal += multiplier * code;\n      }\n\n      // Advance to where the next code will to start\n      lastStart = nextStart;\n      for (int counter : counters) {\n        nextStart += counter;\n      }\n\n      // Take care of illegal start codes\n      switch (code) {\n        case CODE_START_A:\n        case CODE_START_B:\n        case CODE_START_C:\n          throw FormatException.getFormatInstance();\n      }\n\n      switch (codeSet) {\n\n        case CODE_CODE_A:\n          if (code < 64) {\n            if (shiftUpperMode == upperMode) {\n              result.append((char) (' ' + code));\n            } else {\n              result.append((char) (' ' + code + 128));\n            }\n            shiftUpperMode = false;\n          } else if (code < 96) {\n            if (shiftUpperMode == upperMode) {\n              result.append((char) (code - 64));\n            } else {\n              result.append((char) (code + 64));\n            }\n            shiftUpperMode = false;\n          } else {\n            // Don't let CODE_STOP, which always appears, affect whether whether we think the last\n            // code was printable or not.\n            if (code != CODE_STOP) {\n              lastCharacterWasPrintable = false;\n            }\n            switch (code) {\n              case CODE_FNC_1:\n                if (convertFNC1) {\n                  if (result.length() == 0) {\n                    // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code\n                    // is FNC1 then this is GS1-128. We add the symbology identifier.\n                    result.append(\"]C1\");\n                  } else {\n                    // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)\n                    result.append((char) 29);\n                  }\n                }\n                break;\n              case CODE_FNC_2:\n              case CODE_FNC_3:\n                // do nothing?\n                break;\n              case CODE_FNC_4_A:\n                if (!upperMode && shiftUpperMode) {\n                  upperMode = true;\n                  shiftUpperMode = false;\n                } else if (upperMode && shiftUpperMode) {\n                  upperMode = false;\n                  shiftUpperMode = false;\n                } else {\n                  shiftUpperMode = true;\n                }\n                break;\n              case CODE_SHIFT:\n                isNextShifted = true;\n                codeSet = CODE_CODE_B;\n                break;\n              case CODE_CODE_B:\n                codeSet = CODE_CODE_B;\n                break;\n              case CODE_CODE_C:\n                codeSet = CODE_CODE_C;\n                break;\n              case CODE_STOP:\n                done = true;\n                break;\n            }\n          }\n          break;\n        case CODE_CODE_B:\n          if (code < 96) {\n            if (shiftUpperMode == upperMode) {\n              result.append((char) (' ' + code));\n            } else {\n              result.append((char) (' ' + code + 128));\n            }\n            shiftUpperMode = false;\n          } else {\n            if (code != CODE_STOP) {\n              lastCharacterWasPrintable = false;\n            }\n            switch (code) {\n              case CODE_FNC_1:\n                if (convertFNC1) {\n                  if (result.length() == 0) {\n                    // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code\n                    // is FNC1 then this is GS1-128. We add the symbology identifier.\n                    result.append(\"]C1\");\n                  } else {\n                    // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)\n                    result.append((char) 29);\n                  }\n                }\n                break;\n              case CODE_FNC_2:\n              case CODE_FNC_3:\n                // do nothing?\n                break;\n              case CODE_FNC_4_B:\n                if (!upperMode && shiftUpperMode) {\n                  upperMode = true;\n                  shiftUpperMode = false;\n                } else if (upperMode && shiftUpperMode) {\n                  upperMode = false;\n                  shiftUpperMode = false;\n                } else {\n                  shiftUpperMode = true;\n                }\n                break;\n              case CODE_SHIFT:\n                isNextShifted = true;\n                codeSet = CODE_CODE_A;\n                break;\n              case CODE_CODE_A:\n                codeSet = CODE_CODE_A;\n                break;\n              case CODE_CODE_C:\n                codeSet = CODE_CODE_C;\n                break;\n              case CODE_STOP:\n                done = true;\n                break;\n            }\n          }\n          break;\n        case CODE_CODE_C:\n          if (code < 100) {\n            if (code < 10) {\n              result.append('0');\n            }\n            result.append(code);\n          } else {\n            if (code != CODE_STOP) {\n              lastCharacterWasPrintable = false;\n            }\n            switch (code) {\n              case CODE_FNC_1:\n                if (convertFNC1) {\n                  if (result.length() == 0) {\n                    // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code\n                    // is FNC1 then this is GS1-128. We add the symbology identifier.\n                    result.append(\"]C1\");\n                  } else {\n                    // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS)\n                    result.append((char) 29);\n                  }\n                }\n                break;\n              case CODE_CODE_A:\n                codeSet = CODE_CODE_A;\n                break;\n              case CODE_CODE_B:\n                codeSet = CODE_CODE_B;\n                break;\n              case CODE_STOP:\n                done = true;\n                break;\n            }\n          }\n          break;\n      }\n\n      // Unshift back to another code set if we were shifted\n      if (unshift) {\n        codeSet = codeSet == CODE_CODE_A ? CODE_CODE_B : CODE_CODE_A;\n      }\n\n    }\n\n    int lastPatternSize = nextStart - lastStart;\n\n    // Check for ample whitespace following pattern, but, to do this we first need to remember that\n    // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left\n    // to read off. Would be slightly better to properly read. Here we just skip it:\n    nextStart = row.getNextUnset(nextStart);\n    if (!row.isRange(nextStart,\n                     Math.min(row.getSize(), nextStart + (nextStart - lastStart) / 2),\n                     false)) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    // Pull out from sum the value of the penultimate check code\n    checksumTotal -= multiplier * lastCode;\n    // lastCode is the checksum then:\n    if (checksumTotal % 103 != lastCode) {\n      throw ChecksumException.getChecksumInstance();\n    }\n\n    // Need to pull out the check digits from string\n    int resultLength = result.length();\n    if (resultLength == 0) {\n      // false positive\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    // Only bother if the result had at least one character, and if the checksum digit happened to\n    // be a printable character. If it was just interpreted as a control code, nothing to remove.\n    if (resultLength > 0 && lastCharacterWasPrintable) {\n      if (codeSet == CODE_CODE_C) {\n        result.delete(resultLength - 2, resultLength);\n      } else {\n        result.delete(resultLength - 1, resultLength);\n      }\n    }\n\n    float left = (startPatternInfo[1] + startPatternInfo[0]) / 2.0f;\n    float right = lastStart + lastPatternSize / 2.0f;\n\n    int rawCodesSize = rawCodes.size();\n    byte[] rawBytes = new byte[rawCodesSize];\n    for (int i = 0; i < rawCodesSize; i++) {\n      rawBytes[i] = rawCodes.get(i);\n    }\n\n    return new Result(\n        result.toString(),\n        rawBytes,\n        new ResultPoint[]{\n            new ResultPoint(left, rowNumber),\n            new ResultPoint(right, rowNumber)},\n        BarcodeFormat.CODE_128);\n\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/Code128Writer.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * This object renders a CODE128 code as a {@link BitMatrix}.\n *\n * @author erik.barbara@gmail.com (Erik Barbara)\n */\npublic final class Code128Writer extends OneDimensionalCodeWriter {\n\n  private static final int CODE_START_A = 103;\n  private static final int CODE_START_B = 104;\n  private static final int CODE_START_C = 105;\n  private static final int CODE_CODE_A = 101;\n  private static final int CODE_CODE_B = 100;\n  private static final int CODE_CODE_C = 99;\n  private static final int CODE_STOP = 106;\n\n  // Dummy characters used to specify control characters in input\n  private static final char ESCAPE_FNC_1 = '\\u00f1';\n  private static final char ESCAPE_FNC_2 = '\\u00f2';\n  private static final char ESCAPE_FNC_3 = '\\u00f3';\n  private static final char ESCAPE_FNC_4 = '\\u00f4';\n\n  private static final int CODE_FNC_1 = 102;   // Code A, Code B, Code C\n  private static final int CODE_FNC_2 = 97;    // Code A, Code B\n  private static final int CODE_FNC_3 = 96;    // Code A, Code B\n  private static final int CODE_FNC_4_A = 101; // Code A\n  private static final int CODE_FNC_4_B = 100; // Code B\n\n  // Results of minimal lookahead for code C\n  private enum CType {\n    UNCODABLE,\n    ONE_DIGIT,\n    TWO_DIGITS,\n    FNC_1\n  }\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.CODE_128) {\n      throw new IllegalArgumentException(\"Can only encode CODE_128, but got \" + format);\n    }\n    return super.encode(contents, format, width, height, hints);\n  }\n\n  @Override\n  public boolean[] encode(String contents) {\n    int length = contents.length();\n    // Check length\n    if (length < 1 || length > 80) {\n      throw new IllegalArgumentException(\n          \"Contents length should be between 1 and 80 characters, but got \" + length);\n    }\n    // Check content\n    for (int i = 0; i < length; i++) {\n      char c = contents.charAt(i);\n      switch (c) {\n        case ESCAPE_FNC_1:\n        case ESCAPE_FNC_2:\n        case ESCAPE_FNC_3:\n        case ESCAPE_FNC_4:\n          break;\n        default:\n          if (c > 127) {\n            // support for FNC4 isn't implemented, no full Latin-1 character set available at the moment\n            throw new IllegalArgumentException(\"Bad character in input: \" + c);\n          }\n      }\n    }\n\n    Collection<int[]> patterns = new ArrayList<>(); // temporary storage for patterns\n    int checkSum = 0;\n    int checkWeight = 1;\n    int codeSet = 0; // selected code (CODE_CODE_B or CODE_CODE_C)\n    int position = 0; // position in contents\n\n    while (position < length) {\n      //Select code to use\n      int newCodeSet = chooseCode(contents, position, codeSet);\n\n      //Get the pattern index\n      int patternIndex;\n      if (newCodeSet == codeSet) {\n        // Encode the current character\n        // First handle escapes\n        switch (contents.charAt(position)) {\n          case ESCAPE_FNC_1:\n            patternIndex = CODE_FNC_1;\n            break;\n          case ESCAPE_FNC_2:\n            patternIndex = CODE_FNC_2;\n            break;\n          case ESCAPE_FNC_3:\n            patternIndex = CODE_FNC_3;\n            break;\n          case ESCAPE_FNC_4:\n            if (codeSet == CODE_CODE_A) {\n              patternIndex = CODE_FNC_4_A;\n            } else {\n              patternIndex = CODE_FNC_4_B;\n            }\n            break;\n          default:\n            // Then handle normal characters otherwise\n            switch (codeSet) {\n              case CODE_CODE_A:\n                patternIndex = contents.charAt(position) - ' ';\n                if (patternIndex < 0) {\n                  // everything below a space character comes behind the underscore in the code patterns table\n                  patternIndex += '`';\n                }\n                break;\n              case CODE_CODE_B:\n                patternIndex = contents.charAt(position) - ' ';\n                break;\n              default:\n                // CODE_CODE_C\n                patternIndex = Integer.parseInt(contents.substring(position, position + 2));\n                position++; // Also incremented below\n                break;\n            }\n        }\n        position++;\n      } else {\n        // Should we change the current code?\n        // Do we have a code set?\n        if (codeSet == 0) {\n          // No, we don't have a code set\n          switch (newCodeSet) {\n            case CODE_CODE_A:\n              patternIndex = CODE_START_A;\n              break;\n            case CODE_CODE_B:\n              patternIndex = CODE_START_B;\n              break;\n            default:\n              patternIndex = CODE_START_C;\n              break;\n          }\n        } else {\n          // Yes, we have a code set\n          patternIndex = newCodeSet;\n        }\n        codeSet = newCodeSet;\n      }\n\n      // Get the pattern\n      patterns.add(Code128Reader.CODE_PATTERNS[patternIndex]);\n\n      // Compute checksum\n      checkSum += patternIndex * checkWeight;\n      if (position != 0) {\n        checkWeight++;\n      }\n    }\n\n    // Compute and append checksum\n    checkSum %= 103;\n    patterns.add(Code128Reader.CODE_PATTERNS[checkSum]);\n\n    // Append stop code\n    patterns.add(Code128Reader.CODE_PATTERNS[CODE_STOP]);\n\n    // Compute code width\n    int codeWidth = 0;\n    for (int[] pattern : patterns) {\n      for (int width : pattern) {\n        codeWidth += width;\n      }\n    }\n\n    // Compute result\n    boolean[] result = new boolean[codeWidth];\n    int pos = 0;\n    for (int[] pattern : patterns) {\n      pos += appendPattern(result, pos, pattern, true);\n    }\n\n    return result;\n  }\n\n  private static CType findCType(CharSequence value, int start) {\n    int last = value.length();\n    if (start >= last) {\n      return CType.UNCODABLE;\n    }\n    char c = value.charAt(start);\n    if (c == ESCAPE_FNC_1) {\n      return CType.FNC_1;\n    }\n    if (c < '0' || c > '9') {\n      return CType.UNCODABLE;\n    }\n    if (start + 1 >= last) {\n      return CType.ONE_DIGIT;\n    }\n    c = value.charAt(start + 1);\n    if (c < '0' || c > '9') {\n      return CType.ONE_DIGIT;\n    }\n    return CType.TWO_DIGITS;\n  }\n\n  private static int chooseCode(CharSequence value, int start, int oldCode) {\n    CType lookahead = findCType(value, start);\n    if (lookahead == CType.ONE_DIGIT) {\n       if (oldCode == CODE_CODE_A) {\n         return CODE_CODE_A;\n       }\n       return CODE_CODE_B;\n    }\n    if (lookahead == CType.UNCODABLE) {\n      if (start < value.length()) {\n        char c = value.charAt(start);\n        if (c < ' ' || (oldCode == CODE_CODE_A && (c < '`' || (c >= ESCAPE_FNC_1 && c <= ESCAPE_FNC_4)))) {\n          // can continue in code A, encodes ASCII 0 to 95 or FNC1 to FNC4\n          return CODE_CODE_A;\n        }\n      }\n      return CODE_CODE_B; // no choice\n    }\n    if (oldCode == CODE_CODE_A && lookahead == CType.FNC_1) {\n      return CODE_CODE_A;\n    }\n    if (oldCode == CODE_CODE_C) { // can continue in code C\n      return CODE_CODE_C;\n    }\n    if (oldCode == CODE_CODE_B) {\n      if (lookahead == CType.FNC_1) {\n        return CODE_CODE_B; // can continue in code B\n      }\n      // Seen two consecutive digits, see what follows\n      lookahead = findCType(value, start + 2);\n      if (lookahead == CType.UNCODABLE || lookahead == CType.ONE_DIGIT) {\n        return CODE_CODE_B; // not worth switching now\n      }\n      if (lookahead == CType.FNC_1) { // two digits, then FNC_1...\n        lookahead = findCType(value, start + 3);\n        if (lookahead == CType.TWO_DIGITS) { // then two more digits, switch\n          return CODE_CODE_C;\n        } else {\n          return CODE_CODE_B; // otherwise not worth switching\n        }\n      }\n      // At this point, there are at least 4 consecutive digits.\n      // Look ahead to choose whether to switch now or on the next round.\n      int index = start + 4;\n      while ((lookahead = findCType(value, index)) == CType.TWO_DIGITS) {\n        index += 2;\n      }\n      if (lookahead == CType.ONE_DIGIT) { // odd number of digits, switch later\n        return CODE_CODE_B;\n      }\n      return CODE_CODE_C; // even number of digits, switch now\n    }\n    // Here oldCode == 0, which means we are choosing the initial code\n    if (lookahead == CType.FNC_1) { // ignore FNC_1\n      lookahead = findCType(value, start + 1);\n    }\n    if (lookahead == CType.TWO_DIGITS) { // at least two digits, start in code C\n      return CODE_CODE_C;\n    }\n    return CODE_CODE_B;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/Code39Reader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Arrays;\nimport java.util.Map;\n\n/**\n * <p>Decodes Code 39 barcodes. Supports \"Full ASCII Code 39\" if USE_CODE_39_EXTENDED_MODE is set.</p>\n *\n * @author Sean Owen\n * @see Code93Reader\n */\npublic final class Code39Reader extends OneDReader {\n\n  static final String ALPHABET_STRING = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%\";\n\n  /**\n   * These represent the encodings of characters, as patterns of wide and narrow bars.\n   * The 9 least-significant bits of each int correspond to the pattern of wide and narrow,\n   * with 1s representing \"wide\" and 0s representing narrow.\n   */\n  static final int[] CHARACTER_ENCODINGS = {\n      0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9\n      0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J\n      0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T\n      0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x0A8, // U-$\n      0x0A2, 0x08A, 0x02A // /-%\n  };\n\n  static final int ASTERISK_ENCODING = 0x094;\n\n  private final boolean usingCheckDigit;\n  private final boolean extendedMode;\n  private final StringBuilder decodeRowResult;\n  private final int[] counters;\n\n  /**\n   * Creates a reader that assumes all encoded data is data, and does not treat the final\n   * character as a check digit. It will not decoded \"extended Code 39\" sequences.\n   */\n  public Code39Reader() {\n    this(false);\n  }\n\n  /**\n   * Creates a reader that can be configured to check the last character as a check digit.\n   * It will not decoded \"extended Code 39\" sequences.\n   *\n   * @param usingCheckDigit if true, treat the last data character as a check digit, not\n   * data, and verify that the checksum passes.\n   */\n  public Code39Reader(boolean usingCheckDigit) {\n    this(usingCheckDigit, false);\n  }\n\n  /**\n   * Creates a reader that can be configured to check the last character as a check digit,\n   * or optionally attempt to decode \"extended Code 39\" sequences that are used to encode\n   * the full ASCII character set.\n   *\n   * @param usingCheckDigit if true, treat the last data character as a check digit, not\n   * data, and verify that the checksum passes.\n   * @param extendedMode if true, will attempt to decode extended Code 39 sequences in the\n   * text.\n   */\n  public Code39Reader(boolean usingCheckDigit, boolean extendedMode) {\n    this.usingCheckDigit = usingCheckDigit;\n    this.extendedMode = extendedMode;\n    decodeRowResult = new StringBuilder(20);\n    counters = new int[9];\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException {\n\n    int[] theCounters = counters;\n    Arrays.fill(theCounters, 0);\n    StringBuilder result = decodeRowResult;\n    result.setLength(0);\n\n    int[] start = findAsteriskPattern(row, theCounters);\n    // Read off white space\n    int nextStart = row.getNextSet(start[1]);\n    int end = row.getSize();\n\n    char decodedChar;\n    int lastStart;\n    do {\n      recordPattern(row, nextStart, theCounters);\n      int pattern = toNarrowWidePattern(theCounters);\n      if (pattern < 0) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      decodedChar = patternToChar(pattern);\n      result.append(decodedChar);\n      lastStart = nextStart;\n      for (int counter : theCounters) {\n        nextStart += counter;\n      }\n      // Read off white space\n      nextStart = row.getNextSet(nextStart);\n    } while (decodedChar != '*');\n    result.setLength(result.length() - 1); // remove asterisk\n\n    // Look for whitespace after pattern:\n    int lastPatternSize = 0;\n    for (int counter : theCounters) {\n      lastPatternSize += counter;\n    }\n    int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;\n    // If 50% of last pattern size, following last pattern, is not whitespace, fail\n    // (but if it's whitespace to the very end of the image, that's OK)\n    if (nextStart != end && (whiteSpaceAfterEnd * 2) < lastPatternSize) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    if (usingCheckDigit) {\n      int max = result.length() - 1;\n      int total = 0;\n      for (int i = 0; i < max; i++) {\n        total += ALPHABET_STRING.indexOf(decodeRowResult.charAt(i));\n      }\n      if (result.charAt(max) != ALPHABET_STRING.charAt(total % 43)) {\n        throw ChecksumException.getChecksumInstance();\n      }\n      result.setLength(max);\n    }\n\n    if (result.length() == 0) {\n      // false positive\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    String resultString;\n    if (extendedMode) {\n      resultString = decodeExtended(result);\n    } else {\n      resultString = result.toString();\n    }\n\n    float left = (start[1] + start[0]) / 2.0f;\n    float right = lastStart + lastPatternSize / 2.0f;\n    return new Result(\n        resultString,\n        null,\n        new ResultPoint[]{\n            new ResultPoint(left, rowNumber),\n            new ResultPoint(right, rowNumber)},\n        BarcodeFormat.CODE_39);\n\n  }\n\n  private static int[] findAsteriskPattern(BitArray row, int[] counters) throws NotFoundException {\n    int width = row.getSize();\n    int rowOffset = row.getNextSet(0);\n\n    int counterPosition = 0;\n    int patternStart = rowOffset;\n    boolean isWhite = false;\n    int patternLength = counters.length;\n\n    for (int i = rowOffset; i < width; i++) {\n      if (row.get(i) != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (counterPosition == patternLength - 1) {\n          // Look for whitespace before start pattern, >= 50% of width of start pattern\n          if (toNarrowWidePattern(counters) == ASTERISK_ENCODING &&\n              row.isRange(Math.max(0, patternStart - ((i - patternStart) / 2)), patternStart, false)) {\n            return new int[]{patternStart, i};\n          }\n          patternStart += counters[0] + counters[1];\n          System.arraycopy(counters, 2, counters, 0, counterPosition - 1);\n          counters[counterPosition - 1] = 0;\n          counters[counterPosition] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        counters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  // For efficiency, returns -1 on failure. Not throwing here saved as many as 700 exceptions\n  // per image when using some of our blackbox images.\n  private static int toNarrowWidePattern(int[] counters) {\n    int numCounters = counters.length;\n    int maxNarrowCounter = 0;\n    int wideCounters;\n    do {\n      int minCounter = Integer.MAX_VALUE;\n      for (int counter : counters) {\n        if (counter < minCounter && counter > maxNarrowCounter) {\n          minCounter = counter;\n        }\n      }\n      maxNarrowCounter = minCounter;\n      wideCounters = 0;\n      int totalWideCountersWidth = 0;\n      int pattern = 0;\n      for (int i = 0; i < numCounters; i++) {\n        int counter = counters[i];\n        if (counter > maxNarrowCounter) {\n          pattern |= 1 << (numCounters - 1 - i);\n          wideCounters++;\n          totalWideCountersWidth += counter;\n        }\n      }\n      if (wideCounters == 3) {\n        // Found 3 wide counters, but are they close enough in width?\n        // We can perform a cheap, conservative check to see if any individual\n        // counter is more than 1.5 times the average:\n        for (int i = 0; i < numCounters && wideCounters > 0; i++) {\n          int counter = counters[i];\n          if (counter > maxNarrowCounter) {\n            wideCounters--;\n            // totalWideCountersWidth = 3 * average, so this checks if counter >= 3/2 * average\n            if ((counter * 2) >= totalWideCountersWidth) {\n              return -1;\n            }\n          }\n        }\n        return pattern;\n      }\n    } while (wideCounters > 3);\n    return -1;\n  }\n\n  private static char patternToChar(int pattern) throws NotFoundException {\n    for (int i = 0; i < CHARACTER_ENCODINGS.length; i++) {\n      if (CHARACTER_ENCODINGS[i] == pattern) {\n        return ALPHABET_STRING.charAt(i);\n      }\n    }\n    if (pattern == ASTERISK_ENCODING) {\n      return '*';\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private static String decodeExtended(CharSequence encoded) throws FormatException {\n    int length = encoded.length();\n    StringBuilder decoded = new StringBuilder(length);\n    for (int i = 0; i < length; i++) {\n      char c = encoded.charAt(i);\n      if (c == '+' || c == '$' || c == '%' || c == '/') {\n        char next = encoded.charAt(i + 1);\n        char decodedChar = '\\0';\n        switch (c) {\n          case '+':\n            // +A to +Z map to a to z\n            if (next >= 'A' && next <= 'Z') {\n              decodedChar = (char) (next + 32);\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case '$':\n            // $A to $Z map to control codes SH to SB\n            if (next >= 'A' && next <= 'Z') {\n              decodedChar = (char) (next - 64);\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case '%':\n            // %A to %E map to control codes ESC to US\n            if (next >= 'A' && next <= 'E') {\n              decodedChar = (char) (next - 38);\n            } else if (next >= 'F' && next <= 'J') {\n              decodedChar = (char) (next - 11);\n            } else if (next >= 'K' && next <= 'O') {\n              decodedChar = (char) (next + 16);\n            } else if (next >= 'P' && next <= 'T') {\n              decodedChar = (char) (next + 43);\n            } else if (next == 'U') {\n              decodedChar = (char) 0;\n            } else if (next == 'V') {\n              decodedChar = '@';\n            } else if (next == 'W') {\n              decodedChar = '`';\n            } else if (next == 'X' || next == 'Y' || next == 'Z') {\n              decodedChar = (char) 127;\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case '/':\n            // /A to /O map to ! to , and /Z maps to :\n            if (next >= 'A' && next <= 'O') {\n              decodedChar = (char) (next - 32);\n            } else if (next == 'Z') {\n              decodedChar = ':';\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n        }\n        decoded.append(decodedChar);\n        // bump up i again since we read two characters\n        i++;\n      } else {\n        decoded.append(c);\n      }\n    }\n    return decoded.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/Code39Writer.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\n\n/**\n * This object renders a CODE39 code as a {@link BitMatrix}.\n *\n * @author erik.barbara@gmail.com (Erik Barbara)\n */\npublic final class Code39Writer extends OneDimensionalCodeWriter {\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.CODE_39) {\n      throw new IllegalArgumentException(\"Can only encode CODE_39, but got \" + format);\n    }\n    return super.encode(contents, format, width, height, hints);\n  }\n\n  @Override\n  public boolean[] encode(String contents) {\n    int length = contents.length();\n    if (length > 80) {\n      throw new IllegalArgumentException(\n          \"Requested contents should be less than 80 digits long, but got \" + length);\n    }\n\n    for (int i = 0; i < length; i++) {\n      int indexInString = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));\n      if (indexInString < 0) {\n        contents = tryToConvertToExtendedMode(contents);\n        length = contents.length();\n        if (length > 80) {\n          throw new IllegalArgumentException(\n              \"Requested contents should be less than 80 digits long, but got \" + length + \" (extended full ASCII mode)\");\n        }\n        break;\n      }\n    }\n\n    int[] widths = new int[9];\n    int codeWidth = 24 + 1 + (13 * length);\n    boolean[] result = new boolean[codeWidth];\n    toIntArray(Code39Reader.ASTERISK_ENCODING, widths);\n    int pos = appendPattern(result, 0, widths, true);\n    int[] narrowWhite = {1};\n    pos += appendPattern(result, pos, narrowWhite, false);\n    //append next character to byte matrix\n    for (int i = 0; i < length; i++) {\n      int indexInString = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));\n      toIntArray(Code39Reader.CHARACTER_ENCODINGS[indexInString], widths);\n      pos += appendPattern(result, pos, widths, true);\n      pos += appendPattern(result, pos, narrowWhite, false);\n    }\n    toIntArray(Code39Reader.ASTERISK_ENCODING, widths);\n    appendPattern(result, pos, widths, true);\n    return result;\n  }\n\n  private static void toIntArray(int a, int[] toReturn) {\n    for (int i = 0; i < 9; i++) {\n      int temp = a & (1 << (8 - i));\n      toReturn[i] = temp == 0 ? 1 : 2;\n    }\n  }\n\n  private static String tryToConvertToExtendedMode(String contents) {\n     int length = contents.length();\n     StringBuilder extendedContent = new StringBuilder();\n     for (int i = 0; i < length; i++) {\n       char character = contents.charAt(i);\n       switch (character) {\n         case '\\u0000':\n           extendedContent.append(\"%U\");\n           break;\n         case ' ':\n         case '-':\n         case '.':\n           extendedContent.append(character);\n           break;\n         case '@':\n           extendedContent.append(\"%V\");\n           break;\n         case '`':\n           extendedContent.append(\"%W\");\n           break;\n         default:\n           if (character <= 26) {\n             extendedContent.append('$');\n             extendedContent.append((char) ('A' + (character - 1)));\n           } else if (character < ' ') {\n             extendedContent.append('%');\n             extendedContent.append((char) ('A' + (character - 27)));\n           } else if (character <= ',' || character == '/' || character == ':') {\n             extendedContent.append('/');\n             extendedContent.append((char) ('A' + (character - 33)));\n           } else if (character <= '9') {\n             extendedContent.append((char) ('0' + (character - 48)));\n           } else if (character <= '?') {\n             extendedContent.append('%');\n             extendedContent.append((char) ('F' + (character - 59)));\n           } else if (character <= 'Z') {\n             extendedContent.append((char) ('A' + (character - 65)));\n           } else if (character <= '_') {\n             extendedContent.append('%');\n             extendedContent.append((char) ('K' + (character - 91)));\n           } else if (character <= 'z') {\n             extendedContent.append('+');\n             extendedContent.append((char) ('A' + (character - 97)));\n           } else if (character <= 127) {\n             extendedContent.append('%');\n             extendedContent.append((char) ('P' + (character - 123)));\n           } else {\n             throw new IllegalArgumentException(\"Requested content contains a non-encodable character: '\" + contents.charAt(i) + \"'\");\n           }\n           break;\n       }\n    }\n\n    return extendedContent.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/Code93Reader.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Arrays;\nimport java.util.Map;\n\n/**\n * <p>Decodes Code 93 barcodes.</p>\n *\n * @author Sean Owen\n * @see Code39Reader\n */\npublic final class Code93Reader extends OneDReader {\n\n  // Note that 'abcd' are dummy characters in place of control characters.\n  static final String ALPHABET_STRING = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*\";\n  private static final char[] ALPHABET = ALPHABET_STRING.toCharArray();\n\n  /**\n   * These represent the encodings of characters, as patterns of wide and narrow bars.\n   * The 9 least-significant bits of each int correspond to the pattern of wide and narrow.\n   */\n  static final int[] CHARACTER_ENCODINGS = {\n      0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A, // 0-9\n      0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134, // A-J\n      0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6, // K-T\n      0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, // U-Z\n      0x12E, 0x1D4, 0x1D2, 0x1CA, 0x16E, 0x176, 0x1AE, // - - %\n      0x126, 0x1DA, 0x1D6, 0x132, 0x15E, // Control chars? $-*\n  };\n  static final int ASTERISK_ENCODING = CHARACTER_ENCODINGS[47];\n\n  private final StringBuilder decodeRowResult;\n  private final int[] counters;\n\n  public Code93Reader() {\n    decodeRowResult = new StringBuilder(20);\n    counters = new int[6];\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException {\n\n    int[] start = findAsteriskPattern(row);\n    // Read off white space\n    int nextStart = row.getNextSet(start[1]);\n    int end = row.getSize();\n\n    int[] theCounters = counters;\n    Arrays.fill(theCounters, 0);\n    StringBuilder result = decodeRowResult;\n    result.setLength(0);\n\n    char decodedChar;\n    int lastStart;\n    do {\n      recordPattern(row, nextStart, theCounters);\n      int pattern = toPattern(theCounters);\n      if (pattern < 0) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      decodedChar = patternToChar(pattern);\n      result.append(decodedChar);\n      lastStart = nextStart;\n      for (int counter : theCounters) {\n        nextStart += counter;\n      }\n      // Read off white space\n      nextStart = row.getNextSet(nextStart);\n    } while (decodedChar != '*');\n    result.deleteCharAt(result.length() - 1); // remove asterisk\n\n    int lastPatternSize = 0;\n    for (int counter : theCounters) {\n      lastPatternSize += counter;\n    }\n\n    // Should be at least one more black module\n    if (nextStart == end || !row.get(nextStart)) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    if (result.length() < 2) {\n      // false positive -- need at least 2 checksum digits\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    checkChecksums(result);\n    // Remove checksum digits\n    result.setLength(result.length() - 2);\n\n    String resultString = decodeExtended(result);\n\n    float left = (start[1] + start[0]) / 2.0f;\n    float right = lastStart + lastPatternSize / 2.0f;\n    return new Result(\n        resultString,\n        null,\n        new ResultPoint[]{\n            new ResultPoint(left, rowNumber),\n            new ResultPoint(right, rowNumber)},\n        BarcodeFormat.CODE_93);\n\n  }\n\n  private int[] findAsteriskPattern(BitArray row) throws NotFoundException {\n    int width = row.getSize();\n    int rowOffset = row.getNextSet(0);\n\n    Arrays.fill(counters, 0);\n    int[] theCounters = counters;\n    int patternStart = rowOffset;\n    boolean isWhite = false;\n    int patternLength = theCounters.length;\n\n    int counterPosition = 0;\n    for (int i = rowOffset; i < width; i++) {\n      if (row.get(i) != isWhite) {\n        theCounters[counterPosition]++;\n      } else {\n        if (counterPosition == patternLength - 1) {\n          if (toPattern(theCounters) == ASTERISK_ENCODING) {\n            return new int[]{patternStart, i};\n          }\n          patternStart += theCounters[0] + theCounters[1];\n          System.arraycopy(theCounters, 2, theCounters, 0, counterPosition - 1);\n          theCounters[counterPosition - 1] = 0;\n          theCounters[counterPosition] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        theCounters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private static int toPattern(int[] counters) {\n    int sum = 0;\n    for (int counter : counters) {\n      sum += counter;\n    }\n    int pattern = 0;\n    int max = counters.length;\n    for (int i = 0; i < max; i++) {\n      int scaled = Math.round(counters[i] * 9.0f / sum);\n      if (scaled < 1 || scaled > 4) {\n        return -1;\n      }\n      if ((i & 0x01) == 0) {\n        for (int j = 0; j < scaled; j++) {\n          pattern = (pattern << 1) | 0x01;\n        }\n      } else {\n        pattern <<= scaled;\n      }\n    }\n    return pattern;\n  }\n\n  private static char patternToChar(int pattern) throws NotFoundException {\n    for (int i = 0; i < CHARACTER_ENCODINGS.length; i++) {\n      if (CHARACTER_ENCODINGS[i] == pattern) {\n        return ALPHABET[i];\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private static String decodeExtended(CharSequence encoded) throws FormatException {\n    int length = encoded.length();\n    StringBuilder decoded = new StringBuilder(length);\n    for (int i = 0; i < length; i++) {\n      char c = encoded.charAt(i);\n      if (c >= 'a' && c <= 'd') {\n        if (i >= length - 1) {\n          throw FormatException.getFormatInstance();\n        }\n        char next = encoded.charAt(i + 1);\n        char decodedChar = '\\0';\n        switch (c) {\n          case 'd':\n            // +A to +Z map to a to z\n            if (next >= 'A' && next <= 'Z') {\n              decodedChar = (char) (next + 32);\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case 'a':\n            // $A to $Z map to control codes SH to SB\n            if (next >= 'A' && next <= 'Z') {\n              decodedChar = (char) (next - 64);\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case 'b':\n            if (next >= 'A' && next <= 'E') {\n              // %A to %E map to control codes ESC to USep\n              decodedChar = (char) (next - 38);\n            } else if (next >= 'F' && next <= 'J') {\n              // %F to %J map to ; < = > ?\n              decodedChar = (char) (next - 11);\n            } else if (next >= 'K' && next <= 'O') {\n              // %K to %O map to [ \\ ] ^ _\n              decodedChar = (char) (next + 16);\n            } else if (next >= 'P' && next <= 'T') {\n              // %P to %T map to { | } ~ DEL\n              decodedChar = (char) (next + 43);\n            } else if (next == 'U') {\n              // %U map to NUL\n              decodedChar = '\\0';\n            } else if (next == 'V') {\n              // %V map to @\n              decodedChar = '@';\n            } else if (next == 'W') {\n              // %W map to `\n              decodedChar = '`';\n            } else if (next >= 'X' && next <= 'Z') {\n              // %X to %Z all map to DEL (127)\n              decodedChar = 127;\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case 'c':\n            // /A to /O map to ! to , and /Z maps to :\n            if (next >= 'A' && next <= 'O') {\n              decodedChar = (char) (next - 32);\n            } else if (next == 'Z') {\n              decodedChar = ':';\n            } else {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n        }\n        decoded.append(decodedChar);\n        // bump up i again since we read two characters\n        i++;\n      } else {\n        decoded.append(c);\n      }\n    }\n    return decoded.toString();\n  }\n\n  private static void checkChecksums(CharSequence result) throws ChecksumException {\n    int length = result.length();\n    checkOneChecksum(result, length - 2, 20);\n    checkOneChecksum(result, length - 1, 15);\n  }\n\n  private static void checkOneChecksum(CharSequence result, int checkPosition, int weightMax)\n      throws ChecksumException {\n    int weight = 1;\n    int total = 0;\n    for (int i = checkPosition - 1; i >= 0; i--) {\n      total += weight * ALPHABET_STRING.indexOf(result.charAt(i));\n      if (++weight > weightMax) {\n        weight = 1;\n      }\n    }\n    if (result.charAt(checkPosition) != ALPHABET[total % 47]) {\n      throw ChecksumException.getChecksumInstance();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/Code93Writer.java",
    "content": "/*\n * Copyright 2015 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\n\n/**\n * This object renders a CODE93 code as a BitMatrix\n */\npublic class Code93Writer extends OneDimensionalCodeWriter {\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.CODE_93) {\n      throw new IllegalArgumentException(\"Can only encode CODE_93, but got \" + format);\n    }\n    return super.encode(contents, format, width, height, hints);\n  }\n\n  /**\n   * @param contents barcode contents to encode. It should not be encoded for extended characters.\n   * @return a {@code boolean[]} of horizontal pixels (false = white, true = black)\n   */\n  @Override\n  public boolean[] encode(String contents) {\n    contents = convertToExtended(contents);\n    int length = contents.length();\n    if (length > 80) {\n      throw new IllegalArgumentException(\n        \"Requested contents should be less than 80 digits long after converting to extended encoding, but got \" + length);\n    }\n\n    //length of code + 2 start/stop characters + 2 checksums, each of 9 bits, plus a termination bar\n    int codeWidth = (contents.length() + 2 + 2) * 9 + 1;\n\n    boolean[] result = new boolean[codeWidth];\n\n    //start character (*)\n    int pos = appendPattern(result, 0, Code93Reader.ASTERISK_ENCODING);\n\n    for (int i = 0; i < length; i++) {\n      int indexInString = Code93Reader.ALPHABET_STRING.indexOf(contents.charAt(i));\n      pos += appendPattern(result, pos, Code93Reader.CHARACTER_ENCODINGS[indexInString]);\n    }\n\n    //add two checksums\n    int check1 = computeChecksumIndex(contents, 20);\n    pos += appendPattern(result, pos, Code93Reader.CHARACTER_ENCODINGS[check1]);\n\n    //append the contents to reflect the first checksum added\n    contents += Code93Reader.ALPHABET_STRING.charAt(check1);\n\n    int check2 = computeChecksumIndex(contents, 15);\n    pos += appendPattern(result, pos, Code93Reader.CHARACTER_ENCODINGS[check2]);\n\n    //end character (*)\n    pos += appendPattern(result, pos, Code93Reader.ASTERISK_ENCODING);\n\n    //termination bar (single black bar)\n    result[pos] = true;\n\n    return result;\n  }\n\n  /**\n   * @param target output to append to\n   * @param pos start position\n   * @param pattern pattern to append\n   * @param startColor unused\n   * @return 9\n   * @deprecated without replacement; intended as an internal-only method\n   */\n  @Deprecated\n  protected static int appendPattern(boolean[] target, int pos, int[] pattern, boolean startColor) {\n    for (int bit : pattern) {\n      target[pos++] = bit != 0;\n    }\n    return 9;\n  }\n\n  private static int appendPattern(boolean[] target, int pos, int a) {\n    for (int i = 0; i < 9; i++) {\n      int temp = a & (1 << (8 - i));\n      target[pos + i] = temp != 0;\n    }\n    return 9;\n  }\n\n  private static int computeChecksumIndex(String contents, int maxWeight) {\n    int weight = 1;\n    int total = 0;\n\n    for (int i = contents.length() - 1; i >= 0; i--) {\n      int indexInString = Code93Reader.ALPHABET_STRING.indexOf(contents.charAt(i));\n      total += indexInString * weight;\n      if (++weight > maxWeight) {\n        weight = 1;\n      }\n    }\n    return total % 47;\n  }\n\n  static String convertToExtended(String contents) {\n    int length = contents.length();\n    StringBuilder extendedContent = new StringBuilder(length * 2);\n    for (int i = 0; i < length; i++) {\n      char character = contents.charAt(i);\n      // ($)=a, (%)=b, (/)=c, (+)=d. see Code93Reader.ALPHABET_STRING\n      if (character == 0) {\n        // NUL: (%)U\n        extendedContent.append(\"bU\");\n      } else if (character <= 26) {\n        // SOH - SUB: ($)A - ($)Z\n        extendedContent.append('a');\n        extendedContent.append((char) ('A' + character - 1));\n      } else if (character <= 31) {\n        // ESC - US: (%)A - (%)E\n        extendedContent.append('b');\n        extendedContent.append((char) ('A' + character - 27));\n      } else if (character == ' ' || character == '$' || character == '%' || character == '+') {\n        // space $ % +\n        extendedContent.append(character);\n      } else if (character <= ',') {\n        // ! \" # & ' ( ) * ,: (/)A - (/)L\n        extendedContent.append('c');\n        extendedContent.append((char) ('A' + character - '!'));\n      } else if (character <= '9') {\n        extendedContent.append(character);\n      } else if (character == ':') {\n        // :: (/)Z\n        extendedContent.append(\"cZ\");\n      } else if (character <= '?') {\n        // ; - ?: (%)F - (%)J\n        extendedContent.append('b');\n        extendedContent.append((char) ('F' + character - ';'));\n      } else if (character == '@') {\n        // @: (%)V\n        extendedContent.append(\"bV\");\n      } else if (character <= 'Z') {\n        // A - Z\n        extendedContent.append(character);\n      } else if (character <= '_') {\n        // [ - _: (%)K - (%)O\n        extendedContent.append('b');\n        extendedContent.append((char) ('K' + character - '['));\n      } else if (character == '`') {\n        // `: (%)W\n        extendedContent.append(\"bW\");\n      } else if (character <= 'z') {\n        // a - z: (*)A - (*)Z\n        extendedContent.append('d');\n        extendedContent.append((char) ('A' + character - 'a'));\n      } else if (character <= 127) {\n        // { - DEL: (%)P - (%)T\n        extendedContent.append('b');\n        extendedContent.append((char) ('P' + character - '{'));\n      } else {\n        throw new IllegalArgumentException(\n          \"Requested content contains a non-encodable character: '\" + character + \"'\");\n      }\n    }\n    return extendedContent.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/EAN13Reader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * <p>Implements decoding of the EAN-13 format.</p>\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Sean Owen\n * @author alasdair@google.com (Alasdair Mackintosh)\n */\npublic final class EAN13Reader extends UPCEANReader {\n\n  // For an EAN-13 barcode, the first digit is represented by the parities used\n  // to encode the next six digits, according to the table below. For example,\n  // if the barcode is 5 123456 789012 then the value of the first digit is\n  // signified by using odd for '1', even for '2', even for '3', odd for '4',\n  // odd for '5', and even for '6'. See http://en.wikipedia.org/wiki/EAN-13\n  //\n  //                Parity of next 6 digits\n  //    Digit   0     1     2     3     4     5\n  //       0    Odd   Odd   Odd   Odd   Odd   Odd\n  //       1    Odd   Odd   Even  Odd   Even  Even\n  //       2    Odd   Odd   Even  Even  Odd   Even\n  //       3    Odd   Odd   Even  Even  Even  Odd\n  //       4    Odd   Even  Odd   Odd   Even  Even\n  //       5    Odd   Even  Even  Odd   Odd   Even\n  //       6    Odd   Even  Even  Even  Odd   Odd\n  //       7    Odd   Even  Odd   Even  Odd   Even\n  //       8    Odd   Even  Odd   Even  Even  Odd\n  //       9    Odd   Even  Even  Odd   Even  Odd\n  //\n  // Note that the encoding for '0' uses the same parity as a UPC barcode. Hence\n  // a UPC barcode can be converted to an EAN-13 barcode by prepending a 0.\n  //\n  // The encoding is represented by the following array, which is a bit pattern\n  // using Odd = 0 and Even = 1. For example, 5 is represented by:\n  //\n  //              Odd Even Even Odd Odd Even\n  // in binary:\n  //                0    1    1   0   0    1   == 0x19\n  //\n  static final int[] FIRST_DIGIT_ENCODINGS = {\n      0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A\n  };\n\n  private final int[] decodeMiddleCounters;\n\n  public EAN13Reader() {\n    decodeMiddleCounters = new int[4];\n  }\n\n  @Override\n  protected int decodeMiddle(BitArray row,\n                             int[] startRange,\n                             StringBuilder resultString) throws NotFoundException {\n    int[] counters = decodeMiddleCounters;\n    counters[0] = 0;\n    counters[1] = 0;\n    counters[2] = 0;\n    counters[3] = 0;\n    int end = row.getSize();\n    int rowOffset = startRange[1];\n\n    int lgPatternFound = 0;\n\n    for (int x = 0; x < 6 && rowOffset < end; x++) {\n      int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS);\n      resultString.append((char) ('0' + bestMatch % 10));\n      for (int counter : counters) {\n        rowOffset += counter;\n      }\n      if (bestMatch >= 10) {\n        lgPatternFound |= 1 << (5 - x);\n      }\n    }\n\n    determineFirstDigit(resultString, lgPatternFound);\n\n    int[] middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN);\n    rowOffset = middleRange[1];\n\n    for (int x = 0; x < 6 && rowOffset < end; x++) {\n      int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS);\n      resultString.append((char) ('0' + bestMatch));\n      for (int counter : counters) {\n        rowOffset += counter;\n      }\n    }\n\n    return rowOffset;\n  }\n\n  @Override\n  BarcodeFormat getBarcodeFormat() {\n    return BarcodeFormat.EAN_13;\n  }\n\n  /**\n   * Based on pattern of odd-even ('L' and 'G') patterns used to encoded the explicitly-encoded\n   * digits in a barcode, determines the implicitly encoded first digit and adds it to the\n   * result string.\n   *\n   * @param resultString string to insert decoded first digit into\n   * @param lgPatternFound int whose bits indicates the pattern of odd/even L/G patterns used to\n   *  encode digits\n   * @throws NotFoundException if first digit cannot be determined\n   */\n  private static void determineFirstDigit(StringBuilder resultString, int lgPatternFound)\n      throws NotFoundException {\n    for (int d = 0; d < 10; d++) {\n      if (lgPatternFound == FIRST_DIGIT_ENCODINGS[d]) {\n        resultString.insert(0, (char) ('0' + d));\n        return;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/EAN13Writer.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\n\n/**\n * This object renders an EAN13 code as a {@link BitMatrix}.\n *\n * @author aripollak@gmail.com (Ari Pollak)\n */\npublic final class EAN13Writer extends UPCEANWriter {\n\n  private static final int CODE_WIDTH = 3 + // start guard\n      (7 * 6) + // left bars\n      5 + // middle guard\n      (7 * 6) + // right bars\n      3; // end guard\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.EAN_13) {\n      throw new IllegalArgumentException(\"Can only encode EAN_13, but got \" + format);\n    }\n\n    return super.encode(contents, format, width, height, hints);\n  }\n\n  @Override\n  public boolean[] encode(String contents) {\n    int length = contents.length();\n    switch (length) {\n      case 12:\n        // No check digit present, calculate it and add it\n        int check;\n        try {\n          check = UPCEANReader.getStandardUPCEANChecksum(contents);\n        } catch (FormatException fe) {\n          throw new IllegalArgumentException(fe);\n        }\n        contents += check;\n        break;\n      case 13:\n        try {\n          if (!UPCEANReader.checkStandardUPCEANChecksum(contents)) {\n            throw new IllegalArgumentException(\"Contents do not pass checksum\");\n          }\n        } catch (FormatException ignored) {\n          throw new IllegalArgumentException(\"Illegal contents\");\n        }\n        break;\n      default:\n        throw new IllegalArgumentException(\n            \"Requested contents should be 12 or 13 digits long, but got \" + length);\n    }\n\n    checkNumeric(contents);\n\n    int firstDigit = Character.digit(contents.charAt(0), 10);\n    int parities = EAN13Reader.FIRST_DIGIT_ENCODINGS[firstDigit];\n    boolean[] result = new boolean[CODE_WIDTH];\n    int pos = 0;\n\n    pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, true);\n\n    // See EAN13Reader for a description of how the first digit & left bars are encoded\n    for (int i = 1; i <= 6; i++) {\n      int digit = Character.digit(contents.charAt(i), 10);\n      if ((parities >> (6 - i) & 1) == 1) {\n        digit += 10;\n      }\n      pos += appendPattern(result, pos, UPCEANReader.L_AND_G_PATTERNS[digit], false);\n    }\n\n    pos += appendPattern(result, pos, UPCEANReader.MIDDLE_PATTERN, false);\n\n    for (int i = 7; i <= 12; i++) {\n      int digit = Character.digit(contents.charAt(i), 10);\n      pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit], true);\n    }\n    appendPattern(result, pos, UPCEANReader.START_END_PATTERN, true);\n\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/EAN8Reader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * <p>Implements decoding of the EAN-8 format.</p>\n *\n * @author Sean Owen\n */\npublic final class EAN8Reader extends UPCEANReader {\n\n  private final int[] decodeMiddleCounters;\n\n  public EAN8Reader() {\n    decodeMiddleCounters = new int[4];\n  }\n\n  @Override\n  protected int decodeMiddle(BitArray row,\n                             int[] startRange,\n                             StringBuilder result) throws NotFoundException {\n    int[] counters = decodeMiddleCounters;\n    counters[0] = 0;\n    counters[1] = 0;\n    counters[2] = 0;\n    counters[3] = 0;\n    int end = row.getSize();\n    int rowOffset = startRange[1];\n\n    for (int x = 0; x < 4 && rowOffset < end; x++) {\n      int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS);\n      result.append((char) ('0' + bestMatch));\n      for (int counter : counters) {\n        rowOffset += counter;\n      }\n    }\n\n    int[] middleRange = findGuardPattern(row, rowOffset, true, MIDDLE_PATTERN);\n    rowOffset = middleRange[1];\n\n    for (int x = 0; x < 4 && rowOffset < end; x++) {\n      int bestMatch = decodeDigit(row, counters, rowOffset, L_PATTERNS);\n      result.append((char) ('0' + bestMatch));\n      for (int counter : counters) {\n        rowOffset += counter;\n      }\n    }\n\n    return rowOffset;\n  }\n\n  @Override\n  BarcodeFormat getBarcodeFormat() {\n    return BarcodeFormat.EAN_8;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/EAN8Writer.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\n\n/**\n * This object renders an EAN8 code as a {@link BitMatrix}.\n *\n * @author aripollak@gmail.com (Ari Pollak)\n */\npublic final class EAN8Writer extends UPCEANWriter {\n\n  private static final int CODE_WIDTH = 3 + // start guard\n      (7 * 4) + // left bars\n      5 + // middle guard\n      (7 * 4) + // right bars\n      3; // end guard\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.EAN_8) {\n      throw new IllegalArgumentException(\"Can only encode EAN_8, but got \"\n          + format);\n    }\n\n    return super.encode(contents, format, width, height, hints);\n  }\n\n  /**\n   * @return a byte array of horizontal pixels (false = white, true = black)\n   */\n  @Override\n  public boolean[] encode(String contents) {\n    int length = contents.length();\n    switch (length) {\n      case 7:\n        // No check digit present, calculate it and add it\n        int check;\n        try {\n          check = UPCEANReader.getStandardUPCEANChecksum(contents);\n        } catch (FormatException fe) {\n          throw new IllegalArgumentException(fe);\n        }\n        contents += check;\n        break;\n      case 8:\n        try {\n          if (!UPCEANReader.checkStandardUPCEANChecksum(contents)) {\n            throw new IllegalArgumentException(\"Contents do not pass checksum\");\n          }\n        } catch (FormatException ignored) {\n          throw new IllegalArgumentException(\"Illegal contents\");\n        }\n        break;\n      default:\n        throw new IllegalArgumentException(\n            \"Requested contents should be 7 or 8 digits long, but got \" + length);\n    }\n\n    checkNumeric(contents);\n\n    boolean[] result = new boolean[CODE_WIDTH];\n    int pos = 0;\n\n    pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, true);\n\n    for (int i = 0; i <= 3; i++) {\n      int digit = Character.digit(contents.charAt(i), 10);\n      pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit], false);\n    }\n\n    pos += appendPattern(result, pos, UPCEANReader.MIDDLE_PATTERN, false);\n\n    for (int i = 4; i <= 7; i++) {\n      int digit = Character.digit(contents.charAt(i), 10);\n      pos += appendPattern(result, pos, UPCEANReader.L_PATTERNS[digit], true);\n    }\n    appendPattern(result, pos, UPCEANReader.START_END_PATTERN, true);\n\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/EANManufacturerOrgSupport.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Records EAN prefix to GS1 Member Organization, where the member organization\n * correlates strongly with a country. This is an imperfect means of identifying\n * a country of origin by EAN-13 barcode value. See\n * <a href=\"http://en.wikipedia.org/wiki/List_of_GS1_country_codes\">\n * http://en.wikipedia.org/wiki/List_of_GS1_country_codes</a>.\n *\n * @author Sean Owen\n */\nfinal class EANManufacturerOrgSupport {\n\n  private final List<int[]> ranges = new ArrayList<>();\n  private final List<String> countryIdentifiers = new ArrayList<>();\n\n  String lookupCountryIdentifier(String productCode) {\n    initIfNeeded();\n    int prefix = Integer.parseInt(productCode.substring(0, 3));\n    int max = ranges.size();\n    for (int i = 0; i < max; i++) {\n      int[] range = ranges.get(i);\n      int start = range[0];\n      if (prefix < start) {\n        return null;\n      }\n      int end = range.length == 1 ? start : range[1];\n      if (prefix <= end) {\n        return countryIdentifiers.get(i);\n      }\n    }\n    return null;\n  }\n\n  private void add(int[] range, String id) {\n    ranges.add(range);\n    countryIdentifiers.add(id);\n  }\n\n  private synchronized void initIfNeeded() {\n    if (!ranges.isEmpty()) {\n      return;\n    }\n    add(new int[] {0,19},    \"US/CA\");\n    add(new int[] {30,39},   \"US\");\n    add(new int[] {60,139},  \"US/CA\");\n    add(new int[] {300,379}, \"FR\");\n    add(new int[] {380},     \"BG\");\n    add(new int[] {383},     \"SI\");\n    add(new int[] {385},     \"HR\");\n    add(new int[] {387},     \"BA\");\n    add(new int[] {400,440}, \"DE\");\n    add(new int[] {450,459}, \"JP\");\n    add(new int[] {460,469}, \"RU\");\n    add(new int[] {471},     \"TW\");\n    add(new int[] {474},     \"EE\");\n    add(new int[] {475},     \"LV\");\n    add(new int[] {476},     \"AZ\");\n    add(new int[] {477},     \"LT\");\n    add(new int[] {478},     \"UZ\");\n    add(new int[] {479},     \"LK\");\n    add(new int[] {480},     \"PH\");\n    add(new int[] {481},     \"BY\");\n    add(new int[] {482},     \"UA\");\n    add(new int[] {484},     \"MD\");\n    add(new int[] {485},     \"AM\");\n    add(new int[] {486},     \"GE\");\n    add(new int[] {487},     \"KZ\");\n    add(new int[] {489},     \"HK\");\n    add(new int[] {490,499}, \"JP\");\n    add(new int[] {500,509}, \"GB\");\n    add(new int[] {520},     \"GR\");\n    add(new int[] {528},     \"LB\");\n    add(new int[] {529},     \"CY\");\n    add(new int[] {531},     \"MK\");\n    add(new int[] {535},     \"MT\");\n    add(new int[] {539},     \"IE\");\n    add(new int[] {540,549}, \"BE/LU\");\n    add(new int[] {560},     \"PT\");\n    add(new int[] {569},     \"IS\");\n    add(new int[] {570,579}, \"DK\");\n    add(new int[] {590},     \"PL\");\n    add(new int[] {594},     \"RO\");\n    add(new int[] {599},     \"HU\");\n    add(new int[] {600,601}, \"ZA\");\n    add(new int[] {603},     \"GH\");\n    add(new int[] {608},     \"BH\");\n    add(new int[] {609},     \"MU\");\n    add(new int[] {611},     \"MA\");\n    add(new int[] {613},     \"DZ\");\n    add(new int[] {616},     \"KE\");\n    add(new int[] {618},     \"CI\");\n    add(new int[] {619},     \"TN\");\n    add(new int[] {621},     \"SY\");\n    add(new int[] {622},     \"EG\");\n    add(new int[] {624},     \"LY\");\n    add(new int[] {625},     \"JO\");\n    add(new int[] {626},     \"IR\");\n    add(new int[] {627},     \"KW\");\n    add(new int[] {628},     \"SA\");\n    add(new int[] {629},     \"AE\");\n    add(new int[] {640,649}, \"FI\");\n    add(new int[] {690,695}, \"CN\");\n    add(new int[] {700,709}, \"NO\");\n    add(new int[] {729},     \"IL\");\n    add(new int[] {730,739}, \"SE\");\n    add(new int[] {740},     \"GT\");\n    add(new int[] {741},     \"SV\");\n    add(new int[] {742},     \"HN\");\n    add(new int[] {743},     \"NI\");\n    add(new int[] {744},     \"CR\");\n    add(new int[] {745},     \"PA\");\n    add(new int[] {746},     \"DO\");\n    add(new int[] {750},     \"MX\");\n    add(new int[] {754,755}, \"CA\");\n    add(new int[] {759},     \"VE\");\n    add(new int[] {760,769}, \"CH\");\n    add(new int[] {770},     \"CO\");\n    add(new int[] {773},     \"UY\");\n    add(new int[] {775},     \"PE\");\n    add(new int[] {777},     \"BO\");\n    add(new int[] {779},     \"AR\");\n    add(new int[] {780},     \"CL\");\n    add(new int[] {784},     \"PY\");\n    add(new int[] {785},     \"PE\");\n    add(new int[] {786},     \"EC\");\n    add(new int[] {789,790}, \"BR\");\n    add(new int[] {800,839}, \"IT\");\n    add(new int[] {840,849}, \"ES\");\n    add(new int[] {850},     \"CU\");\n    add(new int[] {858},     \"SK\");\n    add(new int[] {859},     \"CZ\");\n    add(new int[] {860},     \"YU\");\n    add(new int[] {865},     \"MN\");\n    add(new int[] {867},     \"KP\");\n    add(new int[] {868,869}, \"TR\");\n    add(new int[] {870,879}, \"NL\");\n    add(new int[] {880},     \"KR\");\n    add(new int[] {885},     \"TH\");\n    add(new int[] {888},     \"SG\");\n    add(new int[] {890},     \"IN\");\n    add(new int[] {893},     \"VN\");\n    add(new int[] {896},     \"PK\");\n    add(new int[] {899},     \"ID\");\n    add(new int[] {900,919}, \"AT\");\n    add(new int[] {930,939}, \"AU\");\n    add(new int[] {940,949}, \"AZ\");\n    add(new int[] {955},     \"MY\");\n    add(new int[] {958},     \"MO\");\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/ITFReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Map;\n\n/**\n * <p>Implements decoding of the ITF format, or Interleaved Two of Five.</p>\n *\n * <p>This Reader will scan ITF barcodes of certain lengths only.\n * At the moment it reads length 6, 8, 10, 12, 14, 16, 18, 20, 24, and 44 as these have appeared \"in the wild\". Not all\n * lengths are scanned, especially shorter ones, to avoid false positives. This in turn is due to a lack of\n * required checksum function.</p>\n *\n * <p>The checksum is optional and is not applied by this Reader. The consumer of the decoded\n * value will have to apply a checksum if required.</p>\n *\n * <p><a href=\"http://en.wikipedia.org/wiki/Interleaved_2_of_5\">http://en.wikipedia.org/wiki/Interleaved_2_of_5</a>\n * is a great reference for Interleaved 2 of 5 information.</p>\n *\n * @author kevin.osullivan@sita.aero, SITA Lab.\n */\npublic final class ITFReader extends OneDReader {\n\n  private static final float MAX_AVG_VARIANCE = 0.38f;\n  private static final float MAX_INDIVIDUAL_VARIANCE = 0.5f;\n\n  private static final int W = 3; // Pixel width of a 3x wide line\n  private static final int w = 2; // Pixel width of a 2x wide line\n  private static final int N = 1; // Pixed width of a narrow line\n\n  /** Valid ITF lengths. Anything longer than the largest value is also allowed. */\n  private static final int[] DEFAULT_ALLOWED_LENGTHS = {6, 8, 10, 12, 14};\n\n  // Stores the actual narrow line width of the image being decoded.\n  private int narrowLineWidth = -1;\n\n  /**\n   * Start/end guard pattern.\n   *\n   * Note: The end pattern is reversed because the row is reversed before\n   * searching for the END_PATTERN\n   */\n  private static final int[] START_PATTERN = {N, N, N, N};\n  private static final int[][] END_PATTERN_REVERSED = {\n      {N, N, w}, // 2x\n      {N, N, W}  // 3x\n  };\n\n  // See ITFWriter.PATTERNS\n\n  /**\n   * Patterns of Wide / Narrow lines to indicate each digit\n   */\n  private static final int[][] PATTERNS = {\n      {N, N, w, w, N}, // 0\n      {w, N, N, N, w}, // 1\n      {N, w, N, N, w}, // 2\n      {w, w, N, N, N}, // 3\n      {N, N, w, N, w}, // 4\n      {w, N, w, N, N}, // 5\n      {N, w, w, N, N}, // 6\n      {N, N, N, w, w}, // 7\n      {w, N, N, w, N}, // 8\n      {N, w, N, w, N}, // 9\n      {N, N, W, W, N}, // 0\n      {W, N, N, N, W}, // 1\n      {N, W, N, N, W}, // 2\n      {W, W, N, N, N}, // 3\n      {N, N, W, N, W}, // 4\n      {W, N, W, N, N}, // 5\n      {N, W, W, N, N}, // 6\n      {N, N, N, W, W}, // 7\n      {W, N, N, W, N}, // 8\n      {N, W, N, W, N}  // 9\n  };\n\n  @Override\n  public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)\n      throws FormatException, NotFoundException {\n\n    // Find out where the Middle section (payload) starts & ends\n    int[] startRange = decodeStart(row);\n    int[] endRange = decodeEnd(row);\n\n    StringBuilder result = new StringBuilder(20);\n    decodeMiddle(row, startRange[1], endRange[0], result);\n    String resultString = result.toString();\n\n    int[] allowedLengths = null;\n    if (hints != null) {\n      allowedLengths = (int[]) hints.get(DecodeHintType.ALLOWED_LENGTHS);\n\n    }\n    if (allowedLengths == null) {\n      allowedLengths = DEFAULT_ALLOWED_LENGTHS;\n    }\n\n    // To avoid false positives with 2D barcodes (and other patterns), make\n    // an assumption that the decoded string must be a 'standard' length if it's short\n    int length = resultString.length();\n    boolean lengthOK = false;\n    int maxAllowedLength = 0;\n    for (int allowedLength : allowedLengths) {\n      if (length == allowedLength) {\n        lengthOK = true;\n        break;\n      }\n      if (allowedLength > maxAllowedLength) {\n        maxAllowedLength = allowedLength;\n      }\n    }\n    if (!lengthOK && length > maxAllowedLength) {\n      lengthOK = true;\n    }\n    if (!lengthOK) {\n      throw FormatException.getFormatInstance();\n    }\n\n    return new Result(\n        resultString,\n        null, // no natural byte representation for these barcodes\n        new ResultPoint[] {new ResultPoint(startRange[1], rowNumber),\n                           new ResultPoint(endRange[0], rowNumber)},\n        BarcodeFormat.ITF);\n  }\n\n  /**\n   * @param row          row of black/white values to search\n   * @param payloadStart offset of start pattern\n   * @param resultString {@link StringBuilder} to append decoded chars to\n   * @throws NotFoundException if decoding could not complete successfully\n   */\n  private static void decodeMiddle(BitArray row,\n                                   int payloadStart,\n                                   int payloadEnd,\n                                   StringBuilder resultString) throws NotFoundException {\n\n    // Digits are interleaved in pairs - 5 black lines for one digit, and the\n    // 5\n    // interleaved white lines for the second digit.\n    // Therefore, need to scan 10 lines and then\n    // split these into two arrays\n    int[] counterDigitPair = new int[10];\n    int[] counterBlack = new int[5];\n    int[] counterWhite = new int[5];\n\n    while (payloadStart < payloadEnd) {\n\n      // Get 10 runs of black/white.\n      recordPattern(row, payloadStart, counterDigitPair);\n      // Split them into each array\n      for (int k = 0; k < 5; k++) {\n        int twoK = 2 * k;\n        counterBlack[k] = counterDigitPair[twoK];\n        counterWhite[k] = counterDigitPair[twoK + 1];\n      }\n\n      int bestMatch = decodeDigit(counterBlack);\n      resultString.append((char) ('0' + bestMatch));\n      bestMatch = decodeDigit(counterWhite);\n      resultString.append((char) ('0' + bestMatch));\n\n      for (int counterDigit : counterDigitPair) {\n        payloadStart += counterDigit;\n      }\n    }\n  }\n\n  /**\n   * Identify where the start of the middle / payload section starts.\n   *\n   * @param row row of black/white values to search\n   * @return Array, containing index of start of 'start block' and end of\n   *         'start block'\n   */\n  private int[] decodeStart(BitArray row) throws NotFoundException {\n    int endStart = skipWhiteSpace(row);\n    int[] startPattern = findGuardPattern(row, endStart, START_PATTERN);\n\n    // Determine the width of a narrow line in pixels. We can do this by\n    // getting the width of the start pattern and dividing by 4 because its\n    // made up of 4 narrow lines.\n    this.narrowLineWidth = (startPattern[1] - startPattern[0]) / 4;\n\n    validateQuietZone(row, startPattern[0]);\n\n    return startPattern;\n  }\n\n  /**\n   * The start & end patterns must be pre/post fixed by a quiet zone. This\n   * zone must be at least 10 times the width of a narrow line.  Scan back until\n   * we either get to the start of the barcode or match the necessary number of\n   * quiet zone pixels.\n   *\n   * Note: Its assumed the row is reversed when using this method to find\n   * quiet zone after the end pattern.\n   *\n   * ref: http://www.barcode-1.net/i25code.html\n   *\n   * @param row bit array representing the scanned barcode.\n   * @param startPattern index into row of the start or end pattern.\n   * @throws NotFoundException if the quiet zone cannot be found\n   */\n  private void validateQuietZone(BitArray row, int startPattern) throws NotFoundException {\n\n    int quietCount = this.narrowLineWidth * 10;  // expect to find this many pixels of quiet zone\n\n    // if there are not so many pixel at all let's try as many as possible\n    quietCount = quietCount < startPattern ? quietCount : startPattern;\n\n    for (int i = startPattern - 1; quietCount > 0 && i >= 0; i--) {\n      if (row.get(i)) {\n        break;\n      }\n      quietCount--;\n    }\n    if (quietCount != 0) {\n      // Unable to find the necessary number of quiet zone pixels.\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n  /**\n   * Skip all whitespace until we get to the first black line.\n   *\n   * @param row row of black/white values to search\n   * @return index of the first black line.\n   * @throws NotFoundException Throws exception if no black lines are found in the row\n   */\n  private static int skipWhiteSpace(BitArray row) throws NotFoundException {\n    int width = row.getSize();\n    int endStart = row.getNextSet(0);\n    if (endStart == width) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    return endStart;\n  }\n\n  /**\n   * Identify where the end of the middle / payload section ends.\n   *\n   * @param row row of black/white values to search\n   * @return Array, containing index of start of 'end block' and end of 'end\n   *         block'\n   */\n  private int[] decodeEnd(BitArray row) throws NotFoundException {\n\n    // For convenience, reverse the row and then\n    // search from 'the start' for the end block\n    row.reverse();\n    try {\n      int endStart = skipWhiteSpace(row);\n      int[] endPattern;\n      try {\n        endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED[0]);\n      } catch (NotFoundException nfe) {\n        endPattern = findGuardPattern(row, endStart, END_PATTERN_REVERSED[1]);\n      }\n\n      // The start & end patterns must be pre/post fixed by a quiet zone. This\n      // zone must be at least 10 times the width of a narrow line.\n      // ref: http://www.barcode-1.net/i25code.html\n      validateQuietZone(row, endPattern[0]);\n\n      // Now recalculate the indices of where the 'endblock' starts & stops to\n      // accommodate\n      // the reversed nature of the search\n      int temp = endPattern[0];\n      endPattern[0] = row.getSize() - endPattern[1];\n      endPattern[1] = row.getSize() - temp;\n\n      return endPattern;\n    } finally {\n      // Put the row back the right way.\n      row.reverse();\n    }\n  }\n\n  /**\n   * @param row       row of black/white values to search\n   * @param rowOffset position to start search\n   * @param pattern   pattern of counts of number of black and white pixels that are\n   *                  being searched for as a pattern\n   * @return start/end horizontal offset of guard pattern, as an array of two\n   *         ints\n   * @throws NotFoundException if pattern is not found\n   */\n  private static int[] findGuardPattern(BitArray row,\n                                        int rowOffset,\n                                        int[] pattern) throws NotFoundException {\n    int patternLength = pattern.length;\n    int[] counters = new int[patternLength];\n    int width = row.getSize();\n    boolean isWhite = false;\n\n    int counterPosition = 0;\n    int patternStart = rowOffset;\n    for (int x = rowOffset; x < width; x++) {\n      if (row.get(x) != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (counterPosition == patternLength - 1) {\n          if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {\n            return new int[]{patternStart, x};\n          }\n          patternStart += counters[0] + counters[1];\n          System.arraycopy(counters, 2, counters, 0, counterPosition - 1);\n          counters[counterPosition - 1] = 0;\n          counters[counterPosition] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        counters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * Attempts to decode a sequence of ITF black/white lines into single\n   * digit.\n   *\n   * @param counters the counts of runs of observed black/white/black/... values\n   * @return The decoded digit\n   * @throws NotFoundException if digit cannot be decoded\n   */\n  private static int decodeDigit(int[] counters) throws NotFoundException {\n    float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\n    int bestMatch = -1;\n    int max = PATTERNS.length;\n    for (int i = 0; i < max; i++) {\n      int[] pattern = PATTERNS[i];\n      float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\n      if (variance < bestVariance) {\n        bestVariance = variance;\n        bestMatch = i;\n      } else if (variance == bestVariance) {\n        // if we find a second 'best match' with the same variance, we can not reliably report to have a suitable match\n        bestMatch = -1;\n      }\n    }\n    if (bestMatch >= 0) {\n      return bestMatch % 10;\n    } else {\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/ITFWriter.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\n\n/**\n * This object renders a ITF code as a {@link BitMatrix}.\n *\n * @author erik.barbara@gmail.com (Erik Barbara)\n */\npublic final class ITFWriter extends OneDimensionalCodeWriter {\n\n  private static final int[] START_PATTERN = {1, 1, 1, 1};\n  private static final int[] END_PATTERN = {3, 1, 1};\n\n  private static final int W = 3; // Pixel width of a 3x wide line\n  private static final int N = 1; // Pixed width of a narrow line\n\n  // See ITFReader.PATTERNS\n\n  private static final int[][] PATTERNS = {\n      {N, N, W, W, N}, // 0\n      {W, N, N, N, W}, // 1\n      {N, W, N, N, W}, // 2\n      {W, W, N, N, N}, // 3\n      {N, N, W, N, W}, // 4\n      {W, N, W, N, N}, // 5\n      {N, W, W, N, N}, // 6\n      {N, N, N, W, W}, // 7\n      {W, N, N, W, N}, // 8\n      {N, W, N, W, N}  // 9\n  };\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.ITF) {\n      throw new IllegalArgumentException(\"Can only encode ITF, but got \" + format);\n    }\n\n    return super.encode(contents, format, width, height, hints);\n  }\n\n  @Override\n  public boolean[] encode(String contents) {\n    int length = contents.length();\n    if (length % 2 != 0) {\n      throw new IllegalArgumentException(\"The length of the input should be even\");\n    }\n    if (length > 80) {\n      throw new IllegalArgumentException(\n          \"Requested contents should be less than 80 digits long, but got \" + length);\n    }\n\n    checkNumeric(contents);\n\n    boolean[] result = new boolean[9 + 9 * length];\n    int pos = appendPattern(result, 0, START_PATTERN, true);\n    for (int i = 0; i < length; i += 2) {\n      int one = Character.digit(contents.charAt(i), 10);\n      int two = Character.digit(contents.charAt(i + 1), 10);\n      int[] encoding = new int[10];\n      for (int j = 0; j < 5; j++) {\n        encoding[2 * j] = PATTERNS[one][j];\n        encoding[2 * j + 1] = PATTERNS[two][j];\n      }\n      pos += appendPattern(result, pos, encoding, true);\n    }\n    appendPattern(result, pos, END_PATTERN, true);\n\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/MultiFormatOneDReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.oned.rss.RSS14Reader;\nimport com.google.zxing.oned.rss.expanded.RSSExpandedReader;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Sean Owen\n */\npublic final class MultiFormatOneDReader extends OneDReader {\n\n  private static final OneDReader[] EMPTY_ONED_ARRAY = new OneDReader[0];\n\n  private final OneDReader[] readers;\n\n  public MultiFormatOneDReader(Map<DecodeHintType,?> hints) {\n    @SuppressWarnings(\"unchecked\")\n    Collection<BarcodeFormat> possibleFormats = hints == null ? null :\n        (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);\n    boolean useCode39CheckDigit = hints != null &&\n        hints.get(DecodeHintType.ASSUME_CODE_39_CHECK_DIGIT) != null;\n    Collection<OneDReader> readers = new ArrayList<>();\n    if (possibleFormats != null) {\n      if (possibleFormats.contains(BarcodeFormat.EAN_13) ||\n          possibleFormats.contains(BarcodeFormat.UPC_A) ||\n          possibleFormats.contains(BarcodeFormat.EAN_8) ||\n          possibleFormats.contains(BarcodeFormat.UPC_E)) {\n        readers.add(new MultiFormatUPCEANReader(hints));\n      }\n      if (possibleFormats.contains(BarcodeFormat.CODE_39)) {\n        readers.add(new Code39Reader(useCode39CheckDigit));\n      }\n      if (possibleFormats.contains(BarcodeFormat.CODE_93)) {\n        readers.add(new Code93Reader());\n      }\n      if (possibleFormats.contains(BarcodeFormat.CODE_128)) {\n        readers.add(new Code128Reader());\n      }\n      if (possibleFormats.contains(BarcodeFormat.ITF)) {\n         readers.add(new ITFReader());\n      }\n      if (possibleFormats.contains(BarcodeFormat.CODABAR)) {\n         readers.add(new CodaBarReader());\n      }\n      if (possibleFormats.contains(BarcodeFormat.RSS_14)) {\n         readers.add(new RSS14Reader());\n      }\n      if (possibleFormats.contains(BarcodeFormat.RSS_EXPANDED)) {\n        readers.add(new RSSExpandedReader());\n      }\n    }\n    if (readers.isEmpty()) {\n      readers.add(new MultiFormatUPCEANReader(hints));\n      readers.add(new Code39Reader());\n      readers.add(new CodaBarReader());\n      readers.add(new Code93Reader());\n      readers.add(new Code128Reader());\n      readers.add(new ITFReader());\n      readers.add(new RSS14Reader());\n      readers.add(new RSSExpandedReader());\n    }\n    this.readers = readers.toArray(EMPTY_ONED_ARRAY);\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber,\n                          BitArray row,\n                          Map<DecodeHintType,?> hints) throws NotFoundException {\n    for (OneDReader reader : readers) {\n      try {\n        return reader.decodeRow(rowNumber, row, hints);\n      } catch (ReaderException re) {\n        // continue\n      }\n    }\n\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  @Override\n  public void reset() {\n    for (Reader reader : readers) {\n      reader.reset();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/MultiFormatUPCEANReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * <p>A reader that can read all available UPC/EAN formats. If a caller wants to try to\n * read all such formats, it is most efficient to use this implementation rather than invoke\n * individual readers.</p>\n *\n * @author Sean Owen\n */\npublic final class MultiFormatUPCEANReader extends OneDReader {\n\n  private static final UPCEANReader[] EMPTY_READER_ARRAY = new UPCEANReader[0];\n\n  private final UPCEANReader[] readers;\n\n  public MultiFormatUPCEANReader(Map<DecodeHintType,?> hints) {\n    @SuppressWarnings(\"unchecked\")\n    Collection<BarcodeFormat> possibleFormats = hints == null ? null :\n        (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);\n    Collection<UPCEANReader> readers = new ArrayList<>();\n    if (possibleFormats != null) {\n      if (possibleFormats.contains(BarcodeFormat.EAN_13)) {\n        readers.add(new EAN13Reader());\n      } else if (possibleFormats.contains(BarcodeFormat.UPC_A)) {\n        readers.add(new UPCAReader());\n      }\n      if (possibleFormats.contains(BarcodeFormat.EAN_8)) {\n        readers.add(new EAN8Reader());\n      }\n      if (possibleFormats.contains(BarcodeFormat.UPC_E)) {\n        readers.add(new UPCEReader());\n      }\n    }\n    if (readers.isEmpty()) {\n      readers.add(new EAN13Reader());\n      // UPC-A is covered by EAN-13\n      readers.add(new EAN8Reader());\n      readers.add(new UPCEReader());\n    }\n    this.readers = readers.toArray(EMPTY_READER_ARRAY);\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber,\n                          BitArray row,\n                          Map<DecodeHintType,?> hints) throws NotFoundException {\n    // Compute this location once and reuse it on multiple implementations\n    int[] startGuardPattern = UPCEANReader.findStartGuardPattern(row);\n    for (UPCEANReader reader : readers) {\n      try {\n        Result result = reader.decodeRow(rowNumber, row, startGuardPattern, hints);\n        // Special case: a 12-digit code encoded in UPC-A is identical to a \"0\"\n        // followed by those 12 digits encoded as EAN-13. Each will recognize such a code,\n        // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with \"0\".\n        // Individually these are correct and their readers will both read such a code\n        // and correctly call it EAN-13, or UPC-A, respectively.\n        //\n        // In this case, if we've been looking for both types, we'd like to call it\n        // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read\n        // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A\n        // result if appropriate.\n        //\n        // But, don't return UPC-A if UPC-A was not a requested format!\n        boolean ean13MayBeUPCA =\n            result.getBarcodeFormat() == BarcodeFormat.EAN_13 &&\n                result.getText().charAt(0) == '0';\n        @SuppressWarnings(\"unchecked\")\n        Collection<BarcodeFormat> possibleFormats =\n            hints == null ? null : (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);\n        boolean canReturnUPCA = possibleFormats == null || possibleFormats.contains(BarcodeFormat.UPC_A);\n  \n        if (ean13MayBeUPCA && canReturnUPCA) {\n          // Transfer the metdata across\n          Result resultUPCA = new Result(result.getText().substring(1),\n                                         result.getRawBytes(),\n                                         result.getResultPoints(),\n                                         BarcodeFormat.UPC_A);\n          resultUPCA.putAllMetadata(result.getResultMetadata());\n          return resultUPCA;\n        }\n        return result;\n      } catch (ReaderException ignored) {\n        // continue\n      }\n    }\n\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  @Override\n  public void reset() {\n    for (Reader reader : readers) {\n      reader.reset();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/OneDReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Arrays;\nimport java.util.EnumMap;\nimport java.util.Map;\n\n/**\n * Encapsulates functionality and implementation that is common to all families\n * of one-dimensional barcodes.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Sean Owen\n */\npublic abstract class OneDReader implements Reader {\n\n  @Override\n  public Result decode(BinaryBitmap image) throws NotFoundException, FormatException {\n    return decode(image, null);\n  }\n\n  // Note that we don't try rotation without the try harder flag, even if rotation was supported.\n  @Override\n  public Result decode(BinaryBitmap image,\n                       Map<DecodeHintType,?> hints) throws NotFoundException, FormatException {\n    try {\n      return doDecode(image, hints);\n    } catch (NotFoundException nfe) {\n      boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);\n      if (tryHarder && image.isRotateSupported()) {\n        BinaryBitmap rotatedImage = image.rotateCounterClockwise();\n        Result result = doDecode(rotatedImage, hints);\n        // Record that we found it rotated 90 degrees CCW / 270 degrees CW\n        Map<ResultMetadataType,?> metadata = result.getResultMetadata();\n        int orientation = 270;\n        if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) {\n          // But if we found it reversed in doDecode(), add in that result here:\n          orientation = (orientation +\n              (Integer) metadata.get(ResultMetadataType.ORIENTATION)) % 360;\n        }\n        result.putMetadata(ResultMetadataType.ORIENTATION, orientation);\n        // Update result points\n        ResultPoint[] points = result.getResultPoints();\n        if (points != null) {\n          int height = rotatedImage.getHeight();\n          for (int i = 0; i < points.length; i++) {\n            points[i] = new ResultPoint(height - points[i].getY() - 1, points[i].getX());\n          }\n        }\n        return result;\n      } else {\n        throw nfe;\n      }\n    }\n  }\n\n  @Override\n  public void reset() {\n    // do nothing\n  }\n\n  /**\n   * We're going to examine rows from the middle outward, searching alternately above and below the\n   * middle, and farther out each time. rowStep is the number of rows between each successive\n   * attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then\n   * middle + rowStep, then middle - (2 * rowStep), etc.\n   * rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily\n   * decided that moving up and down by about 1/16 of the image is pretty good; we try more of the\n   * image if \"trying harder\".\n   *\n   * @param image The image to decode\n   * @param hints Any hints that were requested\n   * @return The contents of the decoded barcode\n   * @throws NotFoundException Any spontaneous errors which occur\n   */\n  private Result doDecode(BinaryBitmap image,\n                          Map<DecodeHintType,?> hints) throws NotFoundException {\n    int width = image.getWidth();\n    int height = image.getHeight();\n    BitArray row = new BitArray(width);\n\n    boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);\n    int rowStep = Math.max(1, height >> (tryHarder ? 8 : 5));\n    int maxLines;\n    if (tryHarder) {\n      maxLines = height; // Look at the whole image, not just the center\n    } else {\n      maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image\n    }\n\n    int middle = height / 2;\n    for (int x = 0; x < maxLines; x++) {\n\n      // Scanning from the middle out. Determine which row we're looking at next:\n      int rowStepsAboveOrBelow = (x + 1) / 2;\n      boolean isAbove = (x & 0x01) == 0; // i.e. is x even?\n      int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);\n      if (rowNumber < 0 || rowNumber >= height) {\n        // Oops, if we run off the top or bottom, stop\n        break;\n      }\n\n      // Estimate black point for this row and load it:\n      try {\n        row = image.getBlackRow(rowNumber, row);\n      } catch (NotFoundException ignored) {\n        continue;\n      }\n\n      // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to\n      // handle decoding upside down barcodes.\n      for (int attempt = 0; attempt < 2; attempt++) {\n        if (attempt == 1) { // trying again?\n          row.reverse(); // reverse the row and continue\n          // This means we will only ever draw result points *once* in the life of this method\n          // since we want to avoid drawing the wrong points after flipping the row, and,\n          // don't want to clutter with noise from every single row scan -- just the scans\n          // that start on the center line.\n          if (hints != null && hints.containsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) {\n            Map<DecodeHintType,Object> newHints = new EnumMap<>(DecodeHintType.class);\n            newHints.putAll(hints);\n            newHints.remove(DecodeHintType.NEED_RESULT_POINT_CALLBACK);\n            hints = newHints;\n          }\n        }\n        try {\n          // Look for a barcode\n          Result result = decodeRow(rowNumber, row, hints);\n          // We found our barcode\n          if (attempt == 1) {\n            // But it was upside down, so note that\n            result.putMetadata(ResultMetadataType.ORIENTATION, 180);\n            // And remember to flip the result points horizontally.\n            ResultPoint[] points = result.getResultPoints();\n            if (points != null) {\n              points[0] = new ResultPoint(width - points[0].getX() - 1, points[0].getY());\n              points[1] = new ResultPoint(width - points[1].getX() - 1, points[1].getY());\n            }\n          }\n          return result;\n        } catch (ReaderException re) {\n          // continue -- just couldn't decode this row\n        }\n      }\n    }\n\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * Records the size of successive runs of white and black pixels in a row, starting at a given point.\n   * The values are recorded in the given array, and the number of runs recorded is equal to the size\n   * of the array. If the row starts on a white pixel at the given start point, then the first count\n   * recorded is the run of white pixels starting from that point; likewise it is the count of a run\n   * of black pixels if the row begin on a black pixels at that point.\n   *\n   * @param row row to count from\n   * @param start offset into row to start at\n   * @param counters array into which to record counts\n   * @throws NotFoundException if counters cannot be filled entirely from row before running out\n   *  of pixels\n   */\n  protected static void recordPattern(BitArray row,\n                                      int start,\n                                      int[] counters) throws NotFoundException {\n    int numCounters = counters.length;\n    Arrays.fill(counters, 0, numCounters, 0);\n    int end = row.getSize();\n    if (start >= end) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    boolean isWhite = !row.get(start);\n    int counterPosition = 0;\n    int i = start;\n    while (i < end) {\n      if (row.get(i) != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (++counterPosition == numCounters) {\n          break;\n        } else {\n          counters[counterPosition] = 1;\n          isWhite = !isWhite;\n        }\n      }\n      i++;\n    }\n    // If we read fully the last section of pixels and filled up our counters -- or filled\n    // the last counter but ran off the side of the image, OK. Otherwise, a problem.\n    if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n  protected static void recordPatternInReverse(BitArray row, int start, int[] counters)\n      throws NotFoundException {\n    // This could be more efficient I guess\n    int numTransitionsLeft = counters.length;\n    boolean last = row.get(start);\n    while (start > 0 && numTransitionsLeft >= 0) {\n      if (row.get(--start) != last) {\n        numTransitionsLeft--;\n        last = !last;\n      }\n    }\n    if (numTransitionsLeft >= 0) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    recordPattern(row, start + 1, counters);\n  }\n\n  /**\n   * Determines how closely a set of observed counts of runs of black/white values matches a given\n   * target pattern. This is reported as the ratio of the total variance from the expected pattern\n   * proportions across all pattern elements, to the length of the pattern.\n   *\n   * @param counters observed counters\n   * @param pattern expected pattern\n   * @param maxIndividualVariance The most any counter can differ before we give up\n   * @return ratio of total variance between counters and pattern compared to total pattern size\n   */\n  protected static float patternMatchVariance(int[] counters,\n                                              int[] pattern,\n                                              float maxIndividualVariance) {\n    int numCounters = counters.length;\n    int total = 0;\n    int patternLength = 0;\n    for (int i = 0; i < numCounters; i++) {\n      total += counters[i];\n      patternLength += pattern[i];\n    }\n    if (total < patternLength) {\n      // If we don't even have one pixel per unit of bar width, assume this is too small\n      // to reliably match, so fail:\n      return Float.POSITIVE_INFINITY;\n    }\n\n    float unitBarWidth = (float) total / patternLength;\n    maxIndividualVariance *= unitBarWidth;\n\n    float totalVariance = 0.0f;\n    for (int x = 0; x < numCounters; x++) {\n      int counter = counters[x];\n      float scaledPattern = pattern[x] * unitBarWidth;\n      float variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;\n      if (variance > maxIndividualVariance) {\n        return Float.POSITIVE_INFINITY;\n      }\n      totalVariance += variance;\n    }\n    return totalVariance / total;\n  }\n\n  /**\n   * <p>Attempts to decode a one-dimensional barcode format given a single row of\n   * an image.</p>\n   *\n   * @param rowNumber row number from top of the row\n   * @param row the black/white pixel data of the row\n   * @param hints decode hints\n   * @return {@link Result} containing encoded string and start/end of barcode\n   * @throws NotFoundException if no potential barcode is found\n   * @throws ChecksumException if a potential barcode is found but does not pass its checksum\n   * @throws FormatException if a potential barcode is found but format is invalid\n   */\n  public abstract Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException;\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/OneDimensionalCodeWriter.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.Writer;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\n/**\n * <p>Encapsulates functionality and implementation that is common to one-dimensional barcodes.</p>\n *\n * @author dsbnatut@gmail.com (Kazuki Nishiura)\n */\npublic abstract class OneDimensionalCodeWriter implements Writer {\n  private static final Pattern NUMERIC = Pattern.compile(\"[0-9]+\");\n\n  @Override\n  public final BitMatrix encode(String contents, BarcodeFormat format, int width, int height)\n      throws WriterException {\n    return encode(contents, format, width, height, null);\n  }\n\n  /**\n   * Encode the contents following specified format.\n   * {@code width} and {@code height} are required size. This method may return bigger size\n   * {@code BitMatrix} when specified size is too small. The user can set both {@code width} and\n   * {@code height} to zero to get minimum size barcode. If negative value is set to {@code width}\n   * or {@code height}, {@code IllegalArgumentException} is thrown.\n   */\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (contents.isEmpty()) {\n      throw new IllegalArgumentException(\"Found empty contents\");\n    }\n\n    if (width < 0 || height < 0) {\n      throw new IllegalArgumentException(\"Negative size is not allowed. Input: \"\n                                             + width + 'x' + height);\n    }\n\n    int sidesMargin = getDefaultMargin();\n    if (hints != null && hints.containsKey(EncodeHintType.MARGIN)) {\n      sidesMargin = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());\n    }\n\n    boolean[] code = encode(contents);\n    return renderResult(code, width, height, sidesMargin);\n  }\n\n  /**\n   * @return a byte array of horizontal pixels (0 = white, 1 = black)\n   */\n  private static BitMatrix renderResult(boolean[] code, int width, int height, int sidesMargin) {\n    int inputWidth = code.length;\n    // Add quiet zone on both sides.\n    int fullWidth = inputWidth + sidesMargin;\n    int outputWidth = Math.max(width, fullWidth);\n    int outputHeight = Math.max(1, height);\n\n    int multiple = outputWidth / fullWidth;\n    int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;\n\n    BitMatrix output = new BitMatrix(outputWidth, outputHeight);\n    for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {\n      if (code[inputX]) {\n        output.setRegion(outputX, 0, multiple, outputHeight);\n      }\n    }\n    return output;\n  }\n\n  /**\n   * @param contents string to check for numeric characters\n   * @throws IllegalArgumentException if input contains characters other than digits 0-9.\n   */\n  protected static void checkNumeric(String contents) {\n    if (!NUMERIC.matcher(contents).matches()) {\n      throw new IllegalArgumentException(\"Input should only contain digits 0-9\");\n    }\n  }\n\n  /**\n   * @param target encode black/white pattern into this array\n   * @param pos position to start encoding at in {@code target}\n   * @param pattern lengths of black/white runs to encode\n   * @param startColor starting color - false for white, true for black\n   * @return the number of elements added to target.\n   */\n  protected static int appendPattern(boolean[] target, int pos, int[] pattern, boolean startColor) {\n    boolean color = startColor;\n    int numAdded = 0;\n    for (int len : pattern) {\n      for (int j = 0; j < len; j++) {\n        target[pos++] = color;\n      }\n      numAdded += len;\n      color = !color; // flip color after each segment\n    }\n    return numAdded;\n  }\n\n  public int getDefaultMargin() {\n    // CodaBar spec requires a side margin to be more than ten times wider than narrow space.\n    // This seems like a decent idea for a default for all formats.\n    return 10;\n  }\n\n  /**\n   * Encode the contents to boolean array expression of one-dimensional barcode.\n   * Start code and end code should be included in result, and side margins should not be included.\n   *\n   * @param contents barcode contents to encode\n   * @return a {@code boolean[]} of horizontal pixels (false = white, true = black)\n   */\n  public abstract boolean[] encode(String contents);\n}\n\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCAReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Map;\n\n/**\n * <p>Implements decoding of the UPC-A format.</p>\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Sean Owen\n */\npublic final class UPCAReader extends UPCEANReader {\n\n  private final UPCEANReader ean13Reader = new EAN13Reader();\n\n  @Override\n  public Result decodeRow(int rowNumber,\n                          BitArray row,\n                          int[] startGuardRange,\n                          Map<DecodeHintType,?> hints)\n      throws NotFoundException, FormatException, ChecksumException {\n    return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange, hints));\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)\n      throws NotFoundException, FormatException, ChecksumException {\n    return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, hints));\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image) throws NotFoundException, FormatException {\n    return maybeReturnResult(ean13Reader.decode(image));\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)\n      throws NotFoundException, FormatException {\n    return maybeReturnResult(ean13Reader.decode(image, hints));\n  }\n\n  @Override\n  BarcodeFormat getBarcodeFormat() {\n    return BarcodeFormat.UPC_A;\n  }\n\n  @Override\n  protected int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString)\n      throws NotFoundException {\n    return ean13Reader.decodeMiddle(row, startRange, resultString);\n  }\n\n  private static Result maybeReturnResult(Result result) throws FormatException {\n    String text = result.getText();\n    if (text.charAt(0) == '0') {\n      Result upcaResult = new Result(text.substring(1), null, result.getResultPoints(), BarcodeFormat.UPC_A);\n      if (result.getResultMetadata() != null) {\n        upcaResult.putAllMetadata(result.getResultMetadata());\n      }\n      return upcaResult;\n    } else {\n      throw FormatException.getFormatInstance();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCAWriter.java",
    "content": "/*\n * Copyright 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.Writer;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.Map;\n\n/**\n * This object renders a UPC-A code as a {@link BitMatrix}.\n *\n * @author qwandor@google.com (Andrew Walbran)\n */\npublic final class UPCAWriter implements Writer {\n\n  private final EAN13Writer subWriter = new EAN13Writer();\n\n  @Override\n  public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)\n      throws WriterException {\n    return encode(contents, format, width, height, null);\n  }\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.UPC_A) {\n      throw new IllegalArgumentException(\"Can only encode UPC-A, but got \" + format);\n    }\n    // Transform a UPC-A code into the equivalent EAN-13 code and write it that way\n    return subWriter.encode('0' + contents, BarcodeFormat.EAN_13, width, height, hints);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCEANExtension2Support.java",
    "content": "/*\n * Copyright (C) 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.EnumMap;\nimport java.util.Map;\n\n/**\n * @see UPCEANExtension5Support\n */\nfinal class UPCEANExtension2Support {\n\n  private final int[] decodeMiddleCounters = new int[4];\n  private final StringBuilder decodeRowStringBuffer = new StringBuilder();\n\n  Result decodeRow(int rowNumber, BitArray row, int[] extensionStartRange) throws NotFoundException {\n\n    StringBuilder result = decodeRowStringBuffer;\n    result.setLength(0);\n    int end = decodeMiddle(row, extensionStartRange, result);\n\n    String resultString = result.toString();\n    Map<ResultMetadataType,Object> extensionData = parseExtensionString(resultString);\n\n    Result extensionResult =\n        new Result(resultString,\n                   null,\n                   new ResultPoint[] {\n                       new ResultPoint((extensionStartRange[0] + extensionStartRange[1]) / 2.0f, rowNumber),\n                       new ResultPoint(end, rowNumber),\n                   },\n                   BarcodeFormat.UPC_EAN_EXTENSION);\n    if (extensionData != null) {\n      extensionResult.putAllMetadata(extensionData);\n    }\n    return extensionResult;\n  }\n\n  private int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString) throws NotFoundException {\n    int[] counters = decodeMiddleCounters;\n    counters[0] = 0;\n    counters[1] = 0;\n    counters[2] = 0;\n    counters[3] = 0;\n    int end = row.getSize();\n    int rowOffset = startRange[1];\n\n    int checkParity = 0;\n\n    for (int x = 0; x < 2 && rowOffset < end; x++) {\n      int bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);\n      resultString.append((char) ('0' + bestMatch % 10));\n      for (int counter : counters) {\n        rowOffset += counter;\n      }\n      if (bestMatch >= 10) {\n        checkParity |= 1 << (1 - x);\n      }\n      if (x != 1) {\n        // Read off separator if not last\n        rowOffset = row.getNextSet(rowOffset);\n        rowOffset = row.getNextUnset(rowOffset);\n      }\n    }\n\n    if (resultString.length() != 2) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    if (Integer.parseInt(resultString.toString()) % 4 != checkParity) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    return rowOffset;\n  }\n\n  /**\n   * @param raw raw content of extension\n   * @return formatted interpretation of raw content as a {@link Map} mapping\n   *  one {@link ResultMetadataType} to appropriate value, or {@code null} if not known\n   */\n  private static Map<ResultMetadataType,Object> parseExtensionString(String raw) {\n    if (raw.length() != 2) {\n      return null;\n    }\n    Map<ResultMetadataType,Object> result = new EnumMap<>(ResultMetadataType.class);\n    result.put(ResultMetadataType.ISSUE_NUMBER, Integer.valueOf(raw));\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCEANExtension5Support.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.EnumMap;\nimport java.util.Map;\n\n/**\n * @see UPCEANExtension2Support\n */\nfinal class UPCEANExtension5Support {\n\n  private static final int[] CHECK_DIGIT_ENCODINGS = {\n      0x18, 0x14, 0x12, 0x11, 0x0C, 0x06, 0x03, 0x0A, 0x09, 0x05\n  };\n\n  private final int[] decodeMiddleCounters = new int[4];\n  private final StringBuilder decodeRowStringBuffer = new StringBuilder();\n\n  Result decodeRow(int rowNumber, BitArray row, int[] extensionStartRange) throws NotFoundException {\n\n    StringBuilder result = decodeRowStringBuffer;\n    result.setLength(0);\n    int end = decodeMiddle(row, extensionStartRange, result);\n\n    String resultString = result.toString();\n    Map<ResultMetadataType,Object> extensionData = parseExtensionString(resultString);\n\n    Result extensionResult =\n        new Result(resultString,\n                   null,\n                   new ResultPoint[] {\n                       new ResultPoint((extensionStartRange[0] + extensionStartRange[1]) / 2.0f, rowNumber),\n                       new ResultPoint(end, rowNumber),\n                   },\n                   BarcodeFormat.UPC_EAN_EXTENSION);\n    if (extensionData != null) {\n      extensionResult.putAllMetadata(extensionData);\n    }\n    return extensionResult;\n  }\n\n  private int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString) throws NotFoundException {\n    int[] counters = decodeMiddleCounters;\n    counters[0] = 0;\n    counters[1] = 0;\n    counters[2] = 0;\n    counters[3] = 0;\n    int end = row.getSize();\n    int rowOffset = startRange[1];\n\n    int lgPatternFound = 0;\n\n    for (int x = 0; x < 5 && rowOffset < end; x++) {\n      int bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);\n      resultString.append((char) ('0' + bestMatch % 10));\n      for (int counter : counters) {\n        rowOffset += counter;\n      }\n      if (bestMatch >= 10) {\n        lgPatternFound |= 1 << (4 - x);\n      }\n      if (x != 4) {\n        // Read off separator if not last\n        rowOffset = row.getNextSet(rowOffset);\n        rowOffset = row.getNextUnset(rowOffset);\n      }\n    }\n\n    if (resultString.length() != 5) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    int checkDigit = determineCheckDigit(lgPatternFound);\n    if (extensionChecksum(resultString.toString()) != checkDigit) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    return rowOffset;\n  }\n\n  private static int extensionChecksum(CharSequence s) {\n    int length = s.length();\n    int sum = 0;\n    for (int i = length - 2; i >= 0; i -= 2) {\n      sum += s.charAt(i) - '0';\n    }\n    sum *= 3;\n    for (int i = length - 1; i >= 0; i -= 2) {\n      sum += s.charAt(i) - '0';\n    }\n    sum *= 3;\n    return sum % 10;\n  }\n\n  private static int determineCheckDigit(int lgPatternFound)\n      throws NotFoundException {\n    for (int d = 0; d < 10; d++) {\n      if (lgPatternFound == CHECK_DIGIT_ENCODINGS[d]) {\n        return d;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * @param raw raw content of extension\n   * @return formatted interpretation of raw content as a {@link Map} mapping\n   *  one {@link ResultMetadataType} to appropriate value, or {@code null} if not known\n   */\n  private static Map<ResultMetadataType,Object> parseExtensionString(String raw) {\n    if (raw.length() != 5) {\n      return null;\n    }\n    Object value = parseExtension5String(raw);\n    if (value == null) {\n      return null;\n    }\n    Map<ResultMetadataType,Object> result = new EnumMap<>(ResultMetadataType.class);\n    result.put(ResultMetadataType.SUGGESTED_PRICE, value);\n    return result;\n  }\n\n  private static String parseExtension5String(String raw) {\n    String currency;\n    switch (raw.charAt(0)) {\n      case '0':\n        currency = \"£\";\n        break;\n      case '5':\n        currency = \"$\";\n        break;\n      case '9':\n        // Reference: http://www.jollytech.com\n        switch (raw) {\n          case \"90000\":\n            // No suggested retail price\n            return null;\n          case \"99991\":\n            // Complementary\n            return \"0.00\";\n          case \"99990\":\n            return \"Used\";\n        }\n        // Otherwise... unknown currency?\n        currency = \"\";\n        break;\n      default:\n        currency = \"\";\n        break;\n    }\n    int rawAmount = Integer.parseInt(raw.substring(1));\n    String unitsString = String.valueOf(rawAmount / 100);\n    int hundredths = rawAmount % 100;\n    String hundredthsString = hundredths < 10 ? \"0\" + hundredths : String.valueOf(hundredths);\n    return currency + unitsString + '.' + hundredthsString;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCEANExtensionSupport.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.common.BitArray;\n\nfinal class UPCEANExtensionSupport {\n\n  private static final int[] EXTENSION_START_PATTERN = {1,1,2};\n\n  private final UPCEANExtension2Support twoSupport = new UPCEANExtension2Support();\n  private final UPCEANExtension5Support fiveSupport = new UPCEANExtension5Support();\n\n  Result decodeRow(int rowNumber, BitArray row, int rowOffset) throws NotFoundException {\n    int[] extensionStartRange = UPCEANReader.findGuardPattern(row, rowOffset, false, EXTENSION_START_PATTERN);\n    try {\n      return fiveSupport.decodeRow(rowNumber, row, extensionStartRange);\n    } catch (ReaderException ignored) {\n      return twoSupport.decodeRow(rowNumber, row, extensionStartRange);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCEANReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.common.BitArray;\n\nimport java.util.Arrays;\nimport java.util.Map;\n\n/**\n * <p>Encapsulates functionality and implementation that is common to UPC and EAN families\n * of one-dimensional barcodes.</p>\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Sean Owen\n * @author alasdair@google.com (Alasdair Mackintosh)\n */\npublic abstract class UPCEANReader extends OneDReader {\n\n  // These two values are critical for determining how permissive the decoding will be.\n  // We've arrived at these values through a lot of trial and error. Setting them any higher\n  // lets false positives creep in quickly.\n  private static final float MAX_AVG_VARIANCE = 0.48f;\n  private static final float MAX_INDIVIDUAL_VARIANCE = 0.7f;\n\n  /**\n   * Start/end guard pattern.\n   */\n  static final int[] START_END_PATTERN = {1, 1, 1,};\n\n  /**\n   * Pattern marking the middle of a UPC/EAN pattern, separating the two halves.\n   */\n  static final int[] MIDDLE_PATTERN = {1, 1, 1, 1, 1};\n  /**\n   * end guard pattern.\n   */\n  static final int[] END_PATTERN = {1, 1, 1, 1, 1, 1};\n  /**\n   * \"Odd\", or \"L\" patterns used to encode UPC/EAN digits.\n   */\n  static final int[][] L_PATTERNS = {\n      {3, 2, 1, 1}, // 0\n      {2, 2, 2, 1}, // 1\n      {2, 1, 2, 2}, // 2\n      {1, 4, 1, 1}, // 3\n      {1, 1, 3, 2}, // 4\n      {1, 2, 3, 1}, // 5\n      {1, 1, 1, 4}, // 6\n      {1, 3, 1, 2}, // 7\n      {1, 2, 1, 3}, // 8\n      {3, 1, 1, 2}  // 9\n  };\n\n  /**\n   * As above but also including the \"even\", or \"G\" patterns used to encode UPC/EAN digits.\n   */\n  static final int[][] L_AND_G_PATTERNS;\n\n  static {\n    L_AND_G_PATTERNS = new int[20][];\n    System.arraycopy(L_PATTERNS, 0, L_AND_G_PATTERNS, 0, 10);\n    for (int i = 10; i < 20; i++) {\n      int[] widths = L_PATTERNS[i - 10];\n      int[] reversedWidths = new int[widths.length];\n      for (int j = 0; j < widths.length; j++) {\n        reversedWidths[j] = widths[widths.length - j - 1];\n      }\n      L_AND_G_PATTERNS[i] = reversedWidths;\n    }\n  }\n\n  private final StringBuilder decodeRowStringBuffer;\n  private final UPCEANExtensionSupport extensionReader;\n  private final EANManufacturerOrgSupport eanManSupport;\n\n  protected UPCEANReader() {\n    decodeRowStringBuffer = new StringBuilder(20);\n    extensionReader = new UPCEANExtensionSupport();\n    eanManSupport = new EANManufacturerOrgSupport();\n  }\n\n  static int[] findStartGuardPattern(BitArray row) throws NotFoundException {\n    boolean foundStart = false;\n    int[] startRange = null;\n    int nextStart = 0;\n    int[] counters = new int[START_END_PATTERN.length];\n    while (!foundStart) {\n      Arrays.fill(counters, 0, START_END_PATTERN.length, 0);\n      startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN, counters);\n      int start = startRange[0];\n      nextStart = startRange[1];\n      // Make sure there is a quiet zone at least as big as the start pattern before the barcode.\n      // If this check would run off the left edge of the image, do not accept this barcode,\n      // as it is very likely to be a false positive.\n      int quietStart = start - (nextStart - start);\n      if (quietStart >= 0) {\n        foundStart = row.isRange(quietStart, start, false);\n      }\n    }\n    return startRange;\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException {\n    return decodeRow(rowNumber, row, findStartGuardPattern(row), hints);\n  }\n\n  /**\n   * <p>Like {@link #decodeRow(int, BitArray, Map)}, but\n   * allows caller to inform method about where the UPC/EAN start pattern is\n   * found. This allows this to be computed once and reused across many implementations.</p>\n   *\n   * @param rowNumber row index into the image\n   * @param row encoding of the row of the barcode image\n   * @param startGuardRange start/end column where the opening start pattern was found\n   * @param hints optional hints that influence decoding\n   * @return {@link Result} encapsulating the result of decoding a barcode in the row\n   * @throws NotFoundException if no potential barcode is found\n   * @throws ChecksumException if a potential barcode is found but does not pass its checksum\n   * @throws FormatException if a potential barcode is found but format is invalid\n   */\n  public Result decodeRow(int rowNumber,\n                          BitArray row,\n                          int[] startGuardRange,\n                          Map<DecodeHintType,?> hints)\n      throws NotFoundException, ChecksumException, FormatException {\n\n    ResultPointCallback resultPointCallback = hints == null ? null :\n        (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);\n\n    if (resultPointCallback != null) {\n      resultPointCallback.foundPossibleResultPoint(new ResultPoint(\n          (startGuardRange[0] + startGuardRange[1]) / 2.0f, rowNumber\n      ));\n    }\n\n    StringBuilder result = decodeRowStringBuffer;\n    result.setLength(0);\n    int endStart = decodeMiddle(row, startGuardRange, result);\n\n    if (resultPointCallback != null) {\n      resultPointCallback.foundPossibleResultPoint(new ResultPoint(\n          endStart, rowNumber\n      ));\n    }\n\n    int[] endRange = decodeEnd(row, endStart);\n\n    if (resultPointCallback != null) {\n      resultPointCallback.foundPossibleResultPoint(new ResultPoint(\n          (endRange[0] + endRange[1]) / 2.0f, rowNumber\n      ));\n    }\n\n\n    // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The\n    // spec might want more whitespace, but in practice this is the maximum we can count on.\n    int end = endRange[1];\n    int quietEnd = end + (end - endRange[0]);\n    if (quietEnd >= row.getSize() || !row.isRange(end, quietEnd, false)) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    String resultString = result.toString();\n    // UPC/EAN should never be less than 8 chars anyway\n    if (resultString.length() < 8) {\n      throw FormatException.getFormatInstance();\n    }\n    if (!checkChecksum(resultString)) {\n      throw ChecksumException.getChecksumInstance();\n    }\n\n    float left = (startGuardRange[1] + startGuardRange[0]) / 2.0f;\n    float right = (endRange[1] + endRange[0]) / 2.0f;\n    BarcodeFormat format = getBarcodeFormat();\n    Result decodeResult = new Result(resultString,\n        null, // no natural byte representation for these barcodes\n        new ResultPoint[]{\n            new ResultPoint(left, rowNumber),\n            new ResultPoint(right, rowNumber)},\n        format);\n\n    int extensionLength = 0;\n\n    try {\n      Result extensionResult = extensionReader.decodeRow(rowNumber, row, endRange[1]);\n      decodeResult.putMetadata(ResultMetadataType.UPC_EAN_EXTENSION, extensionResult.getText());\n      decodeResult.putAllMetadata(extensionResult.getResultMetadata());\n      decodeResult.addResultPoints(extensionResult.getResultPoints());\n      extensionLength = extensionResult.getText().length();\n    } catch (ReaderException re) {\n      // continue\n    }\n\n    int[] allowedExtensions =\n        hints == null ? null : (int[]) hints.get(DecodeHintType.ALLOWED_EAN_EXTENSIONS);\n    if (allowedExtensions != null) {\n      boolean valid = false;\n      for (int length : allowedExtensions) {\n        if (extensionLength == length) {\n          valid = true;\n          break;\n        }\n      }\n      if (!valid) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n    }\n\n    if (format == BarcodeFormat.EAN_13 || format == BarcodeFormat.UPC_A) {\n      String countryID = eanManSupport.lookupCountryIdentifier(resultString);\n      if (countryID != null) {\n        decodeResult.putMetadata(ResultMetadataType.POSSIBLE_COUNTRY, countryID);\n      }\n    }\n\n    return decodeResult;\n  }\n\n  /**\n   * @param s string of digits to check\n   * @return {@link #checkStandardUPCEANChecksum(CharSequence)}\n   * @throws FormatException if the string does not contain only digits\n   */\n  boolean checkChecksum(String s) throws FormatException {\n    return checkStandardUPCEANChecksum(s);\n  }\n\n  /**\n   * Computes the UPC/EAN checksum on a string of digits, and reports\n   * whether the checksum is correct or not.\n   *\n   * @param s string of digits to check\n   * @return true iff string of digits passes the UPC/EAN checksum algorithm\n   * @throws FormatException if the string does not contain only digits\n   */\n  static boolean checkStandardUPCEANChecksum(CharSequence s) throws FormatException {\n    int length = s.length();\n    if (length == 0) {\n      return false;\n    }\n    int check = Character.digit(s.charAt(length - 1), 10);\n    return getStandardUPCEANChecksum(s.subSequence(0, length - 1)) == check;\n  }\n\n  static int getStandardUPCEANChecksum(CharSequence s) throws FormatException {\n    int length = s.length();\n    int sum = 0;\n    for (int i = length - 1; i >= 0; i -= 2) {\n      int digit = s.charAt(i) - '0';\n      if (digit < 0 || digit > 9) {\n        throw FormatException.getFormatInstance();\n      }\n      sum += digit;\n    }\n    sum *= 3;\n    for (int i = length - 2; i >= 0; i -= 2) {\n      int digit = s.charAt(i) - '0';\n      if (digit < 0 || digit > 9) {\n        throw FormatException.getFormatInstance();\n      }\n      sum += digit;\n    }\n    return (1000 - sum) % 10;\n  }\n\n  int[] decodeEnd(BitArray row, int endStart) throws NotFoundException {\n    return findGuardPattern(row, endStart, false, START_END_PATTERN);\n  }\n\n  static int[] findGuardPattern(BitArray row,\n                                int rowOffset,\n                                boolean whiteFirst,\n                                int[] pattern) throws NotFoundException {\n    return findGuardPattern(row, rowOffset, whiteFirst, pattern, new int[pattern.length]);\n  }\n\n  /**\n   * @param row row of black/white values to search\n   * @param rowOffset position to start search\n   * @param whiteFirst if true, indicates that the pattern specifies white/black/white/...\n   * pixel counts, otherwise, it is interpreted as black/white/black/...\n   * @param pattern pattern of counts of number of black and white pixels that are being\n   * searched for as a pattern\n   * @param counters array of counters, as long as pattern, to re-use\n   * @return start/end horizontal offset of guard pattern, as an array of two ints\n   * @throws NotFoundException if pattern is not found\n   */\n  private static int[] findGuardPattern(BitArray row,\n                                        int rowOffset,\n                                        boolean whiteFirst,\n                                        int[] pattern,\n                                        int[] counters) throws NotFoundException {\n    int width = row.getSize();\n    rowOffset = whiteFirst ? row.getNextUnset(rowOffset) : row.getNextSet(rowOffset);\n    int counterPosition = 0;\n    int patternStart = rowOffset;\n    int patternLength = pattern.length;\n    boolean isWhite = whiteFirst;\n    for (int x = rowOffset; x < width; x++) {\n      if (row.get(x) != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (counterPosition == patternLength - 1) {\n          if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {\n            return new int[]{patternStart, x};\n          }\n          patternStart += counters[0] + counters[1];\n          System.arraycopy(counters, 2, counters, 0, counterPosition - 1);\n          counters[counterPosition - 1] = 0;\n          counters[counterPosition] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        counters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * Attempts to decode a single UPC/EAN-encoded digit.\n   *\n   * @param row row of black/white values to decode\n   * @param counters the counts of runs of observed black/white/black/... values\n   * @param rowOffset horizontal offset to start decoding from\n   * @param patterns the set of patterns to use to decode -- sometimes different encodings\n   * for the digits 0-9 are used, and this indicates the encodings for 0 to 9 that should\n   * be used\n   * @return horizontal offset of first pixel beyond the decoded digit\n   * @throws NotFoundException if digit cannot be decoded\n   */\n  static int decodeDigit(BitArray row, int[] counters, int rowOffset, int[][] patterns)\n      throws NotFoundException {\n    recordPattern(row, rowOffset, counters);\n    float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept\n    int bestMatch = -1;\n    int max = patterns.length;\n    for (int i = 0; i < max; i++) {\n      int[] pattern = patterns[i];\n      float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE);\n      if (variance < bestVariance) {\n        bestVariance = variance;\n        bestMatch = i;\n      }\n    }\n    if (bestMatch >= 0) {\n      return bestMatch;\n    } else {\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n  /**\n   * Get the format of this decoder.\n   *\n   * @return The 1D format.\n   */\n  abstract BarcodeFormat getBarcodeFormat();\n\n  /**\n   * Subclasses override this to decode the portion of a barcode between the start\n   * and end guard patterns.\n   *\n   * @param row row of black/white values to search\n   * @param startRange start/end offset of start guard pattern\n   * @param resultString {@link StringBuilder} to append decoded chars to\n   * @return horizontal offset of first pixel after the \"middle\" that was decoded\n   * @throws NotFoundException if decoding could not complete successfully\n   */\n  protected abstract int decodeMiddle(BitArray row,\n                                      int[] startRange,\n                                      StringBuilder resultString) throws NotFoundException;\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCEANWriter.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\n/**\n * <p>Encapsulates functionality and implementation that is common to UPC and EAN families\n * of one-dimensional barcodes.</p>\n *\n * @author aripollak@gmail.com (Ari Pollak)\n * @author dsbnatut@gmail.com (Kazuki Nishiura)\n */\npublic abstract class UPCEANWriter extends OneDimensionalCodeWriter {\n\n  @Override\n  public int getDefaultMargin() {\n    // Use a different default more appropriate for UPC/EAN\n    return 9;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCEReader.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * <p>Implements decoding of the UPC-E format.</p>\n * <p><a href=\"http://www.barcodeisland.com/upce.phtml\">This</a> is a great reference for\n * UPC-E information.</p>\n *\n * @author Sean Owen\n */\npublic final class UPCEReader extends UPCEANReader {\n\n  /**\n   * The pattern that marks the middle, and end, of a UPC-E pattern.\n   * There is no \"second half\" to a UPC-E barcode.\n   */\n  private static final int[] MIDDLE_END_PATTERN = {1, 1, 1, 1, 1, 1};\n\n  // For an UPC-E barcode, the final digit is represented by the parities used\n  // to encode the middle six digits, according to the table below.\n  //\n  //                Parity of next 6 digits\n  //    Digit   0     1     2     3     4     5\n  //       0    Even   Even  Even Odd  Odd   Odd\n  //       1    Even   Even  Odd  Even Odd   Odd\n  //       2    Even   Even  Odd  Odd  Even  Odd\n  //       3    Even   Even  Odd  Odd  Odd   Even\n  //       4    Even   Odd   Even Even Odd   Odd\n  //       5    Even   Odd   Odd  Even Even  Odd\n  //       6    Even   Odd   Odd  Odd  Even  Even\n  //       7    Even   Odd   Even Odd  Even  Odd\n  //       8    Even   Odd   Even Odd  Odd   Even\n  //       9    Even   Odd   Odd  Even Odd   Even\n  //\n  // The encoding is represented by the following array, which is a bit pattern\n  // using Odd = 0 and Even = 1. For example, 5 is represented by:\n  //\n  //              Odd Even Even Odd Odd Even\n  // in binary:\n  //                0    1    1   0   0    1   == 0x19\n  //\n\n  /**\n   * See {@link #L_AND_G_PATTERNS}; these values similarly represent patterns of\n   * even-odd parity encodings of digits that imply both the number system (0 or 1)\n   * used, and the check digit.\n   */\n  static final int[][] NUMSYS_AND_CHECK_DIGIT_PATTERNS = {\n      {0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25},\n      {0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A}\n  };\n\n  private final int[] decodeMiddleCounters;\n\n  public UPCEReader() {\n    decodeMiddleCounters = new int[4];\n  }\n\n  @Override\n  protected int decodeMiddle(BitArray row, int[] startRange, StringBuilder result)\n      throws NotFoundException {\n    int[] counters = decodeMiddleCounters;\n    counters[0] = 0;\n    counters[1] = 0;\n    counters[2] = 0;\n    counters[3] = 0;\n    int end = row.getSize();\n    int rowOffset = startRange[1];\n\n    int lgPatternFound = 0;\n\n    for (int x = 0; x < 6 && rowOffset < end; x++) {\n      int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS);\n      result.append((char) ('0' + bestMatch % 10));\n      for (int counter : counters) {\n        rowOffset += counter;\n      }\n      if (bestMatch >= 10) {\n        lgPatternFound |= 1 << (5 - x);\n      }\n    }\n\n    determineNumSysAndCheckDigit(result, lgPatternFound);\n\n    return rowOffset;\n  }\n\n  @Override\n  protected int[] decodeEnd(BitArray row, int endStart) throws NotFoundException {\n    return findGuardPattern(row, endStart, true, MIDDLE_END_PATTERN);\n  }\n\n  @Override\n  protected boolean checkChecksum(String s) throws FormatException {\n    return super.checkChecksum(convertUPCEtoUPCA(s));\n  }\n\n  private static void determineNumSysAndCheckDigit(StringBuilder resultString, int lgPatternFound)\n      throws NotFoundException {\n\n    for (int numSys = 0; numSys <= 1; numSys++) {\n      for (int d = 0; d < 10; d++) {\n        if (lgPatternFound == NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) {\n          resultString.insert(0, (char) ('0' + numSys));\n          resultString.append((char) ('0' + d));\n          return;\n        }\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  @Override\n  BarcodeFormat getBarcodeFormat() {\n    return BarcodeFormat.UPC_E;\n  }\n\n  /**\n   * Expands a UPC-E value back into its full, equivalent UPC-A code value.\n   *\n   * @param upce UPC-E code as string of digits\n   * @return equivalent UPC-A code as string of digits\n   */\n  public static String convertUPCEtoUPCA(String upce) {\n    char[] upceChars = new char[6];\n    upce.getChars(1, 7, upceChars, 0);\n    StringBuilder result = new StringBuilder(12);\n    result.append(upce.charAt(0));\n    char lastChar = upceChars[5];\n    switch (lastChar) {\n      case '0':\n      case '1':\n      case '2':\n        result.append(upceChars, 0, 2);\n        result.append(lastChar);\n        result.append(\"0000\");\n        result.append(upceChars, 2, 3);\n        break;\n      case '3':\n        result.append(upceChars, 0, 3);\n        result.append(\"00000\");\n        result.append(upceChars, 3, 2);\n        break;\n      case '4':\n        result.append(upceChars, 0, 4);\n        result.append(\"00000\");\n        result.append(upceChars[4]);\n        break;\n      default:\n        result.append(upceChars, 0, 5);\n        result.append(\"0000\");\n        result.append(lastChar);\n        break;\n    }\n    // Only append check digit in conversion if supplied\n    if (upce.length() >= 8) {\n      result.append(upce.charAt(7));\n    }\n    return result.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/UPCEWriter.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned;\n\nimport java.util.Map;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * This object renders an UPC-E code as a {@link BitMatrix}.\n *\n * @author 0979097955s@gmail.com (RX)\n */\npublic final class UPCEWriter extends UPCEANWriter {\n\n  private static final int CODE_WIDTH = 3 + // start guard\n      (7 * 6) + // bars\n      6; // end guard\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType, ?> hints) throws WriterException {\n    if (format != BarcodeFormat.UPC_E) {\n      throw new IllegalArgumentException(\"Can only encode UPC_E, but got \" + format);\n    }\n\n    return super.encode(contents, format, width, height, hints);\n  }\n\n  @Override\n  public boolean[] encode(String contents) {\n    int length = contents.length();\n    switch (length) {\n      case 7:\n        // No check digit present, calculate it and add it\n        int check;\n        try {\n          check = UPCEANReader.getStandardUPCEANChecksum(UPCEReader.convertUPCEtoUPCA(contents));\n        } catch (FormatException fe) {\n          throw new IllegalArgumentException(fe);\n        }\n        contents += check;\n        break;\n      case 8:\n        try {\n          if (!UPCEANReader.checkStandardUPCEANChecksum(contents)) {\n            throw new IllegalArgumentException(\"Contents do not pass checksum\");\n          }\n        } catch (FormatException ignored) {\n          throw new IllegalArgumentException(\"Illegal contents\");\n        }\n        break;\n      default:\n        throw new IllegalArgumentException(\n            \"Requested contents should be 7 or 8 digits long, but got \" + length);\n    }\n\n    checkNumeric(contents);\n\n    int firstDigit = Character.digit(contents.charAt(0), 10);\n    if (firstDigit != 0 && firstDigit != 1) {\n      throw new IllegalArgumentException(\"Number system must be 0 or 1\");\n    }\n\n    int checkDigit = Character.digit(contents.charAt(7), 10);\n    int parities = UPCEReader.NUMSYS_AND_CHECK_DIGIT_PATTERNS[firstDigit][checkDigit];\n    boolean[] result = new boolean[CODE_WIDTH];\n    int pos = 0;\n\n    pos += appendPattern(result, pos, UPCEANReader.START_END_PATTERN, true);\n\n    for (int i = 1; i <= 6; i++) {\n      int digit = Character.digit(contents.charAt(i), 10);\n      if ((parities >> (6 - i) & 1) == 1) {\n        digit += 10;\n      }\n      pos += appendPattern(result, pos, UPCEANReader.L_AND_G_PATTERNS[digit], false);\n    }\n\n    appendPattern(result, pos, UPCEANReader.END_PATTERN, false);\n\n    return result;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/AbstractRSSReader.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned.rss;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.detector.MathUtils;\nimport com.google.zxing.oned.OneDReader;\n\n/**\n * Superclass of {@link OneDReader} implementations that read barcodes in the RSS family\n * of formats.\n */\npublic abstract class AbstractRSSReader extends OneDReader {\n\n  private static final float MAX_AVG_VARIANCE = 0.2f;\n  private static final float MAX_INDIVIDUAL_VARIANCE = 0.45f;\n\n  private static final float MIN_FINDER_PATTERN_RATIO = 9.5f / 12.0f;\n  private static final float MAX_FINDER_PATTERN_RATIO = 12.5f / 14.0f;\n\n  private final int[] decodeFinderCounters;\n  private final int[] dataCharacterCounters;\n  private final float[] oddRoundingErrors;\n  private final float[] evenRoundingErrors;\n  private final int[] oddCounts;\n  private final int[] evenCounts;\n\n  protected AbstractRSSReader() {\n    decodeFinderCounters = new int[4];\n    dataCharacterCounters = new int[8];\n    oddRoundingErrors = new float[4];\n    evenRoundingErrors = new float[4];\n    oddCounts = new int[dataCharacterCounters.length / 2];\n    evenCounts = new int[dataCharacterCounters.length / 2];\n  }\n\n  protected final int[] getDecodeFinderCounters() {\n    return decodeFinderCounters;\n  }\n\n  protected final int[] getDataCharacterCounters() {\n    return dataCharacterCounters;\n  }\n\n  protected final float[] getOddRoundingErrors() {\n    return oddRoundingErrors;\n  }\n\n  protected final float[] getEvenRoundingErrors() {\n    return evenRoundingErrors;\n  }\n\n  protected final int[] getOddCounts() {\n    return oddCounts;\n  }\n\n  protected final int[] getEvenCounts() {\n    return evenCounts;\n  }\n\n  protected static int parseFinderValue(int[] counters,\n                                        int[][] finderPatterns) throws NotFoundException {\n    for (int value = 0; value < finderPatterns.length; value++) {\n      if (patternMatchVariance(counters, finderPatterns[value], MAX_INDIVIDUAL_VARIANCE) <\n          MAX_AVG_VARIANCE) {\n        return value;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * @param array values to sum\n   * @return sum of values\n   * @deprecated call {@link MathUtils#sum(int[])}\n   */\n  @Deprecated\n  protected static int count(int[] array) {\n    return MathUtils.sum(array);\n  }\n\n  protected static void increment(int[] array, float[] errors) {\n    int index = 0;\n    float biggestError = errors[0];\n    for (int i = 1; i < array.length; i++) {\n      if (errors[i] > biggestError) {\n        biggestError = errors[i];\n        index = i;\n      }\n    }\n    array[index]++;\n  }\n\n  protected static void decrement(int[] array, float[] errors) {\n    int index = 0;\n    float biggestError = errors[0];\n    for (int i = 1; i < array.length; i++) {\n      if (errors[i] < biggestError) {\n        biggestError = errors[i];\n        index = i;\n      }\n    }\n    array[index]--;\n  }\n\n  protected static boolean isFinderPattern(int[] counters) {\n    int firstTwoSum = counters[0] + counters[1];\n    int sum = firstTwoSum + counters[2] + counters[3];\n    float ratio = firstTwoSum / (float) sum;\n    if (ratio >= MIN_FINDER_PATTERN_RATIO && ratio <= MAX_FINDER_PATTERN_RATIO) {\n      // passes ratio test in spec, but see if the counts are unreasonable\n      int minCounter = Integer.MAX_VALUE;\n      int maxCounter = Integer.MIN_VALUE;\n      for (int counter : counters) {\n        if (counter > maxCounter) {\n          maxCounter = counter;\n        }\n        if (counter < minCounter) {\n          minCounter = counter;\n        }\n      }\n      return maxCounter < 10 * minCounter;\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/DataCharacter.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned.rss;\n\n/**\n * Encapsulates a since character value in an RSS barcode, including its checksum information.\n */\npublic class DataCharacter {\n\n  private final int value;\n  private final int checksumPortion;\n\n  public DataCharacter(int value, int checksumPortion) {\n    this.value = value;\n    this.checksumPortion = checksumPortion;\n  }\n\n  public final int getValue() {\n    return value;\n  }\n\n  public final int getChecksumPortion() {\n    return checksumPortion;\n  }\n\n  @Override\n  public final String toString() {\n    return value + \"(\" + checksumPortion + ')';\n  }\n\n  @Override\n  public final boolean equals(Object o) {\n    if (!(o instanceof DataCharacter)) {\n      return false;\n    }\n    DataCharacter that = (DataCharacter) o;\n    return value == that.value && checksumPortion == that.checksumPortion;\n  }\n\n  @Override\n  public final int hashCode() {\n    return value ^ checksumPortion;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/FinderPattern.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned.rss;\n\nimport com.google.zxing.ResultPoint;\n\n/**\n * Encapsulates an RSS barcode finder pattern, including its start/end position and row.\n */\npublic final class FinderPattern {\n\n  private final int value;\n  private final int[] startEnd;\n  private final ResultPoint[] resultPoints;\n\n  public FinderPattern(int value, int[] startEnd, int start, int end, int rowNumber) {\n    this.value = value;\n    this.startEnd = startEnd;\n    this.resultPoints = new ResultPoint[] {\n        new ResultPoint(start, rowNumber),\n        new ResultPoint(end, rowNumber),\n    };\n  }\n\n  public int getValue() {\n    return value;\n  }\n\n  public int[] getStartEnd() {\n    return startEnd;\n  }\n\n  public ResultPoint[] getResultPoints() {\n    return resultPoints;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (!(o instanceof FinderPattern)) {\n      return false;\n    }\n    FinderPattern that = (FinderPattern) o;\n    return value == that.value;\n  }\n\n  @Override\n  public int hashCode() {\n    return value;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/Pair.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned.rss;\n\nfinal class Pair extends DataCharacter {\n\n  private final FinderPattern finderPattern;\n  private int count;\n\n  Pair(int value, int checksumPortion, FinderPattern finderPattern) {\n    super(value, checksumPortion);\n    this.finderPattern = finderPattern;\n  }\n\n  FinderPattern getFinderPattern() {\n    return finderPattern;\n  }\n\n  int getCount() {\n    return count;\n  }\n\n  void incrementCount() {\n    count++;\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/RSS14Reader.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned.rss;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.common.detector.MathUtils;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Decodes RSS-14, including truncated and stacked variants. See ISO/IEC 24724:2006.\n */\npublic final class RSS14Reader extends AbstractRSSReader {\n\n  private static final int[] OUTSIDE_EVEN_TOTAL_SUBSET = {1,10,34,70,126};\n  private static final int[] INSIDE_ODD_TOTAL_SUBSET = {4,20,48,81};\n  private static final int[] OUTSIDE_GSUM = {0,161,961,2015,2715};\n  private static final int[] INSIDE_GSUM = {0,336,1036,1516};\n  private static final int[] OUTSIDE_ODD_WIDEST = {8,6,4,3,1};\n  private static final int[] INSIDE_ODD_WIDEST = {2,4,6,8};\n\n  private static final int[][] FINDER_PATTERNS = {\n      {3,8,2,1},\n      {3,5,5,1},\n      {3,3,7,1},\n      {3,1,9,1},\n      {2,7,4,1},\n      {2,5,6,1},\n      {2,3,8,1},\n      {1,5,7,1},\n      {1,3,9,1},\n  };\n\n  private final List<Pair> possibleLeftPairs;\n  private final List<Pair> possibleRightPairs;\n\n  public RSS14Reader() {\n    possibleLeftPairs = new ArrayList<>();\n    possibleRightPairs = new ArrayList<>();\n  }\n\n  @Override\n  public Result decodeRow(int rowNumber,\n                          BitArray row,\n                          Map<DecodeHintType,?> hints) throws NotFoundException {\n    Pair leftPair = decodePair(row, false, rowNumber, hints);\n    addOrTally(possibleLeftPairs, leftPair);\n    row.reverse();\n    Pair rightPair = decodePair(row, true, rowNumber, hints);\n    addOrTally(possibleRightPairs, rightPair);\n    row.reverse();\n    for (Pair left : possibleLeftPairs) {\n      if (left.getCount() > 1) {\n        for (Pair right : possibleRightPairs) {\n          if (right.getCount() > 1 && checkChecksum(left, right)) {\n            return constructResult(left, right);\n          }\n        }\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private static void addOrTally(Collection<Pair> possiblePairs, Pair pair) {\n    if (pair == null) {\n      return;\n    }\n    boolean found = false;\n    for (Pair other : possiblePairs) {\n      if (other.getValue() == pair.getValue()) {\n        other.incrementCount();\n        found = true;\n        break;\n      }\n    }\n    if (!found) {\n      possiblePairs.add(pair);\n    }\n  }\n\n  @Override\n  public void reset() {\n    possibleLeftPairs.clear();\n    possibleRightPairs.clear();\n  }\n\n  private static Result constructResult(Pair leftPair, Pair rightPair) {\n    long symbolValue = 4537077L * leftPair.getValue() + rightPair.getValue();\n    String text = String.valueOf(symbolValue);\n\n    StringBuilder buffer = new StringBuilder(14);\n    for (int i = 13 - text.length(); i > 0; i--) {\n      buffer.append('0');\n    }\n    buffer.append(text);\n\n    int checkDigit = 0;\n    for (int i = 0; i < 13; i++) {\n      int digit = buffer.charAt(i) - '0';\n      checkDigit += (i & 0x01) == 0 ? 3 * digit : digit;\n    }\n    checkDigit = 10 - (checkDigit % 10);\n    if (checkDigit == 10) {\n      checkDigit = 0;\n    }\n    buffer.append(checkDigit);\n\n    ResultPoint[] leftPoints = leftPair.getFinderPattern().getResultPoints();\n    ResultPoint[] rightPoints = rightPair.getFinderPattern().getResultPoints();\n    return new Result(\n        buffer.toString(),\n        null,\n        new ResultPoint[] { leftPoints[0], leftPoints[1], rightPoints[0], rightPoints[1], },\n        BarcodeFormat.RSS_14);\n  }\n\n  private static boolean checkChecksum(Pair leftPair, Pair rightPair) {\n    //int leftFPValue = leftPair.getFinderPattern().getValue();\n    //int rightFPValue = rightPair.getFinderPattern().getValue();\n    //if ((leftFPValue == 0 && rightFPValue == 8) ||\n    //    (leftFPValue == 8 && rightFPValue == 0)) {\n    //}\n    int checkValue = (leftPair.getChecksumPortion() + 16 * rightPair.getChecksumPortion()) % 79;\n    int targetCheckValue =\n        9 * leftPair.getFinderPattern().getValue() + rightPair.getFinderPattern().getValue();\n    if (targetCheckValue > 72) {\n      targetCheckValue--;\n    }\n    if (targetCheckValue > 8) {\n      targetCheckValue--;\n    }\n    return checkValue == targetCheckValue;\n  }\n\n  private Pair decodePair(BitArray row, boolean right, int rowNumber, Map<DecodeHintType,?> hints) {\n    try {\n      int[] startEnd = findFinderPattern(row, right);\n      FinderPattern pattern = parseFoundFinderPattern(row, rowNumber, right, startEnd);\n\n      ResultPointCallback resultPointCallback = hints == null ? null :\n        (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);\n\n      if (resultPointCallback != null) {\n        float center = (startEnd[0] + startEnd[1]) / 2.0f;\n        if (right) {\n          // row is actually reversed\n          center = row.getSize() - 1 - center;\n        }\n        resultPointCallback.foundPossibleResultPoint(new ResultPoint(center, rowNumber));\n      }\n\n      DataCharacter outside = decodeDataCharacter(row, pattern, true);\n      DataCharacter inside = decodeDataCharacter(row, pattern, false);\n      return new Pair(1597 * outside.getValue() + inside.getValue(),\n                      outside.getChecksumPortion() + 4 * inside.getChecksumPortion(),\n                      pattern);\n    } catch (NotFoundException ignored) {\n      return null;\n    }\n  }\n\n  private DataCharacter decodeDataCharacter(BitArray row, FinderPattern pattern, boolean outsideChar)\n      throws NotFoundException {\n\n    int[] counters = getDataCharacterCounters();\n    for (int x = 0; x < counters.length; x++) {\n      counters[x] = 0;\n    }\n\n    if (outsideChar) {\n      recordPatternInReverse(row, pattern.getStartEnd()[0], counters);\n    } else {\n      recordPattern(row, pattern.getStartEnd()[1] + 1, counters);\n      // reverse it\n      for (int i = 0, j = counters.length - 1; i < j; i++, j--) {\n        int temp = counters[i];\n        counters[i] = counters[j];\n        counters[j] = temp;\n      }\n    }\n\n    int numModules = outsideChar ? 16 : 15;\n    float elementWidth = MathUtils.sum(counters) / (float) numModules;\n\n    int[] oddCounts = this.getOddCounts();\n    int[] evenCounts = this.getEvenCounts();\n    float[] oddRoundingErrors = this.getOddRoundingErrors();\n    float[] evenRoundingErrors = this.getEvenRoundingErrors();\n\n    for (int i = 0; i < counters.length; i++) {\n      float value = counters[i] / elementWidth;\n      int count = (int) (value + 0.5f); // Round\n      if (count < 1) {\n        count = 1;\n      } else if (count > 8) {\n        count = 8;\n      }\n      int offset = i / 2;\n      if ((i & 0x01) == 0) {\n        oddCounts[offset] = count;\n        oddRoundingErrors[offset] = value - count;\n      } else {\n        evenCounts[offset] = count;\n        evenRoundingErrors[offset] = value - count;\n      }\n    }\n\n    adjustOddEvenCounts(outsideChar, numModules);\n\n    int oddSum = 0;\n    int oddChecksumPortion = 0;\n    for (int i = oddCounts.length - 1; i >= 0; i--) {\n      oddChecksumPortion *= 9;\n      oddChecksumPortion += oddCounts[i];\n      oddSum += oddCounts[i];\n    }\n    int evenChecksumPortion = 0;\n    int evenSum = 0;\n    for (int i = evenCounts.length - 1; i >= 0; i--) {\n      evenChecksumPortion *= 9;\n      evenChecksumPortion += evenCounts[i];\n      evenSum += evenCounts[i];\n    }\n    int checksumPortion = oddChecksumPortion + 3 * evenChecksumPortion;\n\n    if (outsideChar) {\n      if ((oddSum & 0x01) != 0 || oddSum > 12 || oddSum < 4) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      int group = (12 - oddSum) / 2;\n      int oddWidest = OUTSIDE_ODD_WIDEST[group];\n      int evenWidest = 9 - oddWidest;\n      int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, false);\n      int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, true);\n      int tEven = OUTSIDE_EVEN_TOTAL_SUBSET[group];\n      int gSum = OUTSIDE_GSUM[group];\n      return new DataCharacter(vOdd * tEven + vEven + gSum, checksumPortion);\n    } else {\n      if ((evenSum & 0x01) != 0 || evenSum > 10 || evenSum < 4) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      int group = (10 - evenSum) / 2;\n      int oddWidest = INSIDE_ODD_WIDEST[group];\n      int evenWidest = 9 - oddWidest;\n      int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true);\n      int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false);\n      int tOdd = INSIDE_ODD_TOTAL_SUBSET[group];\n      int gSum = INSIDE_GSUM[group];\n      return new DataCharacter(vEven * tOdd + vOdd + gSum, checksumPortion);\n    }\n\n  }\n\n  private int[] findFinderPattern(BitArray row, boolean rightFinderPattern)\n      throws NotFoundException {\n\n    int[] counters = getDecodeFinderCounters();\n    counters[0] = 0;\n    counters[1] = 0;\n    counters[2] = 0;\n    counters[3] = 0;\n\n    int width = row.getSize();\n    boolean isWhite = false;\n    int rowOffset = 0;\n    while (rowOffset < width) {\n      isWhite = !row.get(rowOffset);\n      if (rightFinderPattern == isWhite) {\n        // Will encounter white first when searching for right finder pattern\n        break;\n      }\n      rowOffset++;\n    }\n\n    int counterPosition = 0;\n    int patternStart = rowOffset;\n    for (int x = rowOffset; x < width; x++) {\n      if (row.get(x) != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (counterPosition == 3) {\n          if (isFinderPattern(counters)) {\n            return new int[]{patternStart, x};\n          }\n          patternStart += counters[0] + counters[1];\n          counters[0] = counters[2];\n          counters[1] = counters[3];\n          counters[2] = 0;\n          counters[3] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        counters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n\n  }\n\n  private FinderPattern parseFoundFinderPattern(BitArray row, int rowNumber, boolean right, int[] startEnd)\n      throws NotFoundException {\n    // Actually we found elements 2-5\n    boolean firstIsBlack = row.get(startEnd[0]);\n    int firstElementStart = startEnd[0] - 1;\n    // Locate element 1\n    while (firstElementStart >= 0 && firstIsBlack != row.get(firstElementStart)) {\n      firstElementStart--;\n    }\n    firstElementStart++;\n    int firstCounter = startEnd[0] - firstElementStart;\n    // Make 'counters' hold 1-4\n    int[] counters = getDecodeFinderCounters();\n    System.arraycopy(counters, 0, counters, 1, counters.length - 1);\n    counters[0] = firstCounter;\n    int value = parseFinderValue(counters, FINDER_PATTERNS);\n    int start = firstElementStart;\n    int end = startEnd[1];\n    if (right) {\n      // row is actually reversed\n      start = row.getSize() - 1 - start;\n      end = row.getSize() - 1 - end;\n    }\n    return new FinderPattern(value, new int[] {firstElementStart, startEnd[1]}, start, end, rowNumber);\n  }\n\n  private void adjustOddEvenCounts(boolean outsideChar, int numModules) throws NotFoundException {\n\n    int oddSum = MathUtils.sum(getOddCounts());\n    int evenSum = MathUtils.sum(getEvenCounts());\n\n    boolean incrementOdd = false;\n    boolean decrementOdd = false;\n    boolean incrementEven = false;\n    boolean decrementEven = false;\n\n    if (outsideChar) {\n      if (oddSum > 12) {\n        decrementOdd = true;\n      } else if (oddSum < 4) {\n        incrementOdd = true;\n      }\n      if (evenSum > 12) {\n        decrementEven = true;\n      } else if (evenSum < 4) {\n        incrementEven = true;\n      }\n    } else {\n      if (oddSum > 11) {\n        decrementOdd = true;\n      } else if (oddSum < 5) {\n        incrementOdd = true;\n      }\n      if (evenSum > 10) {\n        decrementEven = true;\n      } else if (evenSum < 4) {\n        incrementEven = true;\n      }\n    }\n\n    int mismatch = oddSum + evenSum - numModules;\n    boolean oddParityBad = (oddSum & 0x01) == (outsideChar ? 1 : 0);\n    boolean evenParityBad = (evenSum & 0x01) == 1;\n    /*if (mismatch == 2) {\n      if (!(oddParityBad && evenParityBad)) {\n        throw ReaderException.getInstance();\n      }\n      decrementOdd = true;\n      decrementEven = true;\n    } else if (mismatch == -2) {\n      if (!(oddParityBad && evenParityBad)) {\n        throw ReaderException.getInstance();\n      }\n      incrementOdd = true;\n      incrementEven = true;\n    } else */ if (mismatch == 1) {\n      if (oddParityBad) {\n        if (evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        decrementOdd = true;\n      } else {\n        if (!evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        decrementEven = true;\n      }\n    } else if (mismatch == -1) {\n      if (oddParityBad) {\n        if (evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        incrementOdd = true;\n      } else {\n        if (!evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        incrementEven = true;\n      }\n    } else if (mismatch == 0) {\n      if (oddParityBad) {\n        if (!evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        // Both bad\n        if (oddSum < evenSum) {\n          incrementOdd = true;\n          decrementEven = true;\n        } else {\n          decrementOdd = true;\n          incrementEven = true;\n        }\n      } else {\n        if (evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        // Nothing to do!\n      }\n    } else {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    if (incrementOdd) {\n      if (decrementOdd) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      increment(getOddCounts(), getOddRoundingErrors());\n    }\n    if (decrementOdd) {\n      decrement(getOddCounts(), getOddRoundingErrors());\n    }\n    if (incrementEven) {\n      if (decrementEven) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      increment(getEvenCounts(), getOddRoundingErrors());\n    }\n    if (decrementEven) {\n      decrement(getEvenCounts(), getEvenRoundingErrors());\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/RSSUtils.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned.rss;\n\n/** Adapted from listings in ISO/IEC 24724 Appendix B and Appendix G. */\npublic final class RSSUtils {\n\n  private RSSUtils() {}\n\n  /*\n  static int[] getRSSwidths(int val, int n, int elements, int maxWidth, boolean noNarrow) {\n    int[] widths = new int[elements];\n    int bar;\n    int narrowMask = 0;\n    for (bar = 0; bar < elements - 1; bar++) {\n      narrowMask |= 1 << bar;\n      int elmWidth = 1;\n      int subVal;\n      while (true) {\n        subVal = combins(n - elmWidth - 1, elements - bar - 2);\n        if (noNarrow && (narrowMask == 0) &&\n            (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {\n          subVal -= combins(n - elmWidth - (elements - bar), elements - bar - 2);\n        }\n        if (elements - bar - 1 > 1) {\n          int lessVal = 0;\n          for (int mxwElement = n - elmWidth - (elements - bar - 2);\n               mxwElement > maxWidth;\n               mxwElement--) {\n            lessVal += combins(n - elmWidth - mxwElement - 1, elements - bar - 3);\n          }\n          subVal -= lessVal * (elements - 1 - bar);\n        } else if (n - elmWidth > maxWidth) {\n          subVal--;\n        }\n        val -= subVal;\n        if (val < 0) {\n          break;\n        }\n        elmWidth++;\n        narrowMask &= ~(1 << bar);\n      }\n      val += subVal;\n      n -= elmWidth;\n      widths[bar] = elmWidth;\n    }\n    widths[bar] = n;\n    return widths;\n  }\n   */\n\n  public static int getRSSvalue(int[] widths, int maxWidth, boolean noNarrow) {\n    int n = 0;\n    for (int width : widths) {\n      n += width;\n    }\n    int val = 0;\n    int narrowMask = 0;\n    int elements = widths.length;\n    for (int bar = 0; bar < elements - 1; bar++) {\n      int elmWidth;\n      for (elmWidth = 1, narrowMask |= 1 << bar;\n           elmWidth < widths[bar];\n           elmWidth++, narrowMask &= ~(1 << bar)) {\n        int subVal = combins(n - elmWidth - 1, elements - bar - 2);\n        if (noNarrow && (narrowMask == 0) &&\n            (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {\n          subVal -= combins(n - elmWidth - (elements - bar),\n                            elements - bar - 2);\n        }\n        if (elements - bar - 1 > 1) {\n          int lessVal = 0;\n          for (int mxwElement = n - elmWidth - (elements - bar - 2);\n               mxwElement > maxWidth; mxwElement--) {\n            lessVal += combins(n - elmWidth - mxwElement - 1,\n                               elements - bar - 3);\n          }\n          subVal -= lessVal * (elements - 1 - bar);\n        } else if (n - elmWidth > maxWidth) {\n          subVal--;\n        }\n        val += subVal;\n      }\n      n -= elmWidth;\n    }\n    return val;\n  }\n\n  private static int combins(int n, int r) {\n    int maxDenom;\n    int minDenom;\n    if (n - r > r) {\n      minDenom = r;\n      maxDenom = n - r;\n    } else {\n      minDenom = n - r;\n      maxDenom = r;\n    }\n    int val = 1;\n    int j = 1;\n    for (int i = n; i > maxDenom; i--) {\n      val *= i;\n      if (j <= minDenom) {\n        val /= j;\n        j++;\n      }\n    }\n    while (j <= minDenom) {\n      val /= j;\n      j++;\n    }\n    return val;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/BitArrayBuilder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded;\n\nimport com.google.zxing.common.BitArray;\n\nimport java.util.List;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class BitArrayBuilder {\n\n  private BitArrayBuilder() {\n  }\n\n  static BitArray buildBitArray(List<ExpandedPair> pairs) {\n    int charNumber = (pairs.size() * 2) - 1;\n    if (pairs.get(pairs.size() - 1).getRightChar() == null) {\n      charNumber -= 1;\n    }\n\n    int size = 12 * charNumber;\n\n    BitArray binary = new BitArray(size);\n    int accPos = 0;\n\n    ExpandedPair firstPair = pairs.get(0);\n    int firstValue = firstPair.getRightChar().getValue();\n    for (int i = 11; i >= 0; --i) {\n      if ((firstValue & (1 << i)) != 0) {\n        binary.set(accPos);\n      }\n      accPos++;\n    }\n\n    for (int i = 1; i < pairs.size(); ++i) {\n      ExpandedPair currentPair = pairs.get(i);\n\n      int leftValue = currentPair.getLeftChar().getValue();\n      for (int j = 11; j >= 0; --j) {\n        if ((leftValue & (1 << j)) != 0) {\n          binary.set(accPos);\n        }\n        accPos++;\n      }\n\n      if (currentPair.getRightChar() != null) {\n        int rightValue = currentPair.getRightChar().getValue();\n        for (int j = 11; j >= 0; --j) {\n          if ((rightValue & (1 << j)) != 0) {\n            binary.set(accPos);\n          }\n          accPos++;\n        }\n      }\n    }\n    return binary;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/ExpandedPair.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded;\n\nimport com.google.zxing.oned.rss.DataCharacter;\nimport com.google.zxing.oned.rss.FinderPattern;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nfinal class ExpandedPair {\n\n  private final boolean mayBeLast;\n  private final DataCharacter leftChar;\n  private final DataCharacter rightChar;\n  private final FinderPattern finderPattern;\n\n  ExpandedPair(DataCharacter leftChar,\n               DataCharacter rightChar,\n               FinderPattern finderPattern,\n               boolean mayBeLast) {\n    this.leftChar = leftChar;\n    this.rightChar = rightChar;\n    this.finderPattern = finderPattern;\n    this.mayBeLast = mayBeLast;\n  }\n\n  boolean mayBeLast() {\n    return this.mayBeLast;\n  }\n\n  DataCharacter getLeftChar() {\n    return this.leftChar;\n  }\n\n  DataCharacter getRightChar() {\n    return this.rightChar;\n  }\n\n  FinderPattern getFinderPattern() {\n    return this.finderPattern;\n  }\n\n  public boolean mustBeLast() {\n    return this.rightChar == null;\n  }\n\n  @Override\n  public String toString() {\n    return\n        \"[ \" + leftChar + \" , \" + rightChar + \" : \" +\n        (finderPattern == null ? \"null\" : finderPattern.getValue()) + \" ]\";\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (!(o instanceof ExpandedPair)) {\n      return false;\n    }\n    ExpandedPair that = (ExpandedPair) o;\n    return\n        equalsOrNull(leftChar, that.leftChar) &&\n        equalsOrNull(rightChar, that.rightChar) &&\n        equalsOrNull(finderPattern, that.finderPattern);\n  }\n\n  private static boolean equalsOrNull(Object o1, Object o2) {\n    return o1 == null ? o2 == null : o1.equals(o2);\n  }\n\n  @Override\n  public int hashCode() {\n    return hashNotNull(leftChar) ^ hashNotNull(rightChar) ^ hashNotNull(finderPattern);\n  }\n\n  private static int hashNotNull(Object o) {\n    return o == null ? 0 : o.hashCode();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/ExpandedRow.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.oned.rss.expanded;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * One row of an RSS Expanded Stacked symbol, consisting of 1+ expanded pairs.\n */\nfinal class ExpandedRow {\n\n  private final List<ExpandedPair> pairs;\n  private final int rowNumber;\n  /** Did this row of the image have to be reversed (mirrored) to recognize the pairs? */\n  private final boolean wasReversed;\n\n  ExpandedRow(List<ExpandedPair> pairs, int rowNumber, boolean wasReversed) {\n    this.pairs = new ArrayList<>(pairs);\n    this.rowNumber = rowNumber;\n    this.wasReversed = wasReversed;\n  }\n\n  List<ExpandedPair> getPairs() {\n    return this.pairs;\n  }\n\n  int getRowNumber() {\n    return this.rowNumber;\n  }\n\n  boolean isReversed() {\n    return this.wasReversed;\n  }\n\n  boolean isEquivalent(List<ExpandedPair> otherPairs) {\n    return this.pairs.equals(otherPairs);\n  }\n\n  @Override\n  public String toString() {\n    return \"{ \" + pairs + \" }\";\n  }\n\n  /**\n   * Two rows are equal if they contain the same pairs in the same order.\n   */\n  @Override\n  public boolean equals(Object o) {\n    if (!(o instanceof ExpandedRow)) {\n      return false;\n    }\n    ExpandedRow that = (ExpandedRow) o;\n    return this.pairs.equals(that.getPairs()) && wasReversed == that.wasReversed;\n  }\n\n  @Override\n  public int hashCode() {\n    return pairs.hashCode() ^ Boolean.valueOf(wasReversed).hashCode();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/RSSExpandedReader.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.common.detector.MathUtils;\nimport com.google.zxing.oned.rss.AbstractRSSReader;\nimport com.google.zxing.oned.rss.DataCharacter;\nimport com.google.zxing.oned.rss.FinderPattern;\nimport com.google.zxing.oned.rss.RSSUtils;\nimport com.google.zxing.oned.rss.expanded.decoders.AbstractExpandedDecoder;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Collections;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\npublic final class RSSExpandedReader extends AbstractRSSReader {\n\n  private static final int[] SYMBOL_WIDEST = {7, 5, 4, 3, 1};\n  private static final int[] EVEN_TOTAL_SUBSET = {4, 20, 52, 104, 204};\n  private static final int[] GSUM = {0, 348, 1388, 2948, 3988};\n\n  private static final int[][] FINDER_PATTERNS = {\n    {1,8,4,1}, // A\n    {3,6,4,1}, // B\n    {3,4,6,1}, // C\n    {3,2,8,1}, // D\n    {2,6,5,1}, // E\n    {2,2,9,1}  // F\n  };\n\n  private static final int[][] WEIGHTS = {\n    {  1,   3,   9,  27,  81,  32,  96,  77},\n    { 20,  60, 180, 118, 143,   7,  21,  63},\n    {189, 145,  13,  39, 117, 140, 209, 205},\n    {193, 157,  49, 147,  19,  57, 171,  91},\n    { 62, 186, 136, 197, 169,  85,  44, 132},\n    {185, 133, 188, 142,   4,  12,  36, 108},\n    {113, 128, 173,  97,  80,  29,  87,  50},\n    {150,  28,  84,  41, 123, 158,  52, 156},\n    { 46, 138, 203, 187, 139, 206, 196, 166},\n    { 76,  17,  51, 153,  37, 111, 122, 155},\n    { 43, 129, 176, 106, 107, 110, 119, 146},\n    { 16,  48, 144,  10,  30,  90,  59, 177},\n    {109, 116, 137, 200, 178, 112, 125, 164},\n    { 70, 210, 208, 202, 184, 130, 179, 115},\n    {134, 191, 151,  31,  93,  68, 204, 190},\n    {148,  22,  66, 198, 172,   94, 71,   2},\n    {  6,  18,  54, 162,  64,  192,154,  40},\n    {120, 149,  25,  75,  14,   42,126, 167},\n    { 79,  26,  78,  23,  69,  207,199, 175},\n    {103,  98,  83,  38, 114, 131, 182, 124},\n    {161,  61, 183, 127, 170,  88,  53, 159},\n    { 55, 165,  73,   8,  24,  72,   5,  15},\n    { 45, 135, 194, 160,  58, 174, 100,  89}\n  };\n\n  private static final int FINDER_PAT_A = 0;\n  private static final int FINDER_PAT_B = 1;\n  private static final int FINDER_PAT_C = 2;\n  private static final int FINDER_PAT_D = 3;\n  private static final int FINDER_PAT_E = 4;\n  private static final int FINDER_PAT_F = 5;\n\n  private static final int[][] FINDER_PATTERN_SEQUENCES = {\n    { FINDER_PAT_A, FINDER_PAT_A },\n    { FINDER_PAT_A, FINDER_PAT_B, FINDER_PAT_B },\n    { FINDER_PAT_A, FINDER_PAT_C, FINDER_PAT_B, FINDER_PAT_D },\n    { FINDER_PAT_A, FINDER_PAT_E, FINDER_PAT_B, FINDER_PAT_D, FINDER_PAT_C },\n    { FINDER_PAT_A, FINDER_PAT_E, FINDER_PAT_B, FINDER_PAT_D, FINDER_PAT_D, FINDER_PAT_F },\n    { FINDER_PAT_A, FINDER_PAT_E, FINDER_PAT_B, FINDER_PAT_D, FINDER_PAT_E, FINDER_PAT_F, FINDER_PAT_F },\n    { FINDER_PAT_A, FINDER_PAT_A, FINDER_PAT_B, FINDER_PAT_B, FINDER_PAT_C, FINDER_PAT_C, FINDER_PAT_D, FINDER_PAT_D },\n    { FINDER_PAT_A, FINDER_PAT_A, FINDER_PAT_B, FINDER_PAT_B, FINDER_PAT_C, FINDER_PAT_C, FINDER_PAT_D, FINDER_PAT_E, FINDER_PAT_E },\n    { FINDER_PAT_A, FINDER_PAT_A, FINDER_PAT_B, FINDER_PAT_B, FINDER_PAT_C, FINDER_PAT_C, FINDER_PAT_D, FINDER_PAT_E, FINDER_PAT_F, FINDER_PAT_F },\n    { FINDER_PAT_A, FINDER_PAT_A, FINDER_PAT_B, FINDER_PAT_B, FINDER_PAT_C, FINDER_PAT_D, FINDER_PAT_D, FINDER_PAT_E, FINDER_PAT_E, FINDER_PAT_F, FINDER_PAT_F },\n  };\n\n  private static final int MAX_PAIRS = 11;\n\n  private final List<ExpandedPair> pairs = new ArrayList<>(MAX_PAIRS);\n  private final List<ExpandedRow> rows = new ArrayList<>();\n  private final int [] startEnd = new int[2];\n  private boolean startFromEven;\n\n  @Override\n  public Result decodeRow(int rowNumber,\n                          BitArray row,\n                          Map<DecodeHintType,?> hints) throws NotFoundException, FormatException {\n    // Rows can start with even pattern in case in prev rows there where odd number of patters.\n    // So lets try twice\n    this.pairs.clear();\n    this.startFromEven = false;\n    try {\n      return constructResult(decodeRow2pairs(rowNumber, row));\n    } catch (NotFoundException e) {\n      // OK\n    }\n\n    this.pairs.clear();\n    this.startFromEven = true;\n    return constructResult(decodeRow2pairs(rowNumber, row));\n  }\n\n  @Override\n  public void reset() {\n    this.pairs.clear();\n    this.rows.clear();\n  }\n\n  // Not private for testing\n  List<ExpandedPair> decodeRow2pairs(int rowNumber, BitArray row) throws NotFoundException {\n    boolean done = false;\n    while (!done) {\n      try {\n        this.pairs.add(retrieveNextPair(row, this.pairs, rowNumber));\n      } catch (NotFoundException nfe) {\n        if (this.pairs.isEmpty()) {\n          throw nfe;\n        }\n        // exit this loop when retrieveNextPair() fails and throws\n        done = true;\n      }\n    }\n\n    // TODO: verify sequence of finder patterns as in checkPairSequence()\n    if (checkChecksum()) {\n      return this.pairs;\n    }\n\n    boolean tryStackedDecode = !this.rows.isEmpty();\n    storeRow(rowNumber, false); // TODO: deal with reversed rows\n    if (tryStackedDecode) {\n      // When the image is 180-rotated, then rows are sorted in wrong direction.\n      // Try twice with both the directions.\n      List<ExpandedPair> ps = checkRows(false);\n      if (ps != null) {\n        return ps;\n      }\n      ps = checkRows(true);\n      if (ps != null) {\n        return ps;\n      }\n    }\n\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private List<ExpandedPair> checkRows(boolean reverse) {\n    // Limit number of rows we are checking\n    // We use recursive algorithm with pure complexity and don't want it to take forever\n    // Stacked barcode can have up to 11 rows, so 25 seems reasonable enough\n    if (this.rows.size() > 25) {\n      this.rows.clear();  // We will never have a chance to get result, so clear it\n      return null;\n    }\n\n    this.pairs.clear();\n    if (reverse) {\n      Collections.reverse(this.rows);\n    }\n\n    List<ExpandedPair> ps = null;\n    try {\n      ps = checkRows(new ArrayList<ExpandedRow>(), 0);\n    } catch (NotFoundException e) {\n      // OK\n    }\n\n    if (reverse) {\n      Collections.reverse(this.rows);\n    }\n\n    return ps;\n  }\n\n  // Try to construct a valid rows sequence\n  // Recursion is used to implement backtracking\n  private List<ExpandedPair> checkRows(List<ExpandedRow> collectedRows, int currentRow) throws NotFoundException {\n    for (int i = currentRow; i < rows.size(); i++) {\n      ExpandedRow row = rows.get(i);\n      this.pairs.clear();\n      for (ExpandedRow collectedRow : collectedRows) {\n        this.pairs.addAll(collectedRow.getPairs());\n      }\n      this.pairs.addAll(row.getPairs());\n\n      if (!isValidSequence(this.pairs)) {\n        continue;\n      }\n\n      if (checkChecksum()) {\n        return this.pairs;\n      }\n\n      List<ExpandedRow> rs = new ArrayList<>(collectedRows);\n      rs.add(row);\n      try {\n        // Recursion: try to add more rows\n        return checkRows(rs, i + 1);\n      } catch (NotFoundException e) {\n        // We failed, try the next candidate\n      }\n    }\n\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  // Whether the pairs form a valid find pattern sequence,\n  // either complete or a prefix\n  private static boolean isValidSequence(List<ExpandedPair> pairs) {\n    for (int[] sequence : FINDER_PATTERN_SEQUENCES) {\n      if (pairs.size() > sequence.length) {\n        continue;\n      }\n\n      boolean stop = true;\n      for (int j = 0; j < pairs.size(); j++) {\n        if (pairs.get(j).getFinderPattern().getValue() != sequence[j]) {\n          stop = false;\n          break;\n        }\n      }\n\n      if (stop) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  private void storeRow(int rowNumber, boolean wasReversed) {\n    // Discard if duplicate above or below; otherwise insert in order by row number.\n    int insertPos = 0;\n    boolean prevIsSame = false;\n    boolean nextIsSame = false;\n    while (insertPos < this.rows.size()) {\n      ExpandedRow erow = this.rows.get(insertPos);\n      if (erow.getRowNumber() > rowNumber) {\n        nextIsSame = erow.isEquivalent(this.pairs);\n        break;\n      }\n      prevIsSame = erow.isEquivalent(this.pairs);\n      insertPos++;\n    }\n    if (nextIsSame || prevIsSame) {\n      return;\n    }\n\n    // When the row was partially decoded (e.g. 2 pairs found instead of 3),\n    // it will prevent us from detecting the barcode.\n    // Try to merge partial rows\n\n    // Check whether the row is part of an allready detected row\n    if (isPartialRow(this.pairs, this.rows)) {\n      return;\n    }\n\n    this.rows.add(insertPos, new ExpandedRow(this.pairs, rowNumber, wasReversed));\n\n    removePartialRows(this.pairs, this.rows);\n  }\n\n  // Remove all the rows that contains only specified pairs\n  private static void removePartialRows(List<ExpandedPair> pairs, List<ExpandedRow> rows) {\n    for (Iterator<ExpandedRow> iterator = rows.iterator(); iterator.hasNext();) {\n      ExpandedRow r = iterator.next();\n      if (r.getPairs().size() == pairs.size()) {\n        continue;\n      }\n      boolean allFound = true;\n      for (ExpandedPair p : r.getPairs()) {\n        boolean found = false;\n        for (ExpandedPair pp : pairs) {\n          if (p.equals(pp)) {\n            found = true;\n            break;\n          }\n        }\n        if (!found) {\n          allFound = false;\n          break;\n        }\n      }\n      if (allFound) {\n        // 'pairs' contains all the pairs from the row 'r'\n        iterator.remove();\n      }\n    }\n  }\n\n  // Returns true when one of the rows already contains all the pairs\n  private static boolean isPartialRow(Iterable<ExpandedPair> pairs, Iterable<ExpandedRow> rows) {\n    for (ExpandedRow r : rows) {\n      boolean allFound = true;\n      for (ExpandedPair p : pairs) {\n        boolean found = false;\n        for (ExpandedPair pp : r.getPairs()) {\n          if (p.equals(pp)) {\n            found = true;\n            break;\n          }\n        }\n        if (!found) {\n          allFound = false;\n          break;\n        }\n      }\n      if (allFound) {\n        // the row 'r' contain all the pairs from 'pairs'\n        return true;\n      }\n    }\n    return false;\n  }\n\n  // Only used for unit testing\n  List<ExpandedRow> getRows() {\n    return this.rows;\n  }\n\n  // Not private for unit testing\n  static Result constructResult(List<ExpandedPair> pairs) throws NotFoundException, FormatException {\n    BitArray binary = BitArrayBuilder.buildBitArray(pairs);\n\n    AbstractExpandedDecoder decoder = AbstractExpandedDecoder.createDecoder(binary);\n    String resultingString = decoder.parseInformation();\n\n    ResultPoint[] firstPoints = pairs.get(0).getFinderPattern().getResultPoints();\n    ResultPoint[] lastPoints  = pairs.get(pairs.size() - 1).getFinderPattern().getResultPoints();\n\n    return new Result(\n          resultingString,\n          null,\n          new ResultPoint[]{firstPoints[0], firstPoints[1], lastPoints[0], lastPoints[1]},\n          BarcodeFormat.RSS_EXPANDED\n      );\n  }\n\n  private boolean checkChecksum() {\n    ExpandedPair firstPair = this.pairs.get(0);\n    DataCharacter checkCharacter = firstPair.getLeftChar();\n    DataCharacter firstCharacter = firstPair.getRightChar();\n\n    if (firstCharacter == null) {\n      return false;\n    }\n\n    int checksum = firstCharacter.getChecksumPortion();\n    int s = 2;\n\n    for (int i = 1; i < this.pairs.size(); ++i) {\n      ExpandedPair currentPair = this.pairs.get(i);\n      checksum += currentPair.getLeftChar().getChecksumPortion();\n      s++;\n      DataCharacter currentRightChar = currentPair.getRightChar();\n      if (currentRightChar != null) {\n        checksum += currentRightChar.getChecksumPortion();\n        s++;\n      }\n    }\n\n    checksum %= 211;\n\n    int checkCharacterValue = 211 * (s - 4) + checksum;\n\n    return checkCharacterValue == checkCharacter.getValue();\n  }\n\n  private static int getNextSecondBar(BitArray row, int initialPos) {\n    int currentPos;\n    if (row.get(initialPos)) {\n      currentPos = row.getNextUnset(initialPos);\n      currentPos = row.getNextSet(currentPos);\n    } else {\n      currentPos = row.getNextSet(initialPos);\n      currentPos = row.getNextUnset(currentPos);\n    }\n    return currentPos;\n  }\n\n  // not private for testing\n  ExpandedPair retrieveNextPair(BitArray row, List<ExpandedPair> previousPairs, int rowNumber)\n      throws NotFoundException {\n    boolean isOddPattern  = previousPairs.size() % 2 == 0;\n    if (startFromEven) {\n      isOddPattern = !isOddPattern;\n    }\n\n    FinderPattern pattern;\n\n    boolean keepFinding = true;\n    int forcedOffset = -1;\n    do {\n      this.findNextPair(row, previousPairs, forcedOffset);\n      pattern = parseFoundFinderPattern(row, rowNumber, isOddPattern);\n      if (pattern == null) {\n        forcedOffset = getNextSecondBar(row, this.startEnd[0]);\n      } else {\n        keepFinding = false;\n      }\n    } while (keepFinding);\n\n    // When stacked symbol is split over multiple rows, there's no way to guess if this pair can be last or not.\n    // boolean mayBeLast = checkPairSequence(previousPairs, pattern);\n\n    DataCharacter leftChar  = this.decodeDataCharacter(row, pattern, isOddPattern, true);\n\n    if (!previousPairs.isEmpty() && previousPairs.get(previousPairs.size() - 1).mustBeLast()) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    DataCharacter rightChar;\n    try {\n      rightChar = this.decodeDataCharacter(row, pattern, isOddPattern, false);\n    } catch (NotFoundException ignored) {\n      rightChar = null;\n    }\n    return new ExpandedPair(leftChar, rightChar, pattern, true);\n  }\n\n  private void findNextPair(BitArray row, List<ExpandedPair> previousPairs, int forcedOffset)\n      throws NotFoundException {\n    int[] counters = this.getDecodeFinderCounters();\n    counters[0] = 0;\n    counters[1] = 0;\n    counters[2] = 0;\n    counters[3] = 0;\n\n    int width = row.getSize();\n\n    int rowOffset;\n    if (forcedOffset >= 0) {\n      rowOffset = forcedOffset;\n    } else if (previousPairs.isEmpty()) {\n      rowOffset = 0;\n    } else {\n      ExpandedPair lastPair = previousPairs.get(previousPairs.size() - 1);\n      rowOffset = lastPair.getFinderPattern().getStartEnd()[1];\n    }\n    boolean searchingEvenPair = previousPairs.size() % 2 != 0;\n    if (startFromEven) {\n      searchingEvenPair = !searchingEvenPair;\n    }\n\n    boolean isWhite = false;\n    while (rowOffset < width) {\n      isWhite = !row.get(rowOffset);\n      if (!isWhite) {\n        break;\n      }\n      rowOffset++;\n    }\n\n    int counterPosition = 0;\n    int patternStart = rowOffset;\n    for (int x = rowOffset; x < width; x++) {\n      if (row.get(x) != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (counterPosition == 3) {\n          if (searchingEvenPair) {\n            reverseCounters(counters);\n          }\n\n          if (isFinderPattern(counters)) {\n            this.startEnd[0] = patternStart;\n            this.startEnd[1] = x;\n            return;\n          }\n\n          if (searchingEvenPair) {\n            reverseCounters(counters);\n          }\n\n          patternStart += counters[0] + counters[1];\n          counters[0] = counters[2];\n          counters[1] = counters[3];\n          counters[2] = 0;\n          counters[3] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        counters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private static void reverseCounters(int [] counters) {\n    int length = counters.length;\n    for (int i = 0; i < length / 2; ++i) {\n      int tmp = counters[i];\n      counters[i] = counters[length - i - 1];\n      counters[length - i - 1] = tmp;\n    }\n  }\n\n  private FinderPattern parseFoundFinderPattern(BitArray row, int rowNumber, boolean oddPattern) {\n    // Actually we found elements 2-5.\n    int firstCounter;\n    int start;\n    int end;\n\n    if (oddPattern) {\n      // If pattern number is odd, we need to locate element 1 *before* the current block.\n\n      int firstElementStart = this.startEnd[0] - 1;\n      // Locate element 1\n      while (firstElementStart >= 0 && !row.get(firstElementStart)) {\n        firstElementStart--;\n      }\n\n      firstElementStart++;\n      firstCounter = this.startEnd[0] - firstElementStart;\n      start = firstElementStart;\n      end = this.startEnd[1];\n\n    } else {\n      // If pattern number is even, the pattern is reversed, so we need to locate element 1 *after* the current block.\n\n      start = this.startEnd[0];\n\n      end = row.getNextUnset(this.startEnd[1] + 1);\n      firstCounter = end - this.startEnd[1];\n    }\n\n    // Make 'counters' hold 1-4\n    int [] counters = this.getDecodeFinderCounters();\n    System.arraycopy(counters, 0, counters, 1, counters.length - 1);\n\n    counters[0] = firstCounter;\n    int value;\n    try {\n      value = parseFinderValue(counters, FINDER_PATTERNS);\n    } catch (NotFoundException ignored) {\n      return null;\n    }\n    return new FinderPattern(value, new int[] {start, end}, start, end, rowNumber);\n  }\n\n  DataCharacter decodeDataCharacter(BitArray row,\n                                    FinderPattern pattern,\n                                    boolean isOddPattern,\n                                    boolean leftChar) throws NotFoundException {\n    int[] counters = this.getDataCharacterCounters();\n    for (int x = 0; x < counters.length; x++) {\n      counters[x] = 0;\n    }\n\n    if (leftChar) {\n      recordPatternInReverse(row, pattern.getStartEnd()[0], counters);\n    } else {\n      recordPattern(row, pattern.getStartEnd()[1], counters);\n      // reverse it\n      for (int i = 0, j = counters.length - 1; i < j; i++, j--) {\n        int temp = counters[i];\n        counters[i] = counters[j];\n        counters[j] = temp;\n      }\n    } //counters[] has the pixels of the module\n\n    int numModules = 17; //left and right data characters have all the same length\n    float elementWidth = MathUtils.sum(counters) / (float) numModules;\n\n    // Sanity check: element width for pattern and the character should match\n    float expectedElementWidth = (pattern.getStartEnd()[1] - pattern.getStartEnd()[0]) / 15.0f;\n    if (Math.abs(elementWidth - expectedElementWidth) / expectedElementWidth > 0.3f) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    int[] oddCounts = this.getOddCounts();\n    int[] evenCounts = this.getEvenCounts();\n    float[] oddRoundingErrors = this.getOddRoundingErrors();\n    float[] evenRoundingErrors = this.getEvenRoundingErrors();\n\n    for (int i = 0; i < counters.length; i++) {\n      float value = 1.0f * counters[i] / elementWidth;\n      int count = (int) (value + 0.5f); // Round\n      if (count < 1) {\n        if (value < 0.3f) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        count = 1;\n      } else if (count > 8) {\n        if (value > 8.7f) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        count = 8;\n      }\n      int offset = i / 2;\n      if ((i & 0x01) == 0) {\n        oddCounts[offset] = count;\n        oddRoundingErrors[offset] = value - count;\n      } else {\n        evenCounts[offset] = count;\n        evenRoundingErrors[offset] = value - count;\n      }\n    }\n\n    adjustOddEvenCounts(numModules);\n\n    int weightRowNumber = 4 * pattern.getValue() + (isOddPattern ? 0 : 2) + (leftChar ? 0 : 1) - 1;\n\n    int oddSum = 0;\n    int oddChecksumPortion = 0;\n    for (int i = oddCounts.length - 1; i >= 0; i--) {\n      if (isNotA1left(pattern, isOddPattern, leftChar)) {\n        int weight = WEIGHTS[weightRowNumber][2 * i];\n        oddChecksumPortion += oddCounts[i] * weight;\n      }\n      oddSum += oddCounts[i];\n    }\n    int evenChecksumPortion = 0;\n    //int evenSum = 0;\n    for (int i = evenCounts.length - 1; i >= 0; i--) {\n      if (isNotA1left(pattern, isOddPattern, leftChar)) {\n        int weight = WEIGHTS[weightRowNumber][2 * i + 1];\n        evenChecksumPortion += evenCounts[i] * weight;\n      }\n      //evenSum += evenCounts[i];\n    }\n    int checksumPortion = oddChecksumPortion + evenChecksumPortion;\n\n    if ((oddSum & 0x01) != 0 || oddSum > 13 || oddSum < 4) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    int group = (13 - oddSum) / 2;\n    int oddWidest = SYMBOL_WIDEST[group];\n    int evenWidest = 9 - oddWidest;\n    int vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true);\n    int vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false);\n    int tEven = EVEN_TOTAL_SUBSET[group];\n    int gSum = GSUM[group];\n    int value = vOdd * tEven + vEven + gSum;\n\n    return new DataCharacter(value, checksumPortion);\n  }\n\n  private static boolean isNotA1left(FinderPattern pattern, boolean isOddPattern, boolean leftChar) {\n    // A1: pattern.getValue is 0 (A), and it's an oddPattern, and it is a left char\n    return !(pattern.getValue() == 0 && isOddPattern && leftChar);\n  }\n\n  private void adjustOddEvenCounts(int numModules) throws NotFoundException {\n\n    int oddSum = MathUtils.sum(this.getOddCounts());\n    int evenSum = MathUtils.sum(this.getEvenCounts());\n\n    boolean incrementOdd = false;\n    boolean decrementOdd = false;\n\n    if (oddSum > 13) {\n      decrementOdd = true;\n    } else if (oddSum < 4) {\n      incrementOdd = true;\n    }\n    boolean incrementEven = false;\n    boolean decrementEven = false;\n    if (evenSum > 13) {\n      decrementEven = true;\n    } else if (evenSum < 4) {\n      incrementEven = true;\n    }\n\n    int mismatch = oddSum + evenSum - numModules;\n    boolean oddParityBad = (oddSum & 0x01) == 1;\n    boolean evenParityBad = (evenSum & 0x01) == 0;\n    if (mismatch == 1) {\n      if (oddParityBad) {\n        if (evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        decrementOdd = true;\n      } else {\n        if (!evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        decrementEven = true;\n      }\n    } else if (mismatch == -1) {\n      if (oddParityBad) {\n        if (evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        incrementOdd = true;\n      } else {\n        if (!evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        incrementEven = true;\n      }\n    } else if (mismatch == 0) {\n      if (oddParityBad) {\n        if (!evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        // Both bad\n        if (oddSum < evenSum) {\n          incrementOdd = true;\n          decrementEven = true;\n        } else {\n          decrementOdd = true;\n          incrementEven = true;\n        }\n      } else {\n        if (evenParityBad) {\n          throw NotFoundException.getNotFoundInstance();\n        }\n        // Nothing to do!\n      }\n    } else {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    if (incrementOdd) {\n      if (decrementOdd) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      increment(this.getOddCounts(), this.getOddRoundingErrors());\n    }\n    if (decrementOdd) {\n      decrement(this.getOddCounts(), this.getOddRoundingErrors());\n    }\n    if (incrementEven) {\n      if (decrementEven) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      increment(this.getEvenCounts(), this.getOddRoundingErrors());\n    }\n    if (decrementEven) {\n      decrement(this.getEvenCounts(), this.getEvenRoundingErrors());\n    }\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI013103decoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nfinal class AI013103decoder extends AI013x0xDecoder {\n\n  AI013103decoder(BitArray information) {\n    super(information);\n  }\n\n  @Override\n  protected void addWeightCode(StringBuilder buf, int weight) {\n    buf.append(\"(3103)\");\n  }\n\n  @Override\n  protected int checkWeight(int weight) {\n    return weight;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI01320xDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nfinal class AI01320xDecoder extends AI013x0xDecoder {\n\n  AI01320xDecoder(BitArray information) {\n    super(information);\n  }\n\n  @Override\n  protected void addWeightCode(StringBuilder buf, int weight) {\n    if (weight < 10000) {\n      buf.append(\"(3202)\");\n    } else {\n      buf.append(\"(3203)\");\n    }\n  }\n\n  @Override\n  protected int checkWeight(int weight) {\n    if (weight < 10000) {\n      return weight;\n    }\n    return weight - 10000;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI01392xDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nfinal class AI01392xDecoder extends AI01decoder {\n\n  private static final int HEADER_SIZE = 5 + 1 + 2;\n  private static final int LAST_DIGIT_SIZE = 2;\n\n  AI01392xDecoder(BitArray information) {\n    super(information);\n  }\n\n  @Override\n  public String parseInformation() throws NotFoundException, FormatException {\n    if (this.getInformation().getSize() < HEADER_SIZE + GTIN_SIZE) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    StringBuilder buf = new StringBuilder();\n\n    encodeCompressedGtin(buf, HEADER_SIZE);\n\n    int lastAIdigit =\n        this.getGeneralDecoder().extractNumericValueFromBitArray(HEADER_SIZE + GTIN_SIZE, LAST_DIGIT_SIZE);\n    buf.append(\"(392\");\n    buf.append(lastAIdigit);\n    buf.append(')');\n\n    DecodedInformation decodedInformation =\n        this.getGeneralDecoder().decodeGeneralPurposeField(HEADER_SIZE + GTIN_SIZE + LAST_DIGIT_SIZE, null);\n    buf.append(decodedInformation.getNewString());\n\n    return buf.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI01393xDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nfinal class AI01393xDecoder extends AI01decoder {\n\n  private static final int HEADER_SIZE = 5 + 1 + 2;\n  private static final int LAST_DIGIT_SIZE = 2;\n  private static final int FIRST_THREE_DIGITS_SIZE = 10;\n\n  AI01393xDecoder(BitArray information) {\n    super(information);\n  }\n\n  @Override\n  public String parseInformation() throws NotFoundException, FormatException {\n    if (this.getInformation().getSize() < HEADER_SIZE + GTIN_SIZE) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    StringBuilder buf = new StringBuilder();\n\n    encodeCompressedGtin(buf, HEADER_SIZE);\n\n    int lastAIdigit =\n        this.getGeneralDecoder().extractNumericValueFromBitArray(HEADER_SIZE + GTIN_SIZE, LAST_DIGIT_SIZE);\n\n    buf.append(\"(393\");\n    buf.append(lastAIdigit);\n    buf.append(')');\n\n    int firstThreeDigits =\n        this.getGeneralDecoder().extractNumericValueFromBitArray(HEADER_SIZE + GTIN_SIZE + LAST_DIGIT_SIZE, FIRST_THREE_DIGITS_SIZE);\n    if (firstThreeDigits / 100 == 0) {\n      buf.append('0');\n    }\n    if (firstThreeDigits / 10 == 0) {\n      buf.append('0');\n    }\n    buf.append(firstThreeDigits);\n\n    DecodedInformation generalInformation =\n        this.getGeneralDecoder().decodeGeneralPurposeField(HEADER_SIZE + GTIN_SIZE + LAST_DIGIT_SIZE + FIRST_THREE_DIGITS_SIZE, null);\n    buf.append(generalInformation.getNewString());\n\n    return buf.toString();\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI013x0x1xDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class AI013x0x1xDecoder extends AI01weightDecoder {\n\n  private static final int HEADER_SIZE = 7 + 1;\n  private static final int WEIGHT_SIZE = 20;\n  private static final int DATE_SIZE = 16;\n\n  private final String dateCode;\n  private final String firstAIdigits;\n\n  AI013x0x1xDecoder(BitArray information, String firstAIdigits, String dateCode) {\n    super(information);\n    this.dateCode = dateCode;\n    this.firstAIdigits = firstAIdigits;\n  }\n\n  @Override\n  public String parseInformation() throws NotFoundException {\n    if (this.getInformation().getSize() != HEADER_SIZE + GTIN_SIZE + WEIGHT_SIZE + DATE_SIZE) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    StringBuilder buf = new StringBuilder();\n\n    encodeCompressedGtin(buf, HEADER_SIZE);\n    encodeCompressedWeight(buf, HEADER_SIZE + GTIN_SIZE, WEIGHT_SIZE);\n    encodeCompressedDate(buf, HEADER_SIZE + GTIN_SIZE + WEIGHT_SIZE);\n\n    return buf.toString();\n  }\n\n  private void encodeCompressedDate(StringBuilder buf, int currentPos) {\n    int numericDate = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos, DATE_SIZE);\n    if (numericDate == 38400) {\n      return;\n    }\n\n    buf.append('(');\n    buf.append(this.dateCode);\n    buf.append(')');\n\n    int day   = numericDate % 32;\n    numericDate /= 32;\n    int month = numericDate % 12 + 1;\n    numericDate /= 12;\n    int year  = numericDate;\n\n    if (year / 10 == 0) {\n      buf.append('0');\n    }\n    buf.append(year);\n    if (month / 10 == 0) {\n      buf.append('0');\n    }\n    buf.append(month);\n    if (day / 10 == 0) {\n      buf.append('0');\n    }\n    buf.append(day);\n  }\n\n  @Override\n  protected void addWeightCode(StringBuilder buf, int weight) {\n    buf.append('(');\n    buf.append(this.firstAIdigits);\n    buf.append(weight / 100000);\n    buf.append(')');\n  }\n\n  @Override\n  protected int checkWeight(int weight) {\n    return weight % 100000;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI013x0xDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nabstract class AI013x0xDecoder extends AI01weightDecoder {\n\n  private static final int HEADER_SIZE = 4 + 1;\n  private static final int WEIGHT_SIZE = 15;\n\n  AI013x0xDecoder(BitArray information) {\n    super(information);\n  }\n\n  @Override\n  public String parseInformation() throws NotFoundException {\n    if (this.getInformation().getSize() != HEADER_SIZE + GTIN_SIZE + WEIGHT_SIZE) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    StringBuilder buf = new StringBuilder();\n\n    encodeCompressedGtin(buf, HEADER_SIZE);\n    encodeCompressedWeight(buf, HEADER_SIZE + GTIN_SIZE, WEIGHT_SIZE);\n\n    return buf.toString();\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI01AndOtherAIs.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class AI01AndOtherAIs extends AI01decoder {\n\n  private static final int HEADER_SIZE = 1 + 1 + 2; //first bit encodes the linkage flag,\n                          //the second one is the encodation method, and the other two are for the variable length\n  AI01AndOtherAIs(BitArray information) {\n    super(information);\n  }\n\n  @Override\n  public String parseInformation() throws NotFoundException, FormatException {\n    StringBuilder buff = new StringBuilder();\n\n    buff.append(\"(01)\");\n    int initialGtinPosition = buff.length();\n    int firstGtinDigit = this.getGeneralDecoder().extractNumericValueFromBitArray(HEADER_SIZE, 4);\n    buff.append(firstGtinDigit);\n\n    this.encodeCompressedGtinWithoutAI(buff, HEADER_SIZE + 4, initialGtinPosition);\n\n    return this.getGeneralDecoder().decodeAllCodes(buff, HEADER_SIZE + 44);\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI01decoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nabstract class AI01decoder extends AbstractExpandedDecoder {\n\n  static final int GTIN_SIZE = 40;\n\n  AI01decoder(BitArray information) {\n    super(information);\n  }\n\n  final void encodeCompressedGtin(StringBuilder buf, int currentPos) {\n    buf.append(\"(01)\");\n    int initialPosition = buf.length();\n    buf.append('9');\n\n    encodeCompressedGtinWithoutAI(buf, currentPos, initialPosition);\n  }\n\n  final void encodeCompressedGtinWithoutAI(StringBuilder buf, int currentPos, int initialBufferPosition) {\n    for (int i = 0; i < 4; ++i) {\n      int currentBlock = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos + 10 * i, 10);\n      if (currentBlock / 100 == 0) {\n        buf.append('0');\n      }\n      if (currentBlock / 10 == 0) {\n        buf.append('0');\n      }\n      buf.append(currentBlock);\n    }\n\n      appendCheckDigit(buf, initialBufferPosition);\n  }\n\n  private static void appendCheckDigit(StringBuilder buf, int currentPos) {\n    int checkDigit = 0;\n    for (int i = 0; i < 13; i++) {\n      int digit = buf.charAt(i + currentPos) - '0';\n      checkDigit += (i & 0x01) == 0 ? 3 * digit : digit;\n    }\n\n    checkDigit = 10 - (checkDigit % 10);\n    if (checkDigit == 10) {\n      checkDigit = 0;\n    }\n\n    buf.append(checkDigit);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AI01weightDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nabstract class AI01weightDecoder extends AI01decoder {\n\n  AI01weightDecoder(BitArray information) {\n    super(information);\n  }\n\n  final void encodeCompressedWeight(StringBuilder buf, int currentPos, int weightSize) {\n    int originalWeightNumeric = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos, weightSize);\n    addWeightCode(buf, originalWeightNumeric);\n\n    int weightNumeric = checkWeight(originalWeightNumeric);\n\n    int currentDivisor = 100000;\n    for (int i = 0; i < 5; ++i) {\n      if (weightNumeric / currentDivisor == 0) {\n        buf.append('0');\n      }\n      currentDivisor /= 10;\n    }\n    buf.append(weightNumeric);\n  }\n\n  protected abstract void addWeightCode(StringBuilder buf, int weight);\n\n  protected abstract int checkWeight(int weight);\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AbstractExpandedDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\npublic abstract class AbstractExpandedDecoder {\n\n  private final BitArray information;\n  private final GeneralAppIdDecoder generalDecoder;\n\n  AbstractExpandedDecoder(BitArray information) {\n    this.information = information;\n    this.generalDecoder = new GeneralAppIdDecoder(information);\n  }\n\n  protected final BitArray getInformation() {\n    return information;\n  }\n\n  protected final GeneralAppIdDecoder getGeneralDecoder() {\n    return generalDecoder;\n  }\n\n  public abstract String parseInformation() throws NotFoundException, FormatException;\n\n  public static AbstractExpandedDecoder createDecoder(BitArray information) {\n    if (information.get(1)) {\n      return new AI01AndOtherAIs(information);\n    }\n    if (!information.get(2)) {\n      return new AnyAIDecoder(information);\n    }\n\n    int fourBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 4);\n\n    switch (fourBitEncodationMethod) {\n      case 4: return new AI013103decoder(information);\n      case 5: return new AI01320xDecoder(information);\n    }\n\n    int fiveBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 5);\n    switch (fiveBitEncodationMethod) {\n      case 12: return new AI01392xDecoder(information);\n      case 13: return new AI01393xDecoder(information);\n    }\n\n    int sevenBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 7);\n    switch (sevenBitEncodationMethod) {\n      case 56: return new AI013x0x1xDecoder(information, \"310\", \"11\");\n      case 57: return new AI013x0x1xDecoder(information, \"320\", \"11\");\n      case 58: return new AI013x0x1xDecoder(information, \"310\", \"13\");\n      case 59: return new AI013x0x1xDecoder(information, \"320\", \"13\");\n      case 60: return new AI013x0x1xDecoder(information, \"310\", \"15\");\n      case 61: return new AI013x0x1xDecoder(information, \"320\", \"15\");\n      case 62: return new AI013x0x1xDecoder(information, \"310\", \"17\");\n      case 63: return new AI013x0x1xDecoder(information, \"320\", \"17\");\n    }\n\n    throw new IllegalStateException(\"unknown decoder: \" + information);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/AnyAIDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class AnyAIDecoder extends AbstractExpandedDecoder {\n\n  private static final int HEADER_SIZE = 2 + 1 + 2;\n\n  AnyAIDecoder(BitArray information) {\n    super(information);\n  }\n\n  @Override\n  public String parseInformation() throws NotFoundException, FormatException {\n    StringBuilder buf = new StringBuilder();\n    return this.getGeneralDecoder().decodeAllCodes(buf, HEADER_SIZE);\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/BlockParsedResult.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class BlockParsedResult {\n\n  private final DecodedInformation decodedInformation;\n  private final boolean finished;\n\n  BlockParsedResult(boolean finished) {\n    this(null, finished);\n  }\n\n  BlockParsedResult(DecodedInformation information, boolean finished) {\n    this.finished = finished;\n    this.decodedInformation = information;\n  }\n\n  DecodedInformation getDecodedInformation() {\n    return this.decodedInformation;\n  }\n\n  boolean isFinished() {\n    return this.finished;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/CurrentParsingState.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nfinal class CurrentParsingState {\n\n  private int position;\n  private State encoding;\n\n  private enum State {\n    NUMERIC,\n    ALPHA,\n    ISO_IEC_646\n  }\n\n  CurrentParsingState() {\n    this.position = 0;\n    this.encoding = State.NUMERIC;\n  }\n\n  int getPosition() {\n    return position;\n  }\n\n  void setPosition(int position) {\n    this.position = position;\n  }\n\n  void incrementPosition(int delta) {\n    position += delta;\n  }\n\n  boolean isAlpha() {\n    return this.encoding == State.ALPHA;\n  }\n\n  boolean isNumeric() {\n    return this.encoding == State.NUMERIC;\n  }\n\n  boolean isIsoIec646() {\n    return this.encoding == State.ISO_IEC_646;\n  }\n\n  void setNumeric() {\n    this.encoding = State.NUMERIC;\n  }\n\n  void setAlpha() {\n    this.encoding = State.ALPHA;\n  }\n\n  void setIsoIec646() {\n    this.encoding = State.ISO_IEC_646;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/DecodedChar.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class DecodedChar extends DecodedObject {\n\n  private final char value;\n\n  static final char FNC1 = '$'; // It's not in Alphanumeric neither in ISO/IEC 646 charset\n\n  DecodedChar(int newPosition, char value) {\n    super(newPosition);\n    this.value = value;\n  }\n\n  char getValue() {\n    return this.value;\n  }\n\n  boolean isFNC1() {\n    return this.value == FNC1;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/DecodedInformation.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class DecodedInformation extends DecodedObject {\n\n  private final String newString;\n  private final int remainingValue;\n  private final boolean remaining;\n\n  DecodedInformation(int newPosition, String newString) {\n    super(newPosition);\n    this.newString = newString;\n    this.remaining = false;\n    this.remainingValue = 0;\n  }\n\n  DecodedInformation(int newPosition, String newString, int remainingValue) {\n    super(newPosition);\n    this.remaining = true;\n    this.remainingValue = remainingValue;\n    this.newString = newString;\n  }\n\n  String getNewString() {\n    return this.newString;\n  }\n\n  boolean isRemaining() {\n    return this.remaining;\n  }\n\n  int getRemainingValue() {\n    return this.remainingValue;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/DecodedNumeric.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.FormatException;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class DecodedNumeric extends DecodedObject {\n\n  private final int firstDigit;\n  private final int secondDigit;\n\n  static final int FNC1 = 10;\n\n  DecodedNumeric(int newPosition, int firstDigit, int secondDigit) throws FormatException {\n    super(newPosition);\n\n    if (firstDigit < 0 || firstDigit > 10 || secondDigit < 0 || secondDigit > 10) {\n      throw FormatException.getFormatInstance();\n    }\n\n    this.firstDigit  = firstDigit;\n    this.secondDigit = secondDigit;\n  }\n\n  int getFirstDigit() {\n    return this.firstDigit;\n  }\n\n  int getSecondDigit() {\n    return this.secondDigit;\n  }\n\n  int getValue() {\n    return this.firstDigit * 10 + this.secondDigit;\n  }\n\n  boolean isFirstDigitFNC1() {\n    return this.firstDigit == FNC1;\n  }\n\n  boolean isSecondDigitFNC1() {\n    return this.secondDigit == FNC1;\n  }\n\n  boolean isAnyFNC1() {\n    return this.firstDigit == FNC1 || this.secondDigit == FNC1;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/DecodedObject.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n */\nabstract class DecodedObject {\n\n  private final int newPosition;\n\n  DecodedObject(int newPosition) {\n    this.newPosition = newPosition;\n  }\n\n  final int getNewPosition() {\n    return this.newPosition;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/FieldParser.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.NotFoundException;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class FieldParser {\n\n  private static final Object VARIABLE_LENGTH = new Object();\n\n  private static final Object [][] TWO_DIGIT_DATA_LENGTH = {\n    // \"DIGITS\", new Integer(LENGTH)\n    //    or\n    // \"DIGITS\", VARIABLE_LENGTH, new Integer(MAX_SIZE)\n\n    { \"00\", 18},\n    { \"01\", 14},\n    { \"02\", 14},\n\n    { \"10\", VARIABLE_LENGTH, 20},\n    { \"11\", 6},\n    { \"12\", 6},\n    { \"13\", 6},\n    { \"15\", 6},\n    { \"17\", 6},\n\n    { \"20\", 2},\n    { \"21\", VARIABLE_LENGTH, 20},\n    { \"22\", VARIABLE_LENGTH, 29},\n\n    { \"30\", VARIABLE_LENGTH, 8},\n    { \"37\", VARIABLE_LENGTH, 8},\n\n    //internal company codes\n    { \"90\", VARIABLE_LENGTH, 30},\n    { \"91\", VARIABLE_LENGTH, 30},\n    { \"92\", VARIABLE_LENGTH, 30},\n    { \"93\", VARIABLE_LENGTH, 30},\n    { \"94\", VARIABLE_LENGTH, 30},\n    { \"95\", VARIABLE_LENGTH, 30},\n    { \"96\", VARIABLE_LENGTH, 30},\n    { \"97\", VARIABLE_LENGTH, 30},\n    { \"98\", VARIABLE_LENGTH, 30},\n    { \"99\", VARIABLE_LENGTH, 30},\n  };\n\n  private static final Object [][] THREE_DIGIT_DATA_LENGTH = {\n    // Same format as above\n\n    { \"240\", VARIABLE_LENGTH, 30},\n    { \"241\", VARIABLE_LENGTH, 30},\n    { \"242\", VARIABLE_LENGTH, 6},\n    { \"250\", VARIABLE_LENGTH, 30},\n    { \"251\", VARIABLE_LENGTH, 30},\n    { \"253\", VARIABLE_LENGTH, 17},\n    { \"254\", VARIABLE_LENGTH, 20},\n\n    { \"400\", VARIABLE_LENGTH, 30},\n    { \"401\", VARIABLE_LENGTH, 30},\n    { \"402\", 17},\n    { \"403\", VARIABLE_LENGTH, 30},\n    { \"410\", 13},\n    { \"411\", 13},\n    { \"412\", 13},\n    { \"413\", 13},\n    { \"414\", 13},\n    { \"420\", VARIABLE_LENGTH, 20},\n    { \"421\", VARIABLE_LENGTH, 15},\n    { \"422\", 3},\n    { \"423\", VARIABLE_LENGTH, 15},\n    { \"424\", 3},\n    { \"425\", 3},\n    { \"426\", 3},\n  };\n\n  private static final Object [][] THREE_DIGIT_PLUS_DIGIT_DATA_LENGTH = {\n    // Same format as above\n\n    { \"310\", 6},\n    { \"311\", 6},\n    { \"312\", 6},\n    { \"313\", 6},\n    { \"314\", 6},\n    { \"315\", 6},\n    { \"316\", 6},\n    { \"320\", 6},\n    { \"321\", 6},\n    { \"322\", 6},\n    { \"323\", 6},\n    { \"324\", 6},\n    { \"325\", 6},\n    { \"326\", 6},\n    { \"327\", 6},\n    { \"328\", 6},\n    { \"329\", 6},\n    { \"330\", 6},\n    { \"331\", 6},\n    { \"332\", 6},\n    { \"333\", 6},\n    { \"334\", 6},\n    { \"335\", 6},\n    { \"336\", 6},\n    { \"340\", 6},\n    { \"341\", 6},\n    { \"342\", 6},\n    { \"343\", 6},\n    { \"344\", 6},\n    { \"345\", 6},\n    { \"346\", 6},\n    { \"347\", 6},\n    { \"348\", 6},\n    { \"349\", 6},\n    { \"350\", 6},\n    { \"351\", 6},\n    { \"352\", 6},\n    { \"353\", 6},\n    { \"354\", 6},\n    { \"355\", 6},\n    { \"356\", 6},\n    { \"357\", 6},\n    { \"360\", 6},\n    { \"361\", 6},\n    { \"362\", 6},\n    { \"363\", 6},\n    { \"364\", 6},\n    { \"365\", 6},\n    { \"366\", 6},\n    { \"367\", 6},\n    { \"368\", 6},\n    { \"369\", 6},\n    { \"390\", VARIABLE_LENGTH, 15},\n    { \"391\", VARIABLE_LENGTH, 18},\n    { \"392\", VARIABLE_LENGTH, 15},\n    { \"393\", VARIABLE_LENGTH, 18},\n    { \"703\", VARIABLE_LENGTH, 30},\n  };\n\n  private static final Object [][] FOUR_DIGIT_DATA_LENGTH = {\n    // Same format as above\n\n    { \"7001\", 13},\n    { \"7002\", VARIABLE_LENGTH, 30},\n    { \"7003\", 10},\n\n    { \"8001\", 14},\n    { \"8002\", VARIABLE_LENGTH, 20},\n    { \"8003\", VARIABLE_LENGTH, 30},\n    { \"8004\", VARIABLE_LENGTH, 30},\n    { \"8005\", 6},\n    { \"8006\", 18},\n    { \"8007\", VARIABLE_LENGTH, 30},\n    { \"8008\", VARIABLE_LENGTH, 12},\n    { \"8018\", 18},\n    { \"8020\", VARIABLE_LENGTH, 25},\n    { \"8100\", 6},\n    { \"8101\", 10},\n    { \"8102\", 2},\n    { \"8110\", VARIABLE_LENGTH, 70},\n    { \"8200\", VARIABLE_LENGTH, 70},\n  };\n\n  private FieldParser() {\n  }\n\n  static String parseFieldsInGeneralPurpose(String rawInformation) throws NotFoundException {\n    if (rawInformation.isEmpty()) {\n      return null;\n    }\n\n    // Processing 2-digit AIs\n\n    if (rawInformation.length() < 2) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    String firstTwoDigits = rawInformation.substring(0, 2);\n\n    for (Object[] dataLength : TWO_DIGIT_DATA_LENGTH) {\n      if (dataLength[0].equals(firstTwoDigits)) {\n        if (dataLength[1] == VARIABLE_LENGTH) {\n          return processVariableAI(2, (Integer) dataLength[2], rawInformation);\n        }\n        return processFixedAI(2, (Integer) dataLength[1], rawInformation);\n      }\n    }\n\n    if (rawInformation.length() < 3) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    String firstThreeDigits = rawInformation.substring(0, 3);\n\n    for (Object[] dataLength : THREE_DIGIT_DATA_LENGTH) {\n      if (dataLength[0].equals(firstThreeDigits)) {\n        if (dataLength[1] == VARIABLE_LENGTH) {\n          return processVariableAI(3, (Integer) dataLength[2], rawInformation);\n        }\n        return processFixedAI(3, (Integer) dataLength[1], rawInformation);\n      }\n    }\n\n\n    for (Object[] dataLength : THREE_DIGIT_PLUS_DIGIT_DATA_LENGTH) {\n      if (dataLength[0].equals(firstThreeDigits)) {\n        if (dataLength[1] == VARIABLE_LENGTH) {\n          return processVariableAI(4, (Integer) dataLength[2], rawInformation);\n        }\n        return processFixedAI(4, (Integer) dataLength[1], rawInformation);\n      }\n    }\n\n    if (rawInformation.length() < 4) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    String firstFourDigits = rawInformation.substring(0, 4);\n\n    for (Object[] dataLength : FOUR_DIGIT_DATA_LENGTH) {\n      if (dataLength[0].equals(firstFourDigits)) {\n        if (dataLength[1] == VARIABLE_LENGTH) {\n          return processVariableAI(4, (Integer) dataLength[2], rawInformation);\n        }\n        return processFixedAI(4, (Integer) dataLength[1], rawInformation);\n      }\n    }\n\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  private static String processFixedAI(int aiSize, int fieldSize, String rawInformation) throws NotFoundException {\n    if (rawInformation.length() < aiSize) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    String ai = rawInformation.substring(0, aiSize);\n\n    if (rawInformation.length() < aiSize + fieldSize) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    String field = rawInformation.substring(aiSize, aiSize + fieldSize);\n    String remaining = rawInformation.substring(aiSize + fieldSize);\n    String result = '(' + ai + ')' + field;\n    String parsedAI = parseFieldsInGeneralPurpose(remaining);\n    return parsedAI == null ? result : result + parsedAI;\n  }\n\n  private static String processVariableAI(int aiSize, int variableFieldSize, String rawInformation)\n      throws NotFoundException {\n    String ai = rawInformation.substring(0, aiSize);\n    int maxSize;\n    if (rawInformation.length() < aiSize + variableFieldSize) {\n      maxSize = rawInformation.length();\n    } else {\n      maxSize = aiSize + variableFieldSize;\n    }\n    String field = rawInformation.substring(aiSize, maxSize);\n    String remaining = rawInformation.substring(maxSize);\n    String result = '(' + ai + ')' + field;\n    String parsedAI = parseFieldsInGeneralPurpose(remaining);\n    return parsedAI == null ? result : result + parsedAI;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/oned/rss/expanded/decoders/GeneralAppIdDecoder.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * These authors would like to acknowledge the Spanish Ministry of Industry,\n * Tourism and Trade, for the support in the project TSI020301-2008-2\n * \"PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled\n * Mobile Dynamic Environments\", led by Treelogic\n * ( http://www.treelogic.com/ ):\n *\n *   http://www.piramidepse.com/\n */\n\npackage com.google.zxing.oned.rss.expanded.decoders;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.common.BitArray;\n\n/**\n * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es)\n * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es)\n */\nfinal class GeneralAppIdDecoder {\n\n  private final BitArray information;\n  private final CurrentParsingState current = new CurrentParsingState();\n  private final StringBuilder buffer = new StringBuilder();\n\n  GeneralAppIdDecoder(BitArray information) {\n    this.information = information;\n  }\n\n  String decodeAllCodes(StringBuilder buff, int initialPosition) throws NotFoundException, FormatException {\n    int currentPosition = initialPosition;\n    String remaining = null;\n    do {\n      DecodedInformation info = this.decodeGeneralPurposeField(currentPosition, remaining);\n      String parsedFields = FieldParser.parseFieldsInGeneralPurpose(info.getNewString());\n      if (parsedFields != null) {\n        buff.append(parsedFields);\n      }\n      if (info.isRemaining()) {\n        remaining = String.valueOf(info.getRemainingValue());\n      } else {\n        remaining = null;\n      }\n\n      if (currentPosition == info.getNewPosition()) { // No step forward!\n        break;\n      }\n      currentPosition = info.getNewPosition();\n    } while (true);\n\n    return buff.toString();\n  }\n\n  private boolean isStillNumeric(int pos) {\n    // It's numeric if it still has 7 positions\n    // and one of the first 4 bits is \"1\".\n    if (pos + 7 > this.information.getSize()) {\n      return pos + 4 <= this.information.getSize();\n    }\n\n    for (int i = pos; i < pos + 3; ++i) {\n      if (this.information.get(i)) {\n        return true;\n      }\n    }\n\n    return this.information.get(pos + 3);\n  }\n\n  private DecodedNumeric decodeNumeric(int pos) throws FormatException {\n    if (pos + 7 > this.information.getSize()) {\n      int numeric = extractNumericValueFromBitArray(pos, 4);\n      if (numeric == 0) {\n        return new DecodedNumeric(this.information.getSize(), DecodedNumeric.FNC1, DecodedNumeric.FNC1);\n      }\n      return new DecodedNumeric(this.information.getSize(), numeric - 1, DecodedNumeric.FNC1);\n    }\n    int numeric = extractNumericValueFromBitArray(pos, 7);\n\n    int digit1  = (numeric - 8) / 11;\n    int digit2  = (numeric - 8) % 11;\n\n    return new DecodedNumeric(pos + 7, digit1, digit2);\n  }\n\n  int extractNumericValueFromBitArray(int pos, int bits) {\n    return extractNumericValueFromBitArray(this.information, pos, bits);\n  }\n\n  static int extractNumericValueFromBitArray(BitArray information, int pos, int bits) {\n    int value = 0;\n    for (int i = 0; i < bits; ++i) {\n      if (information.get(pos + i)) {\n        value |= 1 << (bits - i - 1);\n      }\n    }\n\n    return value;\n  }\n\n  DecodedInformation decodeGeneralPurposeField(int pos, String remaining) throws FormatException {\n    this.buffer.setLength(0);\n\n    if (remaining != null) {\n      this.buffer.append(remaining);\n    }\n\n    this.current.setPosition(pos);\n\n    DecodedInformation lastDecoded = parseBlocks();\n    if (lastDecoded != null && lastDecoded.isRemaining()) {\n      return new DecodedInformation(this.current.getPosition(), this.buffer.toString(), lastDecoded.getRemainingValue());\n    }\n    return new DecodedInformation(this.current.getPosition(), this.buffer.toString());\n  }\n\n  private DecodedInformation parseBlocks() throws FormatException {\n    boolean isFinished;\n    BlockParsedResult result;\n    do {\n      int initialPosition = current.getPosition();\n\n      if (current.isAlpha()) {\n        result = parseAlphaBlock();\n        isFinished = result.isFinished();\n      } else if (current.isIsoIec646()) {\n        result = parseIsoIec646Block();\n        isFinished = result.isFinished();\n      } else { // it must be numeric\n        result = parseNumericBlock();\n        isFinished = result.isFinished();\n      }\n\n      boolean positionChanged = initialPosition != current.getPosition();\n      if (!positionChanged && !isFinished) {\n        break;\n      }\n    } while (!isFinished);\n\n    return result.getDecodedInformation();\n  }\n\n  private BlockParsedResult parseNumericBlock() throws FormatException {\n    while (isStillNumeric(current.getPosition())) {\n      DecodedNumeric numeric = decodeNumeric(current.getPosition());\n      current.setPosition(numeric.getNewPosition());\n\n      if (numeric.isFirstDigitFNC1()) {\n        DecodedInformation information;\n        if (numeric.isSecondDigitFNC1()) {\n          information = new DecodedInformation(current.getPosition(), buffer.toString());\n        } else {\n          information = new DecodedInformation(current.getPosition(), buffer.toString(), numeric.getSecondDigit());\n        }\n        return new BlockParsedResult(information, true);\n      }\n      buffer.append(numeric.getFirstDigit());\n\n      if (numeric.isSecondDigitFNC1()) {\n        DecodedInformation information = new DecodedInformation(current.getPosition(), buffer.toString());\n        return new BlockParsedResult(information, true);\n      }\n      buffer.append(numeric.getSecondDigit());\n    }\n\n    if (isNumericToAlphaNumericLatch(current.getPosition())) {\n      current.setAlpha();\n      current.incrementPosition(4);\n    }\n    return new BlockParsedResult(false);\n  }\n\n  private BlockParsedResult parseIsoIec646Block() throws FormatException {\n    while (isStillIsoIec646(current.getPosition())) {\n      DecodedChar iso = decodeIsoIec646(current.getPosition());\n      current.setPosition(iso.getNewPosition());\n\n      if (iso.isFNC1()) {\n        DecodedInformation information = new DecodedInformation(current.getPosition(), buffer.toString());\n        return new BlockParsedResult(information, true);\n      }\n      buffer.append(iso.getValue());\n    }\n\n    if (isAlphaOr646ToNumericLatch(current.getPosition())) {\n      current.incrementPosition(3);\n      current.setNumeric();\n    } else if (isAlphaTo646ToAlphaLatch(current.getPosition())) {\n      if (current.getPosition() + 5 < this.information.getSize()) {\n        current.incrementPosition(5);\n      } else {\n        current.setPosition(this.information.getSize());\n      }\n\n      current.setAlpha();\n    }\n    return new BlockParsedResult(false);\n  }\n\n  private BlockParsedResult parseAlphaBlock() {\n    while (isStillAlpha(current.getPosition())) {\n      DecodedChar alpha = decodeAlphanumeric(current.getPosition());\n      current.setPosition(alpha.getNewPosition());\n\n      if (alpha.isFNC1()) {\n        DecodedInformation information = new DecodedInformation(current.getPosition(), buffer.toString());\n        return new BlockParsedResult(information, true); //end of the char block\n      }\n\n      buffer.append(alpha.getValue());\n    }\n\n    if (isAlphaOr646ToNumericLatch(current.getPosition())) {\n      current.incrementPosition(3);\n      current.setNumeric();\n    } else if (isAlphaTo646ToAlphaLatch(current.getPosition())) {\n      if (current.getPosition() + 5 < this.information.getSize()) {\n        current.incrementPosition(5);\n      } else {\n        current.setPosition(this.information.getSize());\n      }\n\n      current.setIsoIec646();\n    }\n    return new BlockParsedResult(false);\n  }\n\n  private boolean isStillIsoIec646(int pos) {\n    if (pos + 5 > this.information.getSize()) {\n      return false;\n    }\n\n    int fiveBitValue = extractNumericValueFromBitArray(pos, 5);\n    if (fiveBitValue >= 5 && fiveBitValue < 16) {\n      return true;\n    }\n\n    if (pos + 7 > this.information.getSize()) {\n      return false;\n    }\n\n    int sevenBitValue = extractNumericValueFromBitArray(pos, 7);\n    if (sevenBitValue >= 64 && sevenBitValue < 116) {\n      return true;\n    }\n\n    if (pos + 8 > this.information.getSize()) {\n      return false;\n    }\n\n    int eightBitValue = extractNumericValueFromBitArray(pos, 8);\n    return eightBitValue >= 232 && eightBitValue < 253;\n\n  }\n\n  private DecodedChar decodeIsoIec646(int pos) throws FormatException {\n    int fiveBitValue = extractNumericValueFromBitArray(pos, 5);\n    if (fiveBitValue == 15) {\n      return new DecodedChar(pos + 5, DecodedChar.FNC1);\n    }\n\n    if (fiveBitValue >= 5 && fiveBitValue < 15) {\n      return new DecodedChar(pos + 5, (char) ('0' + fiveBitValue - 5));\n    }\n\n    int sevenBitValue = extractNumericValueFromBitArray(pos, 7);\n\n    if (sevenBitValue >= 64 && sevenBitValue < 90) {\n      return new DecodedChar(pos + 7, (char) (sevenBitValue + 1));\n    }\n\n    if (sevenBitValue >= 90 && sevenBitValue < 116) {\n      return new DecodedChar(pos + 7, (char) (sevenBitValue + 7));\n    }\n\n    int eightBitValue = extractNumericValueFromBitArray(pos, 8);\n    char c;\n    switch (eightBitValue) {\n      case 232:\n        c = '!';\n        break;\n      case 233:\n        c = '\"';\n        break;\n      case 234:\n        c = '%';\n        break;\n      case 235:\n        c = '&';\n        break;\n      case 236:\n        c = '\\'';\n        break;\n      case 237:\n        c = '(';\n        break;\n      case 238:\n        c = ')';\n        break;\n      case 239:\n        c = '*';\n        break;\n      case 240:\n        c = '+';\n        break;\n      case 241:\n        c = ',';\n        break;\n      case 242:\n        c = '-';\n        break;\n      case 243:\n        c = '.';\n        break;\n      case 244:\n        c = '/';\n        break;\n      case 245:\n        c = ':';\n        break;\n      case 246:\n        c = ';';\n        break;\n      case 247:\n        c = '<';\n        break;\n      case 248:\n        c = '=';\n        break;\n      case 249:\n        c = '>';\n        break;\n      case 250:\n        c = '?';\n        break;\n      case 251:\n        c = '_';\n        break;\n      case 252:\n        c = ' ';\n        break;\n      default:\n        throw FormatException.getFormatInstance();\n    }\n    return new DecodedChar(pos + 8, c);\n  }\n\n  private boolean isStillAlpha(int pos) {\n    if (pos + 5 > this.information.getSize()) {\n      return false;\n    }\n\n    // We now check if it's a valid 5-bit value (0..9 and FNC1)\n    int fiveBitValue = extractNumericValueFromBitArray(pos, 5);\n    if (fiveBitValue >= 5 && fiveBitValue < 16) {\n      return true;\n    }\n\n    if (pos + 6 > this.information.getSize()) {\n      return false;\n    }\n\n    int sixBitValue =  extractNumericValueFromBitArray(pos, 6);\n    return sixBitValue >= 16 && sixBitValue < 63; // 63 not included\n  }\n\n  private DecodedChar decodeAlphanumeric(int pos) {\n    int fiveBitValue = extractNumericValueFromBitArray(pos, 5);\n    if (fiveBitValue == 15) {\n      return new DecodedChar(pos + 5, DecodedChar.FNC1);\n    }\n\n    if (fiveBitValue >= 5 && fiveBitValue < 15) {\n      return new DecodedChar(pos + 5, (char) ('0' + fiveBitValue - 5));\n    }\n\n    int sixBitValue =  extractNumericValueFromBitArray(pos, 6);\n\n    if (sixBitValue >= 32 && sixBitValue < 58) {\n      return new DecodedChar(pos + 6, (char) (sixBitValue + 33));\n    }\n\n    char c;\n    switch (sixBitValue) {\n      case 58:\n        c = '*';\n        break;\n      case 59:\n        c = ',';\n        break;\n      case 60:\n        c = '-';\n        break;\n      case 61:\n        c = '.';\n        break;\n      case 62:\n        c = '/';\n        break;\n      default:\n        throw new IllegalStateException(\"Decoding invalid alphanumeric value: \" + sixBitValue);\n    }\n    return new DecodedChar(pos + 6, c);\n  }\n\n  private boolean isAlphaTo646ToAlphaLatch(int pos) {\n    if (pos + 1 > this.information.getSize()) {\n      return false;\n    }\n\n    for (int i = 0; i < 5 && i + pos < this.information.getSize(); ++i) {\n      if (i == 2) {\n        if (!this.information.get(pos + 2)) {\n          return false;\n        }\n      } else if (this.information.get(pos + i)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  private boolean isAlphaOr646ToNumericLatch(int pos) {\n    // Next is alphanumeric if there are 3 positions and they are all zeros\n    if (pos + 3 > this.information.getSize()) {\n      return false;\n    }\n\n    for (int i = pos; i < pos + 3; ++i) {\n      if (this.information.get(i)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private boolean isNumericToAlphaNumericLatch(int pos) {\n    // Next is alphanumeric if there are 4 positions and they are all zeros, or\n    // if there is a subset of this just before the end of the symbol\n    if (pos + 1 > this.information.getSize()) {\n      return false;\n    }\n\n    for (int i = 0; i < 4 && i + pos < this.information.getSize(); ++i) {\n      if (this.information.get(pos + i)) {\n        return false;\n      }\n    }\n    return true;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/PDF417Common.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.google.zxing.pdf417;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport com.google.zxing.common.detector.MathUtils;\n\n/**\n * @author SITA Lab (kevin.osullivan@sita.aero)\n * @author Guenther Grau\n */\npublic final class PDF417Common {\n\n  public static final int NUMBER_OF_CODEWORDS = 929;\n  // Maximum Codewords (Data + Error).\n  public static final int MAX_CODEWORDS_IN_BARCODE = NUMBER_OF_CODEWORDS - 1;\n  public static final int MIN_ROWS_IN_BARCODE = 3;\n  public static final int MAX_ROWS_IN_BARCODE = 90;\n  // One left row indication column + max 30 data columns + one right row indicator column\n  //public static final int MAX_CODEWORDS_IN_ROW = 32;\n  public static final int MODULES_IN_CODEWORD = 17;\n  public static final int MODULES_IN_STOP_PATTERN = 18;\n  public static final int BARS_IN_MODULE = 8;\n\n  private static final int[] EMPTY_INT_ARRAY = {};\n\n  private PDF417Common() {\n  }\n\n  /**\n   * @param moduleBitCount values to sum\n   * @return sum of values\n   * @deprecated call {@link MathUtils#sum(int[])}\n   */\n  @Deprecated\n  public static int getBitCountSum(int[] moduleBitCount) {\n    return MathUtils.sum(moduleBitCount);\n  }\n\n  public static int[] toIntArray(Collection<Integer> list) {\n    if (list == null || list.isEmpty()) {\n      return EMPTY_INT_ARRAY;\n    }\n    int[] result = new int[list.size()];\n    int i = 0;\n    for (Integer integer : list) {\n      result[i++] = integer;\n    }\n    return result;\n  }\n\n  /**\n   * @param symbol encoded symbol to translate to a codeword\n   * @return the codeword corresponding to the symbol.\n   */\n  public static int getCodeword(int symbol) {\n    int i = Arrays.binarySearch(SYMBOL_TABLE, symbol & 0x3FFFF);\n    if (i < 0) {\n      return -1;\n    }\n    return (CODEWORD_TABLE[i] - 1) % NUMBER_OF_CODEWORDS;\n  }\n\n  /**\n   * The sorted table of all possible symbols. Extracted from the PDF417\n   * specification. The index of a symbol in this table corresponds to the\n   * index into the codeword table.\n   */\n  public static final int[] SYMBOL_TABLE = {\n      0x1025e, 0x1027a, 0x1029e, 0x102bc, 0x102f2, 0x102f4, 0x1032e, 0x1034e, 0x1035c, 0x10396, 0x103a6, 0x103ac,\n      0x10422, 0x10428, 0x10436, 0x10442, 0x10444, 0x10448, 0x10450, 0x1045e, 0x10466, 0x1046c, 0x1047a, 0x10482,\n      0x1049e, 0x104a0, 0x104bc, 0x104c6, 0x104d8, 0x104ee, 0x104f2, 0x104f4, 0x10504, 0x10508, 0x10510, 0x1051e,\n      0x10520, 0x1053c, 0x10540, 0x10578, 0x10586, 0x1058c, 0x10598, 0x105b0, 0x105be, 0x105ce, 0x105dc, 0x105e2,\n      0x105e4, 0x105e8, 0x105f6, 0x1062e, 0x1064e, 0x1065c, 0x1068e, 0x1069c, 0x106b8, 0x106de, 0x106fa, 0x10716,\n      0x10726, 0x1072c, 0x10746, 0x1074c, 0x10758, 0x1076e, 0x10792, 0x10794, 0x107a2, 0x107a4, 0x107a8, 0x107b6,\n      0x10822, 0x10828, 0x10842, 0x10848, 0x10850, 0x1085e, 0x10866, 0x1086c, 0x1087a, 0x10882, 0x10884, 0x10890,\n      0x1089e, 0x108a0, 0x108bc, 0x108c6, 0x108cc, 0x108d8, 0x108ee, 0x108f2, 0x108f4, 0x10902, 0x10908, 0x1091e,\n      0x10920, 0x1093c, 0x10940, 0x10978, 0x10986, 0x10998, 0x109b0, 0x109be, 0x109ce, 0x109dc, 0x109e2, 0x109e4,\n      0x109e8, 0x109f6, 0x10a08, 0x10a10, 0x10a1e, 0x10a20, 0x10a3c, 0x10a40, 0x10a78, 0x10af0, 0x10b06, 0x10b0c,\n      0x10b18, 0x10b30, 0x10b3e, 0x10b60, 0x10b7c, 0x10b8e, 0x10b9c, 0x10bb8, 0x10bc2, 0x10bc4, 0x10bc8, 0x10bd0,\n      0x10bde, 0x10be6, 0x10bec, 0x10c2e, 0x10c4e, 0x10c5c, 0x10c62, 0x10c64, 0x10c68, 0x10c76, 0x10c8e, 0x10c9c,\n      0x10cb8, 0x10cc2, 0x10cc4, 0x10cc8, 0x10cd0, 0x10cde, 0x10ce6, 0x10cec, 0x10cfa, 0x10d0e, 0x10d1c, 0x10d38,\n      0x10d70, 0x10d7e, 0x10d82, 0x10d84, 0x10d88, 0x10d90, 0x10d9e, 0x10da0, 0x10dbc, 0x10dc6, 0x10dcc, 0x10dd8,\n      0x10dee, 0x10df2, 0x10df4, 0x10e16, 0x10e26, 0x10e2c, 0x10e46, 0x10e58, 0x10e6e, 0x10e86, 0x10e8c, 0x10e98,\n      0x10eb0, 0x10ebe, 0x10ece, 0x10edc, 0x10f0a, 0x10f12, 0x10f14, 0x10f22, 0x10f28, 0x10f36, 0x10f42, 0x10f44,\n      0x10f48, 0x10f50, 0x10f5e, 0x10f66, 0x10f6c, 0x10fb2, 0x10fb4, 0x11022, 0x11028, 0x11042, 0x11048, 0x11050,\n      0x1105e, 0x1107a, 0x11082, 0x11084, 0x11090, 0x1109e, 0x110a0, 0x110bc, 0x110c6, 0x110cc, 0x110d8, 0x110ee,\n      0x110f2, 0x110f4, 0x11102, 0x1111e, 0x11120, 0x1113c, 0x11140, 0x11178, 0x11186, 0x11198, 0x111b0, 0x111be,\n      0x111ce, 0x111dc, 0x111e2, 0x111e4, 0x111e8, 0x111f6, 0x11208, 0x1121e, 0x11220, 0x11278, 0x112f0, 0x1130c,\n      0x11330, 0x1133e, 0x11360, 0x1137c, 0x1138e, 0x1139c, 0x113b8, 0x113c2, 0x113c8, 0x113d0, 0x113de, 0x113e6,\n      0x113ec, 0x11408, 0x11410, 0x1141e, 0x11420, 0x1143c, 0x11440, 0x11478, 0x114f0, 0x115e0, 0x1160c, 0x11618,\n      0x11630, 0x1163e, 0x11660, 0x1167c, 0x116c0, 0x116f8, 0x1171c, 0x11738, 0x11770, 0x1177e, 0x11782, 0x11784,\n      0x11788, 0x11790, 0x1179e, 0x117a0, 0x117bc, 0x117c6, 0x117cc, 0x117d8, 0x117ee, 0x1182e, 0x11834, 0x1184e,\n      0x1185c, 0x11862, 0x11864, 0x11868, 0x11876, 0x1188e, 0x1189c, 0x118b8, 0x118c2, 0x118c8, 0x118d0, 0x118de,\n      0x118e6, 0x118ec, 0x118fa, 0x1190e, 0x1191c, 0x11938, 0x11970, 0x1197e, 0x11982, 0x11984, 0x11990, 0x1199e,\n      0x119a0, 0x119bc, 0x119c6, 0x119cc, 0x119d8, 0x119ee, 0x119f2, 0x119f4, 0x11a0e, 0x11a1c, 0x11a38, 0x11a70,\n      0x11a7e, 0x11ae0, 0x11afc, 0x11b08, 0x11b10, 0x11b1e, 0x11b20, 0x11b3c, 0x11b40, 0x11b78, 0x11b8c, 0x11b98,\n      0x11bb0, 0x11bbe, 0x11bce, 0x11bdc, 0x11be2, 0x11be4, 0x11be8, 0x11bf6, 0x11c16, 0x11c26, 0x11c2c, 0x11c46,\n      0x11c4c, 0x11c58, 0x11c6e, 0x11c86, 0x11c98, 0x11cb0, 0x11cbe, 0x11cce, 0x11cdc, 0x11ce2, 0x11ce4, 0x11ce8,\n      0x11cf6, 0x11d06, 0x11d0c, 0x11d18, 0x11d30, 0x11d3e, 0x11d60, 0x11d7c, 0x11d8e, 0x11d9c, 0x11db8, 0x11dc4,\n      0x11dc8, 0x11dd0, 0x11dde, 0x11de6, 0x11dec, 0x11dfa, 0x11e0a, 0x11e12, 0x11e14, 0x11e22, 0x11e24, 0x11e28,\n      0x11e36, 0x11e42, 0x11e44, 0x11e50, 0x11e5e, 0x11e66, 0x11e6c, 0x11e82, 0x11e84, 0x11e88, 0x11e90, 0x11e9e,\n      0x11ea0, 0x11ebc, 0x11ec6, 0x11ecc, 0x11ed8, 0x11eee, 0x11f1a, 0x11f2e, 0x11f32, 0x11f34, 0x11f4e, 0x11f5c,\n      0x11f62, 0x11f64, 0x11f68, 0x11f76, 0x12048, 0x1205e, 0x12082, 0x12084, 0x12090, 0x1209e, 0x120a0, 0x120bc,\n      0x120d8, 0x120f2, 0x120f4, 0x12108, 0x1211e, 0x12120, 0x1213c, 0x12140, 0x12178, 0x12186, 0x12198, 0x121b0,\n      0x121be, 0x121e2, 0x121e4, 0x121e8, 0x121f6, 0x12204, 0x12210, 0x1221e, 0x12220, 0x12278, 0x122f0, 0x12306,\n      0x1230c, 0x12330, 0x1233e, 0x12360, 0x1237c, 0x1238e, 0x1239c, 0x123b8, 0x123c2, 0x123c8, 0x123d0, 0x123e6,\n      0x123ec, 0x1241e, 0x12420, 0x1243c, 0x124f0, 0x125e0, 0x12618, 0x1263e, 0x12660, 0x1267c, 0x126c0, 0x126f8,\n      0x12738, 0x12770, 0x1277e, 0x12782, 0x12784, 0x12790, 0x1279e, 0x127a0, 0x127bc, 0x127c6, 0x127cc, 0x127d8,\n      0x127ee, 0x12820, 0x1283c, 0x12840, 0x12878, 0x128f0, 0x129e0, 0x12bc0, 0x12c18, 0x12c30, 0x12c3e, 0x12c60,\n      0x12c7c, 0x12cc0, 0x12cf8, 0x12df0, 0x12e1c, 0x12e38, 0x12e70, 0x12e7e, 0x12ee0, 0x12efc, 0x12f04, 0x12f08,\n      0x12f10, 0x12f20, 0x12f3c, 0x12f40, 0x12f78, 0x12f86, 0x12f8c, 0x12f98, 0x12fb0, 0x12fbe, 0x12fce, 0x12fdc,\n      0x1302e, 0x1304e, 0x1305c, 0x13062, 0x13068, 0x1308e, 0x1309c, 0x130b8, 0x130c2, 0x130c8, 0x130d0, 0x130de,\n      0x130ec, 0x130fa, 0x1310e, 0x13138, 0x13170, 0x1317e, 0x13182, 0x13184, 0x13190, 0x1319e, 0x131a0, 0x131bc,\n      0x131c6, 0x131cc, 0x131d8, 0x131f2, 0x131f4, 0x1320e, 0x1321c, 0x13270, 0x1327e, 0x132e0, 0x132fc, 0x13308,\n      0x1331e, 0x13320, 0x1333c, 0x13340, 0x13378, 0x13386, 0x13398, 0x133b0, 0x133be, 0x133ce, 0x133dc, 0x133e2,\n      0x133e4, 0x133e8, 0x133f6, 0x1340e, 0x1341c, 0x13438, 0x13470, 0x1347e, 0x134e0, 0x134fc, 0x135c0, 0x135f8,\n      0x13608, 0x13610, 0x1361e, 0x13620, 0x1363c, 0x13640, 0x13678, 0x136f0, 0x1370c, 0x13718, 0x13730, 0x1373e,\n      0x13760, 0x1377c, 0x1379c, 0x137b8, 0x137c2, 0x137c4, 0x137c8, 0x137d0, 0x137de, 0x137e6, 0x137ec, 0x13816,\n      0x13826, 0x1382c, 0x13846, 0x1384c, 0x13858, 0x1386e, 0x13874, 0x13886, 0x13898, 0x138b0, 0x138be, 0x138ce,\n      0x138dc, 0x138e2, 0x138e4, 0x138e8, 0x13906, 0x1390c, 0x13930, 0x1393e, 0x13960, 0x1397c, 0x1398e, 0x1399c,\n      0x139b8, 0x139c8, 0x139d0, 0x139de, 0x139e6, 0x139ec, 0x139fa, 0x13a06, 0x13a0c, 0x13a18, 0x13a30, 0x13a3e,\n      0x13a60, 0x13a7c, 0x13ac0, 0x13af8, 0x13b0e, 0x13b1c, 0x13b38, 0x13b70, 0x13b7e, 0x13b88, 0x13b90, 0x13b9e,\n      0x13ba0, 0x13bbc, 0x13bcc, 0x13bd8, 0x13bee, 0x13bf2, 0x13bf4, 0x13c12, 0x13c14, 0x13c22, 0x13c24, 0x13c28,\n      0x13c36, 0x13c42, 0x13c48, 0x13c50, 0x13c5e, 0x13c66, 0x13c6c, 0x13c82, 0x13c84, 0x13c90, 0x13c9e, 0x13ca0,\n      0x13cbc, 0x13cc6, 0x13ccc, 0x13cd8, 0x13cee, 0x13d02, 0x13d04, 0x13d08, 0x13d10, 0x13d1e, 0x13d20, 0x13d3c,\n      0x13d40, 0x13d78, 0x13d86, 0x13d8c, 0x13d98, 0x13db0, 0x13dbe, 0x13dce, 0x13ddc, 0x13de4, 0x13de8, 0x13df6,\n      0x13e1a, 0x13e2e, 0x13e32, 0x13e34, 0x13e4e, 0x13e5c, 0x13e62, 0x13e64, 0x13e68, 0x13e76, 0x13e8e, 0x13e9c,\n      0x13eb8, 0x13ec2, 0x13ec4, 0x13ec8, 0x13ed0, 0x13ede, 0x13ee6, 0x13eec, 0x13f26, 0x13f2c, 0x13f3a, 0x13f46,\n      0x13f4c, 0x13f58, 0x13f6e, 0x13f72, 0x13f74, 0x14082, 0x1409e, 0x140a0, 0x140bc, 0x14104, 0x14108, 0x14110,\n      0x1411e, 0x14120, 0x1413c, 0x14140, 0x14178, 0x1418c, 0x14198, 0x141b0, 0x141be, 0x141e2, 0x141e4, 0x141e8,\n      0x14208, 0x14210, 0x1421e, 0x14220, 0x1423c, 0x14240, 0x14278, 0x142f0, 0x14306, 0x1430c, 0x14318, 0x14330,\n      0x1433e, 0x14360, 0x1437c, 0x1438e, 0x143c2, 0x143c4, 0x143c8, 0x143d0, 0x143e6, 0x143ec, 0x14408, 0x14410,\n      0x1441e, 0x14420, 0x1443c, 0x14440, 0x14478, 0x144f0, 0x145e0, 0x1460c, 0x14618, 0x14630, 0x1463e, 0x14660,\n      0x1467c, 0x146c0, 0x146f8, 0x1471c, 0x14738, 0x14770, 0x1477e, 0x14782, 0x14784, 0x14788, 0x14790, 0x147a0,\n      0x147bc, 0x147c6, 0x147cc, 0x147d8, 0x147ee, 0x14810, 0x14820, 0x1483c, 0x14840, 0x14878, 0x148f0, 0x149e0,\n      0x14bc0, 0x14c30, 0x14c3e, 0x14c60, 0x14c7c, 0x14cc0, 0x14cf8, 0x14df0, 0x14e38, 0x14e70, 0x14e7e, 0x14ee0,\n      0x14efc, 0x14f04, 0x14f08, 0x14f10, 0x14f1e, 0x14f20, 0x14f3c, 0x14f40, 0x14f78, 0x14f86, 0x14f8c, 0x14f98,\n      0x14fb0, 0x14fce, 0x14fdc, 0x15020, 0x15040, 0x15078, 0x150f0, 0x151e0, 0x153c0, 0x15860, 0x1587c, 0x158c0,\n      0x158f8, 0x159f0, 0x15be0, 0x15c70, 0x15c7e, 0x15ce0, 0x15cfc, 0x15dc0, 0x15df8, 0x15e08, 0x15e10, 0x15e20,\n      0x15e40, 0x15e78, 0x15ef0, 0x15f0c, 0x15f18, 0x15f30, 0x15f60, 0x15f7c, 0x15f8e, 0x15f9c, 0x15fb8, 0x1604e,\n      0x1605c, 0x1608e, 0x1609c, 0x160b8, 0x160c2, 0x160c4, 0x160c8, 0x160de, 0x1610e, 0x1611c, 0x16138, 0x16170,\n      0x1617e, 0x16184, 0x16188, 0x16190, 0x1619e, 0x161a0, 0x161bc, 0x161c6, 0x161cc, 0x161d8, 0x161f2, 0x161f4,\n      0x1620e, 0x1621c, 0x16238, 0x16270, 0x1627e, 0x162e0, 0x162fc, 0x16304, 0x16308, 0x16310, 0x1631e, 0x16320,\n      0x1633c, 0x16340, 0x16378, 0x16386, 0x1638c, 0x16398, 0x163b0, 0x163be, 0x163ce, 0x163dc, 0x163e2, 0x163e4,\n      0x163e8, 0x163f6, 0x1640e, 0x1641c, 0x16438, 0x16470, 0x1647e, 0x164e0, 0x164fc, 0x165c0, 0x165f8, 0x16610,\n      0x1661e, 0x16620, 0x1663c, 0x16640, 0x16678, 0x166f0, 0x16718, 0x16730, 0x1673e, 0x16760, 0x1677c, 0x1678e,\n      0x1679c, 0x167b8, 0x167c2, 0x167c4, 0x167c8, 0x167d0, 0x167de, 0x167e6, 0x167ec, 0x1681c, 0x16838, 0x16870,\n      0x168e0, 0x168fc, 0x169c0, 0x169f8, 0x16bf0, 0x16c10, 0x16c1e, 0x16c20, 0x16c3c, 0x16c40, 0x16c78, 0x16cf0,\n      0x16de0, 0x16e18, 0x16e30, 0x16e3e, 0x16e60, 0x16e7c, 0x16ec0, 0x16ef8, 0x16f1c, 0x16f38, 0x16f70, 0x16f7e,\n      0x16f84, 0x16f88, 0x16f90, 0x16f9e, 0x16fa0, 0x16fbc, 0x16fc6, 0x16fcc, 0x16fd8, 0x17026, 0x1702c, 0x17046,\n      0x1704c, 0x17058, 0x1706e, 0x17086, 0x1708c, 0x17098, 0x170b0, 0x170be, 0x170ce, 0x170dc, 0x170e8, 0x17106,\n      0x1710c, 0x17118, 0x17130, 0x1713e, 0x17160, 0x1717c, 0x1718e, 0x1719c, 0x171b8, 0x171c2, 0x171c4, 0x171c8,\n      0x171d0, 0x171de, 0x171e6, 0x171ec, 0x171fa, 0x17206, 0x1720c, 0x17218, 0x17230, 0x1723e, 0x17260, 0x1727c,\n      0x172c0, 0x172f8, 0x1730e, 0x1731c, 0x17338, 0x17370, 0x1737e, 0x17388, 0x17390, 0x1739e, 0x173a0, 0x173bc,\n      0x173cc, 0x173d8, 0x173ee, 0x173f2, 0x173f4, 0x1740c, 0x17418, 0x17430, 0x1743e, 0x17460, 0x1747c, 0x174c0,\n      0x174f8, 0x175f0, 0x1760e, 0x1761c, 0x17638, 0x17670, 0x1767e, 0x176e0, 0x176fc, 0x17708, 0x17710, 0x1771e,\n      0x17720, 0x1773c, 0x17740, 0x17778, 0x17798, 0x177b0, 0x177be, 0x177dc, 0x177e2, 0x177e4, 0x177e8, 0x17822,\n      0x17824, 0x17828, 0x17836, 0x17842, 0x17844, 0x17848, 0x17850, 0x1785e, 0x17866, 0x1786c, 0x17882, 0x17884,\n      0x17888, 0x17890, 0x1789e, 0x178a0, 0x178bc, 0x178c6, 0x178cc, 0x178d8, 0x178ee, 0x178f2, 0x178f4, 0x17902,\n      0x17904, 0x17908, 0x17910, 0x1791e, 0x17920, 0x1793c, 0x17940, 0x17978, 0x17986, 0x1798c, 0x17998, 0x179b0,\n      0x179be, 0x179ce, 0x179dc, 0x179e2, 0x179e4, 0x179e8, 0x179f6, 0x17a04, 0x17a08, 0x17a10, 0x17a1e, 0x17a20,\n      0x17a3c, 0x17a40, 0x17a78, 0x17af0, 0x17b06, 0x17b0c, 0x17b18, 0x17b30, 0x17b3e, 0x17b60, 0x17b7c, 0x17b8e,\n      0x17b9c, 0x17bb8, 0x17bc4, 0x17bc8, 0x17bd0, 0x17bde, 0x17be6, 0x17bec, 0x17c2e, 0x17c32, 0x17c34, 0x17c4e,\n      0x17c5c, 0x17c62, 0x17c64, 0x17c68, 0x17c76, 0x17c8e, 0x17c9c, 0x17cb8, 0x17cc2, 0x17cc4, 0x17cc8, 0x17cd0,\n      0x17cde, 0x17ce6, 0x17cec, 0x17d0e, 0x17d1c, 0x17d38, 0x17d70, 0x17d82, 0x17d84, 0x17d88, 0x17d90, 0x17d9e,\n      0x17da0, 0x17dbc, 0x17dc6, 0x17dcc, 0x17dd8, 0x17dee, 0x17e26, 0x17e2c, 0x17e3a, 0x17e46, 0x17e4c, 0x17e58,\n      0x17e6e, 0x17e72, 0x17e74, 0x17e86, 0x17e8c, 0x17e98, 0x17eb0, 0x17ece, 0x17edc, 0x17ee2, 0x17ee4, 0x17ee8,\n      0x17ef6, 0x1813a, 0x18172, 0x18174, 0x18216, 0x18226, 0x1823a, 0x1824c, 0x18258, 0x1826e, 0x18272, 0x18274,\n      0x18298, 0x182be, 0x182e2, 0x182e4, 0x182e8, 0x182f6, 0x1835e, 0x1837a, 0x183ae, 0x183d6, 0x18416, 0x18426,\n      0x1842c, 0x1843a, 0x18446, 0x18458, 0x1846e, 0x18472, 0x18474, 0x18486, 0x184b0, 0x184be, 0x184ce, 0x184dc,\n      0x184e2, 0x184e4, 0x184e8, 0x184f6, 0x18506, 0x1850c, 0x18518, 0x18530, 0x1853e, 0x18560, 0x1857c, 0x1858e,\n      0x1859c, 0x185b8, 0x185c2, 0x185c4, 0x185c8, 0x185d0, 0x185de, 0x185e6, 0x185ec, 0x185fa, 0x18612, 0x18614,\n      0x18622, 0x18628, 0x18636, 0x18642, 0x18650, 0x1865e, 0x1867a, 0x18682, 0x18684, 0x18688, 0x18690, 0x1869e,\n      0x186a0, 0x186bc, 0x186c6, 0x186cc, 0x186d8, 0x186ee, 0x186f2, 0x186f4, 0x1872e, 0x1874e, 0x1875c, 0x18796,\n      0x187a6, 0x187ac, 0x187d2, 0x187d4, 0x18826, 0x1882c, 0x1883a, 0x18846, 0x1884c, 0x18858, 0x1886e, 0x18872,\n      0x18874, 0x18886, 0x18898, 0x188b0, 0x188be, 0x188ce, 0x188dc, 0x188e2, 0x188e4, 0x188e8, 0x188f6, 0x1890c,\n      0x18930, 0x1893e, 0x18960, 0x1897c, 0x1898e, 0x189b8, 0x189c2, 0x189c8, 0x189d0, 0x189de, 0x189e6, 0x189ec,\n      0x189fa, 0x18a18, 0x18a30, 0x18a3e, 0x18a60, 0x18a7c, 0x18ac0, 0x18af8, 0x18b1c, 0x18b38, 0x18b70, 0x18b7e,\n      0x18b82, 0x18b84, 0x18b88, 0x18b90, 0x18b9e, 0x18ba0, 0x18bbc, 0x18bc6, 0x18bcc, 0x18bd8, 0x18bee, 0x18bf2,\n      0x18bf4, 0x18c22, 0x18c24, 0x18c28, 0x18c36, 0x18c42, 0x18c48, 0x18c50, 0x18c5e, 0x18c66, 0x18c7a, 0x18c82,\n      0x18c84, 0x18c90, 0x18c9e, 0x18ca0, 0x18cbc, 0x18ccc, 0x18cf2, 0x18cf4, 0x18d04, 0x18d08, 0x18d10, 0x18d1e,\n      0x18d20, 0x18d3c, 0x18d40, 0x18d78, 0x18d86, 0x18d98, 0x18dce, 0x18de2, 0x18de4, 0x18de8, 0x18e2e, 0x18e32,\n      0x18e34, 0x18e4e, 0x18e5c, 0x18e62, 0x18e64, 0x18e68, 0x18e8e, 0x18e9c, 0x18eb8, 0x18ec2, 0x18ec4, 0x18ec8,\n      0x18ed0, 0x18efa, 0x18f16, 0x18f26, 0x18f2c, 0x18f46, 0x18f4c, 0x18f58, 0x18f6e, 0x18f8a, 0x18f92, 0x18f94,\n      0x18fa2, 0x18fa4, 0x18fa8, 0x18fb6, 0x1902c, 0x1903a, 0x19046, 0x1904c, 0x19058, 0x19072, 0x19074, 0x19086,\n      0x19098, 0x190b0, 0x190be, 0x190ce, 0x190dc, 0x190e2, 0x190e8, 0x190f6, 0x19106, 0x1910c, 0x19130, 0x1913e,\n      0x19160, 0x1917c, 0x1918e, 0x1919c, 0x191b8, 0x191c2, 0x191c8, 0x191d0, 0x191de, 0x191e6, 0x191ec, 0x191fa,\n      0x19218, 0x1923e, 0x19260, 0x1927c, 0x192c0, 0x192f8, 0x19338, 0x19370, 0x1937e, 0x19382, 0x19384, 0x19390,\n      0x1939e, 0x193a0, 0x193bc, 0x193c6, 0x193cc, 0x193d8, 0x193ee, 0x193f2, 0x193f4, 0x19430, 0x1943e, 0x19460,\n      0x1947c, 0x194c0, 0x194f8, 0x195f0, 0x19638, 0x19670, 0x1967e, 0x196e0, 0x196fc, 0x19702, 0x19704, 0x19708,\n      0x19710, 0x19720, 0x1973c, 0x19740, 0x19778, 0x19786, 0x1978c, 0x19798, 0x197b0, 0x197be, 0x197ce, 0x197dc,\n      0x197e2, 0x197e4, 0x197e8, 0x19822, 0x19824, 0x19842, 0x19848, 0x19850, 0x1985e, 0x19866, 0x1987a, 0x19882,\n      0x19884, 0x19890, 0x1989e, 0x198a0, 0x198bc, 0x198cc, 0x198f2, 0x198f4, 0x19902, 0x19908, 0x1991e, 0x19920,\n      0x1993c, 0x19940, 0x19978, 0x19986, 0x19998, 0x199ce, 0x199e2, 0x199e4, 0x199e8, 0x19a08, 0x19a10, 0x19a1e,\n      0x19a20, 0x19a3c, 0x19a40, 0x19a78, 0x19af0, 0x19b18, 0x19b3e, 0x19b60, 0x19b9c, 0x19bc2, 0x19bc4, 0x19bc8,\n      0x19bd0, 0x19be6, 0x19c2e, 0x19c34, 0x19c4e, 0x19c5c, 0x19c62, 0x19c64, 0x19c68, 0x19c8e, 0x19c9c, 0x19cb8,\n      0x19cc2, 0x19cc8, 0x19cd0, 0x19ce6, 0x19cfa, 0x19d0e, 0x19d1c, 0x19d38, 0x19d70, 0x19d7e, 0x19d82, 0x19d84,\n      0x19d88, 0x19d90, 0x19da0, 0x19dcc, 0x19df2, 0x19df4, 0x19e16, 0x19e26, 0x19e2c, 0x19e46, 0x19e4c, 0x19e58,\n      0x19e74, 0x19e86, 0x19e8c, 0x19e98, 0x19eb0, 0x19ebe, 0x19ece, 0x19ee2, 0x19ee4, 0x19ee8, 0x19f0a, 0x19f12,\n      0x19f14, 0x19f22, 0x19f24, 0x19f28, 0x19f42, 0x19f44, 0x19f48, 0x19f50, 0x19f5e, 0x19f6c, 0x19f9a, 0x19fae,\n      0x19fb2, 0x19fb4, 0x1a046, 0x1a04c, 0x1a072, 0x1a074, 0x1a086, 0x1a08c, 0x1a098, 0x1a0b0, 0x1a0be, 0x1a0e2,\n      0x1a0e4, 0x1a0e8, 0x1a0f6, 0x1a106, 0x1a10c, 0x1a118, 0x1a130, 0x1a13e, 0x1a160, 0x1a17c, 0x1a18e, 0x1a19c,\n      0x1a1b8, 0x1a1c2, 0x1a1c4, 0x1a1c8, 0x1a1d0, 0x1a1de, 0x1a1e6, 0x1a1ec, 0x1a218, 0x1a230, 0x1a23e, 0x1a260,\n      0x1a27c, 0x1a2c0, 0x1a2f8, 0x1a31c, 0x1a338, 0x1a370, 0x1a37e, 0x1a382, 0x1a384, 0x1a388, 0x1a390, 0x1a39e,\n      0x1a3a0, 0x1a3bc, 0x1a3c6, 0x1a3cc, 0x1a3d8, 0x1a3ee, 0x1a3f2, 0x1a3f4, 0x1a418, 0x1a430, 0x1a43e, 0x1a460,\n      0x1a47c, 0x1a4c0, 0x1a4f8, 0x1a5f0, 0x1a61c, 0x1a638, 0x1a670, 0x1a67e, 0x1a6e0, 0x1a6fc, 0x1a702, 0x1a704,\n      0x1a708, 0x1a710, 0x1a71e, 0x1a720, 0x1a73c, 0x1a740, 0x1a778, 0x1a786, 0x1a78c, 0x1a798, 0x1a7b0, 0x1a7be,\n      0x1a7ce, 0x1a7dc, 0x1a7e2, 0x1a7e4, 0x1a7e8, 0x1a830, 0x1a860, 0x1a87c, 0x1a8c0, 0x1a8f8, 0x1a9f0, 0x1abe0,\n      0x1ac70, 0x1ac7e, 0x1ace0, 0x1acfc, 0x1adc0, 0x1adf8, 0x1ae04, 0x1ae08, 0x1ae10, 0x1ae20, 0x1ae3c, 0x1ae40,\n      0x1ae78, 0x1aef0, 0x1af06, 0x1af0c, 0x1af18, 0x1af30, 0x1af3e, 0x1af60, 0x1af7c, 0x1af8e, 0x1af9c, 0x1afb8,\n      0x1afc4, 0x1afc8, 0x1afd0, 0x1afde, 0x1b042, 0x1b05e, 0x1b07a, 0x1b082, 0x1b084, 0x1b088, 0x1b090, 0x1b09e,\n      0x1b0a0, 0x1b0bc, 0x1b0cc, 0x1b0f2, 0x1b0f4, 0x1b102, 0x1b104, 0x1b108, 0x1b110, 0x1b11e, 0x1b120, 0x1b13c,\n      0x1b140, 0x1b178, 0x1b186, 0x1b198, 0x1b1ce, 0x1b1e2, 0x1b1e4, 0x1b1e8, 0x1b204, 0x1b208, 0x1b210, 0x1b21e,\n      0x1b220, 0x1b23c, 0x1b240, 0x1b278, 0x1b2f0, 0x1b30c, 0x1b33e, 0x1b360, 0x1b39c, 0x1b3c2, 0x1b3c4, 0x1b3c8,\n      0x1b3d0, 0x1b3e6, 0x1b410, 0x1b41e, 0x1b420, 0x1b43c, 0x1b440, 0x1b478, 0x1b4f0, 0x1b5e0, 0x1b618, 0x1b660,\n      0x1b67c, 0x1b6c0, 0x1b738, 0x1b782, 0x1b784, 0x1b788, 0x1b790, 0x1b79e, 0x1b7a0, 0x1b7cc, 0x1b82e, 0x1b84e,\n      0x1b85c, 0x1b88e, 0x1b89c, 0x1b8b8, 0x1b8c2, 0x1b8c4, 0x1b8c8, 0x1b8d0, 0x1b8e6, 0x1b8fa, 0x1b90e, 0x1b91c,\n      0x1b938, 0x1b970, 0x1b97e, 0x1b982, 0x1b984, 0x1b988, 0x1b990, 0x1b99e, 0x1b9a0, 0x1b9cc, 0x1b9f2, 0x1b9f4,\n      0x1ba0e, 0x1ba1c, 0x1ba38, 0x1ba70, 0x1ba7e, 0x1bae0, 0x1bafc, 0x1bb08, 0x1bb10, 0x1bb20, 0x1bb3c, 0x1bb40,\n      0x1bb98, 0x1bbce, 0x1bbe2, 0x1bbe4, 0x1bbe8, 0x1bc16, 0x1bc26, 0x1bc2c, 0x1bc46, 0x1bc4c, 0x1bc58, 0x1bc72,\n      0x1bc74, 0x1bc86, 0x1bc8c, 0x1bc98, 0x1bcb0, 0x1bcbe, 0x1bcce, 0x1bce2, 0x1bce4, 0x1bce8, 0x1bd06, 0x1bd0c,\n      0x1bd18, 0x1bd30, 0x1bd3e, 0x1bd60, 0x1bd7c, 0x1bd9c, 0x1bdc2, 0x1bdc4, 0x1bdc8, 0x1bdd0, 0x1bde6, 0x1bdfa,\n      0x1be12, 0x1be14, 0x1be22, 0x1be24, 0x1be28, 0x1be42, 0x1be44, 0x1be48, 0x1be50, 0x1be5e, 0x1be66, 0x1be82,\n      0x1be84, 0x1be88, 0x1be90, 0x1be9e, 0x1bea0, 0x1bebc, 0x1becc, 0x1bef4, 0x1bf1a, 0x1bf2e, 0x1bf32, 0x1bf34,\n      0x1bf4e, 0x1bf5c, 0x1bf62, 0x1bf64, 0x1bf68, 0x1c09a, 0x1c0b2, 0x1c0b4, 0x1c11a, 0x1c132, 0x1c134, 0x1c162,\n      0x1c164, 0x1c168, 0x1c176, 0x1c1ba, 0x1c21a, 0x1c232, 0x1c234, 0x1c24e, 0x1c25c, 0x1c262, 0x1c264, 0x1c268,\n      0x1c276, 0x1c28e, 0x1c2c2, 0x1c2c4, 0x1c2c8, 0x1c2d0, 0x1c2de, 0x1c2e6, 0x1c2ec, 0x1c2fa, 0x1c316, 0x1c326,\n      0x1c33a, 0x1c346, 0x1c34c, 0x1c372, 0x1c374, 0x1c41a, 0x1c42e, 0x1c432, 0x1c434, 0x1c44e, 0x1c45c, 0x1c462,\n      0x1c464, 0x1c468, 0x1c476, 0x1c48e, 0x1c49c, 0x1c4b8, 0x1c4c2, 0x1c4c8, 0x1c4d0, 0x1c4de, 0x1c4e6, 0x1c4ec,\n      0x1c4fa, 0x1c51c, 0x1c538, 0x1c570, 0x1c57e, 0x1c582, 0x1c584, 0x1c588, 0x1c590, 0x1c59e, 0x1c5a0, 0x1c5bc,\n      0x1c5c6, 0x1c5cc, 0x1c5d8, 0x1c5ee, 0x1c5f2, 0x1c5f4, 0x1c616, 0x1c626, 0x1c62c, 0x1c63a, 0x1c646, 0x1c64c,\n      0x1c658, 0x1c66e, 0x1c672, 0x1c674, 0x1c686, 0x1c68c, 0x1c698, 0x1c6b0, 0x1c6be, 0x1c6ce, 0x1c6dc, 0x1c6e2,\n      0x1c6e4, 0x1c6e8, 0x1c712, 0x1c714, 0x1c722, 0x1c728, 0x1c736, 0x1c742, 0x1c744, 0x1c748, 0x1c750, 0x1c75e,\n      0x1c766, 0x1c76c, 0x1c77a, 0x1c7ae, 0x1c7d6, 0x1c7ea, 0x1c81a, 0x1c82e, 0x1c832, 0x1c834, 0x1c84e, 0x1c85c,\n      0x1c862, 0x1c864, 0x1c868, 0x1c876, 0x1c88e, 0x1c89c, 0x1c8b8, 0x1c8c2, 0x1c8c8, 0x1c8d0, 0x1c8de, 0x1c8e6,\n      0x1c8ec, 0x1c8fa, 0x1c90e, 0x1c938, 0x1c970, 0x1c97e, 0x1c982, 0x1c984, 0x1c990, 0x1c99e, 0x1c9a0, 0x1c9bc,\n      0x1c9c6, 0x1c9cc, 0x1c9d8, 0x1c9ee, 0x1c9f2, 0x1c9f4, 0x1ca38, 0x1ca70, 0x1ca7e, 0x1cae0, 0x1cafc, 0x1cb02,\n      0x1cb04, 0x1cb08, 0x1cb10, 0x1cb20, 0x1cb3c, 0x1cb40, 0x1cb78, 0x1cb86, 0x1cb8c, 0x1cb98, 0x1cbb0, 0x1cbbe,\n      0x1cbce, 0x1cbdc, 0x1cbe2, 0x1cbe4, 0x1cbe8, 0x1cbf6, 0x1cc16, 0x1cc26, 0x1cc2c, 0x1cc3a, 0x1cc46, 0x1cc58,\n      0x1cc72, 0x1cc74, 0x1cc86, 0x1ccb0, 0x1ccbe, 0x1ccce, 0x1cce2, 0x1cce4, 0x1cce8, 0x1cd06, 0x1cd0c, 0x1cd18,\n      0x1cd30, 0x1cd3e, 0x1cd60, 0x1cd7c, 0x1cd9c, 0x1cdc2, 0x1cdc4, 0x1cdc8, 0x1cdd0, 0x1cdde, 0x1cde6, 0x1cdfa,\n      0x1ce22, 0x1ce28, 0x1ce42, 0x1ce50, 0x1ce5e, 0x1ce66, 0x1ce7a, 0x1ce82, 0x1ce84, 0x1ce88, 0x1ce90, 0x1ce9e,\n      0x1cea0, 0x1cebc, 0x1cecc, 0x1cef2, 0x1cef4, 0x1cf2e, 0x1cf32, 0x1cf34, 0x1cf4e, 0x1cf5c, 0x1cf62, 0x1cf64,\n      0x1cf68, 0x1cf96, 0x1cfa6, 0x1cfac, 0x1cfca, 0x1cfd2, 0x1cfd4, 0x1d02e, 0x1d032, 0x1d034, 0x1d04e, 0x1d05c,\n      0x1d062, 0x1d064, 0x1d068, 0x1d076, 0x1d08e, 0x1d09c, 0x1d0b8, 0x1d0c2, 0x1d0c4, 0x1d0c8, 0x1d0d0, 0x1d0de,\n      0x1d0e6, 0x1d0ec, 0x1d0fa, 0x1d11c, 0x1d138, 0x1d170, 0x1d17e, 0x1d182, 0x1d184, 0x1d188, 0x1d190, 0x1d19e,\n      0x1d1a0, 0x1d1bc, 0x1d1c6, 0x1d1cc, 0x1d1d8, 0x1d1ee, 0x1d1f2, 0x1d1f4, 0x1d21c, 0x1d238, 0x1d270, 0x1d27e,\n      0x1d2e0, 0x1d2fc, 0x1d302, 0x1d304, 0x1d308, 0x1d310, 0x1d31e, 0x1d320, 0x1d33c, 0x1d340, 0x1d378, 0x1d386,\n      0x1d38c, 0x1d398, 0x1d3b0, 0x1d3be, 0x1d3ce, 0x1d3dc, 0x1d3e2, 0x1d3e4, 0x1d3e8, 0x1d3f6, 0x1d470, 0x1d47e,\n      0x1d4e0, 0x1d4fc, 0x1d5c0, 0x1d5f8, 0x1d604, 0x1d608, 0x1d610, 0x1d620, 0x1d640, 0x1d678, 0x1d6f0, 0x1d706,\n      0x1d70c, 0x1d718, 0x1d730, 0x1d73e, 0x1d760, 0x1d77c, 0x1d78e, 0x1d79c, 0x1d7b8, 0x1d7c2, 0x1d7c4, 0x1d7c8,\n      0x1d7d0, 0x1d7de, 0x1d7e6, 0x1d7ec, 0x1d826, 0x1d82c, 0x1d83a, 0x1d846, 0x1d84c, 0x1d858, 0x1d872, 0x1d874,\n      0x1d886, 0x1d88c, 0x1d898, 0x1d8b0, 0x1d8be, 0x1d8ce, 0x1d8e2, 0x1d8e4, 0x1d8e8, 0x1d8f6, 0x1d90c, 0x1d918,\n      0x1d930, 0x1d93e, 0x1d960, 0x1d97c, 0x1d99c, 0x1d9c2, 0x1d9c4, 0x1d9c8, 0x1d9d0, 0x1d9e6, 0x1d9fa, 0x1da0c,\n      0x1da18, 0x1da30, 0x1da3e, 0x1da60, 0x1da7c, 0x1dac0, 0x1daf8, 0x1db38, 0x1db82, 0x1db84, 0x1db88, 0x1db90,\n      0x1db9e, 0x1dba0, 0x1dbcc, 0x1dbf2, 0x1dbf4, 0x1dc22, 0x1dc42, 0x1dc44, 0x1dc48, 0x1dc50, 0x1dc5e, 0x1dc66,\n      0x1dc7a, 0x1dc82, 0x1dc84, 0x1dc88, 0x1dc90, 0x1dc9e, 0x1dca0, 0x1dcbc, 0x1dccc, 0x1dcf2, 0x1dcf4, 0x1dd04,\n      0x1dd08, 0x1dd10, 0x1dd1e, 0x1dd20, 0x1dd3c, 0x1dd40, 0x1dd78, 0x1dd86, 0x1dd98, 0x1ddce, 0x1dde2, 0x1dde4,\n      0x1dde8, 0x1de2e, 0x1de32, 0x1de34, 0x1de4e, 0x1de5c, 0x1de62, 0x1de64, 0x1de68, 0x1de8e, 0x1de9c, 0x1deb8,\n      0x1dec2, 0x1dec4, 0x1dec8, 0x1ded0, 0x1dee6, 0x1defa, 0x1df16, 0x1df26, 0x1df2c, 0x1df46, 0x1df4c, 0x1df58,\n      0x1df72, 0x1df74, 0x1df8a, 0x1df92, 0x1df94, 0x1dfa2, 0x1dfa4, 0x1dfa8, 0x1e08a, 0x1e092, 0x1e094, 0x1e0a2,\n      0x1e0a4, 0x1e0a8, 0x1e0b6, 0x1e0da, 0x1e10a, 0x1e112, 0x1e114, 0x1e122, 0x1e124, 0x1e128, 0x1e136, 0x1e142,\n      0x1e144, 0x1e148, 0x1e150, 0x1e166, 0x1e16c, 0x1e17a, 0x1e19a, 0x1e1b2, 0x1e1b4, 0x1e20a, 0x1e212, 0x1e214,\n      0x1e222, 0x1e224, 0x1e228, 0x1e236, 0x1e242, 0x1e248, 0x1e250, 0x1e25e, 0x1e266, 0x1e26c, 0x1e27a, 0x1e282,\n      0x1e284, 0x1e288, 0x1e290, 0x1e2a0, 0x1e2bc, 0x1e2c6, 0x1e2cc, 0x1e2d8, 0x1e2ee, 0x1e2f2, 0x1e2f4, 0x1e31a,\n      0x1e332, 0x1e334, 0x1e35c, 0x1e362, 0x1e364, 0x1e368, 0x1e3ba, 0x1e40a, 0x1e412, 0x1e414, 0x1e422, 0x1e428,\n      0x1e436, 0x1e442, 0x1e448, 0x1e450, 0x1e45e, 0x1e466, 0x1e46c, 0x1e47a, 0x1e482, 0x1e484, 0x1e490, 0x1e49e,\n      0x1e4a0, 0x1e4bc, 0x1e4c6, 0x1e4cc, 0x1e4d8, 0x1e4ee, 0x1e4f2, 0x1e4f4, 0x1e502, 0x1e504, 0x1e508, 0x1e510,\n      0x1e51e, 0x1e520, 0x1e53c, 0x1e540, 0x1e578, 0x1e586, 0x1e58c, 0x1e598, 0x1e5b0, 0x1e5be, 0x1e5ce, 0x1e5dc,\n      0x1e5e2, 0x1e5e4, 0x1e5e8, 0x1e5f6, 0x1e61a, 0x1e62e, 0x1e632, 0x1e634, 0x1e64e, 0x1e65c, 0x1e662, 0x1e668,\n      0x1e68e, 0x1e69c, 0x1e6b8, 0x1e6c2, 0x1e6c4, 0x1e6c8, 0x1e6d0, 0x1e6e6, 0x1e6fa, 0x1e716, 0x1e726, 0x1e72c,\n      0x1e73a, 0x1e746, 0x1e74c, 0x1e758, 0x1e772, 0x1e774, 0x1e792, 0x1e794, 0x1e7a2, 0x1e7a4, 0x1e7a8, 0x1e7b6,\n      0x1e812, 0x1e814, 0x1e822, 0x1e824, 0x1e828, 0x1e836, 0x1e842, 0x1e844, 0x1e848, 0x1e850, 0x1e85e, 0x1e866,\n      0x1e86c, 0x1e87a, 0x1e882, 0x1e884, 0x1e888, 0x1e890, 0x1e89e, 0x1e8a0, 0x1e8bc, 0x1e8c6, 0x1e8cc, 0x1e8d8,\n      0x1e8ee, 0x1e8f2, 0x1e8f4, 0x1e902, 0x1e904, 0x1e908, 0x1e910, 0x1e920, 0x1e93c, 0x1e940, 0x1e978, 0x1e986,\n      0x1e98c, 0x1e998, 0x1e9b0, 0x1e9be, 0x1e9ce, 0x1e9dc, 0x1e9e2, 0x1e9e4, 0x1e9e8, 0x1e9f6, 0x1ea04, 0x1ea08,\n      0x1ea10, 0x1ea20, 0x1ea40, 0x1ea78, 0x1eaf0, 0x1eb06, 0x1eb0c, 0x1eb18, 0x1eb30, 0x1eb3e, 0x1eb60, 0x1eb7c,\n      0x1eb8e, 0x1eb9c, 0x1ebb8, 0x1ebc2, 0x1ebc4, 0x1ebc8, 0x1ebd0, 0x1ebde, 0x1ebe6, 0x1ebec, 0x1ec1a, 0x1ec2e,\n      0x1ec32, 0x1ec34, 0x1ec4e, 0x1ec5c, 0x1ec62, 0x1ec64, 0x1ec68, 0x1ec8e, 0x1ec9c, 0x1ecb8, 0x1ecc2, 0x1ecc4,\n      0x1ecc8, 0x1ecd0, 0x1ece6, 0x1ecfa, 0x1ed0e, 0x1ed1c, 0x1ed38, 0x1ed70, 0x1ed7e, 0x1ed82, 0x1ed84, 0x1ed88,\n      0x1ed90, 0x1ed9e, 0x1eda0, 0x1edcc, 0x1edf2, 0x1edf4, 0x1ee16, 0x1ee26, 0x1ee2c, 0x1ee3a, 0x1ee46, 0x1ee4c,\n      0x1ee58, 0x1ee6e, 0x1ee72, 0x1ee74, 0x1ee86, 0x1ee8c, 0x1ee98, 0x1eeb0, 0x1eebe, 0x1eece, 0x1eedc, 0x1eee2,\n      0x1eee4, 0x1eee8, 0x1ef12, 0x1ef22, 0x1ef24, 0x1ef28, 0x1ef36, 0x1ef42, 0x1ef44, 0x1ef48, 0x1ef50, 0x1ef5e,\n      0x1ef66, 0x1ef6c, 0x1ef7a, 0x1efae, 0x1efb2, 0x1efb4, 0x1efd6, 0x1f096, 0x1f0a6, 0x1f0ac, 0x1f0ba, 0x1f0ca,\n      0x1f0d2, 0x1f0d4, 0x1f116, 0x1f126, 0x1f12c, 0x1f13a, 0x1f146, 0x1f14c, 0x1f158, 0x1f16e, 0x1f172, 0x1f174,\n      0x1f18a, 0x1f192, 0x1f194, 0x1f1a2, 0x1f1a4, 0x1f1a8, 0x1f1da, 0x1f216, 0x1f226, 0x1f22c, 0x1f23a, 0x1f246,\n      0x1f258, 0x1f26e, 0x1f272, 0x1f274, 0x1f286, 0x1f28c, 0x1f298, 0x1f2b0, 0x1f2be, 0x1f2ce, 0x1f2dc, 0x1f2e2,\n      0x1f2e4, 0x1f2e8, 0x1f2f6, 0x1f30a, 0x1f312, 0x1f314, 0x1f322, 0x1f328, 0x1f342, 0x1f344, 0x1f348, 0x1f350,\n      0x1f35e, 0x1f366, 0x1f37a, 0x1f39a, 0x1f3ae, 0x1f3b2, 0x1f3b4, 0x1f416, 0x1f426, 0x1f42c, 0x1f43a, 0x1f446,\n      0x1f44c, 0x1f458, 0x1f46e, 0x1f472, 0x1f474, 0x1f486, 0x1f48c, 0x1f498, 0x1f4b0, 0x1f4be, 0x1f4ce, 0x1f4dc,\n      0x1f4e2, 0x1f4e4, 0x1f4e8, 0x1f4f6, 0x1f506, 0x1f50c, 0x1f518, 0x1f530, 0x1f53e, 0x1f560, 0x1f57c, 0x1f58e,\n      0x1f59c, 0x1f5b8, 0x1f5c2, 0x1f5c4, 0x1f5c8, 0x1f5d0, 0x1f5de, 0x1f5e6, 0x1f5ec, 0x1f5fa, 0x1f60a, 0x1f612,\n      0x1f614, 0x1f622, 0x1f624, 0x1f628, 0x1f636, 0x1f642, 0x1f644, 0x1f648, 0x1f650, 0x1f65e, 0x1f666, 0x1f67a,\n      0x1f682, 0x1f684, 0x1f688, 0x1f690, 0x1f69e, 0x1f6a0, 0x1f6bc, 0x1f6cc, 0x1f6f2, 0x1f6f4, 0x1f71a, 0x1f72e,\n      0x1f732, 0x1f734, 0x1f74e, 0x1f75c, 0x1f762, 0x1f764, 0x1f768, 0x1f776, 0x1f796, 0x1f7a6, 0x1f7ac, 0x1f7ba,\n      0x1f7d2, 0x1f7d4, 0x1f89a, 0x1f8ae, 0x1f8b2, 0x1f8b4, 0x1f8d6, 0x1f8ea, 0x1f91a, 0x1f92e, 0x1f932, 0x1f934,\n      0x1f94e, 0x1f95c, 0x1f962, 0x1f964, 0x1f968, 0x1f976, 0x1f996, 0x1f9a6, 0x1f9ac, 0x1f9ba, 0x1f9ca, 0x1f9d2,\n      0x1f9d4, 0x1fa1a, 0x1fa2e, 0x1fa32, 0x1fa34, 0x1fa4e, 0x1fa5c, 0x1fa62, 0x1fa64, 0x1fa68, 0x1fa76, 0x1fa8e,\n      0x1fa9c, 0x1fab8, 0x1fac2, 0x1fac4, 0x1fac8, 0x1fad0, 0x1fade, 0x1fae6, 0x1faec, 0x1fb16, 0x1fb26, 0x1fb2c,\n      0x1fb3a, 0x1fb46, 0x1fb4c, 0x1fb58, 0x1fb6e, 0x1fb72, 0x1fb74, 0x1fb8a, 0x1fb92, 0x1fb94, 0x1fba2, 0x1fba4,\n      0x1fba8, 0x1fbb6, 0x1fbda};\n\n  /**\n   * This table contains to codewords for all symbols.\n   */\n  private static final int[] CODEWORD_TABLE = {\n      2627, 1819, 2622, 2621, 1813, 1812, 2729, 2724, 2723, 2779, 2774, 2773, 902, 896, 908, 868, 865, 861, 859, 2511,\n      873, 871, 1780, 835, 2493, 825, 2491, 842, 837, 844, 1764, 1762, 811, 810, 809, 2483, 807, 2482, 806, 2480, 815,\n      814, 813, 812, 2484, 817, 816, 1745, 1744, 1742, 1746, 2655, 2637, 2635, 2626, 2625, 2623, 2628, 1820, 2752,\n      2739, 2737, 2728, 2727, 2725, 2730, 2785, 2783, 2778, 2777, 2775, 2780, 787, 781, 747, 739, 736, 2413, 754, 752,\n      1719, 692, 689, 681, 2371, 678, 2369, 700, 697, 694, 703, 1688, 1686, 642, 638, 2343, 631, 2341, 627, 2338, 651,\n      646, 643, 2345, 654, 652, 1652, 1650, 1647, 1654, 601, 599, 2322, 596, 2321, 594, 2319, 2317, 611, 610, 608, 606,\n      2324, 603, 2323, 615, 614, 612, 1617, 1616, 1614, 1612, 616, 1619, 1618, 2575, 2538, 2536, 905, 901, 898, 909,\n      2509, 2507, 2504, 870, 867, 864, 860, 2512, 875, 872, 1781, 2490, 2489, 2487, 2485, 1748, 836, 834, 832, 830,\n      2494, 827, 2492, 843, 841, 839, 845, 1765, 1763, 2701, 2676, 2674, 2653, 2648, 2656, 2634, 2633, 2631, 2629,\n      1821, 2638, 2636, 2770, 2763, 2761, 2750, 2745, 2753, 2736, 2735, 2733, 2731, 1848, 2740, 2738, 2786, 2784, 591,\n      588, 576, 569, 566, 2296, 1590, 537, 534, 526, 2276, 522, 2274, 545, 542, 539, 548, 1572, 1570, 481, 2245, 466,\n      2242, 462, 2239, 492, 485, 482, 2249, 496, 494, 1534, 1531, 1528, 1538, 413, 2196, 406, 2191, 2188, 425, 419,\n      2202, 415, 2199, 432, 430, 427, 1472, 1467, 1464, 433, 1476, 1474, 368, 367, 2160, 365, 2159, 362, 2157, 2155,\n      2152, 378, 377, 375, 2166, 372, 2165, 369, 2162, 383, 381, 379, 2168, 1419, 1418, 1416, 1414, 385, 1411, 384,\n      1423, 1422, 1420, 1424, 2461, 802, 2441, 2439, 790, 786, 783, 794, 2409, 2406, 2403, 750, 742, 738, 2414, 756,\n      753, 1720, 2367, 2365, 2362, 2359, 1663, 693, 691, 684, 2373, 680, 2370, 702, 699, 696, 704, 1690, 1687, 2337,\n      2336, 2334, 2332, 1624, 2329, 1622, 640, 637, 2344, 634, 2342, 630, 2340, 650, 648, 645, 2346, 655, 653, 1653,\n      1651, 1649, 1655, 2612, 2597, 2595, 2571, 2568, 2565, 2576, 2534, 2529, 2526, 1787, 2540, 2537, 907, 904, 900,\n      910, 2503, 2502, 2500, 2498, 1768, 2495, 1767, 2510, 2508, 2506, 869, 866, 863, 2513, 876, 874, 1782, 2720, 2713,\n      2711, 2697, 2694, 2691, 2702, 2672, 2670, 2664, 1828, 2678, 2675, 2647, 2646, 2644, 2642, 1823, 2639, 1822, 2654,\n      2652, 2650, 2657, 2771, 1855, 2765, 2762, 1850, 1849, 2751, 2749, 2747, 2754, 353, 2148, 344, 342, 336, 2142,\n      332, 2140, 345, 1375, 1373, 306, 2130, 299, 2128, 295, 2125, 319, 314, 311, 2132, 1354, 1352, 1349, 1356, 262,\n      257, 2101, 253, 2096, 2093, 274, 273, 267, 2107, 263, 2104, 280, 278, 275, 1316, 1311, 1308, 1320, 1318, 2052,\n      202, 2050, 2044, 2040, 219, 2063, 212, 2060, 208, 2055, 224, 221, 2066, 1260, 1258, 1252, 231, 1248, 229, 1266,\n      1264, 1261, 1268, 155, 1998, 153, 1996, 1994, 1991, 1988, 165, 164, 2007, 162, 2006, 159, 2003, 2000, 172, 171,\n      169, 2012, 166, 2010, 1186, 1184, 1182, 1179, 175, 1176, 173, 1192, 1191, 1189, 1187, 176, 1194, 1193, 2313,\n      2307, 2305, 592, 589, 2294, 2292, 2289, 578, 572, 568, 2297, 580, 1591, 2272, 2267, 2264, 1547, 538, 536, 529,\n      2278, 525, 2275, 547, 544, 541, 1574, 1571, 2237, 2235, 2229, 1493, 2225, 1489, 478, 2247, 470, 2244, 465, 2241,\n      493, 488, 484, 2250, 498, 495, 1536, 1533, 1530, 1539, 2187, 2186, 2184, 2182, 1432, 2179, 1430, 2176, 1427, 414,\n      412, 2197, 409, 2195, 405, 2193, 2190, 426, 424, 421, 2203, 418, 2201, 431, 429, 1473, 1471, 1469, 1466, 434,\n      1477, 1475, 2478, 2472, 2470, 2459, 2457, 2454, 2462, 803, 2437, 2432, 2429, 1726, 2443, 2440, 792, 789, 785,\n      2401, 2399, 2393, 1702, 2389, 1699, 2411, 2408, 2405, 745, 741, 2415, 758, 755, 1721, 2358, 2357, 2355, 2353,\n      1661, 2350, 1660, 2347, 1657, 2368, 2366, 2364, 2361, 1666, 690, 687, 2374, 683, 2372, 701, 698, 705, 1691, 1689,\n      2619, 2617, 2610, 2608, 2605, 2613, 2593, 2588, 2585, 1803, 2599, 2596, 2563, 2561, 2555, 1797, 2551, 1795, 2573,\n      2570, 2567, 2577, 2525, 2524, 2522, 2520, 1786, 2517, 1785, 2514, 1783, 2535, 2533, 2531, 2528, 1788, 2541, 2539,\n      906, 903, 911, 2721, 1844, 2715, 2712, 1838, 1836, 2699, 2696, 2693, 2703, 1827, 1826, 1824, 2673, 2671, 2669,\n      2666, 1829, 2679, 2677, 1858, 1857, 2772, 1854, 1853, 1851, 1856, 2766, 2764, 143, 1987, 139, 1986, 135, 133,\n      131, 1984, 128, 1983, 125, 1981, 138, 137, 136, 1985, 1133, 1132, 1130, 112, 110, 1974, 107, 1973, 104, 1971,\n      1969, 122, 121, 119, 117, 1977, 114, 1976, 124, 1115, 1114, 1112, 1110, 1117, 1116, 84, 83, 1953, 81, 1952, 78,\n      1950, 1948, 1945, 94, 93, 91, 1959, 88, 1958, 85, 1955, 99, 97, 95, 1961, 1086, 1085, 1083, 1081, 1078, 100,\n      1090, 1089, 1087, 1091, 49, 47, 1917, 44, 1915, 1913, 1910, 1907, 59, 1926, 56, 1925, 53, 1922, 1919, 66, 64,\n      1931, 61, 1929, 1042, 1040, 1038, 71, 1035, 70, 1032, 68, 1048, 1047, 1045, 1043, 1050, 1049, 12, 10, 1869, 1867,\n      1864, 1861, 21, 1880, 19, 1877, 1874, 1871, 28, 1888, 25, 1886, 22, 1883, 982, 980, 977, 974, 32, 30, 991, 989,\n      987, 984, 34, 995, 994, 992, 2151, 2150, 2147, 2146, 2144, 356, 355, 354, 2149, 2139, 2138, 2136, 2134, 1359,\n      343, 341, 338, 2143, 335, 2141, 348, 347, 346, 1376, 1374, 2124, 2123, 2121, 2119, 1326, 2116, 1324, 310, 308,\n      305, 2131, 302, 2129, 298, 2127, 320, 318, 316, 313, 2133, 322, 321, 1355, 1353, 1351, 1357, 2092, 2091, 2089,\n      2087, 1276, 2084, 1274, 2081, 1271, 259, 2102, 256, 2100, 252, 2098, 2095, 272, 269, 2108, 266, 2106, 281, 279,\n      277, 1317, 1315, 1313, 1310, 282, 1321, 1319, 2039, 2037, 2035, 2032, 1203, 2029, 1200, 1197, 207, 2053, 205,\n      2051, 201, 2049, 2046, 2043, 220, 218, 2064, 215, 2062, 211, 2059, 228, 226, 223, 2069, 1259, 1257, 1254, 232,\n      1251, 230, 1267, 1265, 1263, 2316, 2315, 2312, 2311, 2309, 2314, 2304, 2303, 2301, 2299, 1593, 2308, 2306, 590,\n      2288, 2287, 2285, 2283, 1578, 2280, 1577, 2295, 2293, 2291, 579, 577, 574, 571, 2298, 582, 581, 1592, 2263, 2262,\n      2260, 2258, 1545, 2255, 1544, 2252, 1541, 2273, 2271, 2269, 2266, 1550, 535, 532, 2279, 528, 2277, 546, 543, 549,\n      1575, 1573, 2224, 2222, 2220, 1486, 2217, 1485, 2214, 1482, 1479, 2238, 2236, 2234, 2231, 1496, 2228, 1492, 480,\n      477, 2248, 473, 2246, 469, 2243, 490, 487, 2251, 497, 1537, 1535, 1532, 2477, 2476, 2474, 2479, 2469, 2468, 2466,\n      2464, 1730, 2473, 2471, 2453, 2452, 2450, 2448, 1729, 2445, 1728, 2460, 2458, 2456, 2463, 805, 804, 2428, 2427,\n      2425, 2423, 1725, 2420, 1724, 2417, 1722, 2438, 2436, 2434, 2431, 1727, 2444, 2442, 793, 791, 788, 795, 2388,\n      2386, 2384, 1697, 2381, 1696, 2378, 1694, 1692, 2402, 2400, 2398, 2395, 1703, 2392, 1701, 2412, 2410, 2407, 751,\n      748, 744, 2416, 759, 757, 1807, 2620, 2618, 1806, 1805, 2611, 2609, 2607, 2614, 1802, 1801, 1799, 2594, 2592,\n      2590, 2587, 1804, 2600, 2598, 1794, 1793, 1791, 1789, 2564, 2562, 2560, 2557, 1798, 2554, 1796, 2574, 2572, 2569,\n      2578, 1847, 1846, 2722, 1843, 1842, 1840, 1845, 2716, 2714, 1835, 1834, 1832, 1830, 1839, 1837, 2700, 2698, 2695,\n      2704, 1817, 1811, 1810, 897, 862, 1777, 829, 826, 838, 1760, 1758, 808, 2481, 1741, 1740, 1738, 1743, 2624, 1818,\n      2726, 2776, 782, 740, 737, 1715, 686, 679, 695, 1682, 1680, 639, 628, 2339, 647, 644, 1645, 1643, 1640, 1648,\n      602, 600, 597, 595, 2320, 593, 2318, 609, 607, 604, 1611, 1610, 1608, 1606, 613, 1615, 1613, 2328, 926, 924, 892,\n      886, 899, 857, 850, 2505, 1778, 824, 823, 821, 819, 2488, 818, 2486, 833, 831, 828, 840, 1761, 1759, 2649, 2632,\n      2630, 2746, 2734, 2732, 2782, 2781, 570, 567, 1587, 531, 527, 523, 540, 1566, 1564, 476, 467, 463, 2240, 486,\n      483, 1524, 1521, 1518, 1529, 411, 403, 2192, 399, 2189, 423, 416, 1462, 1457, 1454, 428, 1468, 1465, 2210, 366,\n      363, 2158, 360, 2156, 357, 2153, 376, 373, 370, 2163, 1410, 1409, 1407, 1405, 382, 1402, 380, 1417, 1415, 1412,\n      1421, 2175, 2174, 777, 774, 771, 784, 732, 725, 722, 2404, 743, 1716, 676, 674, 668, 2363, 665, 2360, 685, 1684,\n      1681, 626, 624, 622, 2335, 620, 2333, 617, 2330, 641, 635, 649, 1646, 1644, 1642, 2566, 928, 925, 2530, 2527,\n      894, 891, 888, 2501, 2499, 2496, 858, 856, 854, 851, 1779, 2692, 2668, 2665, 2645, 2643, 2640, 2651, 2768, 2759,\n      2757, 2744, 2743, 2741, 2748, 352, 1382, 340, 337, 333, 1371, 1369, 307, 300, 296, 2126, 315, 312, 1347, 1342,\n      1350, 261, 258, 250, 2097, 246, 2094, 271, 268, 264, 1306, 1301, 1298, 276, 1312, 1309, 2115, 203, 2048, 195,\n      2045, 191, 2041, 213, 209, 2056, 1246, 1244, 1238, 225, 1234, 222, 1256, 1253, 1249, 1262, 2080, 2079, 154, 1997,\n      150, 1995, 147, 1992, 1989, 163, 160, 2004, 156, 2001, 1175, 1174, 1172, 1170, 1167, 170, 1164, 167, 1185, 1183,\n      1180, 1177, 174, 1190, 1188, 2025, 2024, 2022, 587, 586, 564, 559, 556, 2290, 573, 1588, 520, 518, 512, 2268,\n      508, 2265, 530, 1568, 1565, 461, 457, 2233, 450, 2230, 446, 2226, 479, 471, 489, 1526, 1523, 1520, 397, 395,\n      2185, 392, 2183, 389, 2180, 2177, 410, 2194, 402, 422, 1463, 1461, 1459, 1456, 1470, 2455, 799, 2433, 2430, 779,\n      776, 773, 2397, 2394, 2390, 734, 728, 724, 746, 1717, 2356, 2354, 2351, 2348, 1658, 677, 675, 673, 670, 667, 688,\n      1685, 1683, 2606, 2589, 2586, 2559, 2556, 2552, 927, 2523, 2521, 2518, 2515, 1784, 2532, 895, 893, 890, 2718,\n      2709, 2707, 2689, 2687, 2684, 2663, 2662, 2660, 2658, 1825, 2667, 2769, 1852, 2760, 2758, 142, 141, 1139, 1138,\n      134, 132, 129, 126, 1982, 1129, 1128, 1126, 1131, 113, 111, 108, 105, 1972, 101, 1970, 120, 118, 115, 1109, 1108,\n      1106, 1104, 123, 1113, 1111, 82, 79, 1951, 75, 1949, 72, 1946, 92, 89, 86, 1956, 1077, 1076, 1074, 1072, 98,\n      1069, 96, 1084, 1082, 1079, 1088, 1968, 1967, 48, 45, 1916, 42, 1914, 39, 1911, 1908, 60, 57, 54, 1923, 50, 1920,\n      1031, 1030, 1028, 1026, 67, 1023, 65, 1020, 62, 1041, 1039, 1036, 1033, 69, 1046, 1044, 1944, 1943, 1941, 11, 9,\n      1868, 7, 1865, 1862, 1859, 20, 1878, 16, 1875, 13, 1872, 970, 968, 966, 963, 29, 960, 26, 23, 983, 981, 978, 975,\n      33, 971, 31, 990, 988, 985, 1906, 1904, 1902, 993, 351, 2145, 1383, 331, 330, 328, 326, 2137, 323, 2135, 339,\n      1372, 1370, 294, 293, 291, 289, 2122, 286, 2120, 283, 2117, 309, 303, 317, 1348, 1346, 1344, 245, 244, 242, 2090,\n      239, 2088, 236, 2085, 2082, 260, 2099, 249, 270, 1307, 1305, 1303, 1300, 1314, 189, 2038, 186, 2036, 183, 2033,\n      2030, 2026, 206, 198, 2047, 194, 216, 1247, 1245, 1243, 1240, 227, 1237, 1255, 2310, 2302, 2300, 2286, 2284,\n      2281, 565, 563, 561, 558, 575, 1589, 2261, 2259, 2256, 2253, 1542, 521, 519, 517, 514, 2270, 511, 533, 1569,\n      1567, 2223, 2221, 2218, 2215, 1483, 2211, 1480, 459, 456, 453, 2232, 449, 474, 491, 1527, 1525, 1522, 2475, 2467,\n      2465, 2451, 2449, 2446, 801, 800, 2426, 2424, 2421, 2418, 1723, 2435, 780, 778, 775, 2387, 2385, 2382, 2379,\n      1695, 2375, 1693, 2396, 735, 733, 730, 727, 749, 1718, 2616, 2615, 2604, 2603, 2601, 2584, 2583, 2581, 2579,\n      1800, 2591, 2550, 2549, 2547, 2545, 1792, 2542, 1790, 2558, 929, 2719, 1841, 2710, 2708, 1833, 1831, 2690, 2688,\n      2686, 1815, 1809, 1808, 1774, 1756, 1754, 1737, 1736, 1734, 1739, 1816, 1711, 1676, 1674, 633, 629, 1638, 1636,\n      1633, 1641, 598, 1605, 1604, 1602, 1600, 605, 1609, 1607, 2327, 887, 853, 1775, 822, 820, 1757, 1755, 1584, 524,\n      1560, 1558, 468, 464, 1514, 1511, 1508, 1519, 408, 404, 400, 1452, 1447, 1444, 417, 1458, 1455, 2208, 364, 361,\n      358, 2154, 1401, 1400, 1398, 1396, 374, 1393, 371, 1408, 1406, 1403, 1413, 2173, 2172, 772, 726, 723, 1712, 672,\n      669, 666, 682, 1678, 1675, 625, 623, 621, 618, 2331, 636, 632, 1639, 1637, 1635, 920, 918, 884, 880, 889, 849,\n      848, 847, 846, 2497, 855, 852, 1776, 2641, 2742, 2787, 1380, 334, 1367, 1365, 301, 297, 1340, 1338, 1335, 1343,\n      255, 251, 247, 1296, 1291, 1288, 265, 1302, 1299, 2113, 204, 196, 192, 2042, 1232, 1230, 1224, 214, 1220, 210,\n      1242, 1239, 1235, 1250, 2077, 2075, 151, 148, 1993, 144, 1990, 1163, 1162, 1160, 1158, 1155, 161, 1152, 157,\n      1173, 1171, 1168, 1165, 168, 1181, 1178, 2021, 2020, 2018, 2023, 585, 560, 557, 1585, 516, 509, 1562, 1559, 458,\n      447, 2227, 472, 1516, 1513, 1510, 398, 396, 393, 390, 2181, 386, 2178, 407, 1453, 1451, 1449, 1446, 420, 1460,\n      2209, 769, 764, 720, 712, 2391, 729, 1713, 664, 663, 661, 659, 2352, 656, 2349, 671, 1679, 1677, 2553, 922, 919,\n      2519, 2516, 885, 883, 881, 2685, 2661, 2659, 2767, 2756, 2755, 140, 1137, 1136, 130, 127, 1125, 1124, 1122, 1127,\n      109, 106, 102, 1103, 1102, 1100, 1098, 116, 1107, 1105, 1980, 80, 76, 73, 1947, 1068, 1067, 1065, 1063, 90, 1060,\n      87, 1075, 1073, 1070, 1080, 1966, 1965, 46, 43, 40, 1912, 36, 1909, 1019, 1018, 1016, 1014, 58, 1011, 55, 1008,\n      51, 1029, 1027, 1024, 1021, 63, 1037, 1034, 1940, 1939, 1937, 1942, 8, 1866, 4, 1863, 1, 1860, 956, 954, 952,\n      949, 946, 17, 14, 969, 967, 964, 961, 27, 957, 24, 979, 976, 972, 1901, 1900, 1898, 1896, 986, 1905, 1903, 350,\n      349, 1381, 329, 327, 324, 1368, 1366, 292, 290, 287, 284, 2118, 304, 1341, 1339, 1337, 1345, 243, 240, 237, 2086,\n      233, 2083, 254, 1297, 1295, 1293, 1290, 1304, 2114, 190, 187, 184, 2034, 180, 2031, 177, 2027, 199, 1233, 1231,\n      1229, 1226, 217, 1223, 1241, 2078, 2076, 584, 555, 554, 552, 550, 2282, 562, 1586, 507, 506, 504, 502, 2257, 499,\n      2254, 515, 1563, 1561, 445, 443, 441, 2219, 438, 2216, 435, 2212, 460, 454, 475, 1517, 1515, 1512, 2447, 798,\n      797, 2422, 2419, 770, 768, 766, 2383, 2380, 2376, 721, 719, 717, 714, 731, 1714, 2602, 2582, 2580, 2548, 2546,\n      2543, 923, 921, 2717, 2706, 2705, 2683, 2682, 2680, 1771, 1752, 1750, 1733, 1732, 1731, 1735, 1814, 1707, 1670,\n      1668, 1631, 1629, 1626, 1634, 1599, 1598, 1596, 1594, 1603, 1601, 2326, 1772, 1753, 1751, 1581, 1554, 1552, 1504,\n      1501, 1498, 1509, 1442, 1437, 1434, 401, 1448, 1445, 2206, 1392, 1391, 1389, 1387, 1384, 359, 1399, 1397, 1394,\n      1404, 2171, 2170, 1708, 1672, 1669, 619, 1632, 1630, 1628, 1773, 1378, 1363, 1361, 1333, 1328, 1336, 1286, 1281,\n      1278, 248, 1292, 1289, 2111, 1218, 1216, 1210, 197, 1206, 193, 1228, 1225, 1221, 1236, 2073, 2071, 1151, 1150,\n      1148, 1146, 152, 1143, 149, 1140, 145, 1161, 1159, 1156, 1153, 158, 1169, 1166, 2017, 2016, 2014, 2019, 1582,\n      510, 1556, 1553, 452, 448, 1506, 1500, 394, 391, 387, 1443, 1441, 1439, 1436, 1450, 2207, 765, 716, 713, 1709,\n      662, 660, 657, 1673, 1671, 916, 914, 879, 878, 877, 882, 1135, 1134, 1121, 1120, 1118, 1123, 1097, 1096, 1094,\n      1092, 103, 1101, 1099, 1979, 1059, 1058, 1056, 1054, 77, 1051, 74, 1066, 1064, 1061, 1071, 1964, 1963, 1007,\n      1006, 1004, 1002, 999, 41, 996, 37, 1017, 1015, 1012, 1009, 52, 1025, 1022, 1936, 1935, 1933, 1938, 942, 940,\n      938, 935, 932, 5, 2, 955, 953, 950, 947, 18, 943, 15, 965, 962, 958, 1895, 1894, 1892, 1890, 973, 1899, 1897,\n      1379, 325, 1364, 1362, 288, 285, 1334, 1332, 1330, 241, 238, 234, 1287, 1285, 1283, 1280, 1294, 2112, 188, 185,\n      181, 178, 2028, 1219, 1217, 1215, 1212, 200, 1209, 1227, 2074, 2072, 583, 553, 551, 1583, 505, 503, 500, 513,\n      1557, 1555, 444, 442, 439, 436, 2213, 455, 451, 1507, 1505, 1502, 796, 763, 762, 760, 767, 711, 710, 708, 706,\n      2377, 718, 715, 1710, 2544, 917, 915, 2681, 1627, 1597, 1595, 2325, 1769, 1749, 1747, 1499, 1438, 1435, 2204,\n      1390, 1388, 1385, 1395, 2169, 2167, 1704, 1665, 1662, 1625, 1623, 1620, 1770, 1329, 1282, 1279, 2109, 1214, 1207,\n      1222, 2068, 2065, 1149, 1147, 1144, 1141, 146, 1157, 1154, 2013, 2011, 2008, 2015, 1579, 1549, 1546, 1495, 1487,\n      1433, 1431, 1428, 1425, 388, 1440, 2205, 1705, 658, 1667, 1664, 1119, 1095, 1093, 1978, 1057, 1055, 1052, 1062,\n      1962, 1960, 1005, 1003, 1000, 997, 38, 1013, 1010, 1932, 1930, 1927, 1934, 941, 939, 936, 933, 6, 930, 3, 951,\n      948, 944, 1889, 1887, 1884, 1881, 959, 1893, 1891, 35, 1377, 1360, 1358, 1327, 1325, 1322, 1331, 1277, 1275,\n      1272, 1269, 235, 1284, 2110, 1205, 1204, 1201, 1198, 182, 1195, 179, 1213, 2070, 2067, 1580, 501, 1551, 1548,\n      440, 437, 1497, 1494, 1490, 1503, 761, 709, 707, 1706, 913, 912, 2198, 1386, 2164, 2161, 1621, 1766, 2103, 1208,\n      2058, 2054, 1145, 1142, 2005, 2002, 1999, 2009, 1488, 1429, 1426, 2200, 1698, 1659, 1656, 1975, 1053, 1957, 1954,\n      1001, 998, 1924, 1921, 1918, 1928, 937, 934, 931, 1879, 1876, 1873, 1870, 945, 1885, 1882, 1323, 1273, 1270,\n      2105, 1202, 1199, 1196, 1211, 2061, 2057, 1576, 1543, 1540, 1484, 1481, 1478, 1491, 1700};\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/PDF417Reader.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.multi.MultipleBarcodeReader;\nimport com.google.zxing.pdf417.decoder.PDF417ScanningDecoder;\nimport com.google.zxing.pdf417.detector.Detector;\nimport com.google.zxing.pdf417.detector.PDF417DetectorResult;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * This implementation can detect and decode PDF417 codes in an image.\n *\n * @author Guenther Grau\n */\npublic final class PDF417Reader implements Reader, MultipleBarcodeReader {\n\n  private static final Result[] EMPTY_RESULT_ARRAY = new Result[0];\n\n  /**\n   * Locates and decodes a PDF417 code in an image.\n   *\n   * @return a String representing the content encoded by the PDF417 code\n   * @throws NotFoundException if a PDF417 code cannot be found,\n   * @throws FormatException if a PDF417 cannot be decoded\n   */\n  @Override\n  public Result decode(BinaryBitmap image) throws NotFoundException, FormatException, ChecksumException {\n    return decode(image, null);\n  }\n\n  @Override\n  public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException, FormatException,\n      ChecksumException {\n    Result[] result = decode(image, hints, false);\n    if (result == null || result.length == 0 || result[0] == null) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    return result[0];\n  }\n\n  @Override\n  public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException {\n    return decodeMultiple(image, null);\n  }\n\n  @Override\n  public Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {\n    try {\n      return decode(image, hints, true);\n    } catch (FormatException | ChecksumException ignored) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n  }\n\n  private static Result[] decode(BinaryBitmap image, Map<DecodeHintType, ?> hints, boolean multiple) \n      throws NotFoundException, FormatException, ChecksumException {\n    List<Result> results = new ArrayList<>();\n    PDF417DetectorResult detectorResult = Detector.detect(image, hints, multiple);\n    for (ResultPoint[] points : detectorResult.getPoints()) {\n      DecoderResult decoderResult = PDF417ScanningDecoder.decode(detectorResult.getBits(), points[4], points[5],\n          points[6], points[7], getMinCodewordWidth(points), getMaxCodewordWidth(points));\n      Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.PDF_417);\n      result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, decoderResult.getECLevel());\n      PDF417ResultMetadata pdf417ResultMetadata = (PDF417ResultMetadata) decoderResult.getOther();\n      if (pdf417ResultMetadata != null) {\n        result.putMetadata(ResultMetadataType.PDF417_EXTRA_METADATA, pdf417ResultMetadata);\n      }\n      results.add(result);\n    }\n    return results.toArray(EMPTY_RESULT_ARRAY);\n  }\n\n  private static int getMaxWidth(ResultPoint p1, ResultPoint p2) {\n    if (p1 == null || p2 == null) {\n      return 0;\n    }\n    return (int) Math.abs(p1.getX() - p2.getX());\n  }\n\n  private static int getMinWidth(ResultPoint p1, ResultPoint p2) {\n    if (p1 == null || p2 == null) {\n      return Integer.MAX_VALUE;\n    }\n    return (int) Math.abs(p1.getX() - p2.getX());\n  }\n\n  private static int getMaxCodewordWidth(ResultPoint[] p) {\n    return Math.max(\n        Math.max(getMaxWidth(p[0], p[4]), getMaxWidth(p[6], p[2]) * PDF417Common.MODULES_IN_CODEWORD /\n            PDF417Common.MODULES_IN_STOP_PATTERN),\n        Math.max(getMaxWidth(p[1], p[5]), getMaxWidth(p[7], p[3]) * PDF417Common.MODULES_IN_CODEWORD /\n            PDF417Common.MODULES_IN_STOP_PATTERN));\n  }\n\n  private static int getMinCodewordWidth(ResultPoint[] p) {\n    return Math.min(\n        Math.min(getMinWidth(p[0], p[4]), getMinWidth(p[6], p[2]) * PDF417Common.MODULES_IN_CODEWORD /\n            PDF417Common.MODULES_IN_STOP_PATTERN),\n        Math.min(getMinWidth(p[1], p[5]), getMinWidth(p[7], p[3]) * PDF417Common.MODULES_IN_CODEWORD /\n            PDF417Common.MODULES_IN_STOP_PATTERN));\n  }\n\n  @Override\n  public void reset() {\n    // nothing needs to be reset\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/PDF417ResultMetadata.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417;\n\n/**\n * @author Guenther Grau\n */\npublic final class PDF417ResultMetadata {\n\n  private int segmentIndex;\n  private String fileId;\n  private boolean lastSegment;\n  private int segmentCount = -1;\n  private String sender;\n  private String addressee;\n  private String fileName;\n  private long fileSize = -1;\n  private long timestamp = -1;\n  private int checksum = -1;\n  private int[] optionalData;\n\n  /**\n   * The Segment ID represents the segment of the whole file distributed over different symbols.\n   *\n   * @return File segment index\n   */\n  public int getSegmentIndex() {\n    return segmentIndex;\n  }\n\n  public void setSegmentIndex(int segmentIndex) {\n    this.segmentIndex = segmentIndex;\n  }\n\n  /**\n   * Is the same for each related PDF417 symbol\n   *\n   * @return File ID\n   */\n  public String getFileId() {\n    return fileId;\n  }\n\n  public void setFileId(String fileId) {\n    this.fileId = fileId;\n  }\n\n  /**\n   * @return always null\n   * @deprecated use dedicated already parsed fields\n   */\n  @Deprecated\n  public int[] getOptionalData() {\n    return optionalData;\n  }\n\n  /**\n   * @param optionalData old optional data format as int array\n   * @deprecated parse and use new fields\n   */\n  @Deprecated\n  public void setOptionalData(int[] optionalData) {\n    this.optionalData = optionalData;\n  }\n\n\n  /**\n   * @return true if it is the last segment\n   */\n  public boolean isLastSegment() {\n    return lastSegment;\n  }\n\n  public void setLastSegment(boolean lastSegment) {\n    this.lastSegment = lastSegment;\n  }\n\n  /**\n   * @return count of segments, -1 if not set\n   */\n  public int getSegmentCount() {\n    return segmentCount;\n  }\n\n  public void setSegmentCount(int segmentCount) {\n    this.segmentCount = segmentCount;\n  }\n\n  public String getSender() {\n    return sender;\n  }\n\n  public void setSender(String sender) {\n    this.sender = sender;\n  }\n\n  public String getAddressee() {\n    return addressee;\n  }\n\n  public void setAddressee(String addressee) {\n    this.addressee = addressee;\n  }\n\n  /**\n   * Filename of the encoded file\n   *\n   * @return filename\n   */\n  public String getFileName() {\n    return fileName;\n  }\n\n  public void setFileName(String fileName) {\n    this.fileName = fileName;\n  }\n\n  /**\n   * filesize in bytes of the encoded file\n   *\n   * @return filesize in bytes, -1 if not set\n   */\n  public long getFileSize() {\n    return fileSize;\n  }\n\n  public void setFileSize(long fileSize) {\n    this.fileSize = fileSize;\n  }\n\n  /**\n   * 16-bit CRC checksum using CCITT-16\n   *\n   * @return crc checksum, -1 if not set\n   */\n  public int getChecksum() {\n    return checksum;\n  }\n\n  public void setChecksum(int checksum) {\n    this.checksum = checksum;\n  }\n\n  /**\n   * unix epock timestamp, elapsed seconds since 1970-01-01\n   *\n   * @return elapsed seconds, -1 if not set\n   */\n  public long getTimestamp() {\n    return timestamp;\n  }\n\n  public void setTimestamp(long timestamp) {\n    this.timestamp = timestamp;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/PDF417Writer.java",
    "content": "/*\n * Copyright 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.Writer;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.pdf417.encoder.Compaction;\nimport com.google.zxing.pdf417.encoder.Dimensions;\nimport com.google.zxing.pdf417.encoder.PDF417;\n\nimport java.nio.charset.Charset;\nimport java.util.Map;\n\n/**\n * @author Jacob Haynes\n * @author qwandor@google.com (Andrew Walbran)\n */\npublic final class PDF417Writer implements Writer {\n\n  /**\n   * default white space (margin) around the code\n   */\n  private static final int WHITE_SPACE = 30;\n\n  /**\n   * default error correction level\n   */\n  private static final int DEFAULT_ERROR_CORRECTION_LEVEL = 2;\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n    if (format != BarcodeFormat.PDF_417) {\n      throw new IllegalArgumentException(\"Can only encode PDF_417, but got \" + format);\n    }\n\n    PDF417 encoder = new PDF417();\n    int margin = WHITE_SPACE;\n    int errorCorrectionLevel = DEFAULT_ERROR_CORRECTION_LEVEL;\n\n    if (hints != null) {\n      if (hints.containsKey(EncodeHintType.PDF417_COMPACT)) {\n        encoder.setCompact(Boolean.valueOf(hints.get(EncodeHintType.PDF417_COMPACT).toString()));\n      }\n      if (hints.containsKey(EncodeHintType.PDF417_COMPACTION)) {\n        encoder.setCompaction(Compaction.valueOf(hints.get(EncodeHintType.PDF417_COMPACTION).toString()));\n      }\n      if (hints.containsKey(EncodeHintType.PDF417_DIMENSIONS)) {\n        Dimensions dimensions = (Dimensions) hints.get(EncodeHintType.PDF417_DIMENSIONS);\n        encoder.setDimensions(dimensions.getMaxCols(),\n                              dimensions.getMinCols(),\n                              dimensions.getMaxRows(),\n                              dimensions.getMinRows());\n      }\n      if (hints.containsKey(EncodeHintType.MARGIN)) {\n        margin = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());\n      }\n      if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {\n        errorCorrectionLevel = Integer.parseInt(hints.get(EncodeHintType.ERROR_CORRECTION).toString());\n      }\n      if (hints.containsKey(EncodeHintType.CHARACTER_SET)) {\n        Charset encoding = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());\n        encoder.setEncoding(encoding);\n      }\n    }\n\n    return bitMatrixFromEncoder(encoder, contents, errorCorrectionLevel, width, height, margin);\n  }\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height) throws WriterException {\n    return encode(contents, format, width, height, null);\n  }\n\n  /**\n   * Takes encoder, accounts for width/height, and retrieves bit matrix\n   */\n  private static BitMatrix bitMatrixFromEncoder(PDF417 encoder,\n                                                String contents,\n                                                int errorCorrectionLevel,\n                                                int width,\n                                                int height,\n                                                int margin) throws WriterException {\n    encoder.generateBarcodeLogic(contents, errorCorrectionLevel);\n\n    int aspectRatio = 4;\n    byte[][] originalScale = encoder.getBarcodeMatrix().getScaledMatrix(1, aspectRatio);\n    boolean rotated = false;\n    if ((height > width) != (originalScale[0].length < originalScale.length)) {\n      originalScale = rotateArray(originalScale);\n      rotated = true;\n    }\n\n    int scaleX = width / originalScale[0].length;\n    int scaleY = height / originalScale.length;\n\n    int scale;\n    if (scaleX < scaleY) {\n      scale = scaleX;\n    } else {\n      scale = scaleY;\n    }\n\n    if (scale > 1) {\n      byte[][] scaledMatrix =\n          encoder.getBarcodeMatrix().getScaledMatrix(scale, scale * aspectRatio);\n      if (rotated) {\n        scaledMatrix = rotateArray(scaledMatrix);\n      }\n      return bitMatrixFromBitArray(scaledMatrix, margin);\n    }\n    return bitMatrixFromBitArray(originalScale, margin);\n  }\n\n  /**\n   * This takes an array holding the values of the PDF 417\n   *\n   * @param input a byte array of information with 0 is black, and 1 is white\n   * @param margin border around the barcode\n   * @return BitMatrix of the input\n   */\n  private static BitMatrix bitMatrixFromBitArray(byte[][] input, int margin) {\n    // Creates the bit matrix with extra space for whitespace\n    BitMatrix output = new BitMatrix(input[0].length + 2 * margin, input.length + 2 * margin);\n    output.clear();\n    for (int y = 0, yOutput = output.getHeight() - margin - 1; y < input.length; y++, yOutput--) {\n      byte[] inputY = input[y];\n      for (int x = 0; x < input[0].length; x++) {\n        // Zero is white in the byte matrix\n        if (inputY[x] == 1) {\n          output.set(x + margin, yOutput);\n        }\n      }\n    }\n    return output;\n  }\n\n  /**\n   * Takes and rotates the it 90 degrees\n   */\n  private static byte[][] rotateArray(byte[][] bitarray) {\n    byte[][] temp = new byte[bitarray[0].length][bitarray.length];\n    for (int ii = 0; ii < bitarray.length; ii++) {\n      // This makes the direction consistent on screen when rotating the\n      // screen;\n      int inverseii = bitarray.length - ii - 1;\n      for (int jj = 0; jj < bitarray[0].length; jj++) {\n        temp[jj][inverseii] = bitarray[ii][jj];\n      }\n    }\n    return temp;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/BarcodeMetadata.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\n/**\n * @author Guenther Grau\n */\nfinal class BarcodeMetadata {\n\n  private final int columnCount;\n  private final int errorCorrectionLevel;\n  private final int rowCountUpperPart;\n  private final int rowCountLowerPart;\n  private final int rowCount;\n\n  BarcodeMetadata(int columnCount, int rowCountUpperPart, int rowCountLowerPart, int errorCorrectionLevel) {\n    this.columnCount = columnCount;\n    this.errorCorrectionLevel = errorCorrectionLevel;\n    this.rowCountUpperPart = rowCountUpperPart;\n    this.rowCountLowerPart = rowCountLowerPart;\n    this.rowCount = rowCountUpperPart + rowCountLowerPart;\n  }\n\n  int getColumnCount() {\n    return columnCount;\n  }\n\n  int getErrorCorrectionLevel() {\n    return errorCorrectionLevel;\n  }\n\n  int getRowCount() {\n    return rowCount;\n  }\n\n  int getRowCountUpperPart() {\n    return rowCountUpperPart;\n  }\n\n  int getRowCountLowerPart() {\n    return rowCountLowerPart;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/BarcodeValue.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport com.google.zxing.pdf417.PDF417Common;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\n/**\n * @author Guenther Grau\n */\nfinal class BarcodeValue {\n  private final Map<Integer,Integer> values = new HashMap<>();\n\n  /**\n   * Add an occurrence of a value\n   */\n  void setValue(int value) {\n    Integer confidence = values.get(value);\n    if (confidence == null) {\n      confidence = 0;\n    }\n    confidence++;\n    values.put(value, confidence);\n  }\n\n  /**\n   * Determines the maximum occurrence of a set value and returns all values which were set with this occurrence.\n   * @return an array of int, containing the values with the highest occurrence, or null, if no value was set\n   */\n  int[] getValue() {\n    int maxConfidence = -1;\n    Collection<Integer> result = new ArrayList<>();\n    for (Entry<Integer,Integer> entry : values.entrySet()) {\n      if (entry.getValue() > maxConfidence) {\n        maxConfidence = entry.getValue();\n        result.clear();\n        result.add(entry.getKey());\n      } else if (entry.getValue() == maxConfidence) {\n        result.add(entry.getKey());\n      }\n    }\n    return PDF417Common.toIntArray(result);\n  }\n\n  Integer getConfidence(int value) {\n    return values.get(value);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/BoundingBox.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * @author Guenther Grau\n */\nfinal class BoundingBox {\n\n  private final BitMatrix image;\n  private final ResultPoint topLeft;\n  private final ResultPoint bottomLeft;\n  private final ResultPoint topRight;\n  private final ResultPoint bottomRight;\n  private final int minX;\n  private final int maxX;\n  private final int minY;\n  private final int maxY;\n\n  BoundingBox(BitMatrix image,\n              ResultPoint topLeft,\n              ResultPoint bottomLeft,\n              ResultPoint topRight,\n              ResultPoint bottomRight) throws NotFoundException {\n    boolean leftUnspecified = topLeft == null || bottomLeft == null;\n    boolean rightUnspecified = topRight == null || bottomRight == null;\n    if (leftUnspecified && rightUnspecified) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    if (leftUnspecified) {\n      topLeft = new ResultPoint(0, topRight.getY());\n      bottomLeft = new ResultPoint(0, bottomRight.getY());\n    } else if (rightUnspecified) {\n      topRight = new ResultPoint(image.getWidth() - 1, topLeft.getY());\n      bottomRight = new ResultPoint(image.getWidth() - 1, bottomLeft.getY());\n    }\n    this.image = image;\n    this.topLeft = topLeft;\n    this.bottomLeft = bottomLeft;\n    this.topRight = topRight;\n    this.bottomRight = bottomRight;\n    this.minX = (int) Math.min(topLeft.getX(), bottomLeft.getX());\n    this.maxX = (int) Math.max(topRight.getX(), bottomRight.getX());\n    this.minY = (int) Math.min(topLeft.getY(), topRight.getY());\n    this.maxY = (int) Math.max(bottomLeft.getY(), bottomRight.getY());\n  }\n\n  BoundingBox(BoundingBox boundingBox) {\n    this.image = boundingBox.image;\n    this.topLeft = boundingBox.getTopLeft();\n    this.bottomLeft = boundingBox.getBottomLeft();\n    this.topRight = boundingBox.getTopRight();\n    this.bottomRight = boundingBox.getBottomRight();\n    this.minX = boundingBox.getMinX();\n    this.maxX = boundingBox.getMaxX();\n    this.minY = boundingBox.getMinY();\n    this.maxY = boundingBox.getMaxY();\n  }\n\n  static BoundingBox merge(BoundingBox leftBox, BoundingBox rightBox) throws NotFoundException {\n    if (leftBox == null) {\n      return rightBox;\n    }\n    if (rightBox == null) {\n      return leftBox;\n    }\n    return new BoundingBox(leftBox.image, leftBox.topLeft, leftBox.bottomLeft, rightBox.topRight, rightBox.bottomRight);\n  }\n\n  BoundingBox addMissingRows(int missingStartRows, int missingEndRows, boolean isLeft) throws NotFoundException {\n    ResultPoint newTopLeft = topLeft;\n    ResultPoint newBottomLeft = bottomLeft;\n    ResultPoint newTopRight = topRight;\n    ResultPoint newBottomRight = bottomRight;\n\n    if (missingStartRows > 0) {\n      ResultPoint top = isLeft ? topLeft : topRight;\n      int newMinY = (int) top.getY() - missingStartRows;\n      if (newMinY < 0) {\n        newMinY = 0;\n      }\n      ResultPoint newTop = new ResultPoint(top.getX(), newMinY);\n      if (isLeft) {\n        newTopLeft = newTop;\n      } else {\n        newTopRight = newTop;\n      }\n    }\n\n    if (missingEndRows > 0) {\n      ResultPoint bottom = isLeft ? bottomLeft : bottomRight;\n      int newMaxY = (int) bottom.getY() + missingEndRows;\n      if (newMaxY >= image.getHeight()) {\n        newMaxY = image.getHeight() - 1;\n      }\n      ResultPoint newBottom = new ResultPoint(bottom.getX(), newMaxY);\n      if (isLeft) {\n        newBottomLeft = newBottom;\n      } else {\n        newBottomRight = newBottom;\n      }\n    }\n\n    return new BoundingBox(image, newTopLeft, newBottomLeft, newTopRight, newBottomRight);\n  }\n\n  int getMinX() {\n    return minX;\n  }\n\n  int getMaxX() {\n    return maxX;\n  }\n\n  int getMinY() {\n    return minY;\n  }\n\n  int getMaxY() {\n    return maxY;\n  }\n\n  ResultPoint getTopLeft() {\n    return topLeft;\n  }\n\n  ResultPoint getTopRight() {\n    return topRight;\n  }\n\n  ResultPoint getBottomLeft() {\n    return bottomLeft;\n  }\n\n  ResultPoint getBottomRight() {\n    return bottomRight;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/Codeword.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\n/**\n * @author Guenther Grau\n */\nfinal class Codeword {\n\n  private static final int BARCODE_ROW_UNKNOWN = -1;\n\n  private final int startX;\n  private final int endX;\n  private final int bucket;\n  private final int value;\n  private int rowNumber = BARCODE_ROW_UNKNOWN;\n\n  Codeword(int startX, int endX, int bucket, int value) {\n    this.startX = startX;\n    this.endX = endX;\n    this.bucket = bucket;\n    this.value = value;\n  }\n\n  boolean hasValidRowNumber() {\n    return isValidRowNumber(rowNumber);\n  }\n\n  boolean isValidRowNumber(int rowNumber) {\n    return rowNumber != BARCODE_ROW_UNKNOWN && bucket == (rowNumber % 3) * 3;\n  }\n\n  void setRowNumberAsRowIndicatorColumn() {\n    rowNumber = (value / 30) * 3 + bucket / 3;\n  }\n\n  int getWidth() {\n    return endX - startX;\n  }\n\n  int getStartX() {\n    return startX;\n  }\n\n  int getEndX() {\n    return endX;\n  }\n\n  int getBucket() {\n    return bucket;\n  }\n\n  int getValue() {\n    return value;\n  }\n\n  int getRowNumber() {\n    return rowNumber;\n  }\n\n  void setRowNumber(int rowNumber) {\n    this.rowNumber = rowNumber;\n  }\n\n  @Override\n  public String toString() {\n    return rowNumber + \"|\" + value;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/DecodedBitStreamParser.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.CharacterSetECI;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.pdf417.PDF417ResultMetadata;\n\nimport java.io.ByteArrayOutputStream;\nimport java.math.BigInteger;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\n/**\n * <p>This class contains the methods for decoding the PDF417 codewords.</p>\n *\n * @author SITA Lab (kevin.osullivan@sita.aero)\n * @author Guenther Grau\n */\nfinal class DecodedBitStreamParser {\n\n  private enum Mode {\n    ALPHA,\n    LOWER,\n    MIXED,\n    PUNCT,\n    ALPHA_SHIFT,\n    PUNCT_SHIFT\n  }\n\n  private static final int TEXT_COMPACTION_MODE_LATCH = 900;\n  private static final int BYTE_COMPACTION_MODE_LATCH = 901;\n  private static final int NUMERIC_COMPACTION_MODE_LATCH = 902;\n  private static final int BYTE_COMPACTION_MODE_LATCH_6 = 924;\n  private static final int ECI_USER_DEFINED = 925;\n  private static final int ECI_GENERAL_PURPOSE = 926;\n  private static final int ECI_CHARSET = 927;\n  private static final int BEGIN_MACRO_PDF417_CONTROL_BLOCK = 928;\n  private static final int BEGIN_MACRO_PDF417_OPTIONAL_FIELD = 923;\n  private static final int MACRO_PDF417_TERMINATOR = 922;\n  private static final int MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913;\n  private static final int MAX_NUMERIC_CODEWORDS = 15;\n\n  private static final int MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME = 0;\n  private static final int MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT = 1;\n  private static final int MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP = 2;\n  private static final int MACRO_PDF417_OPTIONAL_FIELD_SENDER = 3;\n  private static final int MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE = 4;\n  private static final int MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE = 5;\n  private static final int MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM = 6;\n\n  private static final int PL = 25;\n  private static final int LL = 27;\n  private static final int AS = 27;\n  private static final int ML = 28;\n  private static final int AL = 28;\n  private static final int PS = 29;\n  private static final int PAL = 29;\n\n  private static final char[] PUNCT_CHARS =\n      \";<>@[\\\\]_`~!\\r\\t,:\\n-.$/\\\"|*()?{}'\".toCharArray();\n\n  private static final char[] MIXED_CHARS =\n      \"0123456789&\\r\\t,:#-.$/+%*=^\".toCharArray();\n\n  /**\n   * Table containing values for the exponent of 900.\n   * This is used in the numeric compaction decode algorithm.\n   */\n  private static final BigInteger[] EXP900;\n\n  static {\n    EXP900 = new BigInteger[16];\n    EXP900[0] = BigInteger.ONE;\n    BigInteger nineHundred = BigInteger.valueOf(900);\n    EXP900[1] = nineHundred;\n    for (int i = 2; i < EXP900.length; i++) {\n      EXP900[i] = EXP900[i - 1].multiply(nineHundred);\n    }\n  }\n\n  private static final int NUMBER_OF_SEQUENCE_CODEWORDS = 2;\n\n  private DecodedBitStreamParser() {\n  }\n\n  static DecoderResult decode(int[] codewords, String ecLevel) throws FormatException {\n    StringBuilder result = new StringBuilder(codewords.length * 2);\n    Charset encoding = StandardCharsets.ISO_8859_1;\n    // Get compaction mode\n    int codeIndex = 1;\n    int code = codewords[codeIndex++];\n    PDF417ResultMetadata resultMetadata = new PDF417ResultMetadata();\n    while (codeIndex < codewords[0]) {\n      switch (code) {\n        case TEXT_COMPACTION_MODE_LATCH:\n          codeIndex = textCompaction(codewords, codeIndex, result);\n          break;\n        case BYTE_COMPACTION_MODE_LATCH:\n        case BYTE_COMPACTION_MODE_LATCH_6:\n          codeIndex = byteCompaction(code, codewords, encoding, codeIndex, result);\n          break;\n        case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:\n          result.append((char) codewords[codeIndex++]);\n          break;\n        case NUMERIC_COMPACTION_MODE_LATCH:\n          codeIndex = numericCompaction(codewords, codeIndex, result);\n          break;\n        case ECI_CHARSET:\n          CharacterSetECI charsetECI =\n              CharacterSetECI.getCharacterSetECIByValue(codewords[codeIndex++]);\n          encoding = Charset.forName(charsetECI.name());\n          break;\n        case ECI_GENERAL_PURPOSE:\n          // Can't do anything with generic ECI; skip its 2 characters\n          codeIndex += 2;\n          break;\n        case ECI_USER_DEFINED:\n          // Can't do anything with user ECI; skip its 1 character\n          codeIndex++;\n          break;\n        case BEGIN_MACRO_PDF417_CONTROL_BLOCK:\n          codeIndex = decodeMacroBlock(codewords, codeIndex, resultMetadata);\n          break;\n        case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:\n        case MACRO_PDF417_TERMINATOR:\n          // Should not see these outside a macro block\n          throw FormatException.getFormatInstance();\n        default:\n          // Default to text compaction. During testing numerous barcodes\n          // appeared to be missing the starting mode. In these cases defaulting\n          // to text compaction seems to work.\n          codeIndex--;\n          codeIndex = textCompaction(codewords, codeIndex, result);\n          break;\n      }\n      if (codeIndex < codewords.length) {\n        code = codewords[codeIndex++];\n      } else {\n        throw FormatException.getFormatInstance();\n      }\n    }\n    if (result.length() == 0) {\n      throw FormatException.getFormatInstance();\n    }\n    DecoderResult decoderResult = new DecoderResult(null, result.toString(), null, ecLevel);\n    decoderResult.setOther(resultMetadata);\n    return decoderResult;\n  }\n\n  @SuppressWarnings(\"deprecation\")\n  static int decodeMacroBlock(int[] codewords, int codeIndex, PDF417ResultMetadata resultMetadata)\n      throws FormatException {\n    if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) {\n      // we must have at least two bytes left for the segment index\n      throw FormatException.getFormatInstance();\n    }\n    int[] segmentIndexArray = new int[NUMBER_OF_SEQUENCE_CODEWORDS];\n    for (int i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) {\n      segmentIndexArray[i] = codewords[codeIndex];\n    }\n    resultMetadata.setSegmentIndex(Integer.parseInt(decodeBase900toBase10(segmentIndexArray,\n        NUMBER_OF_SEQUENCE_CODEWORDS)));\n\n    StringBuilder fileId = new StringBuilder();\n    codeIndex = textCompaction(codewords, codeIndex, fileId);\n    resultMetadata.setFileId(fileId.toString());\n\n    int optionalFieldsStart = -1;\n    if (codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) {\n      optionalFieldsStart = codeIndex + 1;\n    }\n\n    while (codeIndex < codewords[0]) {\n      switch (codewords[codeIndex]) {\n        case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:\n          codeIndex++;\n          switch (codewords[codeIndex]) {\n            case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME:\n              StringBuilder fileName = new StringBuilder();\n              codeIndex = textCompaction(codewords, codeIndex + 1, fileName);\n              resultMetadata.setFileName(fileName.toString());\n              break;\n            case MACRO_PDF417_OPTIONAL_FIELD_SENDER:\n              StringBuilder sender = new StringBuilder();\n              codeIndex = textCompaction(codewords, codeIndex + 1, sender);\n              resultMetadata.setSender(sender.toString());\n              break;\n            case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE:\n              StringBuilder addressee = new StringBuilder();\n              codeIndex = textCompaction(codewords, codeIndex + 1, addressee);\n              resultMetadata.setAddressee(addressee.toString());\n              break;\n            case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT:\n              StringBuilder segmentCount = new StringBuilder();\n              codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCount);\n              resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toString()));\n              break;\n            case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP:\n              StringBuilder timestamp = new StringBuilder();\n              codeIndex = numericCompaction(codewords, codeIndex + 1, timestamp);\n              resultMetadata.setTimestamp(Long.parseLong(timestamp.toString()));\n              break;\n            case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM:\n              StringBuilder checksum = new StringBuilder();\n              codeIndex = numericCompaction(codewords, codeIndex + 1, checksum);\n              resultMetadata.setChecksum(Integer.parseInt(checksum.toString()));\n              break;\n            case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE:\n              StringBuilder fileSize = new StringBuilder();\n              codeIndex = numericCompaction(codewords, codeIndex + 1, fileSize);\n              resultMetadata.setFileSize(Long.parseLong(fileSize.toString()));\n              break;\n            default:\n              throw FormatException.getFormatInstance();\n          }\n          break;\n        case MACRO_PDF417_TERMINATOR:\n          codeIndex++;\n          resultMetadata.setLastSegment(true);\n          break;\n        default:\n          throw FormatException.getFormatInstance();\n      }\n    }\n\n    // copy optional fields to additional options\n    if (optionalFieldsStart != -1) {\n      int optionalFieldsLength = codeIndex - optionalFieldsStart;\n      if (resultMetadata.isLastSegment()) {\n        // do not include terminator\n        optionalFieldsLength--;\n      }\n      resultMetadata.setOptionalData(Arrays.copyOfRange(codewords, optionalFieldsStart, optionalFieldsStart + optionalFieldsLength));\n    }\n\n    return codeIndex;\n  }\n\n  /**\n   * Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters to be\n   * encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV), as\n   * well as selected control characters.\n   *\n   * @param codewords The array of codewords (data + error)\n   * @param codeIndex The current index into the codeword array.\n   * @param result    The decoded data is appended to the result.\n   * @return The next index into the codeword array.\n   */\n  private static int textCompaction(int[] codewords, int codeIndex, StringBuilder result) {\n    // 2 character per codeword\n    int[] textCompactionData = new int[(codewords[0] - codeIndex) * 2];\n    // Used to hold the byte compaction value if there is a mode shift\n    int[] byteCompactionData = new int[(codewords[0] - codeIndex) * 2];\n\n    int index = 0;\n    boolean end = false;\n    while ((codeIndex < codewords[0]) && !end) {\n      int code = codewords[codeIndex++];\n      if (code < TEXT_COMPACTION_MODE_LATCH) {\n        textCompactionData[index] = code / 30;\n        textCompactionData[index + 1] = code % 30;\n        index += 2;\n      } else {\n        switch (code) {\n          case TEXT_COMPACTION_MODE_LATCH:\n            // reinitialize text compaction mode to alpha sub mode\n            textCompactionData[index++] = TEXT_COMPACTION_MODE_LATCH;\n            break;\n          case BYTE_COMPACTION_MODE_LATCH:\n          case BYTE_COMPACTION_MODE_LATCH_6:\n          case NUMERIC_COMPACTION_MODE_LATCH:\n          case BEGIN_MACRO_PDF417_CONTROL_BLOCK:\n          case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:\n          case MACRO_PDF417_TERMINATOR:\n            codeIndex--;\n            end = true;\n            break;\n          case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:\n            // The Mode Shift codeword 913 shall cause a temporary\n            // switch from Text Compaction mode to Byte Compaction mode.\n            // This switch shall be in effect for only the next codeword,\n            // after which the mode shall revert to the prevailing sub-mode\n            // of the Text Compaction mode. Codeword 913 is only available\n            // in Text Compaction mode; its use is described in 5.4.2.4.\n            textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE;\n            code = codewords[codeIndex++];\n            byteCompactionData[index] = code;\n            index++;\n            break;\n        }\n      }\n    }\n    decodeTextCompaction(textCompactionData, byteCompactionData, index, result);\n    return codeIndex;\n  }\n\n  /**\n   * The Text Compaction mode includes all the printable ASCII characters\n   * (i.e. values from 32 to 126) and three ASCII control characters: HT or tab\n   * (ASCII value 9), LF or line feed (ASCII value 10), and CR or carriage\n   * return (ASCII value 13). The Text Compaction mode also includes various latch\n   * and shift characters which are used exclusively within the mode. The Text\n   * Compaction mode encodes up to 2 characters per codeword. The compaction rules\n   * for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-mode\n   * switches are defined in 5.4.2.3.\n   *\n   * @param textCompactionData The text compaction data.\n   * @param byteCompactionData The byte compaction data if there\n   *                           was a mode shift.\n   * @param length             The size of the text compaction and byte compaction data.\n   * @param result             The decoded data is appended to the result.\n   */\n  private static void decodeTextCompaction(int[] textCompactionData,\n                                           int[] byteCompactionData,\n                                           int length,\n                                           StringBuilder result) {\n    // Beginning from an initial state of the Alpha sub-mode\n    // The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text\n    // Compaction mode Alpha sub-mode (uppercase alphabetic). A latch codeword from another mode to the Text\n    // Compaction mode shall always switch to the Text Compaction Alpha sub-mode.\n    Mode subMode = Mode.ALPHA;\n    Mode priorToShiftMode = Mode.ALPHA;\n    int i = 0;\n    while (i < length) {\n      int subModeCh = textCompactionData[i];\n      char ch = 0;\n      switch (subMode) {\n        case ALPHA:\n          // Alpha (uppercase alphabetic)\n          if (subModeCh < 26) {\n            // Upper case Alpha Character\n            ch = (char) ('A' + subModeCh);\n          } else {\n            switch (subModeCh) {\n              case 26:\n                ch = ' ';\n                break;\n              case LL:\n                subMode = Mode.LOWER;\n                break;\n              case ML:\n                subMode = Mode.MIXED;\n                break;\n              case PS:\n                // Shift to punctuation\n                priorToShiftMode = subMode;\n                subMode = Mode.PUNCT_SHIFT;\n                break;\n              case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:\n                result.append((char) byteCompactionData[i]);\n                break;\n              case TEXT_COMPACTION_MODE_LATCH:\n                subMode = Mode.ALPHA;\n                break;\n            }\n          }\n          break;\n\n        case LOWER:\n          // Lower (lowercase alphabetic)\n          if (subModeCh < 26) {\n            ch = (char) ('a' + subModeCh);\n          } else {\n            switch (subModeCh) {\n              case 26:\n                ch = ' ';\n                break;\n              case AS:\n                // Shift to alpha\n                priorToShiftMode = subMode;\n                subMode = Mode.ALPHA_SHIFT;\n                break;\n              case ML:\n                subMode = Mode.MIXED;\n                break;\n              case PS:\n                // Shift to punctuation\n                priorToShiftMode = subMode;\n                subMode = Mode.PUNCT_SHIFT;\n                break;\n              case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:\n                // TODO Does this need to use the current character encoding? See other occurrences below\n                result.append((char) byteCompactionData[i]);\n                break;\n              case TEXT_COMPACTION_MODE_LATCH:\n                subMode = Mode.ALPHA;\n                break;\n            }\n          }\n          break;\n\n        case MIXED:\n          // Mixed (numeric and some punctuation)\n          if (subModeCh < PL) {\n            ch = MIXED_CHARS[subModeCh];\n          } else {\n            switch (subModeCh) {\n              case PL:\n                subMode = Mode.PUNCT;\n                break;\n              case 26:\n                ch = ' ';\n                break;\n              case LL:\n                subMode = Mode.LOWER;\n                break;\n              case AL:\n                subMode = Mode.ALPHA;\n                break;\n              case PS:\n                // Shift to punctuation\n                priorToShiftMode = subMode;\n                subMode = Mode.PUNCT_SHIFT;\n                break;\n              case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:\n                result.append((char) byteCompactionData[i]);\n                break;\n              case TEXT_COMPACTION_MODE_LATCH:\n                subMode = Mode.ALPHA;\n                break;\n            }\n          }\n          break;\n\n        case PUNCT:\n          // Punctuation\n          if (subModeCh < PAL) {\n            ch = PUNCT_CHARS[subModeCh];\n          } else {\n            switch (subModeCh) {\n              case PAL:\n                subMode = Mode.ALPHA;\n                break;\n              case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:\n                result.append((char) byteCompactionData[i]);\n                break;\n              case TEXT_COMPACTION_MODE_LATCH:\n                subMode = Mode.ALPHA;\n                break;\n            }\n          }\n          break;\n\n        case ALPHA_SHIFT:\n          // Restore sub-mode\n          subMode = priorToShiftMode;\n          if (subModeCh < 26) {\n            ch = (char) ('A' + subModeCh);\n          } else {\n            switch (subModeCh) {\n              case 26:\n                ch = ' ';\n                break;\n              case TEXT_COMPACTION_MODE_LATCH:\n                subMode = Mode.ALPHA;\n                break;\n            }\n          }\n          break;\n\n        case PUNCT_SHIFT:\n          // Restore sub-mode\n          subMode = priorToShiftMode;\n          if (subModeCh < PAL) {\n            ch = PUNCT_CHARS[subModeCh];\n          } else {\n            switch (subModeCh) {\n              case PAL:\n                subMode = Mode.ALPHA;\n                break;\n              case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:\n                // PS before Shift-to-Byte is used as a padding character,\n                // see 5.4.2.4 of the specification\n                result.append((char) byteCompactionData[i]);\n                break;\n              case TEXT_COMPACTION_MODE_LATCH:\n                subMode = Mode.ALPHA;\n                break;\n            }\n          }\n          break;\n      }\n      if (ch != 0) {\n        // Append decoded character to result\n        result.append(ch);\n      }\n      i++;\n    }\n  }\n\n  /**\n   * Byte Compaction mode (see 5.4.3) permits all 256 possible 8-bit byte values to be encoded.\n   * This includes all ASCII characters value 0 to 127 inclusive and provides for international\n   * character set support.\n   *\n   * @param mode      The byte compaction mode i.e. 901 or 924\n   * @param codewords The array of codewords (data + error)\n   * @param encoding  Currently active character encoding\n   * @param codeIndex The current index into the codeword array.\n   * @param result    The decoded data is appended to the result.\n   * @return The next index into the codeword array.\n   */\n  private static int byteCompaction(int mode,\n                                    int[] codewords,\n                                    Charset encoding,\n                                    int codeIndex,\n                                    StringBuilder result) {\n    ByteArrayOutputStream decodedBytes = new ByteArrayOutputStream();\n    int count = 0;\n    long value = 0;\n    boolean end = false;\n\n    switch (mode) {\n      case BYTE_COMPACTION_MODE_LATCH:\n        // Total number of Byte Compaction characters to be encoded\n        // is not a multiple of 6\n\n        int[] byteCompactedCodewords = new int[6];\n        int nextCode = codewords[codeIndex++];\n        while ((codeIndex < codewords[0]) && !end) {\n          byteCompactedCodewords[count++] = nextCode;\n          // Base 900\n          value = 900 * value + nextCode;\n          nextCode = codewords[codeIndex++];\n          // perhaps it should be ok to check only nextCode >= TEXT_COMPACTION_MODE_LATCH\n          switch (nextCode) {\n            case TEXT_COMPACTION_MODE_LATCH:\n            case BYTE_COMPACTION_MODE_LATCH:\n            case NUMERIC_COMPACTION_MODE_LATCH:\n            case BYTE_COMPACTION_MODE_LATCH_6:\n            case BEGIN_MACRO_PDF417_CONTROL_BLOCK:\n            case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:\n            case MACRO_PDF417_TERMINATOR:\n              codeIndex--;\n              end = true;\n              break;\n            default:\n              if ((count % 5 == 0) && (count > 0)) {\n                // Decode every 5 codewords\n                // Convert to Base 256\n                for (int j = 0; j < 6; ++j) {\n                  decodedBytes.write((byte) (value >> (8 * (5 - j))));\n                }\n                value = 0;\n                count = 0;\n              }\n              break;\n          }\n        }\n\n        // if the end of all codewords is reached the last codeword needs to be added\n        if (codeIndex == codewords[0] && nextCode < TEXT_COMPACTION_MODE_LATCH) {\n          byteCompactedCodewords[count++] = nextCode;\n        }\n\n        // If Byte Compaction mode is invoked with codeword 901,\n        // the last group of codewords is interpreted directly\n        // as one byte per codeword, without compaction.\n        for (int i = 0; i < count; i++) {\n          decodedBytes.write((byte) byteCompactedCodewords[i]);\n        }\n\n        break;\n\n      case BYTE_COMPACTION_MODE_LATCH_6:\n        // Total number of Byte Compaction characters to be encoded\n        // is an integer multiple of 6\n        while (codeIndex < codewords[0] && !end) {\n          int code = codewords[codeIndex++];\n          if (code < TEXT_COMPACTION_MODE_LATCH) {\n            count++;\n            // Base 900\n            value = 900 * value + code;\n          } else {\n            switch (code) {\n              case TEXT_COMPACTION_MODE_LATCH:\n              case BYTE_COMPACTION_MODE_LATCH:\n              case NUMERIC_COMPACTION_MODE_LATCH:\n              case BYTE_COMPACTION_MODE_LATCH_6:\n              case BEGIN_MACRO_PDF417_CONTROL_BLOCK:\n              case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:\n              case MACRO_PDF417_TERMINATOR:\n                codeIndex--;\n                end = true;\n                break;\n            }\n          }\n          if ((count % 5 == 0) && (count > 0)) {\n            // Decode every 5 codewords\n            // Convert to Base 256\n            for (int j = 0; j < 6; ++j) {\n              decodedBytes.write((byte) (value >> (8 * (5 - j))));\n            }\n            value = 0;\n            count = 0;\n          }\n        }\n        break;\n    }\n    result.append(new String(decodedBytes.toByteArray(), encoding));\n    return codeIndex;\n  }\n\n  /**\n   * Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric data strings.\n   *\n   * @param codewords The array of codewords (data + error)\n   * @param codeIndex The current index into the codeword array.\n   * @param result    The decoded data is appended to the result.\n   * @return The next index into the codeword array.\n   */\n  private static int numericCompaction(int[] codewords, int codeIndex, StringBuilder result) throws FormatException {\n    int count = 0;\n    boolean end = false;\n\n    int[] numericCodewords = new int[MAX_NUMERIC_CODEWORDS];\n\n    while (codeIndex < codewords[0] && !end) {\n      int code = codewords[codeIndex++];\n      if (codeIndex == codewords[0]) {\n        end = true;\n      }\n      if (code < TEXT_COMPACTION_MODE_LATCH) {\n        numericCodewords[count] = code;\n        count++;\n      } else {\n        switch (code) {\n          case TEXT_COMPACTION_MODE_LATCH:\n          case BYTE_COMPACTION_MODE_LATCH:\n          case BYTE_COMPACTION_MODE_LATCH_6:\n          case BEGIN_MACRO_PDF417_CONTROL_BLOCK:\n          case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:\n          case MACRO_PDF417_TERMINATOR:\n            codeIndex--;\n            end = true;\n            break;\n        }\n      }\n      if ((count % MAX_NUMERIC_CODEWORDS == 0 || code == NUMERIC_COMPACTION_MODE_LATCH || end) && count > 0) {\n        // Re-invoking Numeric Compaction mode (by using codeword 902\n        // while in Numeric Compaction mode) serves  to terminate the\n        // current Numeric Compaction mode grouping as described in 5.4.4.2,\n        // and then to start a new one grouping.\n        result.append(decodeBase900toBase10(numericCodewords, count));\n        count = 0;\n      }\n    }\n    return codeIndex;\n  }\n\n  /**\n   * Convert a list of Numeric Compacted codewords from Base 900 to Base 10.\n   *\n   * @param codewords The array of codewords\n   * @param count     The number of codewords\n   * @return The decoded string representing the Numeric data.\n   */\n  /*\n     EXAMPLE\n     Encode the fifteen digit numeric string 000213298174000\n     Prefix the numeric string with a 1 and set the initial value of\n     t = 1 000 213 298 174 000\n     Calculate codeword 0\n     d0 = 1 000 213 298 174 000 mod 900 = 200\n\n     t = 1 000 213 298 174 000 div 900 = 1 111 348 109 082\n     Calculate codeword 1\n     d1 = 1 111 348 109 082 mod 900 = 282\n\n     t = 1 111 348 109 082 div 900 = 1 234 831 232\n     Calculate codeword 2\n     d2 = 1 234 831 232 mod 900 = 632\n\n     t = 1 234 831 232 div 900 = 1 372 034\n     Calculate codeword 3\n     d3 = 1 372 034 mod 900 = 434\n\n     t = 1 372 034 div 900 = 1 524\n     Calculate codeword 4\n     d4 = 1 524 mod 900 = 624\n\n     t = 1 524 div 900 = 1\n     Calculate codeword 5\n     d5 = 1 mod 900 = 1\n     t = 1 div 900 = 0\n     Codeword sequence is: 1, 624, 434, 632, 282, 200\n\n     Decode the above codewords involves\n       1 x 900 power of 5 + 624 x 900 power of 4 + 434 x 900 power of 3 +\n     632 x 900 power of 2 + 282 x 900 power of 1 + 200 x 900 power of 0 = 1000213298174000\n\n     Remove leading 1 =>  Result is 000213298174000\n   */\n  private static String decodeBase900toBase10(int[] codewords, int count) throws FormatException {\n    BigInteger result = BigInteger.ZERO;\n    for (int i = 0; i < count; i++) {\n      result = result.add(EXP900[count - i - 1].multiply(BigInteger.valueOf(codewords[i])));\n    }\n    String resultString = result.toString();\n    if (resultString.charAt(0) != '1') {\n      throw FormatException.getFormatInstance();\n    }\n    return resultString.substring(1);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/DetectionResult.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport com.google.zxing.pdf417.PDF417Common;\n\nimport java.util.Formatter;\n\n/**\n * @author Guenther Grau\n */\nfinal class DetectionResult {\n\n  private static final int ADJUST_ROW_NUMBER_SKIP = 2;\n\n  private final BarcodeMetadata barcodeMetadata;\n  private final DetectionResultColumn[] detectionResultColumns;\n  private BoundingBox boundingBox;\n  private final int barcodeColumnCount;\n\n  DetectionResult(BarcodeMetadata barcodeMetadata, BoundingBox boundingBox) {\n    this.barcodeMetadata = barcodeMetadata;\n    this.barcodeColumnCount = barcodeMetadata.getColumnCount();\n    this.boundingBox = boundingBox;\n    detectionResultColumns = new DetectionResultColumn[barcodeColumnCount + 2];\n  }\n\n  DetectionResultColumn[] getDetectionResultColumns() {\n    adjustIndicatorColumnRowNumbers(detectionResultColumns[0]);\n    adjustIndicatorColumnRowNumbers(detectionResultColumns[barcodeColumnCount + 1]);\n    int unadjustedCodewordCount = PDF417Common.MAX_CODEWORDS_IN_BARCODE;\n    int previousUnadjustedCount;\n    do {\n      previousUnadjustedCount = unadjustedCodewordCount;\n      unadjustedCodewordCount = adjustRowNumbers();\n    } while (unadjustedCodewordCount > 0 && unadjustedCodewordCount < previousUnadjustedCount);\n    return detectionResultColumns;\n  }\n\n  private void adjustIndicatorColumnRowNumbers(DetectionResultColumn detectionResultColumn) {\n    if (detectionResultColumn != null) {\n      ((DetectionResultRowIndicatorColumn) detectionResultColumn)\n          .adjustCompleteIndicatorColumnRowNumbers(barcodeMetadata);\n    }\n  }\n\n  // TODO ensure that no detected codewords with unknown row number are left\n  // we should be able to estimate the row height and use it as a hint for the row number\n  // we should also fill the rows top to bottom and bottom to top\n  /**\n   * @return number of codewords which don't have a valid row number. Note that the count is not accurate as codewords\n   * will be counted several times. It just serves as an indicator to see when we can stop adjusting row numbers\n   */\n  private int adjustRowNumbers() {\n    int unadjustedCount = adjustRowNumbersByRow();\n    if (unadjustedCount == 0) {\n      return 0;\n    }\n    for (int barcodeColumn = 1; barcodeColumn < barcodeColumnCount + 1; barcodeColumn++) {\n      Codeword[] codewords = detectionResultColumns[barcodeColumn].getCodewords();\n      for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {\n        if (codewords[codewordsRow] == null) {\n          continue;\n        }\n        if (!codewords[codewordsRow].hasValidRowNumber()) {\n          adjustRowNumbers(barcodeColumn, codewordsRow, codewords);\n        }\n      }\n    }\n    return unadjustedCount;\n  }\n\n  private int adjustRowNumbersByRow() {\n    adjustRowNumbersFromBothRI();\n    // TODO we should only do full row adjustments if row numbers of left and right row indicator column match.\n    // Maybe it's even better to calculated the height (in codeword rows) and divide it by the number of barcode\n    // rows. This, together with the LRI and RRI row numbers should allow us to get a good estimate where a row\n    // number starts and ends.\n    int unadjustedCount = adjustRowNumbersFromLRI();\n    return unadjustedCount + adjustRowNumbersFromRRI();\n  }\n\n  private void adjustRowNumbersFromBothRI() {\n    if (detectionResultColumns[0] == null || detectionResultColumns[barcodeColumnCount + 1] == null) {\n      return;\n    }\n    Codeword[] LRIcodewords = detectionResultColumns[0].getCodewords();\n    Codeword[] RRIcodewords = detectionResultColumns[barcodeColumnCount + 1].getCodewords();\n    for (int codewordsRow = 0; codewordsRow < LRIcodewords.length; codewordsRow++) {\n      if (LRIcodewords[codewordsRow] != null &&\n          RRIcodewords[codewordsRow] != null &&\n          LRIcodewords[codewordsRow].getRowNumber() == RRIcodewords[codewordsRow].getRowNumber()) {\n        for (int barcodeColumn = 1; barcodeColumn <= barcodeColumnCount; barcodeColumn++) {\n          Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];\n          if (codeword == null) {\n            continue;\n          }\n          codeword.setRowNumber(LRIcodewords[codewordsRow].getRowNumber());\n          if (!codeword.hasValidRowNumber()) {\n            detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow] = null;\n          }\n        }\n      }\n    }\n  }\n\n  private int adjustRowNumbersFromRRI() {\n    if (detectionResultColumns[barcodeColumnCount + 1] == null) {\n      return 0;\n    }\n    int unadjustedCount = 0;\n    Codeword[] codewords = detectionResultColumns[barcodeColumnCount + 1].getCodewords();\n    for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {\n      if (codewords[codewordsRow] == null) {\n        continue;\n      }\n      int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();\n      int invalidRowCounts = 0;\n      for (int barcodeColumn = barcodeColumnCount + 1; barcodeColumn > 0 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP; barcodeColumn--) {\n        Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];\n        if (codeword != null) {\n          invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);\n          if (!codeword.hasValidRowNumber()) {\n            unadjustedCount++;\n          }\n        }\n      }\n    }\n    return unadjustedCount;\n  }\n\n  private int adjustRowNumbersFromLRI() {\n    if (detectionResultColumns[0] == null) {\n      return 0;\n    }\n    int unadjustedCount = 0;\n    Codeword[] codewords = detectionResultColumns[0].getCodewords();\n    for (int codewordsRow = 0; codewordsRow < codewords.length; codewordsRow++) {\n      if (codewords[codewordsRow] == null) {\n        continue;\n      }\n      int rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber();\n      int invalidRowCounts = 0;\n      for (int barcodeColumn = 1; barcodeColumn < barcodeColumnCount + 1 && invalidRowCounts < ADJUST_ROW_NUMBER_SKIP; barcodeColumn++) {\n        Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];\n        if (codeword != null) {\n          invalidRowCounts = adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword);\n          if (!codeword.hasValidRowNumber()) {\n            unadjustedCount++;\n          }\n        }\n      }\n    }\n    return unadjustedCount;\n  }\n\n  private static int adjustRowNumberIfValid(int rowIndicatorRowNumber, int invalidRowCounts, Codeword codeword) {\n    if (codeword == null) {\n      return invalidRowCounts;\n    }\n    if (!codeword.hasValidRowNumber()) {\n      if (codeword.isValidRowNumber(rowIndicatorRowNumber)) {\n        codeword.setRowNumber(rowIndicatorRowNumber);\n        invalidRowCounts = 0;\n      } else {\n        ++invalidRowCounts;\n      }\n    }\n    return invalidRowCounts;\n  }\n\n  private void adjustRowNumbers(int barcodeColumn, int codewordsRow, Codeword[] codewords) {\n    Codeword codeword = codewords[codewordsRow];\n    Codeword[] previousColumnCodewords = detectionResultColumns[barcodeColumn - 1].getCodewords();\n    Codeword[] nextColumnCodewords = previousColumnCodewords;\n    if (detectionResultColumns[barcodeColumn + 1] != null) {\n      nextColumnCodewords = detectionResultColumns[barcodeColumn + 1].getCodewords();\n    }\n\n    Codeword[] otherCodewords = new Codeword[14];\n\n    otherCodewords[2] = previousColumnCodewords[codewordsRow];\n    otherCodewords[3] = nextColumnCodewords[codewordsRow];\n\n    if (codewordsRow > 0) {\n      otherCodewords[0] = codewords[codewordsRow - 1];\n      otherCodewords[4] = previousColumnCodewords[codewordsRow - 1];\n      otherCodewords[5] = nextColumnCodewords[codewordsRow - 1];\n    }\n    if (codewordsRow > 1) {\n      otherCodewords[8] = codewords[codewordsRow - 2];\n      otherCodewords[10] = previousColumnCodewords[codewordsRow - 2];\n      otherCodewords[11] = nextColumnCodewords[codewordsRow - 2];\n    }\n    if (codewordsRow < codewords.length - 1) {\n      otherCodewords[1] = codewords[codewordsRow + 1];\n      otherCodewords[6] = previousColumnCodewords[codewordsRow + 1];\n      otherCodewords[7] = nextColumnCodewords[codewordsRow + 1];\n    }\n    if (codewordsRow < codewords.length - 2) {\n      otherCodewords[9] = codewords[codewordsRow + 2];\n      otherCodewords[12] = previousColumnCodewords[codewordsRow + 2];\n      otherCodewords[13] = nextColumnCodewords[codewordsRow + 2];\n    }\n    for (Codeword otherCodeword : otherCodewords) {\n      if (adjustRowNumber(codeword, otherCodeword)) {\n        return;\n      }\n    }\n  }\n\n  /**\n   * @return true, if row number was adjusted, false otherwise\n   */\n  private static boolean adjustRowNumber(Codeword codeword, Codeword otherCodeword) {\n    if (otherCodeword == null) {\n      return false;\n    }\n    if (otherCodeword.hasValidRowNumber() && otherCodeword.getBucket() == codeword.getBucket()) {\n      codeword.setRowNumber(otherCodeword.getRowNumber());\n      return true;\n    }\n    return false;\n  }\n\n  int getBarcodeColumnCount() {\n    return barcodeColumnCount;\n  }\n\n  int getBarcodeRowCount() {\n    return barcodeMetadata.getRowCount();\n  }\n\n  int getBarcodeECLevel() {\n    return barcodeMetadata.getErrorCorrectionLevel();\n  }\n\n  void setBoundingBox(BoundingBox boundingBox) {\n    this.boundingBox = boundingBox;\n  }\n\n  BoundingBox getBoundingBox() {\n    return boundingBox;\n  }\n\n  void setDetectionResultColumn(int barcodeColumn, DetectionResultColumn detectionResultColumn) {\n    detectionResultColumns[barcodeColumn] = detectionResultColumn;\n  }\n\n  DetectionResultColumn getDetectionResultColumn(int barcodeColumn) {\n    return detectionResultColumns[barcodeColumn];\n  }\n\n  @Override\n  public String toString() {\n    DetectionResultColumn rowIndicatorColumn = detectionResultColumns[0];\n    if (rowIndicatorColumn == null) {\n      rowIndicatorColumn = detectionResultColumns[barcodeColumnCount + 1];\n    }\n    try (Formatter formatter = new Formatter()) {\n      for (int codewordsRow = 0; codewordsRow < rowIndicatorColumn.getCodewords().length; codewordsRow++) {\n        formatter.format(\"CW %3d:\", codewordsRow);\n        for (int barcodeColumn = 0; barcodeColumn < barcodeColumnCount + 2; barcodeColumn++) {\n          if (detectionResultColumns[barcodeColumn] == null) {\n            formatter.format(\"    |   \");\n            continue;\n          }\n          Codeword codeword = detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow];\n          if (codeword == null) {\n            formatter.format(\"    |   \");\n            continue;\n          }\n          formatter.format(\" %3d|%3d\", codeword.getRowNumber(), codeword.getValue());\n        }\n        formatter.format(\"%n\");\n      }\n      return formatter.toString();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/DetectionResultColumn.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport java.util.Formatter;\n\n/**\n * @author Guenther Grau\n */\nclass DetectionResultColumn {\n\n  private static final int MAX_NEARBY_DISTANCE = 5;\n\n  private final BoundingBox boundingBox;\n  private final Codeword[] codewords;\n\n  DetectionResultColumn(BoundingBox boundingBox) {\n    this.boundingBox = new BoundingBox(boundingBox);\n    codewords = new Codeword[boundingBox.getMaxY() - boundingBox.getMinY() + 1];\n  }\n\n  final Codeword getCodewordNearby(int imageRow) {\n    Codeword codeword = getCodeword(imageRow);\n    if (codeword != null) {\n      return codeword;\n    }\n    for (int i = 1; i < MAX_NEARBY_DISTANCE; i++) {\n      int nearImageRow = imageRowToCodewordIndex(imageRow) - i;\n      if (nearImageRow >= 0) {\n        codeword = codewords[nearImageRow];\n        if (codeword != null) {\n          return codeword;\n        }\n      }\n      nearImageRow = imageRowToCodewordIndex(imageRow) + i;\n      if (nearImageRow < codewords.length) {\n        codeword = codewords[nearImageRow];\n        if (codeword != null) {\n          return codeword;\n        }\n      }\n    }\n    return null;\n  }\n\n  final int imageRowToCodewordIndex(int imageRow) {\n    return imageRow - boundingBox.getMinY();\n  }\n\n  final void setCodeword(int imageRow, Codeword codeword) {\n    codewords[imageRowToCodewordIndex(imageRow)] = codeword;\n  }\n\n  final Codeword getCodeword(int imageRow) {\n    return codewords[imageRowToCodewordIndex(imageRow)];\n  }\n\n  final BoundingBox getBoundingBox() {\n    return boundingBox;\n  }\n\n  final Codeword[] getCodewords() {\n    return codewords;\n  }\n\n  @Override\n  public String toString() {\n    try (Formatter formatter = new Formatter()) {\n      int row = 0;\n      for (Codeword codeword : codewords) {\n        if (codeword == null) {\n          formatter.format(\"%3d:    |   %n\", row++);\n          continue;\n        }\n        formatter.format(\"%3d: %3d|%3d%n\", row++, codeword.getRowNumber(), codeword.getValue());\n      }\n      return formatter.toString();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/DetectionResultRowIndicatorColumn.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.pdf417.PDF417Common;\n\n/**\n * @author Guenther Grau\n */\nfinal class DetectionResultRowIndicatorColumn extends DetectionResultColumn {\n\n  private final boolean isLeft;\n\n  DetectionResultRowIndicatorColumn(BoundingBox boundingBox, boolean isLeft) {\n    super(boundingBox);\n    this.isLeft = isLeft;\n  }\n\n  private void setRowNumbers() {\n    for (Codeword codeword : getCodewords()) {\n      if (codeword != null) {\n        codeword.setRowNumberAsRowIndicatorColumn();\n      }\n    }\n  }\n\n  // TODO implement properly\n  // TODO maybe we should add missing codewords to store the correct row number to make\n  // finding row numbers for other columns easier\n  // use row height count to make detection of invalid row numbers more reliable\n  void adjustCompleteIndicatorColumnRowNumbers(BarcodeMetadata barcodeMetadata) {\n    Codeword[] codewords = getCodewords();\n    setRowNumbers();\n    removeIncorrectCodewords(codewords, barcodeMetadata);\n    BoundingBox boundingBox = getBoundingBox();\n    ResultPoint top = isLeft ? boundingBox.getTopLeft() : boundingBox.getTopRight();\n    ResultPoint bottom = isLeft ? boundingBox.getBottomLeft() : boundingBox.getBottomRight();\n    int firstRow = imageRowToCodewordIndex((int) top.getY());\n    int lastRow = imageRowToCodewordIndex((int) bottom.getY());\n    // We need to be careful using the average row height. Barcode could be skewed so that we have smaller and\n    // taller rows\n    //float averageRowHeight = (lastRow - firstRow) / (float) barcodeMetadata.getRowCount();\n    int barcodeRow = -1;\n    int maxRowHeight = 1;\n    int currentRowHeight = 0;\n    for (int codewordsRow = firstRow; codewordsRow < lastRow; codewordsRow++) {\n      if (codewords[codewordsRow] == null) {\n        continue;\n      }\n      Codeword codeword = codewords[codewordsRow];\n\n      //      float expectedRowNumber = (codewordsRow - firstRow) / averageRowHeight;\n      //      if (Math.abs(codeword.getRowNumber() - expectedRowNumber) > 2) {\n      //        SimpleLog.log(LEVEL.WARNING,\n      //            \"Removing codeword, rowNumberSkew too high, codeword[\" + codewordsRow + \"]: Expected Row: \" +\n      //                expectedRowNumber + \", RealRow: \" + codeword.getRowNumber() + \", value: \" + codeword.getValue());\n      //        codewords[codewordsRow] = null;\n      //      }\n\n      int rowDifference = codeword.getRowNumber() - barcodeRow;\n\n      // TODO improve handling with case where first row indicator doesn't start with 0\n\n      if (rowDifference == 0) {\n        currentRowHeight++;\n      } else if (rowDifference == 1) {\n        maxRowHeight = Math.max(maxRowHeight, currentRowHeight);\n        currentRowHeight = 1;\n        barcodeRow = codeword.getRowNumber();\n      } else if (rowDifference < 0 ||\n                 codeword.getRowNumber() >= barcodeMetadata.getRowCount() ||\n                 rowDifference > codewordsRow) {\n        codewords[codewordsRow] = null;\n      } else {\n        int checkedRows;\n        if (maxRowHeight > 2) {\n          checkedRows = (maxRowHeight - 2) * rowDifference;\n        } else {\n          checkedRows = rowDifference;\n        }\n        boolean closePreviousCodewordFound = checkedRows >= codewordsRow;\n        for (int i = 1; i <= checkedRows && !closePreviousCodewordFound; i++) {\n          // there must be (height * rowDifference) number of codewords missing. For now we assume height = 1.\n          // This should hopefully get rid of most problems already.\n          closePreviousCodewordFound = codewords[codewordsRow - i] != null;\n        }\n        if (closePreviousCodewordFound) {\n          codewords[codewordsRow] = null;\n        } else {\n          barcodeRow = codeword.getRowNumber();\n          currentRowHeight = 1;\n        }\n      }\n    }\n    //return (int) (averageRowHeight + 0.5);\n  }\n\n  int[] getRowHeights() {\n    BarcodeMetadata barcodeMetadata = getBarcodeMetadata();\n    if (barcodeMetadata == null) {\n      return null;\n    }\n    adjustIncompleteIndicatorColumnRowNumbers(barcodeMetadata);\n    int[] result = new int[barcodeMetadata.getRowCount()];\n    for (Codeword codeword : getCodewords()) {\n      if (codeword != null) {\n        int rowNumber = codeword.getRowNumber();\n        if (rowNumber >= result.length) {\n          // We have more rows than the barcode metadata allows for, ignore them.\n          continue;\n        }\n        result[rowNumber]++;\n      } // else throw exception?\n    }\n    return result;\n  }\n\n  // TODO maybe we should add missing codewords to store the correct row number to make\n  // finding row numbers for other columns easier\n  // use row height count to make detection of invalid row numbers more reliable\n  private void adjustIncompleteIndicatorColumnRowNumbers(BarcodeMetadata barcodeMetadata) {\n    BoundingBox boundingBox = getBoundingBox();\n    ResultPoint top = isLeft ? boundingBox.getTopLeft() : boundingBox.getTopRight();\n    ResultPoint bottom = isLeft ? boundingBox.getBottomLeft() : boundingBox.getBottomRight();\n    int firstRow = imageRowToCodewordIndex((int) top.getY());\n    int lastRow = imageRowToCodewordIndex((int) bottom.getY());\n    //float averageRowHeight = (lastRow - firstRow) / (float) barcodeMetadata.getRowCount();\n    Codeword[] codewords = getCodewords();\n    int barcodeRow = -1;\n    int maxRowHeight = 1;\n    int currentRowHeight = 0;\n    for (int codewordsRow = firstRow; codewordsRow < lastRow; codewordsRow++) {\n      if (codewords[codewordsRow] == null) {\n        continue;\n      }\n      Codeword codeword = codewords[codewordsRow];\n\n      codeword.setRowNumberAsRowIndicatorColumn();\n\n      int rowDifference = codeword.getRowNumber() - barcodeRow;\n\n      // TODO improve handling with case where first row indicator doesn't start with 0\n\n      if (rowDifference == 0) {\n        currentRowHeight++;\n      } else if (rowDifference == 1) {\n        maxRowHeight = Math.max(maxRowHeight, currentRowHeight);\n        currentRowHeight = 1;\n        barcodeRow = codeword.getRowNumber();\n      } else if (codeword.getRowNumber() >= barcodeMetadata.getRowCount()) {\n        codewords[codewordsRow] = null;\n      } else {\n        barcodeRow = codeword.getRowNumber();\n        currentRowHeight = 1;\n      }\n    }\n    //return (int) (averageRowHeight + 0.5);\n  }\n\n  BarcodeMetadata getBarcodeMetadata() {\n    Codeword[] codewords = getCodewords();\n    BarcodeValue barcodeColumnCount = new BarcodeValue();\n    BarcodeValue barcodeRowCountUpperPart = new BarcodeValue();\n    BarcodeValue barcodeRowCountLowerPart = new BarcodeValue();\n    BarcodeValue barcodeECLevel = new BarcodeValue();\n    for (Codeword codeword : codewords) {\n      if (codeword == null) {\n        continue;\n      }\n      codeword.setRowNumberAsRowIndicatorColumn();\n      int rowIndicatorValue = codeword.getValue() % 30;\n      int codewordRowNumber = codeword.getRowNumber();\n      if (!isLeft) {\n        codewordRowNumber += 2;\n      }\n      switch (codewordRowNumber % 3) {\n        case 0:\n          barcodeRowCountUpperPart.setValue(rowIndicatorValue * 3 + 1);\n          break;\n        case 1:\n          barcodeECLevel.setValue(rowIndicatorValue / 3);\n          barcodeRowCountLowerPart.setValue(rowIndicatorValue % 3);\n          break;\n        case 2:\n          barcodeColumnCount.setValue(rowIndicatorValue + 1);\n          break;\n      }\n    }\n    // Maybe we should check if we have ambiguous values?\n    if ((barcodeColumnCount.getValue().length == 0) ||\n        (barcodeRowCountUpperPart.getValue().length == 0) ||\n        (barcodeRowCountLowerPart.getValue().length == 0) ||\n        (barcodeECLevel.getValue().length == 0) ||\n        barcodeColumnCount.getValue()[0] < 1 ||\n        barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0] < PDF417Common.MIN_ROWS_IN_BARCODE ||\n        barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0] > PDF417Common.MAX_ROWS_IN_BARCODE) {\n      return null;\n    }\n    BarcodeMetadata barcodeMetadata = new BarcodeMetadata(barcodeColumnCount.getValue()[0],\n        barcodeRowCountUpperPart.getValue()[0], barcodeRowCountLowerPart.getValue()[0], barcodeECLevel.getValue()[0]);\n    removeIncorrectCodewords(codewords, barcodeMetadata);\n    return barcodeMetadata;\n  }\n\n  private void removeIncorrectCodewords(Codeword[] codewords, BarcodeMetadata barcodeMetadata) {\n    // Remove codewords which do not match the metadata\n    // TODO Maybe we should keep the incorrect codewords for the start and end positions?\n    for (int codewordRow = 0; codewordRow < codewords.length; codewordRow++) {\n      Codeword codeword = codewords[codewordRow];\n      if (codewords[codewordRow] == null) {\n        continue;\n      }\n      int rowIndicatorValue = codeword.getValue() % 30;\n      int codewordRowNumber = codeword.getRowNumber();\n      if (codewordRowNumber > barcodeMetadata.getRowCount()) {\n        codewords[codewordRow] = null;\n        continue;\n      }\n      if (!isLeft) {\n        codewordRowNumber += 2;\n      }\n      switch (codewordRowNumber % 3) {\n        case 0:\n          if (rowIndicatorValue * 3 + 1 != barcodeMetadata.getRowCountUpperPart()) {\n            codewords[codewordRow] = null;\n          }\n          break;\n        case 1:\n          if (rowIndicatorValue / 3 != barcodeMetadata.getErrorCorrectionLevel() ||\n              rowIndicatorValue % 3 != barcodeMetadata.getRowCountLowerPart()) {\n            codewords[codewordRow] = null;\n          }\n          break;\n        case 2:\n          if (rowIndicatorValue + 1 != barcodeMetadata.getColumnCount()) {\n            codewords[codewordRow] = null;\n          }\n          break;\n      }\n    }\n  }\n\n  boolean isLeft() {\n    return isLeft;\n  }\n\n  @Override\n  public String toString() {\n    return \"IsLeft: \" + isLeft + '\\n' + super.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/PDF417CodewordDecoder.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport com.google.zxing.common.detector.MathUtils;\nimport com.google.zxing.pdf417.PDF417Common;\n\n/**\n * @author Guenther Grau\n * @author creatale GmbH (christoph.schulz@creatale.de)\n */\nfinal class PDF417CodewordDecoder {\n\n  private static final float[][] RATIOS_TABLE =\n      new float[PDF417Common.SYMBOL_TABLE.length][PDF417Common.BARS_IN_MODULE];\n\n  static {\n    // Pre-computes the symbol ratio table.\n    for (int i = 0; i < PDF417Common.SYMBOL_TABLE.length; i++) {\n      int currentSymbol = PDF417Common.SYMBOL_TABLE[i];\n      int currentBit = currentSymbol & 0x1;\n      for (int j = 0; j < PDF417Common.BARS_IN_MODULE; j++) {\n        float size = 0.0f;\n        while ((currentSymbol & 0x1) == currentBit) {\n          size += 1.0f;\n          currentSymbol >>= 1;\n        }\n        currentBit = currentSymbol & 0x1;\n        RATIOS_TABLE[i][PDF417Common.BARS_IN_MODULE - j - 1] = size / PDF417Common.MODULES_IN_CODEWORD;\n      }\n    }\n  }\n\n  private PDF417CodewordDecoder() {\n  }\n\n  static int getDecodedValue(int[] moduleBitCount) {\n    int decodedValue = getDecodedCodewordValue(sampleBitCounts(moduleBitCount));\n    if (decodedValue != -1) {\n      return decodedValue;\n    }\n    return getClosestDecodedValue(moduleBitCount);\n  }\n\n  private static int[] sampleBitCounts(int[] moduleBitCount) {\n    float bitCountSum = MathUtils.sum(moduleBitCount);\n    int[] result = new int[PDF417Common.BARS_IN_MODULE];\n    int bitCountIndex = 0;\n    int sumPreviousBits = 0;\n    for (int i = 0; i < PDF417Common.MODULES_IN_CODEWORD; i++) {\n      float sampleIndex = \n          bitCountSum / (2 * PDF417Common.MODULES_IN_CODEWORD) +\n          (i * bitCountSum) / PDF417Common.MODULES_IN_CODEWORD;\n      if (sumPreviousBits + moduleBitCount[bitCountIndex] <= sampleIndex) {\n        sumPreviousBits += moduleBitCount[bitCountIndex];\n        bitCountIndex++;\n      }\n      result[bitCountIndex]++;\n    }\n    return result;\n  }\n\n  private static int getDecodedCodewordValue(int[] moduleBitCount) {\n    int decodedValue = getBitValue(moduleBitCount);\n    return PDF417Common.getCodeword(decodedValue) == -1 ? -1 : decodedValue;\n  }\n\n  private static int getBitValue(int[] moduleBitCount) {\n    long result = 0;\n    for (int i = 0; i < moduleBitCount.length; i++) {\n      for (int bit = 0; bit < moduleBitCount[i]; bit++) {\n        result = (result << 1) | (i % 2 == 0 ? 1 : 0);\n      }\n    }\n    return (int) result;\n  }\n\n  private static int getClosestDecodedValue(int[] moduleBitCount) {\n    int bitCountSum = MathUtils.sum(moduleBitCount);\n    float[] bitCountRatios = new float[PDF417Common.BARS_IN_MODULE];\n    if (bitCountSum > 1) {\n      for (int i = 0; i < bitCountRatios.length; i++) {\n        bitCountRatios[i] = moduleBitCount[i] / (float) bitCountSum;\n      }\n    }\n    float bestMatchError = Float.MAX_VALUE;\n    int bestMatch = -1;\n    for (int j = 0; j < RATIOS_TABLE.length; j++) {\n      float error = 0.0f;\n      float[] ratioTableRow = RATIOS_TABLE[j];\n      for (int k = 0; k < PDF417Common.BARS_IN_MODULE; k++) {\n        float diff = ratioTableRow[k] - bitCountRatios[k];\n        error += diff * diff;\n        if (error >= bestMatchError) {\n          break;\n        }\n      }\n      if (error < bestMatchError) {\n        bestMatchError = error;\n        bestMatch = PDF417Common.SYMBOL_TABLE[j];\n      }\n    }\n    return bestMatch;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/PDF417ScanningDecoder.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder;\n\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.detector.MathUtils;\nimport com.google.zxing.pdf417.PDF417Common;\nimport com.google.zxing.pdf417.decoder.ec.ErrorCorrection;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Formatter;\nimport java.util.List;\n\n/**\n * @author Guenther Grau\n */\npublic final class PDF417ScanningDecoder {\n\n  private static final int CODEWORD_SKEW_SIZE = 2;\n\n  private static final int MAX_ERRORS = 3;\n  private static final int MAX_EC_CODEWORDS = 512;\n  private static final ErrorCorrection errorCorrection = new ErrorCorrection();\n\n  private PDF417ScanningDecoder() {\n  }\n\n  // TODO don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern\n  // columns. That way width can be deducted from the pattern column.\n  // This approach also allows to detect more details about the barcode, e.g. if a bar type (white or black) is wider\n  // than it should be. This can happen if the scanner used a bad blackpoint.\n  public static DecoderResult decode(BitMatrix image,\n                                     ResultPoint imageTopLeft,\n                                     ResultPoint imageBottomLeft,\n                                     ResultPoint imageTopRight,\n                                     ResultPoint imageBottomRight,\n                                     int minCodewordWidth,\n                                     int maxCodewordWidth) throws NotFoundException, FormatException, ChecksumException {\n    BoundingBox boundingBox = new BoundingBox(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight);\n    DetectionResultRowIndicatorColumn leftRowIndicatorColumn = null;\n    DetectionResultRowIndicatorColumn rightRowIndicatorColumn = null;\n    DetectionResult detectionResult;\n    for (boolean firstPass = true; ; firstPass = false) {\n      if (imageTopLeft != null) {\n        leftRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopLeft, true, minCodewordWidth,\n            maxCodewordWidth);\n      }\n      if (imageTopRight != null) {\n        rightRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopRight, false, minCodewordWidth,\n            maxCodewordWidth);\n      }\n      detectionResult = merge(leftRowIndicatorColumn, rightRowIndicatorColumn);\n      if (detectionResult == null) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      BoundingBox resultBox = detectionResult.getBoundingBox();\n      if (firstPass && resultBox != null &&\n          (resultBox.getMinY() < boundingBox.getMinY() || resultBox.getMaxY() > boundingBox.getMaxY())) {\n        boundingBox = resultBox;\n      } else {\n        break;\n      }\n    }\n    detectionResult.setBoundingBox(boundingBox);\n    int maxBarcodeColumn = detectionResult.getBarcodeColumnCount() + 1;\n    detectionResult.setDetectionResultColumn(0, leftRowIndicatorColumn);\n    detectionResult.setDetectionResultColumn(maxBarcodeColumn, rightRowIndicatorColumn);\n\n    boolean leftToRight = leftRowIndicatorColumn != null;\n    for (int barcodeColumnCount = 1; barcodeColumnCount <= maxBarcodeColumn; barcodeColumnCount++) {\n      int barcodeColumn = leftToRight ? barcodeColumnCount : maxBarcodeColumn - barcodeColumnCount;\n      if (detectionResult.getDetectionResultColumn(barcodeColumn) != null) {\n        // This will be the case for the opposite row indicator column, which doesn't need to be decoded again.\n        continue;\n      }\n      DetectionResultColumn detectionResultColumn;\n      if (barcodeColumn == 0 || barcodeColumn == maxBarcodeColumn) {\n        detectionResultColumn = new DetectionResultRowIndicatorColumn(boundingBox, barcodeColumn == 0);\n      } else {\n        detectionResultColumn = new DetectionResultColumn(boundingBox);\n      }\n      detectionResult.setDetectionResultColumn(barcodeColumn, detectionResultColumn);\n      int startColumn = -1;\n      int previousStartColumn = startColumn;\n      // TODO start at a row for which we know the start position, then detect upwards and downwards from there.\n      for (int imageRow = boundingBox.getMinY(); imageRow <= boundingBox.getMaxY(); imageRow++) {\n        startColumn = getStartColumn(detectionResult, barcodeColumn, imageRow, leftToRight);\n        if (startColumn < 0 || startColumn > boundingBox.getMaxX()) {\n          if (previousStartColumn == -1) {\n            continue;\n          }\n          startColumn = previousStartColumn;\n        }\n        Codeword codeword = detectCodeword(image, boundingBox.getMinX(), boundingBox.getMaxX(), leftToRight,\n            startColumn, imageRow, minCodewordWidth, maxCodewordWidth);\n        if (codeword != null) {\n          detectionResultColumn.setCodeword(imageRow, codeword);\n          previousStartColumn = startColumn;\n          minCodewordWidth = Math.min(minCodewordWidth, codeword.getWidth());\n          maxCodewordWidth = Math.max(maxCodewordWidth, codeword.getWidth());\n        }\n      }\n    }\n    return createDecoderResult(detectionResult);\n  }\n\n  private static DetectionResult merge(DetectionResultRowIndicatorColumn leftRowIndicatorColumn,\n                                       DetectionResultRowIndicatorColumn rightRowIndicatorColumn)\n      throws NotFoundException {\n    if (leftRowIndicatorColumn == null && rightRowIndicatorColumn == null) {\n      return null;\n    }\n    BarcodeMetadata barcodeMetadata = getBarcodeMetadata(leftRowIndicatorColumn, rightRowIndicatorColumn);\n    if (barcodeMetadata == null) {\n      return null;\n    }\n    BoundingBox boundingBox = BoundingBox.merge(adjustBoundingBox(leftRowIndicatorColumn),\n        adjustBoundingBox(rightRowIndicatorColumn));\n    return new DetectionResult(barcodeMetadata, boundingBox);\n  }\n\n  private static BoundingBox adjustBoundingBox(DetectionResultRowIndicatorColumn rowIndicatorColumn)\n      throws NotFoundException {\n    if (rowIndicatorColumn == null) {\n      return null;\n    }\n    int[] rowHeights = rowIndicatorColumn.getRowHeights();\n    if (rowHeights == null) {\n      return null;\n    }\n    int maxRowHeight = getMax(rowHeights);\n    int missingStartRows = 0;\n    for (int rowHeight : rowHeights) {\n      missingStartRows += maxRowHeight - rowHeight;\n      if (rowHeight > 0) {\n        break;\n      }\n    }\n    Codeword[] codewords = rowIndicatorColumn.getCodewords();\n    for (int row = 0; missingStartRows > 0 && codewords[row] == null; row++) {\n      missingStartRows--;\n    }\n    int missingEndRows = 0;\n    for (int row = rowHeights.length - 1; row >= 0; row--) {\n      missingEndRows += maxRowHeight - rowHeights[row];\n      if (rowHeights[row] > 0) {\n        break;\n      }\n    }\n    for (int row = codewords.length - 1; missingEndRows > 0 && codewords[row] == null; row--) {\n      missingEndRows--;\n    }\n    return rowIndicatorColumn.getBoundingBox().addMissingRows(missingStartRows, missingEndRows,\n        rowIndicatorColumn.isLeft());\n  }\n\n  private static int getMax(int[] values) {\n    int maxValue = -1;\n    for (int value : values) {\n      maxValue = Math.max(maxValue, value);\n    }\n    return maxValue;\n  }\n\n  private static BarcodeMetadata getBarcodeMetadata(DetectionResultRowIndicatorColumn leftRowIndicatorColumn,\n                                                    DetectionResultRowIndicatorColumn rightRowIndicatorColumn) {\n    BarcodeMetadata leftBarcodeMetadata;\n    if (leftRowIndicatorColumn == null ||\n        (leftBarcodeMetadata = leftRowIndicatorColumn.getBarcodeMetadata()) == null) {\n      return rightRowIndicatorColumn == null ? null : rightRowIndicatorColumn.getBarcodeMetadata();\n    }\n    BarcodeMetadata rightBarcodeMetadata;\n    if (rightRowIndicatorColumn == null ||\n        (rightBarcodeMetadata = rightRowIndicatorColumn.getBarcodeMetadata()) == null) {\n      return leftBarcodeMetadata;\n    }\n\n    if (leftBarcodeMetadata.getColumnCount() != rightBarcodeMetadata.getColumnCount() &&\n        leftBarcodeMetadata.getErrorCorrectionLevel() != rightBarcodeMetadata.getErrorCorrectionLevel() &&\n        leftBarcodeMetadata.getRowCount() != rightBarcodeMetadata.getRowCount()) {\n      return null;\n    }\n    return leftBarcodeMetadata;\n  }\n\n  private static DetectionResultRowIndicatorColumn getRowIndicatorColumn(BitMatrix image,\n                                                                         BoundingBox boundingBox,\n                                                                         ResultPoint startPoint,\n                                                                         boolean leftToRight,\n                                                                         int minCodewordWidth,\n                                                                         int maxCodewordWidth) {\n    DetectionResultRowIndicatorColumn rowIndicatorColumn = new DetectionResultRowIndicatorColumn(boundingBox,\n        leftToRight);\n    for (int i = 0; i < 2; i++) {\n      int increment = i == 0 ? 1 : -1;\n      int startColumn = (int) startPoint.getX();\n      for (int imageRow = (int) startPoint.getY(); imageRow <= boundingBox.getMaxY() &&\n          imageRow >= boundingBox.getMinY(); imageRow += increment) {\n        Codeword codeword = detectCodeword(image, 0, image.getWidth(), leftToRight, startColumn, imageRow,\n            minCodewordWidth, maxCodewordWidth);\n        if (codeword != null) {\n          rowIndicatorColumn.setCodeword(imageRow, codeword);\n          if (leftToRight) {\n            startColumn = codeword.getStartX();\n          } else {\n            startColumn = codeword.getEndX();\n          }\n        }\n      }\n    }\n    return rowIndicatorColumn;\n  }\n\n  private static void adjustCodewordCount(DetectionResult detectionResult, BarcodeValue[][] barcodeMatrix)\n      throws NotFoundException {\n    BarcodeValue barcodeMatrix01 = barcodeMatrix[0][1];\n    int[] numberOfCodewords = barcodeMatrix01.getValue();\n    int calculatedNumberOfCodewords = detectionResult.getBarcodeColumnCount() *\n        detectionResult.getBarcodeRowCount() -\n        getNumberOfECCodeWords(detectionResult.getBarcodeECLevel());\n    if (numberOfCodewords.length == 0) {\n      if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common.MAX_CODEWORDS_IN_BARCODE) {\n        throw NotFoundException.getNotFoundInstance();\n      }\n      barcodeMatrix01.setValue(calculatedNumberOfCodewords);\n    } else if (numberOfCodewords[0] != calculatedNumberOfCodewords) {\n      // The calculated one is more reliable as it is derived from the row indicator columns\n      barcodeMatrix01.setValue(calculatedNumberOfCodewords);\n    }\n  }\n\n  private static DecoderResult createDecoderResult(DetectionResult detectionResult) throws FormatException,\n      ChecksumException, NotFoundException {\n    BarcodeValue[][] barcodeMatrix = createBarcodeMatrix(detectionResult);\n    adjustCodewordCount(detectionResult, barcodeMatrix);\n    Collection<Integer> erasures = new ArrayList<>();\n    int[] codewords = new int[detectionResult.getBarcodeRowCount() * detectionResult.getBarcodeColumnCount()];\n    List<int[]> ambiguousIndexValuesList = new ArrayList<>();\n    Collection<Integer> ambiguousIndexesList = new ArrayList<>();\n    for (int row = 0; row < detectionResult.getBarcodeRowCount(); row++) {\n      for (int column = 0; column < detectionResult.getBarcodeColumnCount(); column++) {\n        int[] values = barcodeMatrix[row][column + 1].getValue();\n        int codewordIndex = row * detectionResult.getBarcodeColumnCount() + column;\n        if (values.length == 0) {\n          erasures.add(codewordIndex);\n        } else if (values.length == 1) {\n          codewords[codewordIndex] = values[0];\n        } else {\n          ambiguousIndexesList.add(codewordIndex);\n          ambiguousIndexValuesList.add(values);\n        }\n      }\n    }\n    int[][] ambiguousIndexValues = new int[ambiguousIndexValuesList.size()][];\n    for (int i = 0; i < ambiguousIndexValues.length; i++) {\n      ambiguousIndexValues[i] = ambiguousIndexValuesList.get(i);\n    }\n    return createDecoderResultFromAmbiguousValues(detectionResult.getBarcodeECLevel(), codewords,\n        PDF417Common.toIntArray(erasures), PDF417Common.toIntArray(ambiguousIndexesList), ambiguousIndexValues);\n  }\n\n  /**\n   * This method deals with the fact, that the decoding process doesn't always yield a single most likely value. The\n   * current error correction implementation doesn't deal with erasures very well, so it's better to provide a value\n   * for these ambiguous codewords instead of treating it as an erasure. The problem is that we don't know which of\n   * the ambiguous values to choose. We try decode using the first value, and if that fails, we use another of the\n   * ambiguous values and try to decode again. This usually only happens on very hard to read and decode barcodes,\n   * so decoding the normal barcodes is not affected by this.\n   *\n   * @param erasureArray contains the indexes of erasures\n   * @param ambiguousIndexes array with the indexes that have more than one most likely value\n   * @param ambiguousIndexValues two dimensional array that contains the ambiguous values. The first dimension must\n   * be the same length as the ambiguousIndexes array\n   */\n  private static DecoderResult createDecoderResultFromAmbiguousValues(int ecLevel,\n                                                                      int[] codewords,\n                                                                      int[] erasureArray,\n                                                                      int[] ambiguousIndexes,\n                                                                      int[][] ambiguousIndexValues)\n      throws FormatException, ChecksumException {\n    int[] ambiguousIndexCount = new int[ambiguousIndexes.length];\n\n    int tries = 100;\n    while (tries-- > 0) {\n      for (int i = 0; i < ambiguousIndexCount.length; i++) {\n        codewords[ambiguousIndexes[i]] = ambiguousIndexValues[i][ambiguousIndexCount[i]];\n      }\n      try {\n        return decodeCodewords(codewords, ecLevel, erasureArray);\n      } catch (ChecksumException ignored) {\n        //\n      }\n      if (ambiguousIndexCount.length == 0) {\n        throw ChecksumException.getChecksumInstance();\n      }\n      for (int i = 0; i < ambiguousIndexCount.length; i++) {\n        if (ambiguousIndexCount[i] < ambiguousIndexValues[i].length - 1) {\n          ambiguousIndexCount[i]++;\n          break;\n        } else {\n          ambiguousIndexCount[i] = 0;\n          if (i == ambiguousIndexCount.length - 1) {\n            throw ChecksumException.getChecksumInstance();\n          }\n        }\n      }\n    }\n    throw ChecksumException.getChecksumInstance();\n  }\n\n  private static BarcodeValue[][] createBarcodeMatrix(DetectionResult detectionResult) {\n    BarcodeValue[][] barcodeMatrix =\n        new BarcodeValue[detectionResult.getBarcodeRowCount()][detectionResult.getBarcodeColumnCount() + 2];\n    for (int row = 0; row < barcodeMatrix.length; row++) {\n      for (int column = 0; column < barcodeMatrix[row].length; column++) {\n        barcodeMatrix[row][column] = new BarcodeValue();\n      }\n    }\n\n    int column = 0;\n    for (DetectionResultColumn detectionResultColumn : detectionResult.getDetectionResultColumns()) {\n      if (detectionResultColumn != null) {\n        for (Codeword codeword : detectionResultColumn.getCodewords()) {\n          if (codeword != null) {\n            int rowNumber = codeword.getRowNumber();\n            if (rowNumber >= 0) {\n              if (rowNumber >= barcodeMatrix.length) {\n                // We have more rows than the barcode metadata allows for, ignore them.\n                continue;\n              }\n              barcodeMatrix[rowNumber][column].setValue(codeword.getValue());\n            }\n          }\n        }\n      }\n      column++;\n    }\n    return barcodeMatrix;\n  }\n\n  private static boolean isValidBarcodeColumn(DetectionResult detectionResult, int barcodeColumn) {\n    return barcodeColumn >= 0 && barcodeColumn <= detectionResult.getBarcodeColumnCount() + 1;\n  }\n\n  private static int getStartColumn(DetectionResult detectionResult,\n                                    int barcodeColumn,\n                                    int imageRow,\n                                    boolean leftToRight) {\n    int offset = leftToRight ? 1 : -1;\n    Codeword codeword = null;\n    if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) {\n      codeword = detectionResult.getDetectionResultColumn(barcodeColumn - offset).getCodeword(imageRow);\n    }\n    if (codeword != null) {\n      return leftToRight ? codeword.getEndX() : codeword.getStartX();\n    }\n    codeword = detectionResult.getDetectionResultColumn(barcodeColumn).getCodewordNearby(imageRow);\n    if (codeword != null) {\n      return leftToRight ? codeword.getStartX() : codeword.getEndX();\n    }\n    if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) {\n      codeword = detectionResult.getDetectionResultColumn(barcodeColumn - offset).getCodewordNearby(imageRow);\n    }\n    if (codeword != null) {\n      return leftToRight ? codeword.getEndX() : codeword.getStartX();\n    }\n    int skippedColumns = 0;\n\n    while (isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) {\n      barcodeColumn -= offset;\n      for (Codeword previousRowCodeword : detectionResult.getDetectionResultColumn(barcodeColumn).getCodewords()) {\n        if (previousRowCodeword != null) {\n          return (leftToRight ? previousRowCodeword.getEndX() : previousRowCodeword.getStartX()) +\n              offset *\n              skippedColumns *\n              (previousRowCodeword.getEndX() - previousRowCodeword.getStartX());\n        }\n      }\n      skippedColumns++;\n    }\n    return leftToRight ? detectionResult.getBoundingBox().getMinX() : detectionResult.getBoundingBox().getMaxX();\n  }\n\n  private static Codeword detectCodeword(BitMatrix image,\n                                         int minColumn,\n                                         int maxColumn,\n                                         boolean leftToRight,\n                                         int startColumn,\n                                         int imageRow,\n                                         int minCodewordWidth,\n                                         int maxCodewordWidth) {\n    startColumn = adjustCodewordStartColumn(image, minColumn, maxColumn, leftToRight, startColumn, imageRow);\n    // we usually know fairly exact now how long a codeword is. We should provide minimum and maximum expected length\n    // and try to adjust the read pixels, e.g. remove single pixel errors or try to cut off exceeding pixels.\n    // min and maxCodewordWidth should not be used as they are calculated for the whole barcode an can be inaccurate\n    // for the current position\n    int[] moduleBitCount = getModuleBitCount(image, minColumn, maxColumn, leftToRight, startColumn, imageRow);\n    if (moduleBitCount == null) {\n      return null;\n    }\n    int endColumn;\n    int codewordBitCount = MathUtils.sum(moduleBitCount);\n    if (leftToRight) {\n      endColumn = startColumn + codewordBitCount;\n    } else {\n      for (int i = 0; i < moduleBitCount.length / 2; i++) {\n        int tmpCount = moduleBitCount[i];\n        moduleBitCount[i] = moduleBitCount[moduleBitCount.length - 1 - i];\n        moduleBitCount[moduleBitCount.length - 1 - i] = tmpCount;\n      }\n      endColumn = startColumn;\n      startColumn = endColumn - codewordBitCount;\n    }\n    // TODO implement check for width and correction of black and white bars\n    // use start (and maybe stop pattern) to determine if black bars are wider than white bars. If so, adjust.\n    // should probably done only for codewords with a lot more than 17 bits.\n    // The following fixes 10-1.png, which has wide black bars and small white bars\n    //    for (int i = 0; i < moduleBitCount.length; i++) {\n    //      if (i % 2 == 0) {\n    //        moduleBitCount[i]--;\n    //      } else {\n    //        moduleBitCount[i]++;\n    //      }\n    //    }\n\n    // We could also use the width of surrounding codewords for more accurate results, but this seems\n    // sufficient for now\n    if (!checkCodewordSkew(codewordBitCount, minCodewordWidth, maxCodewordWidth)) {\n      // We could try to use the startX and endX position of the codeword in the same column in the previous row,\n      // create the bit count from it and normalize it to 8. This would help with single pixel errors.\n      return null;\n    }\n\n    int decodedValue = PDF417CodewordDecoder.getDecodedValue(moduleBitCount);\n    int codeword = PDF417Common.getCodeword(decodedValue);\n    if (codeword == -1) {\n      return null;\n    }\n    return new Codeword(startColumn, endColumn, getCodewordBucketNumber(decodedValue), codeword);\n  }\n\n  private static int[] getModuleBitCount(BitMatrix image,\n                                         int minColumn,\n                                         int maxColumn,\n                                         boolean leftToRight,\n                                         int startColumn,\n                                         int imageRow) {\n    int imageColumn = startColumn;\n    int[] moduleBitCount = new int[8];\n    int moduleNumber = 0;\n    int increment = leftToRight ? 1 : -1;\n    boolean previousPixelValue = leftToRight;\n    while ((leftToRight ? imageColumn < maxColumn : imageColumn >= minColumn) &&\n           moduleNumber < moduleBitCount.length) {\n      if (image.get(imageColumn, imageRow) == previousPixelValue) {\n        moduleBitCount[moduleNumber]++;\n        imageColumn += increment;\n      } else {\n        moduleNumber++;\n        previousPixelValue = !previousPixelValue;\n      }\n    }\n    if (moduleNumber == moduleBitCount.length ||\n        ((imageColumn == (leftToRight ? maxColumn : minColumn)) &&\n         moduleNumber == moduleBitCount.length - 1)) {\n      return moduleBitCount;\n    }\n    return null;\n  }\n\n  private static int getNumberOfECCodeWords(int barcodeECLevel) {\n    return 2 << barcodeECLevel;\n  }\n\n  private static int adjustCodewordStartColumn(BitMatrix image,\n                                               int minColumn,\n                                               int maxColumn,\n                                               boolean leftToRight,\n                                               int codewordStartColumn,\n                                               int imageRow) {\n    int correctedStartColumn = codewordStartColumn;\n    int increment = leftToRight ? -1 : 1;\n    // there should be no black pixels before the start column. If there are, then we need to start earlier.\n    for (int i = 0; i < 2; i++) {\n      while ((leftToRight ? correctedStartColumn >= minColumn : correctedStartColumn < maxColumn) &&\n             leftToRight == image.get(correctedStartColumn, imageRow)) {\n        if (Math.abs(codewordStartColumn - correctedStartColumn) > CODEWORD_SKEW_SIZE) {\n          return codewordStartColumn;\n        }\n        correctedStartColumn += increment;\n      }\n      increment = -increment;\n      leftToRight = !leftToRight;\n    }\n    return correctedStartColumn;\n  }\n\n  private static boolean checkCodewordSkew(int codewordSize, int minCodewordWidth, int maxCodewordWidth) {\n    return minCodewordWidth - CODEWORD_SKEW_SIZE <= codewordSize &&\n        codewordSize <= maxCodewordWidth + CODEWORD_SKEW_SIZE;\n  }\n\n  private static DecoderResult decodeCodewords(int[] codewords, int ecLevel, int[] erasures) throws FormatException,\n      ChecksumException {\n    if (codewords.length == 0) {\n      throw FormatException.getFormatInstance();\n    }\n\n    int numECCodewords = 1 << (ecLevel + 1);\n    int correctedErrorsCount = correctErrors(codewords, erasures, numECCodewords);\n    verifyCodewordCount(codewords, numECCodewords);\n\n    // Decode the codewords\n    DecoderResult decoderResult = DecodedBitStreamParser.decode(codewords, String.valueOf(ecLevel));\n    decoderResult.setErrorsCorrected(correctedErrorsCount);\n    decoderResult.setErasures(erasures.length);\n    return decoderResult;\n  }\n\n  /**\n   * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to\n   * correct the errors in-place.</p>\n   *\n   * @param codewords   data and error correction codewords\n   * @param erasures positions of any known erasures\n   * @param numECCodewords number of error correction codewords that are available in codewords\n   * @throws ChecksumException if error correction fails\n   */\n  private static int correctErrors(int[] codewords, int[] erasures, int numECCodewords) throws ChecksumException {\n    if (erasures != null &&\n        erasures.length > numECCodewords / 2 + MAX_ERRORS ||\n        numECCodewords < 0 ||\n        numECCodewords > MAX_EC_CODEWORDS) {\n      // Too many errors or EC Codewords is corrupted\n      throw ChecksumException.getChecksumInstance();\n    }\n    return errorCorrection.decode(codewords, numECCodewords, erasures);\n  }\n\n  /**\n   * Verify that all is OK with the codeword array.\n   */\n  private static void verifyCodewordCount(int[] codewords, int numECCodewords) throws FormatException {\n    if (codewords.length < 4) {\n      // Codeword array size should be at least 4 allowing for\n      // Count CW, At least one Data CW, Error Correction CW, Error Correction CW\n      throw FormatException.getFormatInstance();\n    }\n    // The first codeword, the Symbol Length Descriptor, shall always encode the total number of data\n    // codewords in the symbol, including the Symbol Length Descriptor itself, data codewords and pad\n    // codewords, but excluding the number of error correction codewords.\n    int numberOfCodewords = codewords[0];\n    if (numberOfCodewords > codewords.length) {\n      throw FormatException.getFormatInstance();\n    }\n    if (numberOfCodewords == 0) {\n      // Reset to the length of the array - 8 (Allow for at least level 3 Error Correction (8 Error Codewords)\n      if (numECCodewords < codewords.length) {\n        codewords[0] = codewords.length - numECCodewords;\n      } else {\n        throw FormatException.getFormatInstance();\n      }\n    }\n  }\n\n  private static int[] getBitCountForCodeword(int codeword) {\n    int[] result = new int[8];\n    int previousValue = 0;\n    int i = result.length - 1;\n    while (true) {\n      if ((codeword & 0x1) != previousValue) {\n        previousValue = codeword & 0x1;\n        i--;\n        if (i < 0) {\n          break;\n        }\n      }\n      result[i]++;\n      codeword >>= 1;\n    }\n    return result;\n  }\n\n  private static int getCodewordBucketNumber(int codeword) {\n    return getCodewordBucketNumber(getBitCountForCodeword(codeword));\n  }\n\n  private static int getCodewordBucketNumber(int[] moduleBitCount) {\n    return (moduleBitCount[0] - moduleBitCount[2] + moduleBitCount[4] - moduleBitCount[6] + 9) % 9;\n  }\n\n  public static String toString(BarcodeValue[][] barcodeMatrix) {\n    try (Formatter formatter = new Formatter()) {\n      for (int row = 0; row < barcodeMatrix.length; row++) {\n        formatter.format(\"Row %2d: \", row);\n        for (int column = 0; column < barcodeMatrix[row].length; column++) {\n          BarcodeValue barcodeValue = barcodeMatrix[row][column];\n          if (barcodeValue.getValue().length == 0) {\n            formatter.format(\"        \", (Object[]) null);\n          } else {\n            formatter.format(\"%4d(%2d)\", barcodeValue.getValue()[0],\n                barcodeValue.getConfidence(barcodeValue.getValue()[0]));\n          }\n        }\n        formatter.format(\"%n\");\n      }\n      return formatter.toString();\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/ec/ErrorCorrection.java",
    "content": "/*\n * Copyright 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder.ec;\n\nimport com.google.zxing.ChecksumException;\n\n/**\n * <p>PDF417 error correction implementation.</p>\n *\n * <p>This <a href=\"http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction#Example\">example</a>\n * is quite useful in understanding the algorithm.</p>\n *\n * @author Sean Owen\n * @see com.google.zxing.common.reedsolomon.ReedSolomonDecoder\n */\npublic final class ErrorCorrection {\n\n  private final ModulusGF field;\n\n  public ErrorCorrection() {\n    this.field = ModulusGF.PDF417_GF;\n  }\n\n  /**\n   * @param received received codewords\n   * @param numECCodewords number of those codewords used for EC\n   * @param erasures location of erasures\n   * @return number of errors\n   * @throws ChecksumException if errors cannot be corrected, maybe because of too many errors\n   */\n  public int decode(int[] received,\n                    int numECCodewords,\n                    int[] erasures) throws ChecksumException {\n\n    ModulusPoly poly = new ModulusPoly(field, received);\n    int[] S = new int[numECCodewords];\n    boolean error = false;\n    for (int i = numECCodewords; i > 0; i--) {\n      int eval = poly.evaluateAt(field.exp(i));\n      S[numECCodewords - i] = eval;\n      if (eval != 0) {\n        error = true;\n      }\n    }\n\n    if (!error) {\n      return 0;\n    }\n\n    ModulusPoly knownErrors = field.getOne();\n    if (erasures != null) {\n      for (int erasure : erasures) {\n        int b = field.exp(received.length - 1 - erasure);\n        // Add (1 - bx) term:\n        ModulusPoly term = new ModulusPoly(field, new int[]{field.subtract(0, b), 1});\n        knownErrors = knownErrors.multiply(term);\n      }\n    }\n\n    ModulusPoly syndrome = new ModulusPoly(field, S);\n    //syndrome = syndrome.multiply(knownErrors);\n\n    ModulusPoly[] sigmaOmega =\n        runEuclideanAlgorithm(field.buildMonomial(numECCodewords, 1), syndrome, numECCodewords);\n    ModulusPoly sigma = sigmaOmega[0];\n    ModulusPoly omega = sigmaOmega[1];\n\n    //sigma = sigma.multiply(knownErrors);\n\n    int[] errorLocations = findErrorLocations(sigma);\n    int[] errorMagnitudes = findErrorMagnitudes(omega, sigma, errorLocations);\n\n    for (int i = 0; i < errorLocations.length; i++) {\n      int position = received.length - 1 - field.log(errorLocations[i]);\n      if (position < 0) {\n        throw ChecksumException.getChecksumInstance();\n      }\n      received[position] = field.subtract(received[position], errorMagnitudes[i]);\n    }\n    return errorLocations.length;\n  }\n\n  private ModulusPoly[] runEuclideanAlgorithm(ModulusPoly a, ModulusPoly b, int R)\n      throws ChecksumException {\n    // Assume a's degree is >= b's\n    if (a.getDegree() < b.getDegree()) {\n      ModulusPoly temp = a;\n      a = b;\n      b = temp;\n    }\n\n    ModulusPoly rLast = a;\n    ModulusPoly r = b;\n    ModulusPoly tLast = field.getZero();\n    ModulusPoly t = field.getOne();\n\n    // Run Euclidean algorithm until r's degree is less than R/2\n    while (r.getDegree() >= R / 2) {\n      ModulusPoly rLastLast = rLast;\n      ModulusPoly tLastLast = tLast;\n      rLast = r;\n      tLast = t;\n\n      // Divide rLastLast by rLast, with quotient in q and remainder in r\n      if (rLast.isZero()) {\n        // Oops, Euclidean algorithm already terminated?\n        throw ChecksumException.getChecksumInstance();\n      }\n      r = rLastLast;\n      ModulusPoly q = field.getZero();\n      int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());\n      int dltInverse = field.inverse(denominatorLeadingTerm);\n      while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {\n        int degreeDiff = r.getDegree() - rLast.getDegree();\n        int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);\n        q = q.add(field.buildMonomial(degreeDiff, scale));\n        r = r.subtract(rLast.multiplyByMonomial(degreeDiff, scale));\n      }\n\n      t = q.multiply(tLast).subtract(tLastLast).negative();\n    }\n\n    int sigmaTildeAtZero = t.getCoefficient(0);\n    if (sigmaTildeAtZero == 0) {\n      throw ChecksumException.getChecksumInstance();\n    }\n\n    int inverse = field.inverse(sigmaTildeAtZero);\n    ModulusPoly sigma = t.multiply(inverse);\n    ModulusPoly omega = r.multiply(inverse);\n    return new ModulusPoly[]{sigma, omega};\n  }\n\n  private int[] findErrorLocations(ModulusPoly errorLocator) throws ChecksumException {\n    // This is a direct application of Chien's search\n    int numErrors = errorLocator.getDegree();\n    int[] result = new int[numErrors];\n    int e = 0;\n    for (int i = 1; i < field.getSize() && e < numErrors; i++) {\n      if (errorLocator.evaluateAt(i) == 0) {\n        result[e] = field.inverse(i);\n        e++;\n      }\n    }\n    if (e != numErrors) {\n      throw ChecksumException.getChecksumInstance();\n    }\n    return result;\n  }\n\n  private int[] findErrorMagnitudes(ModulusPoly errorEvaluator,\n                                    ModulusPoly errorLocator,\n                                    int[] errorLocations) {\n    int errorLocatorDegree = errorLocator.getDegree();\n    int[] formalDerivativeCoefficients = new int[errorLocatorDegree];\n    for (int i = 1; i <= errorLocatorDegree; i++) {\n      formalDerivativeCoefficients[errorLocatorDegree - i] =\n          field.multiply(i, errorLocator.getCoefficient(i));\n    }\n    ModulusPoly formalDerivative = new ModulusPoly(field, formalDerivativeCoefficients);\n\n    // This is directly applying Forney's Formula\n    int s = errorLocations.length;\n    int[] result = new int[s];\n    for (int i = 0; i < s; i++) {\n      int xiInverse = field.inverse(errorLocations[i]);\n      int numerator = field.subtract(0, errorEvaluator.evaluateAt(xiInverse));\n      int denominator = field.inverse(formalDerivative.evaluateAt(xiInverse));\n      result[i] = field.multiply(numerator, denominator);\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/ec/ModulusGF.java",
    "content": "/*\n * Copyright 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder.ec;\n\nimport com.google.zxing.pdf417.PDF417Common;\n\n/**\n * <p>A field based on powers of a generator integer, modulo some modulus.</p>\n *\n * @author Sean Owen\n * @see com.google.zxing.common.reedsolomon.GenericGF\n */\npublic final class ModulusGF {\n\n  public static final ModulusGF PDF417_GF = new ModulusGF(PDF417Common.NUMBER_OF_CODEWORDS, 3);\n\n  private final int[] expTable;\n  private final int[] logTable;\n  private final ModulusPoly zero;\n  private final ModulusPoly one;\n  private final int modulus;\n\n  private ModulusGF(int modulus, int generator) {\n    this.modulus = modulus;\n    expTable = new int[modulus];\n    logTable = new int[modulus];\n    int x = 1;\n    for (int i = 0; i < modulus; i++) {\n      expTable[i] = x;\n      x = (x * generator) % modulus;\n    }\n    for (int i = 0; i < modulus - 1; i++) {\n      logTable[expTable[i]] = i;\n    }\n    // logTable[0] == 0 but this should never be used\n    zero = new ModulusPoly(this, new int[]{0});\n    one = new ModulusPoly(this, new int[]{1});\n  }\n\n\n  ModulusPoly getZero() {\n    return zero;\n  }\n\n  ModulusPoly getOne() {\n    return one;\n  }\n\n  ModulusPoly buildMonomial(int degree, int coefficient) {\n    if (degree < 0) {\n      throw new IllegalArgumentException();\n    }\n    if (coefficient == 0) {\n      return zero;\n    }\n    int[] coefficients = new int[degree + 1];\n    coefficients[0] = coefficient;\n    return new ModulusPoly(this, coefficients);\n  }\n\n  int add(int a, int b) {\n    return (a + b) % modulus;\n  }\n\n  int subtract(int a, int b) {\n    return (modulus + a - b) % modulus;\n  }\n\n  int exp(int a) {\n    return expTable[a];\n  }\n\n  int log(int a) {\n    if (a == 0) {\n      throw new IllegalArgumentException();\n    }\n    return logTable[a];\n  }\n\n  int inverse(int a) {\n    if (a == 0) {\n      throw new ArithmeticException();\n    }\n    return expTable[modulus - logTable[a] - 1];\n  }\n\n  int multiply(int a, int b) {\n    if (a == 0 || b == 0) {\n      return 0;\n    }\n    return expTable[(logTable[a] + logTable[b]) % (modulus - 1)];\n  }\n\n  int getSize() {\n    return modulus;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/decoder/ec/ModulusPoly.java",
    "content": "/*\n * Copyright 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.decoder.ec;\n\n/**\n * @author Sean Owen\n * @see com.google.zxing.common.reedsolomon.GenericGFPoly\n */\nfinal class ModulusPoly {\n\n  private final ModulusGF field;\n  private final int[] coefficients;\n\n  ModulusPoly(ModulusGF field, int[] coefficients) {\n    if (coefficients.length == 0) {\n      throw new IllegalArgumentException();\n    }\n    this.field = field;\n    int coefficientsLength = coefficients.length;\n    if (coefficientsLength > 1 && coefficients[0] == 0) {\n      // Leading term must be non-zero for anything except the constant polynomial \"0\"\n      int firstNonZero = 1;\n      while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {\n        firstNonZero++;\n      }\n      if (firstNonZero == coefficientsLength) {\n        this.coefficients = new int[]{0};\n      } else {\n        this.coefficients = new int[coefficientsLength - firstNonZero];\n        System.arraycopy(coefficients,\n            firstNonZero,\n            this.coefficients,\n            0,\n            this.coefficients.length);\n      }\n    } else {\n      this.coefficients = coefficients;\n    }\n  }\n\n  int[] getCoefficients() {\n    return coefficients;\n  }\n\n  /**\n   * @return degree of this polynomial\n   */\n  int getDegree() {\n    return coefficients.length - 1;\n  }\n\n  /**\n   * @return true iff this polynomial is the monomial \"0\"\n   */\n  boolean isZero() {\n    return coefficients[0] == 0;\n  }\n\n  /**\n   * @return coefficient of x^degree term in this polynomial\n   */\n  int getCoefficient(int degree) {\n    return coefficients[coefficients.length - 1 - degree];\n  }\n\n  /**\n   * @return evaluation of this polynomial at a given point\n   */\n  int evaluateAt(int a) {\n    if (a == 0) {\n      // Just return the x^0 coefficient\n      return getCoefficient(0);\n    }\n    if (a == 1) {\n      // Just the sum of the coefficients\n      int result = 0;\n      for (int coefficient : coefficients) {\n        result = field.add(result, coefficient);\n      }\n      return result;\n    }\n    int result = coefficients[0];\n    int size = coefficients.length;\n    for (int i = 1; i < size; i++) {\n      result = field.add(field.multiply(a, result), coefficients[i]);\n    }\n    return result;\n  }\n\n  ModulusPoly add(ModulusPoly other) {\n    if (!field.equals(other.field)) {\n      throw new IllegalArgumentException(\"ModulusPolys do not have same ModulusGF field\");\n    }\n    if (isZero()) {\n      return other;\n    }\n    if (other.isZero()) {\n      return this;\n    }\n\n    int[] smallerCoefficients = this.coefficients;\n    int[] largerCoefficients = other.coefficients;\n    if (smallerCoefficients.length > largerCoefficients.length) {\n      int[] temp = smallerCoefficients;\n      smallerCoefficients = largerCoefficients;\n      largerCoefficients = temp;\n    }\n    int[] sumDiff = new int[largerCoefficients.length];\n    int lengthDiff = largerCoefficients.length - smallerCoefficients.length;\n    // Copy high-order terms only found in higher-degree polynomial's coefficients\n    System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);\n\n    for (int i = lengthDiff; i < largerCoefficients.length; i++) {\n      sumDiff[i] = field.add(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);\n    }\n\n    return new ModulusPoly(field, sumDiff);\n  }\n\n  ModulusPoly subtract(ModulusPoly other) {\n    if (!field.equals(other.field)) {\n      throw new IllegalArgumentException(\"ModulusPolys do not have same ModulusGF field\");\n    }\n    if (other.isZero()) {\n      return this;\n    }\n    return add(other.negative());\n  }\n\n  ModulusPoly multiply(ModulusPoly other) {\n    if (!field.equals(other.field)) {\n      throw new IllegalArgumentException(\"ModulusPolys do not have same ModulusGF field\");\n    }\n    if (isZero() || other.isZero()) {\n      return field.getZero();\n    }\n    int[] aCoefficients = this.coefficients;\n    int aLength = aCoefficients.length;\n    int[] bCoefficients = other.coefficients;\n    int bLength = bCoefficients.length;\n    int[] product = new int[aLength + bLength - 1];\n    for (int i = 0; i < aLength; i++) {\n      int aCoeff = aCoefficients[i];\n      for (int j = 0; j < bLength; j++) {\n        product[i + j] = field.add(product[i + j], field.multiply(aCoeff, bCoefficients[j]));\n      }\n    }\n    return new ModulusPoly(field, product);\n  }\n\n  ModulusPoly negative() {\n    int size = coefficients.length;\n    int[] negativeCoefficients = new int[size];\n    for (int i = 0; i < size; i++) {\n      negativeCoefficients[i] = field.subtract(0, coefficients[i]);\n    }\n    return new ModulusPoly(field, negativeCoefficients);\n  }\n\n  ModulusPoly multiply(int scalar) {\n    if (scalar == 0) {\n      return field.getZero();\n    }\n    if (scalar == 1) {\n      return this;\n    }\n    int size = coefficients.length;\n    int[] product = new int[size];\n    for (int i = 0; i < size; i++) {\n      product[i] = field.multiply(coefficients[i], scalar);\n    }\n    return new ModulusPoly(field, product);\n  }\n\n  ModulusPoly multiplyByMonomial(int degree, int coefficient) {\n    if (degree < 0) {\n      throw new IllegalArgumentException();\n    }\n    if (coefficient == 0) {\n      return field.getZero();\n    }\n    int size = coefficients.length;\n    int[] product = new int[size + degree];\n    for (int i = 0; i < size; i++) {\n      product[i] = field.multiply(coefficients[i], coefficient);\n    }\n    return new ModulusPoly(field, product);\n  }\n\n  /*\n  ModulusPoly[] divide(ModulusPoly other) {\n    if (!field.equals(other.field)) {\n      throw new IllegalArgumentException(\"ModulusPolys do not have same ModulusGF field\");\n    }\n    if (other.isZero()) {\n      throw new IllegalArgumentException(\"Divide by 0\");\n    }\n\n    ModulusPoly quotient = field.getZero();\n    ModulusPoly remainder = this;\n\n    int denominatorLeadingTerm = other.getCoefficient(other.getDegree());\n    int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);\n\n    while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {\n      int degreeDifference = remainder.getDegree() - other.getDegree();\n      int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);\n      ModulusPoly term = other.multiplyByMonomial(degreeDifference, scale);\n      ModulusPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);\n      quotient = quotient.add(iterationQuotient);\n      remainder = remainder.subtract(term);\n    }\n\n    return new ModulusPoly[] { quotient, remainder };\n  }\n   */\n\n  @Override\n  public String toString() {\n    StringBuilder result = new StringBuilder(8 * getDegree());\n    for (int degree = getDegree(); degree >= 0; degree--) {\n      int coefficient = getCoefficient(degree);\n      if (coefficient != 0) {\n        if (coefficient < 0) {\n          result.append(\" - \");\n          coefficient = -coefficient;\n        } else {\n          if (result.length() > 0) {\n            result.append(\" + \");\n          }\n        }\n        if (degree == 0 || coefficient != 1) {\n          result.append(coefficient);\n        }\n        if (degree != 0) {\n          if (degree == 1) {\n            result.append('x');\n          } else {\n            result.append(\"x^\");\n            result.append(degree);\n          }\n        }\n      }\n    }\n    return result.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/detector/Detector.java",
    "content": "/*\n * Copyright 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.detector;\n\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>Encapsulates logic that can detect a PDF417 Code in an image, even if the\n * PDF417 Code is rotated or skewed, or partially obscured.</p>\n *\n * @author SITA Lab (kevin.osullivan@sita.aero)\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Guenther Grau\n */\npublic final class Detector {\n\n  private static final int[] INDEXES_START_PATTERN = {0, 4, 1, 5};\n  private static final int[] INDEXES_STOP_PATTERN = {6, 2, 7, 3};\n  private static final float MAX_AVG_VARIANCE = 0.42f;\n  private static final float MAX_INDIVIDUAL_VARIANCE = 0.8f;\n\n  // B S B S B S B S Bar/Space pattern\n  // 11111111 0 1 0 1 0 1 000\n  private static final int[] START_PATTERN = {8, 1, 1, 1, 1, 1, 1, 3};\n  // 1111111 0 1 000 1 0 1 00 1\n  private static final int[] STOP_PATTERN = {7, 1, 1, 3, 1, 1, 1, 2, 1};\n  private static final int MAX_PIXEL_DRIFT = 3;\n  private static final int MAX_PATTERN_DRIFT = 5;\n  // if we set the value too low, then we don't detect the correct height of the bar if the start patterns are damaged.\n  // if we set the value too high, then we might detect the start pattern from a neighbor barcode.\n  private static final int SKIPPED_ROW_COUNT_MAX = 25;\n  // A PDF471 barcode should have at least 3 rows, with each row being >= 3 times the module width. Therefore it should be at least\n  // 9 pixels tall. To be conservative, we use about half the size to ensure we don't miss it.\n  private static final int ROW_STEP = 5;\n  private static final int BARCODE_MIN_HEIGHT = 10;\n\n  private Detector() {\n  }\n\n  /**\n   * <p>Detects a PDF417 Code in an image. Only checks 0 and 180 degree rotations.</p>\n   *\n   * @param image barcode image to decode\n   * @param hints optional hints to detector\n   * @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will\n   * be found and returned\n   * @return {@link PDF417DetectorResult} encapsulating results of detecting a PDF417 code\n   * @throws NotFoundException if no PDF417 Code can be found\n   */\n  public static PDF417DetectorResult detect(BinaryBitmap image, Map<DecodeHintType,?> hints, boolean multiple)\n      throws NotFoundException {\n    // TODO detection improvement, tryHarder could try several different luminance thresholds/blackpoints or even \n    // different binarizers\n    //boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);\n\n    BitMatrix bitMatrix = image.getBlackMatrix();\n\n    List<ResultPoint[]> barcodeCoordinates = detect(multiple, bitMatrix);\n    if (barcodeCoordinates.isEmpty()) {\n      bitMatrix = bitMatrix.clone();\n      bitMatrix.rotate180();\n      barcodeCoordinates = detect(multiple, bitMatrix);\n    }\n    return new PDF417DetectorResult(bitMatrix, barcodeCoordinates);\n  }\n\n  /**\n   * Detects PDF417 codes in an image. Only checks 0 degree rotation\n   * @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will\n   * be found and returned\n   * @param bitMatrix bit matrix to detect barcodes in\n   * @return List of ResultPoint arrays containing the coordinates of found barcodes\n   */\n  private static List<ResultPoint[]> detect(boolean multiple, BitMatrix bitMatrix) {\n    List<ResultPoint[]> barcodeCoordinates = new ArrayList<>();\n    int row = 0;\n    int column = 0;\n    boolean foundBarcodeInRow = false;\n    while (row < bitMatrix.getHeight()) {\n      ResultPoint[] vertices = findVertices(bitMatrix, row, column);\n\n      if (vertices[0] == null && vertices[3] == null) {\n        if (!foundBarcodeInRow) {\n          // we didn't find any barcode so that's the end of searching\n          break;\n        }\n        // we didn't find a barcode starting at the given column and row. Try again from the first column and slightly\n        // below the lowest barcode we found so far.\n        foundBarcodeInRow = false;\n        column = 0;\n        for (ResultPoint[] barcodeCoordinate : barcodeCoordinates) {\n          if (barcodeCoordinate[1] != null) {\n            row = (int) Math.max(row, barcodeCoordinate[1].getY());\n          }\n          if (barcodeCoordinate[3] != null) {\n            row = Math.max(row, (int) barcodeCoordinate[3].getY());\n          }\n        }\n        row += ROW_STEP;\n        continue;\n      }\n      foundBarcodeInRow = true;\n      barcodeCoordinates.add(vertices);\n      if (!multiple) {\n        break;\n      }\n      // if we didn't find a right row indicator column, then continue the search for the next barcode after the\n      // start pattern of the barcode just found.\n      if (vertices[2] != null) {\n        column = (int) vertices[2].getX();\n        row = (int) vertices[2].getY();\n      } else {\n        column = (int) vertices[4].getX();\n        row = (int) vertices[4].getY();\n      }\n    }\n    return barcodeCoordinates;\n  }\n\n  /**\n   * Locate the vertices and the codewords area of a black blob using the Start\n   * and Stop patterns as locators.\n   *\n   * @param matrix the scanned barcode image.\n   * @return an array containing the vertices:\n   *           vertices[0] x, y top left barcode\n   *           vertices[1] x, y bottom left barcode\n   *           vertices[2] x, y top right barcode\n   *           vertices[3] x, y bottom right barcode\n   *           vertices[4] x, y top left codeword area\n   *           vertices[5] x, y bottom left codeword area\n   *           vertices[6] x, y top right codeword area\n   *           vertices[7] x, y bottom right codeword area\n   */\n  private static ResultPoint[] findVertices(BitMatrix matrix, int startRow, int startColumn) {\n    int height = matrix.getHeight();\n    int width = matrix.getWidth();\n\n    ResultPoint[] result = new ResultPoint[8];\n    copyToResult(result, findRowsWithPattern(matrix, height, width, startRow, startColumn, START_PATTERN),\n        INDEXES_START_PATTERN);\n\n    if (result[4] != null) {\n      startColumn = (int) result[4].getX();\n      startRow = (int) result[4].getY();\n    }\n    copyToResult(result, findRowsWithPattern(matrix, height, width, startRow, startColumn, STOP_PATTERN),\n        INDEXES_STOP_PATTERN);\n    return result;\n  }\n\n  private static void copyToResult(ResultPoint[] result, ResultPoint[] tmpResult, int[] destinationIndexes) {\n    for (int i = 0; i < destinationIndexes.length; i++) {\n      result[destinationIndexes[i]] = tmpResult[i];\n    }\n  }\n\n  private static ResultPoint[] findRowsWithPattern(BitMatrix matrix,\n                                                   int height,\n                                                   int width,\n                                                   int startRow,\n                                                   int startColumn,\n                                                   int[] pattern) {\n    ResultPoint[] result = new ResultPoint[4];\n    boolean found = false;\n    int[] counters = new int[pattern.length];\n    for (; startRow < height; startRow += ROW_STEP) {\n      int[] loc = findGuardPattern(matrix, startColumn, startRow, width, false, pattern, counters);\n      if (loc != null) {\n        while (startRow > 0) {\n          int[] previousRowLoc = findGuardPattern(matrix, startColumn, --startRow, width, false, pattern, counters);\n          if (previousRowLoc != null) {\n            loc = previousRowLoc;\n          } else {\n            startRow++;\n            break;\n          }\n        }\n        result[0] = new ResultPoint(loc[0], startRow);\n        result[1] = new ResultPoint(loc[1], startRow);\n        found = true;\n        break;\n      }\n    }\n    int stopRow = startRow + 1;\n    // Last row of the current symbol that contains pattern\n    if (found) {\n      int skippedRowCount = 0;\n      int[] previousRowLoc = {(int) result[0].getX(), (int) result[1].getX()};\n      for (; stopRow < height; stopRow++) {\n        int[] loc = findGuardPattern(matrix, previousRowLoc[0], stopRow, width, false, pattern, counters);\n        // a found pattern is only considered to belong to the same barcode if the start and end positions\n        // don't differ too much. Pattern drift should be not bigger than two for consecutive rows. With\n        // a higher number of skipped rows drift could be larger. To keep it simple for now, we allow a slightly\n        // larger drift and don't check for skipped rows.\n        if (loc != null &&\n            Math.abs(previousRowLoc[0] - loc[0]) < MAX_PATTERN_DRIFT &&\n            Math.abs(previousRowLoc[1] - loc[1]) < MAX_PATTERN_DRIFT) {\n          previousRowLoc = loc;\n          skippedRowCount = 0;\n        } else {\n          if (skippedRowCount > SKIPPED_ROW_COUNT_MAX) {\n            break;\n          } else {\n            skippedRowCount++;\n          }\n        }\n      }\n      stopRow -= skippedRowCount + 1;\n      result[2] = new ResultPoint(previousRowLoc[0], stopRow);\n      result[3] = new ResultPoint(previousRowLoc[1], stopRow);\n    }\n    if (stopRow - startRow < BARCODE_MIN_HEIGHT) {\n      Arrays.fill(result, null);\n    }\n    return result;\n  }\n\n  /**\n   * @param matrix row of black/white values to search\n   * @param column x position to start search\n   * @param row y position to start search\n   * @param width the number of pixels to search on this row\n   * @param pattern pattern of counts of number of black and white pixels that are\n   *                 being searched for as a pattern\n   * @param counters array of counters, as long as pattern, to re-use\n   * @return start/end horizontal offset of guard pattern, as an array of two ints.\n   */\n  private static int[] findGuardPattern(BitMatrix matrix,\n                                        int column,\n                                        int row,\n                                        int width,\n                                        boolean whiteFirst,\n                                        int[] pattern,\n                                        int[] counters) {\n    Arrays.fill(counters, 0, counters.length, 0);\n    int patternStart = column;\n    int pixelDrift = 0;\n\n    // if there are black pixels left of the current pixel shift to the left, but only for MAX_PIXEL_DRIFT pixels\n    while (matrix.get(patternStart, row) && patternStart > 0 && pixelDrift++ < MAX_PIXEL_DRIFT) {\n      patternStart--;\n    }\n    int x = patternStart;\n    int counterPosition = 0;\n    int patternLength = pattern.length;\n    for (boolean isWhite = whiteFirst; x < width; x++) {\n      boolean pixel = matrix.get(x, row);\n      if (pixel != isWhite) {\n        counters[counterPosition]++;\n      } else {\n        if (counterPosition == patternLength - 1) {\n          if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {\n            return new int[] {patternStart, x};\n          }\n          patternStart += counters[0] + counters[1];\n          System.arraycopy(counters, 2, counters, 0, counterPosition - 1);\n          counters[counterPosition - 1] = 0;\n          counters[counterPosition] = 0;\n          counterPosition--;\n        } else {\n          counterPosition++;\n        }\n        counters[counterPosition] = 1;\n        isWhite = !isWhite;\n      }\n    }\n    if (counterPosition == patternLength - 1 &&\n        patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {\n      return new int[] {patternStart, x - 1};\n    }\n    return null;\n  }\n\n  /**\n   * Determines how closely a set of observed counts of runs of black/white\n   * values matches a given target pattern. This is reported as the ratio of\n   * the total variance from the expected pattern proportions across all\n   * pattern elements, to the length of the pattern.\n   *\n   * @param counters observed counters\n   * @param pattern expected pattern\n   * @param maxIndividualVariance The most any counter can differ before we give up\n   * @return ratio of total variance between counters and pattern compared to total pattern size\n   */\n  private static float patternMatchVariance(int[] counters, int[] pattern, float maxIndividualVariance) {\n    int numCounters = counters.length;\n    int total = 0;\n    int patternLength = 0;\n    for (int i = 0; i < numCounters; i++) {\n      total += counters[i];\n      patternLength += pattern[i];\n    }\n    if (total < patternLength) {\n      // If we don't even have one pixel per unit of bar width, assume this\n      // is too small to reliably match, so fail:\n      return Float.POSITIVE_INFINITY;\n    }\n    // We're going to fake floating-point math in integers. We just need to use more bits.\n    // Scale up patternLength so that intermediate values below like scaledCounter will have\n    // more \"significant digits\".\n    float unitBarWidth = (float) total / patternLength;\n    maxIndividualVariance *= unitBarWidth;\n\n    float totalVariance = 0.0f;\n    for (int x = 0; x < numCounters; x++) {\n      int counter = counters[x];\n      float scaledPattern = pattern[x] * unitBarWidth;\n      float variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;\n      if (variance > maxIndividualVariance) {\n        return Float.POSITIVE_INFINITY;\n      }\n      totalVariance += variance;\n    }\n    return totalVariance / total;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/detector/PDF417DetectorResult.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.detector;\n\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.List;\n\n/**\n * @author Guenther Grau\n */\npublic final class PDF417DetectorResult {\n\n  private final BitMatrix bits;\n  private final List<ResultPoint[]> points;\n\n  public PDF417DetectorResult(BitMatrix bits, List<ResultPoint[]> points) {\n    this.bits = bits;\n    this.points = points;\n  }\n\n  public BitMatrix getBits() {\n    return bits;\n  }\n\n  public List<ResultPoint[]> getPoints() {\n    return points;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/encoder/BarcodeMatrix.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.encoder;\n\n/**\n * Holds all of the information for a barcode in a format where it can be easily accessible\n *\n * @author Jacob Haynes\n */\npublic final class BarcodeMatrix {\n\n  private final BarcodeRow[] matrix;\n  private int currentRow;\n  private final int height;\n  private final int width;\n\n  /**\n   * @param height the height of the matrix (Rows)\n   * @param width  the width of the matrix (Cols)\n   */\n  BarcodeMatrix(int height, int width) {\n    matrix = new BarcodeRow[height];\n    //Initializes the array to the correct width\n    for (int i = 0, matrixLength = matrix.length; i < matrixLength; i++) {\n      matrix[i] = new BarcodeRow((width + 4) * 17 + 1);\n    }\n    this.width = width * 17;\n    this.height = height;\n    this.currentRow = -1;\n  }\n\n  void set(int x, int y, byte value) {\n    matrix[y].set(x, value);\n  }\n\n  /*\n  void setMatrix(int x, int y, boolean black) {\n    set(x, y, (byte) (black ? 1 : 0));\n  }\n   */\n\n  void startRow() {\n    ++currentRow;\n  }\n\n  BarcodeRow getCurrentRow() {\n    return matrix[currentRow];\n  }\n\n  public byte[][] getMatrix() {\n    return getScaledMatrix(1, 1);\n  }\n\n  /*\n  public byte[][] getScaledMatrix(int scale) {\n    return getScaledMatrix(scale, scale);\n  }\n   */\n\n  public byte[][] getScaledMatrix(int xScale, int yScale) {\n    byte[][] matrixOut = new byte[height * yScale][width * xScale];\n    int yMax = height * yScale;\n    for (int i = 0; i < yMax; i++) {\n      matrixOut[yMax - i - 1] = matrix[i / yScale].getScaledRow(xScale);\n    }\n    return matrixOut;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/encoder/BarcodeRow.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.encoder;\n\n/**\n * @author Jacob Haynes\n */\nfinal class BarcodeRow {\n\n  private final byte[] row;\n  //A tacker for position in the bar\n  private int currentLocation;\n\n  /**\n   * Creates a Barcode row of the width\n   */\n  BarcodeRow(int width) {\n    this.row = new byte[width];\n    currentLocation = 0;\n  }\n\n  /**\n   * Sets a specific location in the bar\n   *\n   * @param x The location in the bar\n   * @param value Black if true, white if false;\n   */\n  void set(int x, byte value) {\n    row[x] = value;\n  }\n\n  /**\n   * Sets a specific location in the bar\n   *\n   * @param x The location in the bar\n   * @param black Black if true, white if false;\n   */\n  private void set(int x, boolean black) {\n    row[x] = (byte) (black ? 1 : 0);\n  }\n\n  /**\n   * @param black A boolean which is true if the bar black false if it is white\n   * @param width How many spots wide the bar is.\n   */\n  void addBar(boolean black, int width) {\n    for (int ii = 0; ii < width; ii++) {\n      set(currentLocation++, black);\n    }\n  }\n\n  /*\n  byte[] getRow() {\n    return row;\n  }\n   */\n\n  /**\n   * This function scales the row\n   *\n   * @param scale How much you want the image to be scaled, must be greater than or equal to 1.\n   * @return the scaled row\n   */\n  byte[] getScaledRow(int scale) {\n    byte[] output = new byte[row.length * scale];\n    for (int i = 0; i < output.length; i++) {\n      output[i] = row[i / scale];\n    }\n    return output;\n  }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/encoder/Compaction.java",
    "content": "/*\n * Copyright 2011 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.encoder;\n\n/**\n * Represents possible PDF417 barcode compaction types.\n */\npublic enum Compaction {\n\n  AUTO,\n  TEXT,\n  BYTE,\n  NUMERIC\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/encoder/Dimensions.java",
    "content": "/*\n * Copyright 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.pdf417.encoder;\n\n/**\n * Data object to specify the minimum and maximum number of rows and columns for a PDF417 barcode.\n *\n * @author qwandor@google.com (Andrew Walbran)\n */\npublic final class Dimensions {\n\n  private final int minCols;\n  private final int maxCols;\n  private final int minRows;\n  private final int maxRows;\n\n  public Dimensions(int minCols, int maxCols, int minRows, int maxRows) {\n    this.minCols = minCols;\n    this.maxCols = maxCols;\n    this.minRows = minRows;\n    this.maxRows = maxRows;\n  }\n\n  public int getMinCols() {\n    return minCols;\n  }\n\n  public int getMaxCols() {\n    return maxCols;\n  }\n\n  public int getMinRows() {\n    return minRows;\n  }\n\n  public int getMaxRows() {\n    return maxRows;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/encoder/PDF417.java",
    "content": "/*\n * Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * This file has been modified from its original form in Barcode4J.\n */\n\npackage com.google.zxing.pdf417.encoder;\n\nimport com.google.zxing.WriterException;\n\nimport java.nio.charset.Charset;\n\n/**\n * Top-level class for the logic part of the PDF417 implementation.\n */\npublic final class PDF417 {\n\n  /**\n   * The start pattern (17 bits)\n   */\n  private static final int START_PATTERN = 0x1fea8;\n  /**\n   * The stop pattern (18 bits)\n   */\n  private static final int STOP_PATTERN = 0x3fa29;\n\n  /**\n   * The codeword table from the Annex A of ISO/IEC 15438:2001(E).\n   */\n  private static final int[][] CODEWORD_TABLE = {\n      {0x1d5c0, 0x1eaf0, 0x1f57c, 0x1d4e0, 0x1ea78, 0x1f53e,\n          0x1a8c0, 0x1d470, 0x1a860, 0x15040, 0x1a830, 0x15020,\n          0x1adc0, 0x1d6f0, 0x1eb7c, 0x1ace0, 0x1d678, 0x1eb3e,\n          0x158c0, 0x1ac70, 0x15860, 0x15dc0, 0x1aef0, 0x1d77c,\n          0x15ce0, 0x1ae78, 0x1d73e, 0x15c70, 0x1ae3c, 0x15ef0,\n          0x1af7c, 0x15e78, 0x1af3e, 0x15f7c, 0x1f5fa, 0x1d2e0,\n          0x1e978, 0x1f4be, 0x1a4c0, 0x1d270, 0x1e93c, 0x1a460,\n          0x1d238, 0x14840, 0x1a430, 0x1d21c, 0x14820, 0x1a418,\n          0x14810, 0x1a6e0, 0x1d378, 0x1e9be, 0x14cc0, 0x1a670,\n          0x1d33c, 0x14c60, 0x1a638, 0x1d31e, 0x14c30, 0x1a61c,\n          0x14ee0, 0x1a778, 0x1d3be, 0x14e70, 0x1a73c, 0x14e38,\n          0x1a71e, 0x14f78, 0x1a7be, 0x14f3c, 0x14f1e, 0x1a2c0,\n          0x1d170, 0x1e8bc, 0x1a260, 0x1d138, 0x1e89e, 0x14440,\n          0x1a230, 0x1d11c, 0x14420, 0x1a218, 0x14410, 0x14408,\n          0x146c0, 0x1a370, 0x1d1bc, 0x14660, 0x1a338, 0x1d19e,\n          0x14630, 0x1a31c, 0x14618, 0x1460c, 0x14770, 0x1a3bc,\n          0x14738, 0x1a39e, 0x1471c, 0x147bc, 0x1a160, 0x1d0b8,\n          0x1e85e, 0x14240, 0x1a130, 0x1d09c, 0x14220, 0x1a118,\n          0x1d08e, 0x14210, 0x1a10c, 0x14208, 0x1a106, 0x14360,\n          0x1a1b8, 0x1d0de, 0x14330, 0x1a19c, 0x14318, 0x1a18e,\n          0x1430c, 0x14306, 0x1a1de, 0x1438e, 0x14140, 0x1a0b0,\n          0x1d05c, 0x14120, 0x1a098, 0x1d04e, 0x14110, 0x1a08c,\n          0x14108, 0x1a086, 0x14104, 0x141b0, 0x14198, 0x1418c,\n          0x140a0, 0x1d02e, 0x1a04c, 0x1a046, 0x14082, 0x1cae0,\n          0x1e578, 0x1f2be, 0x194c0, 0x1ca70, 0x1e53c, 0x19460,\n          0x1ca38, 0x1e51e, 0x12840, 0x19430, 0x12820, 0x196e0,\n          0x1cb78, 0x1e5be, 0x12cc0, 0x19670, 0x1cb3c, 0x12c60,\n          0x19638, 0x12c30, 0x12c18, 0x12ee0, 0x19778, 0x1cbbe,\n          0x12e70, 0x1973c, 0x12e38, 0x12e1c, 0x12f78, 0x197be,\n          0x12f3c, 0x12fbe, 0x1dac0, 0x1ed70, 0x1f6bc, 0x1da60,\n          0x1ed38, 0x1f69e, 0x1b440, 0x1da30, 0x1ed1c, 0x1b420,\n          0x1da18, 0x1ed0e, 0x1b410, 0x1da0c, 0x192c0, 0x1c970,\n          0x1e4bc, 0x1b6c0, 0x19260, 0x1c938, 0x1e49e, 0x1b660,\n          0x1db38, 0x1ed9e, 0x16c40, 0x12420, 0x19218, 0x1c90e,\n          0x16c20, 0x1b618, 0x16c10, 0x126c0, 0x19370, 0x1c9bc,\n          0x16ec0, 0x12660, 0x19338, 0x1c99e, 0x16e60, 0x1b738,\n          0x1db9e, 0x16e30, 0x12618, 0x16e18, 0x12770, 0x193bc,\n          0x16f70, 0x12738, 0x1939e, 0x16f38, 0x1b79e, 0x16f1c,\n          0x127bc, 0x16fbc, 0x1279e, 0x16f9e, 0x1d960, 0x1ecb8,\n          0x1f65e, 0x1b240, 0x1d930, 0x1ec9c, 0x1b220, 0x1d918,\n          0x1ec8e, 0x1b210, 0x1d90c, 0x1b208, 0x1b204, 0x19160,\n          0x1c8b8, 0x1e45e, 0x1b360, 0x19130, 0x1c89c, 0x16640,\n          0x12220, 0x1d99c, 0x1c88e, 0x16620, 0x12210, 0x1910c,\n          0x16610, 0x1b30c, 0x19106, 0x12204, 0x12360, 0x191b8,\n          0x1c8de, 0x16760, 0x12330, 0x1919c, 0x16730, 0x1b39c,\n          0x1918e, 0x16718, 0x1230c, 0x12306, 0x123b8, 0x191de,\n          0x167b8, 0x1239c, 0x1679c, 0x1238e, 0x1678e, 0x167de,\n          0x1b140, 0x1d8b0, 0x1ec5c, 0x1b120, 0x1d898, 0x1ec4e,\n          0x1b110, 0x1d88c, 0x1b108, 0x1d886, 0x1b104, 0x1b102,\n          0x12140, 0x190b0, 0x1c85c, 0x16340, 0x12120, 0x19098,\n          0x1c84e, 0x16320, 0x1b198, 0x1d8ce, 0x16310, 0x12108,\n          0x19086, 0x16308, 0x1b186, 0x16304, 0x121b0, 0x190dc,\n          0x163b0, 0x12198, 0x190ce, 0x16398, 0x1b1ce, 0x1638c,\n          0x12186, 0x16386, 0x163dc, 0x163ce, 0x1b0a0, 0x1d858,\n          0x1ec2e, 0x1b090, 0x1d84c, 0x1b088, 0x1d846, 0x1b084,\n          0x1b082, 0x120a0, 0x19058, 0x1c82e, 0x161a0, 0x12090,\n          0x1904c, 0x16190, 0x1b0cc, 0x19046, 0x16188, 0x12084,\n          0x16184, 0x12082, 0x120d8, 0x161d8, 0x161cc, 0x161c6,\n          0x1d82c, 0x1d826, 0x1b042, 0x1902c, 0x12048, 0x160c8,\n          0x160c4, 0x160c2, 0x18ac0, 0x1c570, 0x1e2bc, 0x18a60,\n          0x1c538, 0x11440, 0x18a30, 0x1c51c, 0x11420, 0x18a18,\n          0x11410, 0x11408, 0x116c0, 0x18b70, 0x1c5bc, 0x11660,\n          0x18b38, 0x1c59e, 0x11630, 0x18b1c, 0x11618, 0x1160c,\n          0x11770, 0x18bbc, 0x11738, 0x18b9e, 0x1171c, 0x117bc,\n          0x1179e, 0x1cd60, 0x1e6b8, 0x1f35e, 0x19a40, 0x1cd30,\n          0x1e69c, 0x19a20, 0x1cd18, 0x1e68e, 0x19a10, 0x1cd0c,\n          0x19a08, 0x1cd06, 0x18960, 0x1c4b8, 0x1e25e, 0x19b60,\n          0x18930, 0x1c49c, 0x13640, 0x11220, 0x1cd9c, 0x1c48e,\n          0x13620, 0x19b18, 0x1890c, 0x13610, 0x11208, 0x13608,\n          0x11360, 0x189b8, 0x1c4de, 0x13760, 0x11330, 0x1cdde,\n          0x13730, 0x19b9c, 0x1898e, 0x13718, 0x1130c, 0x1370c,\n          0x113b8, 0x189de, 0x137b8, 0x1139c, 0x1379c, 0x1138e,\n          0x113de, 0x137de, 0x1dd40, 0x1eeb0, 0x1f75c, 0x1dd20,\n          0x1ee98, 0x1f74e, 0x1dd10, 0x1ee8c, 0x1dd08, 0x1ee86,\n          0x1dd04, 0x19940, 0x1ccb0, 0x1e65c, 0x1bb40, 0x19920,\n          0x1eedc, 0x1e64e, 0x1bb20, 0x1dd98, 0x1eece, 0x1bb10,\n          0x19908, 0x1cc86, 0x1bb08, 0x1dd86, 0x19902, 0x11140,\n          0x188b0, 0x1c45c, 0x13340, 0x11120, 0x18898, 0x1c44e,\n          0x17740, 0x13320, 0x19998, 0x1ccce, 0x17720, 0x1bb98,\n          0x1ddce, 0x18886, 0x17710, 0x13308, 0x19986, 0x17708,\n          0x11102, 0x111b0, 0x188dc, 0x133b0, 0x11198, 0x188ce,\n          0x177b0, 0x13398, 0x199ce, 0x17798, 0x1bbce, 0x11186,\n          0x13386, 0x111dc, 0x133dc, 0x111ce, 0x177dc, 0x133ce,\n          0x1dca0, 0x1ee58, 0x1f72e, 0x1dc90, 0x1ee4c, 0x1dc88,\n          0x1ee46, 0x1dc84, 0x1dc82, 0x198a0, 0x1cc58, 0x1e62e,\n          0x1b9a0, 0x19890, 0x1ee6e, 0x1b990, 0x1dccc, 0x1cc46,\n          0x1b988, 0x19884, 0x1b984, 0x19882, 0x1b982, 0x110a0,\n          0x18858, 0x1c42e, 0x131a0, 0x11090, 0x1884c, 0x173a0,\n          0x13190, 0x198cc, 0x18846, 0x17390, 0x1b9cc, 0x11084,\n          0x17388, 0x13184, 0x11082, 0x13182, 0x110d8, 0x1886e,\n          0x131d8, 0x110cc, 0x173d8, 0x131cc, 0x110c6, 0x173cc,\n          0x131c6, 0x110ee, 0x173ee, 0x1dc50, 0x1ee2c, 0x1dc48,\n          0x1ee26, 0x1dc44, 0x1dc42, 0x19850, 0x1cc2c, 0x1b8d0,\n          0x19848, 0x1cc26, 0x1b8c8, 0x1dc66, 0x1b8c4, 0x19842,\n          0x1b8c2, 0x11050, 0x1882c, 0x130d0, 0x11048, 0x18826,\n          0x171d0, 0x130c8, 0x19866, 0x171c8, 0x1b8e6, 0x11042,\n          0x171c4, 0x130c2, 0x171c2, 0x130ec, 0x171ec, 0x171e6,\n          0x1ee16, 0x1dc22, 0x1cc16, 0x19824, 0x19822, 0x11028,\n          0x13068, 0x170e8, 0x11022, 0x13062, 0x18560, 0x10a40,\n          0x18530, 0x10a20, 0x18518, 0x1c28e, 0x10a10, 0x1850c,\n          0x10a08, 0x18506, 0x10b60, 0x185b8, 0x1c2de, 0x10b30,\n          0x1859c, 0x10b18, 0x1858e, 0x10b0c, 0x10b06, 0x10bb8,\n          0x185de, 0x10b9c, 0x10b8e, 0x10bde, 0x18d40, 0x1c6b0,\n          0x1e35c, 0x18d20, 0x1c698, 0x18d10, 0x1c68c, 0x18d08,\n          0x1c686, 0x18d04, 0x10940, 0x184b0, 0x1c25c, 0x11b40,\n          0x10920, 0x1c6dc, 0x1c24e, 0x11b20, 0x18d98, 0x1c6ce,\n          0x11b10, 0x10908, 0x18486, 0x11b08, 0x18d86, 0x10902,\n          0x109b0, 0x184dc, 0x11bb0, 0x10998, 0x184ce, 0x11b98,\n          0x18dce, 0x11b8c, 0x10986, 0x109dc, 0x11bdc, 0x109ce,\n          0x11bce, 0x1cea0, 0x1e758, 0x1f3ae, 0x1ce90, 0x1e74c,\n          0x1ce88, 0x1e746, 0x1ce84, 0x1ce82, 0x18ca0, 0x1c658,\n          0x19da0, 0x18c90, 0x1c64c, 0x19d90, 0x1cecc, 0x1c646,\n          0x19d88, 0x18c84, 0x19d84, 0x18c82, 0x19d82, 0x108a0,\n          0x18458, 0x119a0, 0x10890, 0x1c66e, 0x13ba0, 0x11990,\n          0x18ccc, 0x18446, 0x13b90, 0x19dcc, 0x10884, 0x13b88,\n          0x11984, 0x10882, 0x11982, 0x108d8, 0x1846e, 0x119d8,\n          0x108cc, 0x13bd8, 0x119cc, 0x108c6, 0x13bcc, 0x119c6,\n          0x108ee, 0x119ee, 0x13bee, 0x1ef50, 0x1f7ac, 0x1ef48,\n          0x1f7a6, 0x1ef44, 0x1ef42, 0x1ce50, 0x1e72c, 0x1ded0,\n          0x1ef6c, 0x1e726, 0x1dec8, 0x1ef66, 0x1dec4, 0x1ce42,\n          0x1dec2, 0x18c50, 0x1c62c, 0x19cd0, 0x18c48, 0x1c626,\n          0x1bdd0, 0x19cc8, 0x1ce66, 0x1bdc8, 0x1dee6, 0x18c42,\n          0x1bdc4, 0x19cc2, 0x1bdc2, 0x10850, 0x1842c, 0x118d0,\n          0x10848, 0x18426, 0x139d0, 0x118c8, 0x18c66, 0x17bd0,\n          0x139c8, 0x19ce6, 0x10842, 0x17bc8, 0x1bde6, 0x118c2,\n          0x17bc4, 0x1086c, 0x118ec, 0x10866, 0x139ec, 0x118e6,\n          0x17bec, 0x139e6, 0x17be6, 0x1ef28, 0x1f796, 0x1ef24,\n          0x1ef22, 0x1ce28, 0x1e716, 0x1de68, 0x1ef36, 0x1de64,\n          0x1ce22, 0x1de62, 0x18c28, 0x1c616, 0x19c68, 0x18c24,\n          0x1bce8, 0x19c64, 0x18c22, 0x1bce4, 0x19c62, 0x1bce2,\n          0x10828, 0x18416, 0x11868, 0x18c36, 0x138e8, 0x11864,\n          0x10822, 0x179e8, 0x138e4, 0x11862, 0x179e4, 0x138e2,\n          0x179e2, 0x11876, 0x179f6, 0x1ef12, 0x1de34, 0x1de32,\n          0x19c34, 0x1bc74, 0x1bc72, 0x11834, 0x13874, 0x178f4,\n          0x178f2, 0x10540, 0x10520, 0x18298, 0x10510, 0x10508,\n          0x10504, 0x105b0, 0x10598, 0x1058c, 0x10586, 0x105dc,\n          0x105ce, 0x186a0, 0x18690, 0x1c34c, 0x18688, 0x1c346,\n          0x18684, 0x18682, 0x104a0, 0x18258, 0x10da0, 0x186d8,\n          0x1824c, 0x10d90, 0x186cc, 0x10d88, 0x186c6, 0x10d84,\n          0x10482, 0x10d82, 0x104d8, 0x1826e, 0x10dd8, 0x186ee,\n          0x10dcc, 0x104c6, 0x10dc6, 0x104ee, 0x10dee, 0x1c750,\n          0x1c748, 0x1c744, 0x1c742, 0x18650, 0x18ed0, 0x1c76c,\n          0x1c326, 0x18ec8, 0x1c766, 0x18ec4, 0x18642, 0x18ec2,\n          0x10450, 0x10cd0, 0x10448, 0x18226, 0x11dd0, 0x10cc8,\n          0x10444, 0x11dc8, 0x10cc4, 0x10442, 0x11dc4, 0x10cc2,\n          0x1046c, 0x10cec, 0x10466, 0x11dec, 0x10ce6, 0x11de6,\n          0x1e7a8, 0x1e7a4, 0x1e7a2, 0x1c728, 0x1cf68, 0x1e7b6,\n          0x1cf64, 0x1c722, 0x1cf62, 0x18628, 0x1c316, 0x18e68,\n          0x1c736, 0x19ee8, 0x18e64, 0x18622, 0x19ee4, 0x18e62,\n          0x19ee2, 0x10428, 0x18216, 0x10c68, 0x18636, 0x11ce8,\n          0x10c64, 0x10422, 0x13de8, 0x11ce4, 0x10c62, 0x13de4,\n          0x11ce2, 0x10436, 0x10c76, 0x11cf6, 0x13df6, 0x1f7d4,\n          0x1f7d2, 0x1e794, 0x1efb4, 0x1e792, 0x1efb2, 0x1c714,\n          0x1cf34, 0x1c712, 0x1df74, 0x1cf32, 0x1df72, 0x18614,\n          0x18e34, 0x18612, 0x19e74, 0x18e32, 0x1bef4},\n      {0x1f560, 0x1fab8, 0x1ea40, 0x1f530, 0x1fa9c, 0x1ea20,\n          0x1f518, 0x1fa8e, 0x1ea10, 0x1f50c, 0x1ea08, 0x1f506,\n          0x1ea04, 0x1eb60, 0x1f5b8, 0x1fade, 0x1d640, 0x1eb30,\n          0x1f59c, 0x1d620, 0x1eb18, 0x1f58e, 0x1d610, 0x1eb0c,\n          0x1d608, 0x1eb06, 0x1d604, 0x1d760, 0x1ebb8, 0x1f5de,\n          0x1ae40, 0x1d730, 0x1eb9c, 0x1ae20, 0x1d718, 0x1eb8e,\n          0x1ae10, 0x1d70c, 0x1ae08, 0x1d706, 0x1ae04, 0x1af60,\n          0x1d7b8, 0x1ebde, 0x15e40, 0x1af30, 0x1d79c, 0x15e20,\n          0x1af18, 0x1d78e, 0x15e10, 0x1af0c, 0x15e08, 0x1af06,\n          0x15f60, 0x1afb8, 0x1d7de, 0x15f30, 0x1af9c, 0x15f18,\n          0x1af8e, 0x15f0c, 0x15fb8, 0x1afde, 0x15f9c, 0x15f8e,\n          0x1e940, 0x1f4b0, 0x1fa5c, 0x1e920, 0x1f498, 0x1fa4e,\n          0x1e910, 0x1f48c, 0x1e908, 0x1f486, 0x1e904, 0x1e902,\n          0x1d340, 0x1e9b0, 0x1f4dc, 0x1d320, 0x1e998, 0x1f4ce,\n          0x1d310, 0x1e98c, 0x1d308, 0x1e986, 0x1d304, 0x1d302,\n          0x1a740, 0x1d3b0, 0x1e9dc, 0x1a720, 0x1d398, 0x1e9ce,\n          0x1a710, 0x1d38c, 0x1a708, 0x1d386, 0x1a704, 0x1a702,\n          0x14f40, 0x1a7b0, 0x1d3dc, 0x14f20, 0x1a798, 0x1d3ce,\n          0x14f10, 0x1a78c, 0x14f08, 0x1a786, 0x14f04, 0x14fb0,\n          0x1a7dc, 0x14f98, 0x1a7ce, 0x14f8c, 0x14f86, 0x14fdc,\n          0x14fce, 0x1e8a0, 0x1f458, 0x1fa2e, 0x1e890, 0x1f44c,\n          0x1e888, 0x1f446, 0x1e884, 0x1e882, 0x1d1a0, 0x1e8d8,\n          0x1f46e, 0x1d190, 0x1e8cc, 0x1d188, 0x1e8c6, 0x1d184,\n          0x1d182, 0x1a3a0, 0x1d1d8, 0x1e8ee, 0x1a390, 0x1d1cc,\n          0x1a388, 0x1d1c6, 0x1a384, 0x1a382, 0x147a0, 0x1a3d8,\n          0x1d1ee, 0x14790, 0x1a3cc, 0x14788, 0x1a3c6, 0x14784,\n          0x14782, 0x147d8, 0x1a3ee, 0x147cc, 0x147c6, 0x147ee,\n          0x1e850, 0x1f42c, 0x1e848, 0x1f426, 0x1e844, 0x1e842,\n          0x1d0d0, 0x1e86c, 0x1d0c8, 0x1e866, 0x1d0c4, 0x1d0c2,\n          0x1a1d0, 0x1d0ec, 0x1a1c8, 0x1d0e6, 0x1a1c4, 0x1a1c2,\n          0x143d0, 0x1a1ec, 0x143c8, 0x1a1e6, 0x143c4, 0x143c2,\n          0x143ec, 0x143e6, 0x1e828, 0x1f416, 0x1e824, 0x1e822,\n          0x1d068, 0x1e836, 0x1d064, 0x1d062, 0x1a0e8, 0x1d076,\n          0x1a0e4, 0x1a0e2, 0x141e8, 0x1a0f6, 0x141e4, 0x141e2,\n          0x1e814, 0x1e812, 0x1d034, 0x1d032, 0x1a074, 0x1a072,\n          0x1e540, 0x1f2b0, 0x1f95c, 0x1e520, 0x1f298, 0x1f94e,\n          0x1e510, 0x1f28c, 0x1e508, 0x1f286, 0x1e504, 0x1e502,\n          0x1cb40, 0x1e5b0, 0x1f2dc, 0x1cb20, 0x1e598, 0x1f2ce,\n          0x1cb10, 0x1e58c, 0x1cb08, 0x1e586, 0x1cb04, 0x1cb02,\n          0x19740, 0x1cbb0, 0x1e5dc, 0x19720, 0x1cb98, 0x1e5ce,\n          0x19710, 0x1cb8c, 0x19708, 0x1cb86, 0x19704, 0x19702,\n          0x12f40, 0x197b0, 0x1cbdc, 0x12f20, 0x19798, 0x1cbce,\n          0x12f10, 0x1978c, 0x12f08, 0x19786, 0x12f04, 0x12fb0,\n          0x197dc, 0x12f98, 0x197ce, 0x12f8c, 0x12f86, 0x12fdc,\n          0x12fce, 0x1f6a0, 0x1fb58, 0x16bf0, 0x1f690, 0x1fb4c,\n          0x169f8, 0x1f688, 0x1fb46, 0x168fc, 0x1f684, 0x1f682,\n          0x1e4a0, 0x1f258, 0x1f92e, 0x1eda0, 0x1e490, 0x1fb6e,\n          0x1ed90, 0x1f6cc, 0x1f246, 0x1ed88, 0x1e484, 0x1ed84,\n          0x1e482, 0x1ed82, 0x1c9a0, 0x1e4d8, 0x1f26e, 0x1dba0,\n          0x1c990, 0x1e4cc, 0x1db90, 0x1edcc, 0x1e4c6, 0x1db88,\n          0x1c984, 0x1db84, 0x1c982, 0x1db82, 0x193a0, 0x1c9d8,\n          0x1e4ee, 0x1b7a0, 0x19390, 0x1c9cc, 0x1b790, 0x1dbcc,\n          0x1c9c6, 0x1b788, 0x19384, 0x1b784, 0x19382, 0x1b782,\n          0x127a0, 0x193d8, 0x1c9ee, 0x16fa0, 0x12790, 0x193cc,\n          0x16f90, 0x1b7cc, 0x193c6, 0x16f88, 0x12784, 0x16f84,\n          0x12782, 0x127d8, 0x193ee, 0x16fd8, 0x127cc, 0x16fcc,\n          0x127c6, 0x16fc6, 0x127ee, 0x1f650, 0x1fb2c, 0x165f8,\n          0x1f648, 0x1fb26, 0x164fc, 0x1f644, 0x1647e, 0x1f642,\n          0x1e450, 0x1f22c, 0x1ecd0, 0x1e448, 0x1f226, 0x1ecc8,\n          0x1f666, 0x1ecc4, 0x1e442, 0x1ecc2, 0x1c8d0, 0x1e46c,\n          0x1d9d0, 0x1c8c8, 0x1e466, 0x1d9c8, 0x1ece6, 0x1d9c4,\n          0x1c8c2, 0x1d9c2, 0x191d0, 0x1c8ec, 0x1b3d0, 0x191c8,\n          0x1c8e6, 0x1b3c8, 0x1d9e6, 0x1b3c4, 0x191c2, 0x1b3c2,\n          0x123d0, 0x191ec, 0x167d0, 0x123c8, 0x191e6, 0x167c8,\n          0x1b3e6, 0x167c4, 0x123c2, 0x167c2, 0x123ec, 0x167ec,\n          0x123e6, 0x167e6, 0x1f628, 0x1fb16, 0x162fc, 0x1f624,\n          0x1627e, 0x1f622, 0x1e428, 0x1f216, 0x1ec68, 0x1f636,\n          0x1ec64, 0x1e422, 0x1ec62, 0x1c868, 0x1e436, 0x1d8e8,\n          0x1c864, 0x1d8e4, 0x1c862, 0x1d8e2, 0x190e8, 0x1c876,\n          0x1b1e8, 0x1d8f6, 0x1b1e4, 0x190e2, 0x1b1e2, 0x121e8,\n          0x190f6, 0x163e8, 0x121e4, 0x163e4, 0x121e2, 0x163e2,\n          0x121f6, 0x163f6, 0x1f614, 0x1617e, 0x1f612, 0x1e414,\n          0x1ec34, 0x1e412, 0x1ec32, 0x1c834, 0x1d874, 0x1c832,\n          0x1d872, 0x19074, 0x1b0f4, 0x19072, 0x1b0f2, 0x120f4,\n          0x161f4, 0x120f2, 0x161f2, 0x1f60a, 0x1e40a, 0x1ec1a,\n          0x1c81a, 0x1d83a, 0x1903a, 0x1b07a, 0x1e2a0, 0x1f158,\n          0x1f8ae, 0x1e290, 0x1f14c, 0x1e288, 0x1f146, 0x1e284,\n          0x1e282, 0x1c5a0, 0x1e2d8, 0x1f16e, 0x1c590, 0x1e2cc,\n          0x1c588, 0x1e2c6, 0x1c584, 0x1c582, 0x18ba0, 0x1c5d8,\n          0x1e2ee, 0x18b90, 0x1c5cc, 0x18b88, 0x1c5c6, 0x18b84,\n          0x18b82, 0x117a0, 0x18bd8, 0x1c5ee, 0x11790, 0x18bcc,\n          0x11788, 0x18bc6, 0x11784, 0x11782, 0x117d8, 0x18bee,\n          0x117cc, 0x117c6, 0x117ee, 0x1f350, 0x1f9ac, 0x135f8,\n          0x1f348, 0x1f9a6, 0x134fc, 0x1f344, 0x1347e, 0x1f342,\n          0x1e250, 0x1f12c, 0x1e6d0, 0x1e248, 0x1f126, 0x1e6c8,\n          0x1f366, 0x1e6c4, 0x1e242, 0x1e6c2, 0x1c4d0, 0x1e26c,\n          0x1cdd0, 0x1c4c8, 0x1e266, 0x1cdc8, 0x1e6e6, 0x1cdc4,\n          0x1c4c2, 0x1cdc2, 0x189d0, 0x1c4ec, 0x19bd0, 0x189c8,\n          0x1c4e6, 0x19bc8, 0x1cde6, 0x19bc4, 0x189c2, 0x19bc2,\n          0x113d0, 0x189ec, 0x137d0, 0x113c8, 0x189e6, 0x137c8,\n          0x19be6, 0x137c4, 0x113c2, 0x137c2, 0x113ec, 0x137ec,\n          0x113e6, 0x137e6, 0x1fba8, 0x175f0, 0x1bafc, 0x1fba4,\n          0x174f8, 0x1ba7e, 0x1fba2, 0x1747c, 0x1743e, 0x1f328,\n          0x1f996, 0x132fc, 0x1f768, 0x1fbb6, 0x176fc, 0x1327e,\n          0x1f764, 0x1f322, 0x1767e, 0x1f762, 0x1e228, 0x1f116,\n          0x1e668, 0x1e224, 0x1eee8, 0x1f776, 0x1e222, 0x1eee4,\n          0x1e662, 0x1eee2, 0x1c468, 0x1e236, 0x1cce8, 0x1c464,\n          0x1dde8, 0x1cce4, 0x1c462, 0x1dde4, 0x1cce2, 0x1dde2,\n          0x188e8, 0x1c476, 0x199e8, 0x188e4, 0x1bbe8, 0x199e4,\n          0x188e2, 0x1bbe4, 0x199e2, 0x1bbe2, 0x111e8, 0x188f6,\n          0x133e8, 0x111e4, 0x177e8, 0x133e4, 0x111e2, 0x177e4,\n          0x133e2, 0x177e2, 0x111f6, 0x133f6, 0x1fb94, 0x172f8,\n          0x1b97e, 0x1fb92, 0x1727c, 0x1723e, 0x1f314, 0x1317e,\n          0x1f734, 0x1f312, 0x1737e, 0x1f732, 0x1e214, 0x1e634,\n          0x1e212, 0x1ee74, 0x1e632, 0x1ee72, 0x1c434, 0x1cc74,\n          0x1c432, 0x1dcf4, 0x1cc72, 0x1dcf2, 0x18874, 0x198f4,\n          0x18872, 0x1b9f4, 0x198f2, 0x1b9f2, 0x110f4, 0x131f4,\n          0x110f2, 0x173f4, 0x131f2, 0x173f2, 0x1fb8a, 0x1717c,\n          0x1713e, 0x1f30a, 0x1f71a, 0x1e20a, 0x1e61a, 0x1ee3a,\n          0x1c41a, 0x1cc3a, 0x1dc7a, 0x1883a, 0x1987a, 0x1b8fa,\n          0x1107a, 0x130fa, 0x171fa, 0x170be, 0x1e150, 0x1f0ac,\n          0x1e148, 0x1f0a6, 0x1e144, 0x1e142, 0x1c2d0, 0x1e16c,\n          0x1c2c8, 0x1e166, 0x1c2c4, 0x1c2c2, 0x185d0, 0x1c2ec,\n          0x185c8, 0x1c2e6, 0x185c4, 0x185c2, 0x10bd0, 0x185ec,\n          0x10bc8, 0x185e6, 0x10bc4, 0x10bc2, 0x10bec, 0x10be6,\n          0x1f1a8, 0x1f8d6, 0x11afc, 0x1f1a4, 0x11a7e, 0x1f1a2,\n          0x1e128, 0x1f096, 0x1e368, 0x1e124, 0x1e364, 0x1e122,\n          0x1e362, 0x1c268, 0x1e136, 0x1c6e8, 0x1c264, 0x1c6e4,\n          0x1c262, 0x1c6e2, 0x184e8, 0x1c276, 0x18de8, 0x184e4,\n          0x18de4, 0x184e2, 0x18de2, 0x109e8, 0x184f6, 0x11be8,\n          0x109e4, 0x11be4, 0x109e2, 0x11be2, 0x109f6, 0x11bf6,\n          0x1f9d4, 0x13af8, 0x19d7e, 0x1f9d2, 0x13a7c, 0x13a3e,\n          0x1f194, 0x1197e, 0x1f3b4, 0x1f192, 0x13b7e, 0x1f3b2,\n          0x1e114, 0x1e334, 0x1e112, 0x1e774, 0x1e332, 0x1e772,\n          0x1c234, 0x1c674, 0x1c232, 0x1cef4, 0x1c672, 0x1cef2,\n          0x18474, 0x18cf4, 0x18472, 0x19df4, 0x18cf2, 0x19df2,\n          0x108f4, 0x119f4, 0x108f2, 0x13bf4, 0x119f2, 0x13bf2,\n          0x17af0, 0x1bd7c, 0x17a78, 0x1bd3e, 0x17a3c, 0x17a1e,\n          0x1f9ca, 0x1397c, 0x1fbda, 0x17b7c, 0x1393e, 0x17b3e,\n          0x1f18a, 0x1f39a, 0x1f7ba, 0x1e10a, 0x1e31a, 0x1e73a,\n          0x1ef7a, 0x1c21a, 0x1c63a, 0x1ce7a, 0x1defa, 0x1843a,\n          0x18c7a, 0x19cfa, 0x1bdfa, 0x1087a, 0x118fa, 0x139fa,\n          0x17978, 0x1bcbe, 0x1793c, 0x1791e, 0x138be, 0x179be,\n          0x178bc, 0x1789e, 0x1785e, 0x1e0a8, 0x1e0a4, 0x1e0a2,\n          0x1c168, 0x1e0b6, 0x1c164, 0x1c162, 0x182e8, 0x1c176,\n          0x182e4, 0x182e2, 0x105e8, 0x182f6, 0x105e4, 0x105e2,\n          0x105f6, 0x1f0d4, 0x10d7e, 0x1f0d2, 0x1e094, 0x1e1b4,\n          0x1e092, 0x1e1b2, 0x1c134, 0x1c374, 0x1c132, 0x1c372,\n          0x18274, 0x186f4, 0x18272, 0x186f2, 0x104f4, 0x10df4,\n          0x104f2, 0x10df2, 0x1f8ea, 0x11d7c, 0x11d3e, 0x1f0ca,\n          0x1f1da, 0x1e08a, 0x1e19a, 0x1e3ba, 0x1c11a, 0x1c33a,\n          0x1c77a, 0x1823a, 0x1867a, 0x18efa, 0x1047a, 0x10cfa,\n          0x11dfa, 0x13d78, 0x19ebe, 0x13d3c, 0x13d1e, 0x11cbe,\n          0x13dbe, 0x17d70, 0x1bebc, 0x17d38, 0x1be9e, 0x17d1c,\n          0x17d0e, 0x13cbc, 0x17dbc, 0x13c9e, 0x17d9e, 0x17cb8,\n          0x1be5e, 0x17c9c, 0x17c8e, 0x13c5e, 0x17cde, 0x17c5c,\n          0x17c4e, 0x17c2e, 0x1c0b4, 0x1c0b2, 0x18174, 0x18172,\n          0x102f4, 0x102f2, 0x1e0da, 0x1c09a, 0x1c1ba, 0x1813a,\n          0x1837a, 0x1027a, 0x106fa, 0x10ebe, 0x11ebc, 0x11e9e,\n          0x13eb8, 0x19f5e, 0x13e9c, 0x13e8e, 0x11e5e, 0x13ede,\n          0x17eb0, 0x1bf5c, 0x17e98, 0x1bf4e, 0x17e8c, 0x17e86,\n          0x13e5c, 0x17edc, 0x13e4e, 0x17ece, 0x17e58, 0x1bf2e,\n          0x17e4c, 0x17e46, 0x13e2e, 0x17e6e, 0x17e2c, 0x17e26,\n          0x10f5e, 0x11f5c, 0x11f4e, 0x13f58, 0x19fae, 0x13f4c,\n          0x13f46, 0x11f2e, 0x13f6e, 0x13f2c, 0x13f26},\n      {0x1abe0, 0x1d5f8, 0x153c0, 0x1a9f0, 0x1d4fc, 0x151e0,\n          0x1a8f8, 0x1d47e, 0x150f0, 0x1a87c, 0x15078, 0x1fad0,\n          0x15be0, 0x1adf8, 0x1fac8, 0x159f0, 0x1acfc, 0x1fac4,\n          0x158f8, 0x1ac7e, 0x1fac2, 0x1587c, 0x1f5d0, 0x1faec,\n          0x15df8, 0x1f5c8, 0x1fae6, 0x15cfc, 0x1f5c4, 0x15c7e,\n          0x1f5c2, 0x1ebd0, 0x1f5ec, 0x1ebc8, 0x1f5e6, 0x1ebc4,\n          0x1ebc2, 0x1d7d0, 0x1ebec, 0x1d7c8, 0x1ebe6, 0x1d7c4,\n          0x1d7c2, 0x1afd0, 0x1d7ec, 0x1afc8, 0x1d7e6, 0x1afc4,\n          0x14bc0, 0x1a5f0, 0x1d2fc, 0x149e0, 0x1a4f8, 0x1d27e,\n          0x148f0, 0x1a47c, 0x14878, 0x1a43e, 0x1483c, 0x1fa68,\n          0x14df0, 0x1a6fc, 0x1fa64, 0x14cf8, 0x1a67e, 0x1fa62,\n          0x14c7c, 0x14c3e, 0x1f4e8, 0x1fa76, 0x14efc, 0x1f4e4,\n          0x14e7e, 0x1f4e2, 0x1e9e8, 0x1f4f6, 0x1e9e4, 0x1e9e2,\n          0x1d3e8, 0x1e9f6, 0x1d3e4, 0x1d3e2, 0x1a7e8, 0x1d3f6,\n          0x1a7e4, 0x1a7e2, 0x145e0, 0x1a2f8, 0x1d17e, 0x144f0,\n          0x1a27c, 0x14478, 0x1a23e, 0x1443c, 0x1441e, 0x1fa34,\n          0x146f8, 0x1a37e, 0x1fa32, 0x1467c, 0x1463e, 0x1f474,\n          0x1477e, 0x1f472, 0x1e8f4, 0x1e8f2, 0x1d1f4, 0x1d1f2,\n          0x1a3f4, 0x1a3f2, 0x142f0, 0x1a17c, 0x14278, 0x1a13e,\n          0x1423c, 0x1421e, 0x1fa1a, 0x1437c, 0x1433e, 0x1f43a,\n          0x1e87a, 0x1d0fa, 0x14178, 0x1a0be, 0x1413c, 0x1411e,\n          0x141be, 0x140bc, 0x1409e, 0x12bc0, 0x195f0, 0x1cafc,\n          0x129e0, 0x194f8, 0x1ca7e, 0x128f0, 0x1947c, 0x12878,\n          0x1943e, 0x1283c, 0x1f968, 0x12df0, 0x196fc, 0x1f964,\n          0x12cf8, 0x1967e, 0x1f962, 0x12c7c, 0x12c3e, 0x1f2e8,\n          0x1f976, 0x12efc, 0x1f2e4, 0x12e7e, 0x1f2e2, 0x1e5e8,\n          0x1f2f6, 0x1e5e4, 0x1e5e2, 0x1cbe8, 0x1e5f6, 0x1cbe4,\n          0x1cbe2, 0x197e8, 0x1cbf6, 0x197e4, 0x197e2, 0x1b5e0,\n          0x1daf8, 0x1ed7e, 0x169c0, 0x1b4f0, 0x1da7c, 0x168e0,\n          0x1b478, 0x1da3e, 0x16870, 0x1b43c, 0x16838, 0x1b41e,\n          0x1681c, 0x125e0, 0x192f8, 0x1c97e, 0x16de0, 0x124f0,\n          0x1927c, 0x16cf0, 0x1b67c, 0x1923e, 0x16c78, 0x1243c,\n          0x16c3c, 0x1241e, 0x16c1e, 0x1f934, 0x126f8, 0x1937e,\n          0x1fb74, 0x1f932, 0x16ef8, 0x1267c, 0x1fb72, 0x16e7c,\n          0x1263e, 0x16e3e, 0x1f274, 0x1277e, 0x1f6f4, 0x1f272,\n          0x16f7e, 0x1f6f2, 0x1e4f4, 0x1edf4, 0x1e4f2, 0x1edf2,\n          0x1c9f4, 0x1dbf4, 0x1c9f2, 0x1dbf2, 0x193f4, 0x193f2,\n          0x165c0, 0x1b2f0, 0x1d97c, 0x164e0, 0x1b278, 0x1d93e,\n          0x16470, 0x1b23c, 0x16438, 0x1b21e, 0x1641c, 0x1640e,\n          0x122f0, 0x1917c, 0x166f0, 0x12278, 0x1913e, 0x16678,\n          0x1b33e, 0x1663c, 0x1221e, 0x1661e, 0x1f91a, 0x1237c,\n          0x1fb3a, 0x1677c, 0x1233e, 0x1673e, 0x1f23a, 0x1f67a,\n          0x1e47a, 0x1ecfa, 0x1c8fa, 0x1d9fa, 0x191fa, 0x162e0,\n          0x1b178, 0x1d8be, 0x16270, 0x1b13c, 0x16238, 0x1b11e,\n          0x1621c, 0x1620e, 0x12178, 0x190be, 0x16378, 0x1213c,\n          0x1633c, 0x1211e, 0x1631e, 0x121be, 0x163be, 0x16170,\n          0x1b0bc, 0x16138, 0x1b09e, 0x1611c, 0x1610e, 0x120bc,\n          0x161bc, 0x1209e, 0x1619e, 0x160b8, 0x1b05e, 0x1609c,\n          0x1608e, 0x1205e, 0x160de, 0x1605c, 0x1604e, 0x115e0,\n          0x18af8, 0x1c57e, 0x114f0, 0x18a7c, 0x11478, 0x18a3e,\n          0x1143c, 0x1141e, 0x1f8b4, 0x116f8, 0x18b7e, 0x1f8b2,\n          0x1167c, 0x1163e, 0x1f174, 0x1177e, 0x1f172, 0x1e2f4,\n          0x1e2f2, 0x1c5f4, 0x1c5f2, 0x18bf4, 0x18bf2, 0x135c0,\n          0x19af0, 0x1cd7c, 0x134e0, 0x19a78, 0x1cd3e, 0x13470,\n          0x19a3c, 0x13438, 0x19a1e, 0x1341c, 0x1340e, 0x112f0,\n          0x1897c, 0x136f0, 0x11278, 0x1893e, 0x13678, 0x19b3e,\n          0x1363c, 0x1121e, 0x1361e, 0x1f89a, 0x1137c, 0x1f9ba,\n          0x1377c, 0x1133e, 0x1373e, 0x1f13a, 0x1f37a, 0x1e27a,\n          0x1e6fa, 0x1c4fa, 0x1cdfa, 0x189fa, 0x1bae0, 0x1dd78,\n          0x1eebe, 0x174c0, 0x1ba70, 0x1dd3c, 0x17460, 0x1ba38,\n          0x1dd1e, 0x17430, 0x1ba1c, 0x17418, 0x1ba0e, 0x1740c,\n          0x132e0, 0x19978, 0x1ccbe, 0x176e0, 0x13270, 0x1993c,\n          0x17670, 0x1bb3c, 0x1991e, 0x17638, 0x1321c, 0x1761c,\n          0x1320e, 0x1760e, 0x11178, 0x188be, 0x13378, 0x1113c,\n          0x17778, 0x1333c, 0x1111e, 0x1773c, 0x1331e, 0x1771e,\n          0x111be, 0x133be, 0x177be, 0x172c0, 0x1b970, 0x1dcbc,\n          0x17260, 0x1b938, 0x1dc9e, 0x17230, 0x1b91c, 0x17218,\n          0x1b90e, 0x1720c, 0x17206, 0x13170, 0x198bc, 0x17370,\n          0x13138, 0x1989e, 0x17338, 0x1b99e, 0x1731c, 0x1310e,\n          0x1730e, 0x110bc, 0x131bc, 0x1109e, 0x173bc, 0x1319e,\n          0x1739e, 0x17160, 0x1b8b8, 0x1dc5e, 0x17130, 0x1b89c,\n          0x17118, 0x1b88e, 0x1710c, 0x17106, 0x130b8, 0x1985e,\n          0x171b8, 0x1309c, 0x1719c, 0x1308e, 0x1718e, 0x1105e,\n          0x130de, 0x171de, 0x170b0, 0x1b85c, 0x17098, 0x1b84e,\n          0x1708c, 0x17086, 0x1305c, 0x170dc, 0x1304e, 0x170ce,\n          0x17058, 0x1b82e, 0x1704c, 0x17046, 0x1302e, 0x1706e,\n          0x1702c, 0x17026, 0x10af0, 0x1857c, 0x10a78, 0x1853e,\n          0x10a3c, 0x10a1e, 0x10b7c, 0x10b3e, 0x1f0ba, 0x1e17a,\n          0x1c2fa, 0x185fa, 0x11ae0, 0x18d78, 0x1c6be, 0x11a70,\n          0x18d3c, 0x11a38, 0x18d1e, 0x11a1c, 0x11a0e, 0x10978,\n          0x184be, 0x11b78, 0x1093c, 0x11b3c, 0x1091e, 0x11b1e,\n          0x109be, 0x11bbe, 0x13ac0, 0x19d70, 0x1cebc, 0x13a60,\n          0x19d38, 0x1ce9e, 0x13a30, 0x19d1c, 0x13a18, 0x19d0e,\n          0x13a0c, 0x13a06, 0x11970, 0x18cbc, 0x13b70, 0x11938,\n          0x18c9e, 0x13b38, 0x1191c, 0x13b1c, 0x1190e, 0x13b0e,\n          0x108bc, 0x119bc, 0x1089e, 0x13bbc, 0x1199e, 0x13b9e,\n          0x1bd60, 0x1deb8, 0x1ef5e, 0x17a40, 0x1bd30, 0x1de9c,\n          0x17a20, 0x1bd18, 0x1de8e, 0x17a10, 0x1bd0c, 0x17a08,\n          0x1bd06, 0x17a04, 0x13960, 0x19cb8, 0x1ce5e, 0x17b60,\n          0x13930, 0x19c9c, 0x17b30, 0x1bd9c, 0x19c8e, 0x17b18,\n          0x1390c, 0x17b0c, 0x13906, 0x17b06, 0x118b8, 0x18c5e,\n          0x139b8, 0x1189c, 0x17bb8, 0x1399c, 0x1188e, 0x17b9c,\n          0x1398e, 0x17b8e, 0x1085e, 0x118de, 0x139de, 0x17bde,\n          0x17940, 0x1bcb0, 0x1de5c, 0x17920, 0x1bc98, 0x1de4e,\n          0x17910, 0x1bc8c, 0x17908, 0x1bc86, 0x17904, 0x17902,\n          0x138b0, 0x19c5c, 0x179b0, 0x13898, 0x19c4e, 0x17998,\n          0x1bcce, 0x1798c, 0x13886, 0x17986, 0x1185c, 0x138dc,\n          0x1184e, 0x179dc, 0x138ce, 0x179ce, 0x178a0, 0x1bc58,\n          0x1de2e, 0x17890, 0x1bc4c, 0x17888, 0x1bc46, 0x17884,\n          0x17882, 0x13858, 0x19c2e, 0x178d8, 0x1384c, 0x178cc,\n          0x13846, 0x178c6, 0x1182e, 0x1386e, 0x178ee, 0x17850,\n          0x1bc2c, 0x17848, 0x1bc26, 0x17844, 0x17842, 0x1382c,\n          0x1786c, 0x13826, 0x17866, 0x17828, 0x1bc16, 0x17824,\n          0x17822, 0x13816, 0x17836, 0x10578, 0x182be, 0x1053c,\n          0x1051e, 0x105be, 0x10d70, 0x186bc, 0x10d38, 0x1869e,\n          0x10d1c, 0x10d0e, 0x104bc, 0x10dbc, 0x1049e, 0x10d9e,\n          0x11d60, 0x18eb8, 0x1c75e, 0x11d30, 0x18e9c, 0x11d18,\n          0x18e8e, 0x11d0c, 0x11d06, 0x10cb8, 0x1865e, 0x11db8,\n          0x10c9c, 0x11d9c, 0x10c8e, 0x11d8e, 0x1045e, 0x10cde,\n          0x11dde, 0x13d40, 0x19eb0, 0x1cf5c, 0x13d20, 0x19e98,\n          0x1cf4e, 0x13d10, 0x19e8c, 0x13d08, 0x19e86, 0x13d04,\n          0x13d02, 0x11cb0, 0x18e5c, 0x13db0, 0x11c98, 0x18e4e,\n          0x13d98, 0x19ece, 0x13d8c, 0x11c86, 0x13d86, 0x10c5c,\n          0x11cdc, 0x10c4e, 0x13ddc, 0x11cce, 0x13dce, 0x1bea0,\n          0x1df58, 0x1efae, 0x1be90, 0x1df4c, 0x1be88, 0x1df46,\n          0x1be84, 0x1be82, 0x13ca0, 0x19e58, 0x1cf2e, 0x17da0,\n          0x13c90, 0x19e4c, 0x17d90, 0x1becc, 0x19e46, 0x17d88,\n          0x13c84, 0x17d84, 0x13c82, 0x17d82, 0x11c58, 0x18e2e,\n          0x13cd8, 0x11c4c, 0x17dd8, 0x13ccc, 0x11c46, 0x17dcc,\n          0x13cc6, 0x17dc6, 0x10c2e, 0x11c6e, 0x13cee, 0x17dee,\n          0x1be50, 0x1df2c, 0x1be48, 0x1df26, 0x1be44, 0x1be42,\n          0x13c50, 0x19e2c, 0x17cd0, 0x13c48, 0x19e26, 0x17cc8,\n          0x1be66, 0x17cc4, 0x13c42, 0x17cc2, 0x11c2c, 0x13c6c,\n          0x11c26, 0x17cec, 0x13c66, 0x17ce6, 0x1be28, 0x1df16,\n          0x1be24, 0x1be22, 0x13c28, 0x19e16, 0x17c68, 0x13c24,\n          0x17c64, 0x13c22, 0x17c62, 0x11c16, 0x13c36, 0x17c76,\n          0x1be14, 0x1be12, 0x13c14, 0x17c34, 0x13c12, 0x17c32,\n          0x102bc, 0x1029e, 0x106b8, 0x1835e, 0x1069c, 0x1068e,\n          0x1025e, 0x106de, 0x10eb0, 0x1875c, 0x10e98, 0x1874e,\n          0x10e8c, 0x10e86, 0x1065c, 0x10edc, 0x1064e, 0x10ece,\n          0x11ea0, 0x18f58, 0x1c7ae, 0x11e90, 0x18f4c, 0x11e88,\n          0x18f46, 0x11e84, 0x11e82, 0x10e58, 0x1872e, 0x11ed8,\n          0x18f6e, 0x11ecc, 0x10e46, 0x11ec6, 0x1062e, 0x10e6e,\n          0x11eee, 0x19f50, 0x1cfac, 0x19f48, 0x1cfa6, 0x19f44,\n          0x19f42, 0x11e50, 0x18f2c, 0x13ed0, 0x19f6c, 0x18f26,\n          0x13ec8, 0x11e44, 0x13ec4, 0x11e42, 0x13ec2, 0x10e2c,\n          0x11e6c, 0x10e26, 0x13eec, 0x11e66, 0x13ee6, 0x1dfa8,\n          0x1efd6, 0x1dfa4, 0x1dfa2, 0x19f28, 0x1cf96, 0x1bf68,\n          0x19f24, 0x1bf64, 0x19f22, 0x1bf62, 0x11e28, 0x18f16,\n          0x13e68, 0x11e24, 0x17ee8, 0x13e64, 0x11e22, 0x17ee4,\n          0x13e62, 0x17ee2, 0x10e16, 0x11e36, 0x13e76, 0x17ef6,\n          0x1df94, 0x1df92, 0x19f14, 0x1bf34, 0x19f12, 0x1bf32,\n          0x11e14, 0x13e34, 0x11e12, 0x17e74, 0x13e32, 0x17e72,\n          0x1df8a, 0x19f0a, 0x1bf1a, 0x11e0a, 0x13e1a, 0x17e3a,\n          0x1035c, 0x1034e, 0x10758, 0x183ae, 0x1074c, 0x10746,\n          0x1032e, 0x1076e, 0x10f50, 0x187ac, 0x10f48, 0x187a6,\n          0x10f44, 0x10f42, 0x1072c, 0x10f6c, 0x10726, 0x10f66,\n          0x18fa8, 0x1c7d6, 0x18fa4, 0x18fa2, 0x10f28, 0x18796,\n          0x11f68, 0x18fb6, 0x11f64, 0x10f22, 0x11f62, 0x10716,\n          0x10f36, 0x11f76, 0x1cfd4, 0x1cfd2, 0x18f94, 0x19fb4,\n          0x18f92, 0x19fb2, 0x10f14, 0x11f34, 0x10f12, 0x13f74,\n          0x11f32, 0x13f72, 0x1cfca, 0x18f8a, 0x19f9a, 0x10f0a,\n          0x11f1a, 0x13f3a, 0x103ac, 0x103a6, 0x107a8, 0x183d6,\n          0x107a4, 0x107a2, 0x10396, 0x107b6, 0x187d4, 0x187d2,\n          0x10794, 0x10fb4, 0x10792, 0x10fb2, 0x1c7ea}};\n\n  private static final float PREFERRED_RATIO = 3.0f;\n  private static final float DEFAULT_MODULE_WIDTH = 0.357f; //1px in mm\n  private static final float HEIGHT = 2.0f; //mm\n\n  private BarcodeMatrix barcodeMatrix;\n  private boolean compact;\n  private Compaction compaction;\n  private Charset encoding;\n  private int minCols;\n  private int maxCols;\n  private int maxRows;\n  private int minRows;\n\n  public PDF417() {\n    this(false);\n  }\n\n  public PDF417(boolean compact) {\n    this.compact = compact;\n    compaction = Compaction.AUTO;\n    encoding = null; // Use default\n    minCols = 2;\n    maxCols = 30;\n    maxRows = 30;\n    minRows = 2;\n  }\n\n  public BarcodeMatrix getBarcodeMatrix() {\n    return barcodeMatrix;\n  }\n\n  /**\n   * Calculates the necessary number of rows as described in annex Q of ISO/IEC 15438:2001(E).\n   *\n   * @param m the number of source codewords prior to the additional of the Symbol Length\n   *          Descriptor and any pad codewords\n   * @param k the number of error correction codewords\n   * @param c the number of columns in the symbol in the data region (excluding start, stop and\n   *          row indicator codewords)\n   * @return the number of rows in the symbol (r)\n   */\n  private static int calculateNumberOfRows(int m, int k, int c) {\n    int r = ((m + 1 + k) / c) + 1;\n    if (c * r >= (m + 1 + k + c)) {\n      r--;\n    }\n    return r;\n  }\n\n  /**\n   * Calculates the number of pad codewords as described in 4.9.2 of ISO/IEC 15438:2001(E).\n   *\n   * @param m the number of source codewords prior to the additional of the Symbol Length\n   *          Descriptor and any pad codewords\n   * @param k the number of error correction codewords\n   * @param c the number of columns in the symbol in the data region (excluding start, stop and\n   *          row indicator codewords)\n   * @param r the number of rows in the symbol\n   * @return the number of pad codewords\n   */\n  private static int getNumberOfPadCodewords(int m, int k, int c, int r) {\n    int n = c * r - k;\n    return n > m + 1 ? n - m - 1 : 0;\n  }\n\n  private static void encodeChar(int pattern, int len, BarcodeRow logic) {\n    int map = 1 << len - 1;\n    boolean last = (pattern & map) != 0; //Initialize to inverse of first bit\n    int width = 0;\n    for (int i = 0; i < len; i++) {\n      boolean black = (pattern & map) != 0;\n      if (last == black) {\n        width++;\n      } else {\n        logic.addBar(last, width);\n\n        last = black;\n        width = 1;\n      }\n      map >>= 1;\n    }\n    logic.addBar(last, width);\n  }\n\n  private void encodeLowLevel(CharSequence fullCodewords,\n                              int c,\n                              int r,\n                              int errorCorrectionLevel,\n                              BarcodeMatrix logic) {\n\n    int idx = 0;\n    for (int y = 0; y < r; y++) {\n      int cluster = y % 3;\n      logic.startRow();\n      encodeChar(START_PATTERN, 17, logic.getCurrentRow());\n\n      int left;\n      int right;\n      if (cluster == 0) {\n        left = (30 * (y / 3)) + ((r - 1) / 3);\n        right = (30 * (y / 3)) + (c - 1);\n      } else if (cluster == 1) {\n        left = (30 * (y / 3)) + (errorCorrectionLevel * 3) + ((r - 1) % 3);\n        right = (30 * (y / 3)) + ((r - 1) / 3);\n      } else {\n        left = (30 * (y / 3)) + (c - 1);\n        right = (30 * (y / 3)) + (errorCorrectionLevel * 3) + ((r - 1) % 3);\n      }\n\n      int pattern = CODEWORD_TABLE[cluster][left];\n      encodeChar(pattern, 17, logic.getCurrentRow());\n\n      for (int x = 0; x < c; x++) {\n        pattern = CODEWORD_TABLE[cluster][fullCodewords.charAt(idx)];\n        encodeChar(pattern, 17, logic.getCurrentRow());\n        idx++;\n      }\n\n      if (compact) {\n        encodeChar(STOP_PATTERN, 1, logic.getCurrentRow()); // encodes stop line for compact pdf417\n      } else {\n        pattern = CODEWORD_TABLE[cluster][right];\n        encodeChar(pattern, 17, logic.getCurrentRow());\n\n        encodeChar(STOP_PATTERN, 18, logic.getCurrentRow());\n      }\n    }\n  }\n\n  /**\n   * @param msg message to encode\n   * @param errorCorrectionLevel PDF417 error correction level to use\n   * @throws WriterException if the contents cannot be encoded in this format\n   */\n  public void generateBarcodeLogic(String msg, int errorCorrectionLevel) throws WriterException {\n\n    //1. step: High-level encoding\n    int errorCorrectionCodeWords = PDF417ErrorCorrection.getErrorCorrectionCodewordCount(errorCorrectionLevel);\n    String highLevel = PDF417HighLevelEncoder.encodeHighLevel(msg, compaction, encoding);\n    int sourceCodeWords = highLevel.length();\n\n    int[] dimension = determineDimensions(sourceCodeWords, errorCorrectionCodeWords);\n\n    int cols = dimension[0];\n    int rows = dimension[1];\n\n    int pad = getNumberOfPadCodewords(sourceCodeWords, errorCorrectionCodeWords, cols, rows);\n\n    //2. step: construct data codewords\n    if (sourceCodeWords + errorCorrectionCodeWords + 1 > 929) { // +1 for symbol length CW\n      throw new WriterException(\n          \"Encoded message contains too many code words, message too big (\" + msg.length() + \" bytes)\");\n    }\n    int n = sourceCodeWords + pad + 1;\n    StringBuilder sb = new StringBuilder(n);\n    sb.append((char) n);\n    sb.append(highLevel);\n    for (int i = 0; i < pad; i++) {\n      sb.append((char) 900); //PAD characters\n    }\n    String dataCodewords = sb.toString();\n\n    //3. step: Error correction\n    String ec = PDF417ErrorCorrection.generateErrorCorrection(dataCodewords, errorCorrectionLevel);\n\n    //4. step: low-level encoding\n    barcodeMatrix = new BarcodeMatrix(rows, cols);\n    encodeLowLevel(dataCodewords + ec, cols, rows, errorCorrectionLevel, barcodeMatrix);\n  }\n\n  /**\n   * Determine optimal nr of columns and rows for the specified number of\n   * codewords.\n   *\n   * @param sourceCodeWords number of code words\n   * @param errorCorrectionCodeWords number of error correction code words\n   * @return dimension object containing cols as width and rows as height\n   */\n  private int[] determineDimensions(int sourceCodeWords, int errorCorrectionCodeWords) throws WriterException {\n    float ratio = 0.0f;\n    int[] dimension = null;\n\n    for (int cols = minCols; cols <= maxCols; cols++) {\n\n      int rows = calculateNumberOfRows(sourceCodeWords, errorCorrectionCodeWords, cols);\n\n      if (rows < minRows) {\n        break;\n      }\n\n      if (rows > maxRows) {\n        continue;\n      }\n\n      float newRatio = ((float) (17 * cols + 69) * DEFAULT_MODULE_WIDTH) / (rows * HEIGHT);\n\n      // ignore if previous ratio is closer to preferred ratio\n      if (dimension != null && Math.abs(newRatio - PREFERRED_RATIO) > Math.abs(ratio - PREFERRED_RATIO)) {\n        continue;\n      }\n\n      ratio = newRatio;\n      dimension = new int[] {cols, rows};\n    }\n\n     // Handle case when min values were larger than necessary\n     if (dimension == null) {\n       int rows = calculateNumberOfRows(sourceCodeWords, errorCorrectionCodeWords, minCols);\n       if (rows < minRows) {\n         dimension = new int[]{minCols, minRows};\n       }\n     }\n\n    if (dimension == null) {\n      throw new WriterException(\"Unable to fit message in columns\");\n    }\n\n    return dimension;\n  }\n\n  /**\n   * Sets max/min row/col values\n   *\n   * @param maxCols maximum allowed columns\n   * @param minCols minimum allowed columns\n   * @param maxRows maximum allowed rows\n   * @param minRows minimum allowed rows\n   */\n  public void setDimensions(int maxCols, int minCols, int maxRows, int minRows) {\n    this.maxCols = maxCols;\n    this.minCols = minCols;\n    this.maxRows = maxRows;\n    this.minRows = minRows;\n  }\n\n  /**\n   * @param compaction compaction mode to use\n   */\n  public void setCompaction(Compaction compaction) {\n    this.compaction = compaction;\n  }\n\n  /**\n   * @param compact if true, enables compaction\n   */\n  public void setCompact(boolean compact) {\n    this.compact = compact;\n  }\n\n  /**\n   * @param encoding sets character encoding to use\n   */\n  public void setEncoding(Charset encoding) {\n    this.encoding = encoding;\n  }\n\n}\n\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/encoder/PDF417ErrorCorrection.java",
    "content": "/*\n * Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * This file has been modified from its original form in Barcode4J.\n */\n\npackage com.google.zxing.pdf417.encoder;\n\nimport com.google.zxing.WriterException;\n\n/**\n * PDF417 error correction code following the algorithm described in ISO/IEC 15438:2001(E) in\n * chapter 4.10.\n */\nfinal class PDF417ErrorCorrection {\n\n  /**\n   * Tables of coefficients for calculating error correction words\n   * (see annex F, ISO/IEC 15438:2001(E))\n   */\n  private static final int[][] EC_COEFFICIENTS = {\n      {27, 917},\n      {522, 568, 723, 809},\n      {237, 308, 436, 284, 646, 653, 428, 379},\n      {274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295,\n          42, 176, 65},\n      {361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687,\n          284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803,\n          133, 231, 390, 685, 330, 63, 410},\n      {539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733,\n          877, 381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543,\n          376, 511, 400, 672, 762, 283, 184, 440, 35, 519, 31, 460,\n          594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502, 648,\n          733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843,\n          623, 264, 543},\n      {521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400,\n          925, 749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246,\n          148, 447, 631, 292, 908, 490, 704, 516, 258, 457, 907, 594,\n          723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569, 193,\n          219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712,\n          463, 646, 776, 171, 491, 297, 763, 156, 732, 95, 270, 447,\n          90, 507, 48, 228, 821, 808, 898, 784, 663, 627, 378, 382,\n          262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157,\n          374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814,\n          587, 804, 34, 211, 330, 539, 297, 827, 865, 37, 517, 834,\n          315, 550, 86, 801, 4, 108, 539},\n      {524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905,\n          786, 138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438,\n          733, 194, 280, 201, 280, 828, 757, 710, 814, 919, 89, 68,\n          569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137, 439,\n          418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284,\n          549, 209, 884, 315, 70, 329, 793, 490, 274, 877, 162, 749,\n          812, 684, 461, 334, 376, 849, 521, 307, 291, 803, 712, 19,\n          358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, 637,\n          731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136,\n          538, 906, 90, 2, 290, 743, 199, 655, 903, 329, 49, 802,\n          580, 355, 588, 188, 462, 10, 134, 628, 320, 479, 130, 739,\n          71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, 722,\n          384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48,\n          60, 732, 621, 895, 544, 261, 852, 655, 309, 697, 755, 756,\n          60, 231, 773, 434, 421, 726, 528, 503, 118, 49, 795, 32,\n          144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, 73,\n          914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180,\n          791, 893, 754, 605, 383, 228, 749, 760, 213, 54, 297, 134,\n          54, 834, 299, 922, 191, 910, 532, 609, 829, 189, 20, 167,\n          29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173, 404,\n          251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648,\n          55, 497, 10},\n      {352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285,\n          380, 350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294,\n          871, 306, 88, 87, 193, 352, 781, 846, 75, 327, 520, 435,\n          543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534, 539,\n          781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858,\n          916, 552, 41, 542, 289, 122, 272, 383, 800, 485, 98, 752,\n          472, 761, 107, 784, 860, 658, 741, 290, 204, 681, 407, 855,\n          85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, 808,\n          684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513,\n          192, 516, 258, 240, 518, 794, 395, 768, 848, 51, 610, 384,\n          168, 190, 826, 328, 596, 786, 303, 570, 381, 415, 641, 156,\n          237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, 40,\n          708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221,\n          92, 358, 785, 288, 357, 850, 836, 827, 736, 707, 94, 8,\n          494, 114, 521, 2, 499, 851, 543, 152, 729, 771, 95, 248,\n          361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, 669,\n          45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699,\n          591, 452, 578, 37, 124, 298, 332, 552, 43, 427, 119, 662,\n          777, 475, 850, 764, 364, 578, 911, 283, 711, 472, 420, 245,\n          288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, 842,\n          383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713,\n          159, 672, 729, 624, 59, 193, 417, 158, 209, 563, 564, 343,\n          693, 109, 608, 563, 365, 181, 772, 677, 310, 248, 353, 708,\n          410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, 618,\n          586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331,\n          247, 184, 45, 787, 680, 18, 66, 407, 369, 54, 492, 228,\n          613, 830, 922, 437, 519, 644, 905, 789, 420, 305, 441, 207,\n          300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, 242,\n          797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756,\n          665, 397, 808, 851, 309, 473, 795, 378, 31, 647, 915, 459,\n          806, 590, 731, 425, 216, 548, 249, 321, 881, 699, 535, 673,\n          782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, 660,\n          162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535,\n          336, 286, 437, 375, 273, 610, 296, 183, 923, 116, 667, 751,\n          353, 62, 366, 691, 379, 687, 842, 37, 357, 720, 742, 330,\n          5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, 342,\n          299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721,\n          610, 46, 656, 447, 171, 616, 464, 190, 531, 297, 321, 762,\n          752, 533, 175, 134, 14, 381, 433, 717, 45, 111, 20, 596,\n          284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, 407,\n          164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768,\n          223, 849, 647, 63, 310, 863, 251, 366, 304, 282, 738, 675,\n          410, 389, 244, 31, 121, 303, 263}};\n\n  private PDF417ErrorCorrection() {\n  }\n\n  /**\n   * Determines the number of error correction codewords for a specified error correction\n   * level.\n   *\n   * @param errorCorrectionLevel the error correction level (0-8)\n   * @return the number of codewords generated for error correction\n   */\n  static int getErrorCorrectionCodewordCount(int errorCorrectionLevel) {\n    if (errorCorrectionLevel < 0 || errorCorrectionLevel > 8) {\n      throw new IllegalArgumentException(\"Error correction level must be between 0 and 8!\");\n    }\n    return 1 << (errorCorrectionLevel + 1);\n  }\n\n  /**\n   * Returns the recommended minimum error correction level as described in annex E of\n   * ISO/IEC 15438:2001(E).\n   *\n   * @param n the number of data codewords\n   * @return the recommended minimum error correction level\n   */\n  static int getRecommendedMinimumErrorCorrectionLevel(int n) throws WriterException {\n    if (n <= 0) {\n      throw new IllegalArgumentException(\"n must be > 0\");\n    }\n    if (n <= 40) {\n      return 2;\n    }\n    if (n <= 160) {\n      return 3;\n    }\n    if (n <= 320) {\n      return 4;\n    }\n    if (n <= 863) {\n      return 5;\n    }\n    throw new WriterException(\"No recommendation possible\");\n  }\n\n  /**\n   * Generates the error correction codewords according to 4.10 in ISO/IEC 15438:2001(E).\n   *\n   * @param dataCodewords        the data codewords\n   * @param errorCorrectionLevel the error correction level (0-8)\n   * @return the String representing the error correction codewords\n   */\n  static String generateErrorCorrection(CharSequence dataCodewords, int errorCorrectionLevel) {\n    int k = getErrorCorrectionCodewordCount(errorCorrectionLevel);\n    char[] e = new char[k];\n    int sld = dataCodewords.length();\n    for (int i = 0; i < sld; i++) {\n      int t1 = (dataCodewords.charAt(i) + e[e.length - 1]) % 929;\n      int t2;\n      int t3;\n      for (int j = k - 1; j >= 1; j--) {\n        t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel][j]) % 929;\n        t3 = 929 - t2;\n        e[j] = (char) ((e[j - 1] + t3) % 929);\n      }\n      t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel][0]) % 929;\n      t3 = 929 - t2;\n      e[0] = (char) (t3 % 929);\n    }\n    StringBuilder sb = new StringBuilder(k);\n    for (int j = k - 1; j >= 0; j--) {\n      if (e[j] != 0) {\n        e[j] = (char) (929 - e[j]);\n      }\n      sb.append(e[j]);\n    }\n    return sb.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/pdf417/encoder/PDF417HighLevelEncoder.java",
    "content": "/*\n * Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * This file has been modified from its original form in Barcode4J.\n */\n\npackage com.google.zxing.pdf417.encoder;\n\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.CharacterSetECI;\n\nimport java.math.BigInteger;\nimport java.nio.charset.Charset;\nimport java.nio.charset.CharsetEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\n/**\n * PDF417 high-level encoder following the algorithm described in ISO/IEC 15438:2001(E) in\n * annex P.\n */\nfinal class PDF417HighLevelEncoder {\n\n  /**\n   * code for Text compaction\n   */\n  private static final int TEXT_COMPACTION = 0;\n\n  /**\n   * code for Byte compaction\n   */\n  private static final int BYTE_COMPACTION = 1;\n\n  /**\n   * code for Numeric compaction\n   */\n  private static final int NUMERIC_COMPACTION = 2;\n\n  /**\n   * Text compaction submode Alpha\n   */\n  private static final int SUBMODE_ALPHA = 0;\n\n  /**\n   * Text compaction submode Lower\n   */\n  private static final int SUBMODE_LOWER = 1;\n\n  /**\n   * Text compaction submode Mixed\n   */\n  private static final int SUBMODE_MIXED = 2;\n\n  /**\n   * Text compaction submode Punctuation\n   */\n  private static final int SUBMODE_PUNCTUATION = 3;\n\n  /**\n   * mode latch to Text Compaction mode\n   */\n  private static final int LATCH_TO_TEXT = 900;\n\n  /**\n   * mode latch to Byte Compaction mode (number of characters NOT a multiple of 6)\n   */\n  private static final int LATCH_TO_BYTE_PADDED = 901;\n\n  /**\n   * mode latch to Numeric Compaction mode\n   */\n  private static final int LATCH_TO_NUMERIC = 902;\n\n  /**\n   * mode shift to Byte Compaction mode\n   */\n  private static final int SHIFT_TO_BYTE = 913;\n\n  /**\n   * mode latch to Byte Compaction mode (number of characters a multiple of 6)\n   */\n  private static final int LATCH_TO_BYTE = 924;\n\n  /**\n   * identifier for a user defined Extended Channel Interpretation (ECI)\n   */\n  private static final int ECI_USER_DEFINED = 925;\n\n  /**\n   * identifier for a general purpose ECO format\n   */\n  private static final int ECI_GENERAL_PURPOSE = 926;\n\n  /**\n   * identifier for an ECI of a character set of code page\n   */\n  private static final int ECI_CHARSET = 927;\n\n  /**\n   * Raw code table for text compaction Mixed sub-mode\n   */\n  private static final byte[] TEXT_MIXED_RAW = {\n      48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58,\n      35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0};\n\n  /**\n   * Raw code table for text compaction: Punctuation sub-mode\n   */\n  private static final byte[] TEXT_PUNCTUATION_RAW = {\n      59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58,\n      10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0};\n\n  private static final byte[] MIXED = new byte[128];\n  private static final byte[] PUNCTUATION = new byte[128];\n\n  private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1;\n\n  private PDF417HighLevelEncoder() {\n  }\n\n  static {\n    //Construct inverse lookups\n    Arrays.fill(MIXED, (byte) -1);\n    for (int i = 0; i < TEXT_MIXED_RAW.length; i++) {\n      byte b = TEXT_MIXED_RAW[i];\n      if (b > 0) {\n        MIXED[b] = (byte) i;\n      }\n    }\n    Arrays.fill(PUNCTUATION, (byte) -1);\n    for (int i = 0; i < TEXT_PUNCTUATION_RAW.length; i++) {\n      byte b = TEXT_PUNCTUATION_RAW[i];\n      if (b > 0) {\n        PUNCTUATION[b] = (byte) i;\n      }\n    }\n  }\n\n  /**\n   * Performs high-level encoding of a PDF417 message using the algorithm described in annex P\n   * of ISO/IEC 15438:2001(E). If byte compaction has been selected, then only byte compaction\n   * is used.\n   *\n   * @param msg the message\n   * @param compaction compaction mode to use\n   * @param encoding character encoding used to encode in default or byte compaction\n   *  or {@code null} for default / not applicable\n   * @return the encoded message (the char values range from 0 to 928)\n   */\n  static String encodeHighLevel(String msg, Compaction compaction, Charset encoding) throws WriterException {\n\n    //the codewords 0..928 are encoded as Unicode characters\n    StringBuilder sb = new StringBuilder(msg.length());\n\n    if (encoding == null) {\n      encoding = DEFAULT_ENCODING;\n    } else if (!DEFAULT_ENCODING.equals(encoding)) {\n      CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding.name());\n      if (eci != null) {\n        encodingECI(eci.getValue(), sb);\n      }\n    }\n\n    int len = msg.length();\n    int p = 0;\n    int textSubMode = SUBMODE_ALPHA;\n\n    // User selected encoding mode\n    switch (compaction) {\n      case TEXT:\n        encodeText(msg, p, len, sb, textSubMode);\n        break;\n      case BYTE:\n        byte[] msgBytes = msg.getBytes(encoding);\n        encodeBinary(msgBytes, p, msgBytes.length, BYTE_COMPACTION, sb);\n        break;\n      case NUMERIC:\n        sb.append((char) LATCH_TO_NUMERIC);\n        encodeNumeric(msg, p, len, sb);\n        break;\n      default:\n        int encodingMode = TEXT_COMPACTION; //Default mode, see 4.4.2.1\n        while (p < len) {\n          int n = determineConsecutiveDigitCount(msg, p);\n          if (n >= 13) {\n            sb.append((char) LATCH_TO_NUMERIC);\n            encodingMode = NUMERIC_COMPACTION;\n            textSubMode = SUBMODE_ALPHA; //Reset after latch\n            encodeNumeric(msg, p, n, sb);\n            p += n;\n          } else {\n            int t = determineConsecutiveTextCount(msg, p);\n            if (t >= 5 || n == len) {\n              if (encodingMode != TEXT_COMPACTION) {\n                sb.append((char) LATCH_TO_TEXT);\n                encodingMode = TEXT_COMPACTION;\n                textSubMode = SUBMODE_ALPHA; //start with submode alpha after latch\n              }\n              textSubMode = encodeText(msg, p, t, sb, textSubMode);\n              p += t;\n            } else {\n              int b = determineConsecutiveBinaryCount(msg, p, encoding);\n              if (b == 0) {\n                b = 1;\n              }\n              byte[] bytes = msg.substring(p, p + b).getBytes(encoding);\n              if (bytes.length == 1 && encodingMode == TEXT_COMPACTION) {\n                //Switch for one byte (instead of latch)\n                encodeBinary(bytes, 0, 1, TEXT_COMPACTION, sb);\n              } else {\n                //Mode latch performed by encodeBinary()\n                encodeBinary(bytes, 0, bytes.length, encodingMode, sb);\n                encodingMode = BYTE_COMPACTION;\n                textSubMode = SUBMODE_ALPHA; //Reset after latch\n              }\n              p += b;\n            }\n          }\n        }\n        break;\n    }\n\n    return sb.toString();\n  }\n\n  /**\n   * Encode parts of the message using Text Compaction as described in ISO/IEC 15438:2001(E),\n   * chapter 4.4.2.\n   *\n   * @param msg            the message\n   * @param startpos       the start position within the message\n   * @param count          the number of characters to encode\n   * @param sb             receives the encoded codewords\n   * @param initialSubmode should normally be SUBMODE_ALPHA\n   * @return the text submode in which this method ends\n   */\n  private static int encodeText(CharSequence msg,\n                                int startpos,\n                                int count,\n                                StringBuilder sb,\n                                int initialSubmode) {\n    StringBuilder tmp = new StringBuilder(count);\n    int submode = initialSubmode;\n    int idx = 0;\n    while (true) {\n      char ch = msg.charAt(startpos + idx);\n      switch (submode) {\n        case SUBMODE_ALPHA:\n          if (isAlphaUpper(ch)) {\n            if (ch == ' ') {\n              tmp.append((char) 26); //space\n            } else {\n              tmp.append((char) (ch - 65));\n            }\n          } else {\n            if (isAlphaLower(ch)) {\n              submode = SUBMODE_LOWER;\n              tmp.append((char) 27); //ll\n              continue;\n            } else if (isMixed(ch)) {\n              submode = SUBMODE_MIXED;\n              tmp.append((char) 28); //ml\n              continue;\n            } else {\n              tmp.append((char) 29); //ps\n              tmp.append((char) PUNCTUATION[ch]);\n              break;\n            }\n          }\n          break;\n        case SUBMODE_LOWER:\n          if (isAlphaLower(ch)) {\n            if (ch == ' ') {\n              tmp.append((char) 26); //space\n            } else {\n              tmp.append((char) (ch - 97));\n            }\n          } else {\n            if (isAlphaUpper(ch)) {\n              tmp.append((char) 27); //as\n              tmp.append((char) (ch - 65));\n              //space cannot happen here, it is also in \"Lower\"\n              break;\n            } else if (isMixed(ch)) {\n              submode = SUBMODE_MIXED;\n              tmp.append((char) 28); //ml\n              continue;\n            } else {\n              tmp.append((char) 29); //ps\n              tmp.append((char) PUNCTUATION[ch]);\n              break;\n            }\n          }\n          break;\n        case SUBMODE_MIXED:\n          if (isMixed(ch)) {\n            tmp.append((char) MIXED[ch]);\n          } else {\n            if (isAlphaUpper(ch)) {\n              submode = SUBMODE_ALPHA;\n              tmp.append((char) 28); //al\n              continue;\n            } else if (isAlphaLower(ch)) {\n              submode = SUBMODE_LOWER;\n              tmp.append((char) 27); //ll\n              continue;\n            } else {\n              if (startpos + idx + 1 < count) {\n                char next = msg.charAt(startpos + idx + 1);\n                if (isPunctuation(next)) {\n                  submode = SUBMODE_PUNCTUATION;\n                  tmp.append((char) 25); //pl\n                  continue;\n                }\n              }\n              tmp.append((char) 29); //ps\n              tmp.append((char) PUNCTUATION[ch]);\n            }\n          }\n          break;\n        default: //SUBMODE_PUNCTUATION\n          if (isPunctuation(ch)) {\n            tmp.append((char) PUNCTUATION[ch]);\n          } else {\n            submode = SUBMODE_ALPHA;\n            tmp.append((char) 29); //al\n            continue;\n          }\n      }\n      idx++;\n      if (idx >= count) {\n        break;\n      }\n    }\n    char h = 0;\n    int len = tmp.length();\n    for (int i = 0; i < len; i++) {\n      boolean odd = (i % 2) != 0;\n      if (odd) {\n        h = (char) ((h * 30) + tmp.charAt(i));\n        sb.append(h);\n      } else {\n        h = tmp.charAt(i);\n      }\n    }\n    if ((len % 2) != 0) {\n      sb.append((char) ((h * 30) + 29)); //ps\n    }\n    return submode;\n  }\n\n  /**\n   * Encode parts of the message using Byte Compaction as described in ISO/IEC 15438:2001(E),\n   * chapter 4.4.3. The Unicode characters will be converted to binary using the cp437\n   * codepage.\n   *\n   * @param bytes     the message converted to a byte array\n   * @param startpos  the start position within the message\n   * @param count     the number of bytes to encode\n   * @param startmode the mode from which this method starts\n   * @param sb        receives the encoded codewords\n   */\n  private static void encodeBinary(byte[] bytes,\n                                   int startpos,\n                                   int count,\n                                   int startmode,\n                                   StringBuilder sb) {\n    if (count == 1 && startmode == TEXT_COMPACTION) {\n      sb.append((char) SHIFT_TO_BYTE);\n    } else {\n      if ((count % 6) == 0) {\n        sb.append((char) LATCH_TO_BYTE);\n      } else {\n        sb.append((char) LATCH_TO_BYTE_PADDED);\n      }\n    }\n\n    int idx = startpos;\n    // Encode sixpacks\n    if (count >= 6) {\n      char[] chars = new char[5];\n      while ((startpos + count - idx) >= 6) {\n        long t = 0;\n        for (int i = 0; i < 6; i++) {\n          t <<= 8;\n          t += bytes[idx + i] & 0xff;\n        }\n        for (int i = 0; i < 5; i++) {\n          chars[i] = (char) (t % 900);\n          t /= 900;\n        }\n        for (int i = chars.length - 1; i >= 0; i--) {\n          sb.append(chars[i]);\n        }\n        idx += 6;\n      }\n    }\n    //Encode rest (remaining n<5 bytes if any)\n    for (int i = idx; i < startpos + count; i++) {\n      int ch = bytes[i] & 0xff;\n      sb.append((char) ch);\n    }\n  }\n\n  private static void encodeNumeric(String msg, int startpos, int count, StringBuilder sb) {\n    int idx = 0;\n    StringBuilder tmp = new StringBuilder(count / 3 + 1);\n    BigInteger num900 = BigInteger.valueOf(900);\n    BigInteger num0 = BigInteger.valueOf(0);\n    while (idx < count) {\n      tmp.setLength(0);\n      int len = Math.min(44, count - idx);\n      String part = '1' + msg.substring(startpos + idx, startpos + idx + len);\n      BigInteger bigint = new BigInteger(part);\n      do {\n        tmp.append((char) bigint.mod(num900).intValue());\n        bigint = bigint.divide(num900);\n      } while (!bigint.equals(num0));\n\n      //Reverse temporary string\n      for (int i = tmp.length() - 1; i >= 0; i--) {\n        sb.append(tmp.charAt(i));\n      }\n      idx += len;\n    }\n  }\n\n\n  private static boolean isDigit(char ch) {\n    return ch >= '0' && ch <= '9';\n  }\n\n  private static boolean isAlphaUpper(char ch) {\n    return ch == ' ' || (ch >= 'A' && ch <= 'Z');\n  }\n\n  private static boolean isAlphaLower(char ch) {\n    return ch == ' ' || (ch >= 'a' && ch <= 'z');\n  }\n\n  private static boolean isMixed(char ch) {\n    return MIXED[ch] != -1;\n  }\n\n  private static boolean isPunctuation(char ch) {\n    return PUNCTUATION[ch] != -1;\n  }\n\n  private static boolean isText(char ch) {\n    return ch == '\\t' || ch == '\\n' || ch == '\\r' || (ch >= 32 && ch <= 126);\n  }\n\n  /**\n   * Determines the number of consecutive characters that are encodable using numeric compaction.\n   *\n   * @param msg      the message\n   * @param startpos the start position within the message\n   * @return the requested character count\n   */\n  private static int determineConsecutiveDigitCount(CharSequence msg, int startpos) {\n    int count = 0;\n    int len = msg.length();\n    int idx = startpos;\n    if (idx < len) {\n      char ch = msg.charAt(idx);\n      while (isDigit(ch) && idx < len) {\n        count++;\n        idx++;\n        if (idx < len) {\n          ch = msg.charAt(idx);\n        }\n      }\n    }\n    return count;\n  }\n\n  /**\n   * Determines the number of consecutive characters that are encodable using text compaction.\n   *\n   * @param msg      the message\n   * @param startpos the start position within the message\n   * @return the requested character count\n   */\n  private static int determineConsecutiveTextCount(CharSequence msg, int startpos) {\n    int len = msg.length();\n    int idx = startpos;\n    while (idx < len) {\n      char ch = msg.charAt(idx);\n      int numericCount = 0;\n      while (numericCount < 13 && isDigit(ch) && idx < len) {\n        numericCount++;\n        idx++;\n        if (idx < len) {\n          ch = msg.charAt(idx);\n        }\n      }\n      if (numericCount >= 13) {\n        return idx - startpos - numericCount;\n      }\n      if (numericCount > 0) {\n        //Heuristic: All text-encodable chars or digits are binary encodable\n        continue;\n      }\n      ch = msg.charAt(idx);\n\n      //Check if character is encodable\n      if (!isText(ch)) {\n        break;\n      }\n      idx++;\n    }\n    return idx - startpos;\n  }\n\n  /**\n   * Determines the number of consecutive characters that are encodable using binary compaction.\n   *\n   * @param msg      the message\n   * @param startpos the start position within the message\n   * @param encoding the charset used to convert the message to a byte array\n   * @return the requested character count\n   */\n  private static int determineConsecutiveBinaryCount(String msg, int startpos, Charset encoding)\n      throws WriterException {\n    CharsetEncoder encoder = encoding.newEncoder();\n    int len = msg.length();\n    int idx = startpos;\n    while (idx < len) {\n      char ch = msg.charAt(idx);\n      int numericCount = 0;\n\n      while (numericCount < 13 && isDigit(ch)) {\n        numericCount++;\n        //textCount++;\n        int i = idx + numericCount;\n        if (i >= len) {\n          break;\n        }\n        ch = msg.charAt(i);\n      }\n      if (numericCount >= 13) {\n        return idx - startpos;\n      }\n      ch = msg.charAt(idx);\n\n      if (!encoder.canEncode(ch)) {\n        throw new WriterException(\"Non-encodable character detected: \" + ch + \" (Unicode: \" + (int) ch + ')');\n      }\n      idx++;\n    }\n    return idx - startpos;\n  }\n\n  private static void encodingECI(int eci, StringBuilder sb) throws WriterException {\n    if (eci >= 0 && eci < 900) {\n      sb.append((char) ECI_CHARSET);\n      sb.append((char) eci);\n    } else if (eci < 810900) {\n      sb.append((char) ECI_GENERAL_PURPOSE);\n      sb.append((char) (eci / 900 - 1));\n      sb.append((char) (eci % 900));\n    } else if (eci < 811800) {\n      sb.append((char) ECI_USER_DEFINED);\n      sb.append((char) (810900 - eci));\n    } else {\n      throw new WriterException(\"ECI number not in valid range from 0..811799, but was \" + eci);\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/QRCodeReader.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode;\n\nimport android.graphics.Rect;\nimport android.hardware.Camera;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.Reader;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultMetadataType;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.DetectorResult;\nimport com.google.zxing.qrcode.decoder.Decoder;\nimport com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;\nimport com.google.zxing.qrcode.detector.Detector;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * This implementation can detect and decode QR Codes in an image.\n *\n * @author Sean Owen\n */\npublic class QRCodeReader implements Reader {\n\n    private static final ResultPoint[] NO_POINTS = new ResultPoint[0];\n\n    private final Decoder decoder = new Decoder();\n\n    protected final Decoder getDecoder() {\n        return decoder;\n    }\n\n    /**\n     * Locates and decodes a QR code in an image.\n     *\n     * @return a String representing the content encoded by the QR code\n     * @throws NotFoundException if a QR code cannot be found\n     * @throws FormatException   if a QR code cannot be decoded\n     * @throws ChecksumException if error correction fails\n     */\n    @Override\n    public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {\n        return decode(image, null);\n    }\n\n\n    /**\n     * 判断是否需要放大\n     */\n    private boolean zoomCamera(Camera camera, ResultPoint[] p, Rect frameRect) {\n        int qrWidth = getQRCodeWidth(p);\n        if (qrWidth == 0 || camera == null) {\n            return false;\n        }\n        int frameWidth = frameRect.right - frameRect.left;\n        Camera.Parameters parameters = camera.getParameters();\n        if (parameters.isZoomSupported()) {\n            int maxZoom = parameters.getMaxZoom();\n            int curZoom = parameters.getZoom();\n            if (qrWidth <= frameWidth / 10) {\n                if (curZoom < maxZoom) {\n                    parameters.setZoom(maxZoom);\n                    camera.setParameters(parameters);\n                    return true;\n                }\n            } else if (qrWidth <= frameWidth / 6) {\n                if (curZoom < maxZoom / 2) {\n                    parameters.setZoom(maxZoom / 2);\n                    camera.setParameters(parameters);\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private int getQRCodeWidth(ResultPoint[] p) {\n        return (int) (p[p.length - 1].getX() - p[0].getX());\n    }\n\n\n    @Override\n    public final Result decode(BinaryBitmap image, Map<DecodeHintType, ?> hints)\n            throws NotFoundException, ChecksumException, FormatException {\n        DecoderResult decoderResult;\n        ResultPoint[] points;\n        if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {\n            BitMatrix bits = extractPureBits(image.getBlackMatrix());\n            decoderResult = decoder.decode(bits, hints);\n            points = NO_POINTS;\n        } else {\n            DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(hints);\n            decoderResult = decoder.decode(detectorResult.getBits(), hints);\n            points = detectorResult.getPoints();\n        }\n\n        // If the code was mirrored: swap the bottom-left and the top-right points.\n        if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {\n            ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);\n        }\n\n        Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE);\n        List<byte[]> byteSegments = decoderResult.getByteSegments();\n        if (byteSegments != null) {\n            result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);\n        }\n        String ecLevel = decoderResult.getECLevel();\n        if (ecLevel != null) {\n            result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);\n        }\n        if (decoderResult.hasStructuredAppend()) {\n            result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,\n                    decoderResult.getStructuredAppendSequenceNumber());\n            result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,\n                    decoderResult.getStructuredAppendParity());\n        }\n        return result;\n    }\n\n    @Override\n    public void reset() {\n        // do nothing\n    }\n\n    /**\n     * This method detects a code in a \"pure\" image -- that is, pure monochrome image\n     * which contains only an unrotated, unskewed, image of a code, with some white border\n     * around it. This is a specialized method that works exceptionally fast in this special\n     * case.\n     *\n     * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)\n     */\n    private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {\n\n        int[] leftTopBlack = image.getTopLeftOnBit();\n        int[] rightBottomBlack = image.getBottomRightOnBit();\n        if (leftTopBlack == null || rightBottomBlack == null) {\n            throw NotFoundException.getNotFoundInstance();\n        }\n\n        float moduleSize = moduleSize(leftTopBlack, image);\n\n        int top = leftTopBlack[1];\n        int bottom = rightBottomBlack[1];\n        int left = leftTopBlack[0];\n        int right = rightBottomBlack[0];\n\n        // Sanity check!\n        if (left >= right || top >= bottom) {\n            throw NotFoundException.getNotFoundInstance();\n        }\n\n        if (bottom - top != right - left) {\n            // Special case, where bottom-right module wasn't black so we found something else in the last row\n            // Assume it's a square, so use height as the width\n            right = left + (bottom - top);\n            if (right >= image.getWidth()) {\n                // Abort if that would not make sense -- off image\n                throw NotFoundException.getNotFoundInstance();\n            }\n        }\n\n        int matrixWidth = Math.round((right - left + 1) / moduleSize);\n        int matrixHeight = Math.round((bottom - top + 1) / moduleSize);\n        if (matrixWidth <= 0 || matrixHeight <= 0) {\n            throw NotFoundException.getNotFoundInstance();\n        }\n        if (matrixHeight != matrixWidth) {\n            // Only possibly decode square regions\n            throw NotFoundException.getNotFoundInstance();\n        }\n\n        // Push in the \"border\" by half the module width so that we start\n        // sampling in the middle of the module. Just in case the image is a\n        // little off, this will help recover.\n        int nudge = (int) (moduleSize / 2.0f);\n        top += nudge;\n        left += nudge;\n\n        // But careful that this does not sample off the edge\n        // \"right\" is the farthest-right valid pixel location -- right+1 is not necessarily\n        // This is positive by how much the inner x loop below would be too large\n        int nudgedTooFarRight = left + (int) ((matrixWidth - 1) * moduleSize) - right;\n        if (nudgedTooFarRight > 0) {\n            if (nudgedTooFarRight > nudge) {\n                // Neither way fits; abort\n                throw NotFoundException.getNotFoundInstance();\n            }\n            left -= nudgedTooFarRight;\n        }\n        // See logic above\n        int nudgedTooFarDown = top + (int) ((matrixHeight - 1) * moduleSize) - bottom;\n        if (nudgedTooFarDown > 0) {\n            if (nudgedTooFarDown > nudge) {\n                // Neither way fits; abort\n                throw NotFoundException.getNotFoundInstance();\n            }\n            top -= nudgedTooFarDown;\n        }\n\n        // Now just read off the bits\n        BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);\n        for (int y = 0; y < matrixHeight; y++) {\n            int iOffset = top + (int) (y * moduleSize);\n            for (int x = 0; x < matrixWidth; x++) {\n                if (image.get(left + (int) (x * moduleSize), iOffset)) {\n                    bits.set(x, y);\n                }\n            }\n        }\n        return bits;\n    }\n\n    private static float moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException {\n        int height = image.getHeight();\n        int width = image.getWidth();\n        int x = leftTopBlack[0];\n        int y = leftTopBlack[1];\n        boolean inBlack = true;\n        int transitions = 0;\n        while (x < width && y < height) {\n            if (inBlack != image.get(x, y)) {\n                if (++transitions == 5) {\n                    break;\n                }\n                inBlack = !inBlack;\n            }\n            x++;\n            y++;\n        }\n        if (x == width || y == height) {\n            throw NotFoundException.getNotFoundInstance();\n        }\n        return (x - leftTopBlack[0]) / 7.0f;\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.Writer;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.qrcode.encoder.ByteMatrix;\nimport com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;\nimport com.google.zxing.qrcode.encoder.Encoder;\nimport com.google.zxing.qrcode.encoder.QRCode;\n\nimport java.util.Map;\n\n/**\n * This object renders a QR Code as a BitMatrix 2D array of greyscale values.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class QRCodeWriter implements Writer {\n\n  private static final int QUIET_ZONE_SIZE = 4;\n\n  @Override\n  public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)\n      throws WriterException {\n\n    return encode(contents, format, width, height, null);\n  }\n\n  @Override\n  public BitMatrix encode(String contents,\n                          BarcodeFormat format,\n                          int width,\n                          int height,\n                          Map<EncodeHintType,?> hints) throws WriterException {\n\n    if (contents.isEmpty()) {\n      throw new IllegalArgumentException(\"Found empty contents\");\n    }\n\n    if (format != BarcodeFormat.QR_CODE) {\n      throw new IllegalArgumentException(\"Can only encode QR_CODE, but got \" + format);\n    }\n\n    if (width < 0 || height < 0) {\n      throw new IllegalArgumentException(\"Requested dimensions are too small: \" + width + 'x' +\n          height);\n    }\n\n    ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;\n    int quietZone = QUIET_ZONE_SIZE;\n    if (hints != null) {\n      if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {\n        errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());\n      }\n      if (hints.containsKey(EncodeHintType.MARGIN)) {\n        quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());\n      }\n    }\n\n    QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);\n    return renderResult(code, width, height, quietZone);\n  }\n\n  // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses\n  // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).\n  private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {\n    ByteMatrix input = code.getMatrix();\n    if (input == null) {\n      throw new IllegalStateException();\n    }\n    int inputWidth = input.getWidth();\n    int inputHeight = input.getHeight();\n    int qrWidth = inputWidth + (quietZone * 2);\n    int qrHeight = inputHeight + (quietZone * 2);\n    int outputWidth = Math.max(width, qrWidth);\n    int outputHeight = Math.max(height, qrHeight);\n\n    int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);\n    // Padding includes both the quiet zone and the extra white pixels to accommodate the requested\n    // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.\n    // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will\n    // handle all the padding from 100x100 (the actual QR) up to 200x160.\n    int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;\n    int topPadding = (outputHeight - (inputHeight * multiple)) / 2;\n\n    BitMatrix output = new BitMatrix(outputWidth, outputHeight);\n\n    for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {\n      // Write the contents of this row of the barcode\n      for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {\n        if (input.get(inputX, inputY) == 1) {\n          output.setRegion(outputX, outputY, multiple, multiple);\n        }\n      }\n    }\n\n    return output;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/BitMatrixParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * @author Sean Owen\n */\nfinal class BitMatrixParser {\n\n  private final BitMatrix bitMatrix;\n  private Version parsedVersion;\n  private FormatInformation parsedFormatInfo;\n  private boolean mirror;\n\n  /**\n   * @param bitMatrix {@link BitMatrix} to parse\n   * @throws FormatException if dimension is not >= 21 and 1 mod 4\n   */\n  BitMatrixParser(BitMatrix bitMatrix) throws FormatException {\n    int dimension = bitMatrix.getHeight();\n    if (dimension < 21 || (dimension & 0x03) != 1) {\n      throw FormatException.getFormatInstance();\n    }\n    this.bitMatrix = bitMatrix;\n  }\n\n  /**\n   * <p>Reads format information from one of its two locations within the QR Code.</p>\n   *\n   * @return {@link FormatInformation} encapsulating the QR Code's format info\n   * @throws FormatException if both format information locations cannot be parsed as\n   * the valid encoding of format information\n   */\n  FormatInformation readFormatInformation() throws FormatException {\n\n    if (parsedFormatInfo != null) {\n      return parsedFormatInfo;\n    }\n\n    // Read top-left format info bits\n    int formatInfoBits1 = 0;\n    for (int i = 0; i < 6; i++) {\n      formatInfoBits1 = copyBit(i, 8, formatInfoBits1);\n    }\n    // .. and skip a bit in the timing pattern ...\n    formatInfoBits1 = copyBit(7, 8, formatInfoBits1);\n    formatInfoBits1 = copyBit(8, 8, formatInfoBits1);\n    formatInfoBits1 = copyBit(8, 7, formatInfoBits1);\n    // .. and skip a bit in the timing pattern ...\n    for (int j = 5; j >= 0; j--) {\n      formatInfoBits1 = copyBit(8, j, formatInfoBits1);\n    }\n\n    // Read the top-right/bottom-left pattern too\n    int dimension = bitMatrix.getHeight();\n    int formatInfoBits2 = 0;\n    int jMin = dimension - 7;\n    for (int j = dimension - 1; j >= jMin; j--) {\n      formatInfoBits2 = copyBit(8, j, formatInfoBits2);\n    }\n    for (int i = dimension - 8; i < dimension; i++) {\n      formatInfoBits2 = copyBit(i, 8, formatInfoBits2);\n    }\n\n    parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);\n    if (parsedFormatInfo != null) {\n      return parsedFormatInfo;\n    }\n    throw FormatException.getFormatInstance();\n  }\n\n  /**\n   * <p>Reads version information from one of its two locations within the QR Code.</p>\n   *\n   * @return {@link Version} encapsulating the QR Code's version\n   * @throws FormatException if both version information locations cannot be parsed as\n   * the valid encoding of version information\n   */\n  Version readVersion() throws FormatException {\n\n    if (parsedVersion != null) {\n      return parsedVersion;\n    }\n\n    int dimension = bitMatrix.getHeight();\n\n    int provisionalVersion = (dimension - 17) / 4;\n    if (provisionalVersion <= 6) {\n      return Version.getVersionForNumber(provisionalVersion);\n    }\n\n    // Read top-right version info: 3 wide by 6 tall\n    int versionBits = 0;\n    int ijMin = dimension - 11;\n    for (int j = 5; j >= 0; j--) {\n      for (int i = dimension - 9; i >= ijMin; i--) {\n        versionBits = copyBit(i, j, versionBits);\n      }\n    }\n\n    Version theParsedVersion = Version.decodeVersionInformation(versionBits);\n    if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) {\n      parsedVersion = theParsedVersion;\n      return theParsedVersion;\n    }\n\n    // Hmm, failed. Try bottom left: 6 wide by 3 tall\n    versionBits = 0;\n    for (int i = 5; i >= 0; i--) {\n      for (int j = dimension - 9; j >= ijMin; j--) {\n        versionBits = copyBit(i, j, versionBits);\n      }\n    }\n\n    theParsedVersion = Version.decodeVersionInformation(versionBits);\n    if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) {\n      parsedVersion = theParsedVersion;\n      return theParsedVersion;\n    }\n    throw FormatException.getFormatInstance();\n  }\n\n  private int copyBit(int i, int j, int versionBits) {\n    boolean bit = mirror ? bitMatrix.get(j, i) : bitMatrix.get(i, j);\n    return bit ? (versionBits << 1) | 0x1 : versionBits << 1;\n  }\n\n  /**\n   * <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the\n   * correct order in order to reconstruct the codewords bytes contained within the\n   * QR Code.</p>\n   *\n   * @return bytes encoded within the QR Code\n   * @throws FormatException if the exact number of bytes expected is not read\n   */\n  byte[] readCodewords() throws FormatException {\n\n    FormatInformation formatInfo = readFormatInformation();\n    Version version = readVersion();\n\n    // Get the data mask for the format used in this QR Code. This will exclude\n    // some bits from reading as we wind through the bit matrix.\n    DataMask dataMask = DataMask.values()[formatInfo.getDataMask()];\n    int dimension = bitMatrix.getHeight();\n    dataMask.unmaskBitMatrix(bitMatrix, dimension);\n\n    BitMatrix functionPattern = version.buildFunctionPattern();\n\n    boolean readingUp = true;\n    byte[] result = new byte[version.getTotalCodewords()];\n    int resultOffset = 0;\n    int currentByte = 0;\n    int bitsRead = 0;\n    // Read columns in pairs, from right to left\n    for (int j = dimension - 1; j > 0; j -= 2) {\n      if (j == 6) {\n        // Skip whole column with vertical alignment pattern;\n        // saves time and makes the other code proceed more cleanly\n        j--;\n      }\n      // Read alternatingly from bottom to top then top to bottom\n      for (int count = 0; count < dimension; count++) {\n        int i = readingUp ? dimension - 1 - count : count;\n        for (int col = 0; col < 2; col++) {\n          // Ignore bits covered by the function pattern\n          if (!functionPattern.get(j - col, i)) {\n            // Read a bit\n            bitsRead++;\n            currentByte <<= 1;\n            if (bitMatrix.get(j - col, i)) {\n              currentByte |= 1;\n            }\n            // If we've made a whole byte, save it off\n            if (bitsRead == 8) {\n              result[resultOffset++] = (byte) currentByte;\n              bitsRead = 0;\n              currentByte = 0;\n            }\n          }\n        }\n      }\n      readingUp ^= true; // readingUp = !readingUp; // switch directions\n    }\n    if (resultOffset != version.getTotalCodewords()) {\n      throw FormatException.getFormatInstance();\n    }\n    return result;\n  }\n\n  /**\n   * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.\n   */\n  void remask() {\n    if (parsedFormatInfo == null) {\n      return; // We have no format information, and have no data mask\n    }\n    DataMask dataMask = DataMask.values()[parsedFormatInfo.getDataMask()];\n    int dimension = bitMatrix.getHeight();\n    dataMask.unmaskBitMatrix(bitMatrix, dimension);\n  }\n\n  /**\n   * Prepare the parser for a mirrored operation.\n   * This flag has effect only on the {@link #readFormatInformation()} and the\n   * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the\n   * {@link #mirror()} method should be called.\n   *\n   * @param mirror Whether to read version and format information mirrored.\n   */\n  void setMirror(boolean mirror) {\n    parsedVersion = null;\n    parsedFormatInfo = null;\n    this.mirror = mirror;\n  }\n\n  /** Mirror the bit matrix in order to attempt a second reading. */\n  void mirror() {\n    for (int x = 0; x < bitMatrix.getWidth(); x++) {\n      for (int y = x + 1; y < bitMatrix.getHeight(); y++) {\n        if (bitMatrix.get(x, y) != bitMatrix.get(y, x)) {\n          bitMatrix.flip(y, x);\n          bitMatrix.flip(x, y);\n        }\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/DataBlock.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\n/**\n * <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into\n * multiple blocks, each of which is a unit of data and error-correction codewords. Each\n * is represented by an instance of this class.</p>\n *\n * @author Sean Owen\n */\nfinal class DataBlock {\n\n  private final int numDataCodewords;\n  private final byte[] codewords;\n\n  private DataBlock(int numDataCodewords, byte[] codewords) {\n    this.numDataCodewords = numDataCodewords;\n    this.codewords = codewords;\n  }\n\n  /**\n   * <p>When QR Codes use multiple data blocks, they are actually interleaved.\n   * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This\n   * method will separate the data into original blocks.</p>\n   *\n   * @param rawCodewords bytes as read directly from the QR Code\n   * @param version version of the QR Code\n   * @param ecLevel error-correction level of the QR Code\n   * @return DataBlocks containing original bytes, \"de-interleaved\" from representation in the\n   *         QR Code\n   */\n  static DataBlock[] getDataBlocks(byte[] rawCodewords,\n                                   Version version,\n                                   ErrorCorrectionLevel ecLevel) {\n\n    if (rawCodewords.length != version.getTotalCodewords()) {\n      throw new IllegalArgumentException();\n    }\n\n    // Figure out the number and size of data blocks used by this version and\n    // error correction level\n    Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);\n\n    // First count the total number of data blocks\n    int totalBlocks = 0;\n    Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();\n    for (Version.ECB ecBlock : ecBlockArray) {\n      totalBlocks += ecBlock.getCount();\n    }\n\n    // Now establish DataBlocks of the appropriate size and number of data codewords\n    DataBlock[] result = new DataBlock[totalBlocks];\n    int numResultBlocks = 0;\n    for (Version.ECB ecBlock : ecBlockArray) {\n      for (int i = 0; i < ecBlock.getCount(); i++) {\n        int numDataCodewords = ecBlock.getDataCodewords();\n        int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords;\n        result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);\n      }\n    }\n\n    // All blocks have the same amount of data, except that the last n\n    // (where n may be 0) have 1 more byte. Figure out where these start.\n    int shorterBlocksTotalCodewords = result[0].codewords.length;\n    int longerBlocksStartAt = result.length - 1;\n    while (longerBlocksStartAt >= 0) {\n      int numCodewords = result[longerBlocksStartAt].codewords.length;\n      if (numCodewords == shorterBlocksTotalCodewords) {\n        break;\n      }\n      longerBlocksStartAt--;\n    }\n    longerBlocksStartAt++;\n\n    int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock();\n    // The last elements of result may be 1 element longer;\n    // first fill out as many elements as all of them have\n    int rawCodewordsOffset = 0;\n    for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {\n      for (int j = 0; j < numResultBlocks; j++) {\n        result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];\n      }\n    }\n    // Fill out the last data block in the longer ones\n    for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {\n      result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];\n    }\n    // Now add in error correction blocks\n    int max = result[0].codewords.length;\n    for (int i = shorterBlocksNumDataCodewords; i < max; i++) {\n      for (int j = 0; j < numResultBlocks; j++) {\n        int iOffset = j < longerBlocksStartAt ? i : i + 1;\n        result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];\n      }\n    }\n    return result;\n  }\n\n  int getNumDataCodewords() {\n    return numDataCodewords;\n  }\n\n  byte[] getCodewords() {\n    return codewords;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/DataMask.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations\n * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,\n * including areas used for finder patterns, timing patterns, etc. These areas should be unused\n * after the point they are unmasked anyway.</p>\n *\n * <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position\n * and j is row position. In fact, as the text says, i is row position and j is column position.</p>\n *\n * @author Sean Owen\n */\nenum DataMask {\n\n  // See ISO 18004:2006 6.8.1\n\n  /**\n   * 000: mask bits for which (x + y) mod 2 == 0\n   */\n  DATA_MASK_000() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return ((i + j) & 0x01) == 0;\n    }\n  },\n\n  /**\n   * 001: mask bits for which x mod 2 == 0\n   */\n  DATA_MASK_001() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return (i & 0x01) == 0;\n    }\n  },\n\n  /**\n   * 010: mask bits for which y mod 3 == 0\n   */\n  DATA_MASK_010() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return j % 3 == 0;\n    }\n  },\n\n  /**\n   * 011: mask bits for which (x + y) mod 3 == 0\n   */\n  DATA_MASK_011() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return (i + j) % 3 == 0;\n    }\n  },\n\n  /**\n   * 100: mask bits for which (x/2 + y/3) mod 2 == 0\n   */\n  DATA_MASK_100() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return (((i / 2) + (j / 3)) & 0x01) == 0;\n    }\n  },\n\n  /**\n   * 101: mask bits for which xy mod 2 + xy mod 3 == 0\n   * equivalently, such that xy mod 6 == 0\n   */\n  DATA_MASK_101() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return (i * j) % 6 == 0;\n    }\n  },\n\n  /**\n   * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0\n   * equivalently, such that xy mod 6 < 3\n   */\n  DATA_MASK_110() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return ((i * j) % 6) < 3;\n    }\n  },\n\n  /**\n   * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0\n   * equivalently, such that (x + y + xy mod 3) mod 2 == 0\n   */\n  DATA_MASK_111() {\n    @Override\n    boolean isMasked(int i, int j) {\n      return ((i + j + ((i * j) % 3)) & 0x01) == 0;\n    }\n  };\n\n  // End of enum constants.\n\n\n  /**\n   * <p>Implementations of this method reverse the data masking process applied to a QR Code and\n   * make its bits ready to read.</p>\n   *\n   * @param bits representation of QR Code bits\n   * @param dimension dimension of QR Code, represented by bits, being unmasked\n   */\n  final void unmaskBitMatrix(BitMatrix bits, int dimension) {\n    for (int i = 0; i < dimension; i++) {\n      for (int j = 0; j < dimension; j++) {\n        if (isMasked(i, j)) {\n          bits.flip(j, i);\n        }\n      }\n    }\n  }\n\n  abstract boolean isMasked(int i, int j);\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitSource;\nimport com.google.zxing.common.CharacterSetECI;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.StringUtils;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes\n * in one QR Code. This class decodes the bits back into text.</p>\n *\n * <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>\n *\n * @author Sean Owen\n */\nfinal class DecodedBitStreamParser {\n\n  /**\n   * See ISO 18004:2006, 6.4.4 Table 5\n   */\n  private static final char[] ALPHANUMERIC_CHARS =\n      \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:\".toCharArray();\n  private static final int GB2312_SUBSET = 1;\n\n  private DecodedBitStreamParser() {\n  }\n\n  static DecoderResult decode(byte[] bytes,\n                              Version version,\n                              ErrorCorrectionLevel ecLevel,\n                              Map<DecodeHintType,?> hints) throws FormatException {\n    BitSource bits = new BitSource(bytes);\n    StringBuilder result = new StringBuilder(50);\n    List<byte[]> byteSegments = new ArrayList<>(1);\n    int symbolSequence = -1;\n    int parityData = -1;\n\n    try {\n      CharacterSetECI currentCharacterSetECI = null;\n      boolean fc1InEffect = false;\n      Mode mode;\n      do {\n        // While still another segment to read...\n        if (bits.available() < 4) {\n          // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here\n          mode = Mode.TERMINATOR;\n        } else {\n          mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits\n        }\n        switch (mode) {\n          case TERMINATOR:\n            break;\n          case FNC1_FIRST_POSITION:\n          case FNC1_SECOND_POSITION:\n            // We do little with FNC1 except alter the parsed result a bit according to the spec\n            fc1InEffect = true;\n            break;\n          case STRUCTURED_APPEND:\n            if (bits.available() < 16) {\n              throw FormatException.getFormatInstance();\n            }\n            // sequence number and parity is added later to the result metadata\n            // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue\n            symbolSequence = bits.readBits(8);\n            parityData = bits.readBits(8);\n            break;\n          case ECI:\n            // Count doesn't apply to ECI\n            int value = parseECIValue(bits);\n            currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);\n            if (currentCharacterSetECI == null) {\n              throw FormatException.getFormatInstance();\n            }\n            break;\n          case HANZI:\n            // First handle Hanzi mode which does not start with character count\n            // Chinese mode contains a sub set indicator right after mode indicator\n            int subset = bits.readBits(4);\n            int countHanzi = bits.readBits(mode.getCharacterCountBits(version));\n            if (subset == GB2312_SUBSET) {\n              decodeHanziSegment(bits, result, countHanzi);\n            }\n            break;\n          default:\n            // \"Normal\" QR code modes:\n            // How many characters will follow, encoded in this mode?\n            int count = bits.readBits(mode.getCharacterCountBits(version));\n            switch (mode) {\n              case NUMERIC:\n                decodeNumericSegment(bits, result, count);\n                break;\n              case ALPHANUMERIC:\n                decodeAlphanumericSegment(bits, result, count, fc1InEffect);\n                break;\n              case BYTE:\n                decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);\n                break;\n              case KANJI:\n                decodeKanjiSegment(bits, result, count);\n                break;\n              default:\n                throw FormatException.getFormatInstance();\n            }\n            break;\n        }\n      } while (mode != Mode.TERMINATOR);\n    } catch (IllegalArgumentException iae) {\n      // from readBits() calls\n      throw FormatException.getFormatInstance();\n    }\n\n    return new DecoderResult(bytes,\n                             result.toString(),\n                             byteSegments.isEmpty() ? null : byteSegments,\n                             ecLevel == null ? null : ecLevel.toString(),\n                             symbolSequence,\n                             parityData);\n  }\n\n  /**\n   * See specification GBT 18284-2000\n   */\n  private static void decodeHanziSegment(BitSource bits,\n                                         StringBuilder result,\n                                         int count) throws FormatException {\n    // Don't crash trying to read more bits than we have available.\n    if (count * 13 > bits.available()) {\n      throw FormatException.getFormatInstance();\n    }\n\n    // Each character will require 2 bytes. Read the characters as 2-byte pairs\n    // and decode as GB2312 afterwards\n    byte[] buffer = new byte[2 * count];\n    int offset = 0;\n    while (count > 0) {\n      // Each 13 bits encodes a 2-byte character\n      int twoBytes = bits.readBits(13);\n      int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);\n      if (assembledTwoBytes < 0x00A00) {\n        // In the 0xA1A1 to 0xAAFE range\n        assembledTwoBytes += 0x0A1A1;\n      } else {\n        // In the 0xB0A1 to 0xFAFE range\n        assembledTwoBytes += 0x0A6A1;\n      }\n      buffer[offset] = (byte) ((assembledTwoBytes >> 8) & 0xFF);\n      buffer[offset + 1] = (byte) (assembledTwoBytes & 0xFF);\n      offset += 2;\n      count--;\n    }\n\n    try {\n      result.append(new String(buffer, StringUtils.GB2312));\n    } catch (UnsupportedEncodingException ignored) {\n      throw FormatException.getFormatInstance();\n    }\n  }\n\n  private static void decodeKanjiSegment(BitSource bits,\n                                         StringBuilder result,\n                                         int count) throws FormatException {\n    // Don't crash trying to read more bits than we have available.\n    if (count * 13 > bits.available()) {\n      throw FormatException.getFormatInstance();\n    }\n\n    // Each character will require 2 bytes. Read the characters as 2-byte pairs\n    // and decode as Shift_JIS afterwards\n    byte[] buffer = new byte[2 * count];\n    int offset = 0;\n    while (count > 0) {\n      // Each 13 bits encodes a 2-byte character\n      int twoBytes = bits.readBits(13);\n      int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);\n      if (assembledTwoBytes < 0x01F00) {\n        // In the 0x8140 to 0x9FFC range\n        assembledTwoBytes += 0x08140;\n      } else {\n        // In the 0xE040 to 0xEBBF range\n        assembledTwoBytes += 0x0C140;\n      }\n      buffer[offset] = (byte) (assembledTwoBytes >> 8);\n      buffer[offset + 1] = (byte) assembledTwoBytes;\n      offset += 2;\n      count--;\n    }\n    // Shift_JIS may not be supported in some environments:\n    try {\n      result.append(new String(buffer, StringUtils.SHIFT_JIS));\n    } catch (UnsupportedEncodingException ignored) {\n      throw FormatException.getFormatInstance();\n    }\n  }\n\n  private static void decodeByteSegment(BitSource bits,\n                                        StringBuilder result,\n                                        int count,\n                                        CharacterSetECI currentCharacterSetECI,\n                                        Collection<byte[]> byteSegments,\n                                        Map<DecodeHintType,?> hints) throws FormatException {\n    // Don't crash trying to read more bits than we have available.\n    if (8 * count > bits.available()) {\n      throw FormatException.getFormatInstance();\n    }\n\n    byte[] readBytes = new byte[count];\n    for (int i = 0; i < count; i++) {\n      readBytes[i] = (byte) bits.readBits(8);\n    }\n    String encoding;\n    if (currentCharacterSetECI == null) {\n      // The spec isn't clear on this mode; see\n      // section 6.4.5: t does not say which encoding to assuming\n      // upon decoding. I have seen ISO-8859-1 used as well as\n      // Shift_JIS -- without anything like an ECI designator to\n      // give a hint.\n      encoding = StringUtils.guessEncoding(readBytes, hints);\n    } else {\n      encoding = currentCharacterSetECI.name();\n    }\n    try {\n      result.append(new String(readBytes, encoding));\n    } catch (UnsupportedEncodingException ignored) {\n      throw FormatException.getFormatInstance();\n    }\n    byteSegments.add(readBytes);\n  }\n\n  private static char toAlphaNumericChar(int value) throws FormatException {\n    if (value >= ALPHANUMERIC_CHARS.length) {\n      throw FormatException.getFormatInstance();\n    }\n    return ALPHANUMERIC_CHARS[value];\n  }\n\n  private static void decodeAlphanumericSegment(BitSource bits,\n                                                StringBuilder result,\n                                                int count,\n                                                boolean fc1InEffect) throws FormatException {\n    // Read two characters at a time\n    int start = result.length();\n    while (count > 1) {\n      if (bits.available() < 11) {\n        throw FormatException.getFormatInstance();\n      }\n      int nextTwoCharsBits = bits.readBits(11);\n      result.append(toAlphaNumericChar(nextTwoCharsBits / 45));\n      result.append(toAlphaNumericChar(nextTwoCharsBits % 45));\n      count -= 2;\n    }\n    if (count == 1) {\n      // special case: one character left\n      if (bits.available() < 6) {\n        throw FormatException.getFormatInstance();\n      }\n      result.append(toAlphaNumericChar(bits.readBits(6)));\n    }\n    // See section 6.4.8.1, 6.4.8.2\n    if (fc1InEffect) {\n      // We need to massage the result a bit if in an FNC1 mode:\n      for (int i = start; i < result.length(); i++) {\n        if (result.charAt(i) == '%') {\n          if (i < result.length() - 1 && result.charAt(i + 1) == '%') {\n            // %% is rendered as %\n            result.deleteCharAt(i + 1);\n          } else {\n            // In alpha mode, % should be converted to FNC1 separator 0x1D\n            result.setCharAt(i, (char) 0x1D);\n          }\n        }\n      }\n    }\n  }\n\n  private static void decodeNumericSegment(BitSource bits,\n                                           StringBuilder result,\n                                           int count) throws FormatException {\n    // Read three digits at a time\n    while (count >= 3) {\n      // Each 10 bits encodes three digits\n      if (bits.available() < 10) {\n        throw FormatException.getFormatInstance();\n      }\n      int threeDigitsBits = bits.readBits(10);\n      if (threeDigitsBits >= 1000) {\n        throw FormatException.getFormatInstance();\n      }\n      result.append(toAlphaNumericChar(threeDigitsBits / 100));\n      result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));\n      result.append(toAlphaNumericChar(threeDigitsBits % 10));\n      count -= 3;\n    }\n    if (count == 2) {\n      // Two digits left over to read, encoded in 7 bits\n      if (bits.available() < 7) {\n        throw FormatException.getFormatInstance();\n      }\n      int twoDigitsBits = bits.readBits(7);\n      if (twoDigitsBits >= 100) {\n        throw FormatException.getFormatInstance();\n      }\n      result.append(toAlphaNumericChar(twoDigitsBits / 10));\n      result.append(toAlphaNumericChar(twoDigitsBits % 10));\n    } else if (count == 1) {\n      // One digit left over to read\n      if (bits.available() < 4) {\n        throw FormatException.getFormatInstance();\n      }\n      int digitBits = bits.readBits(4);\n      if (digitBits >= 10) {\n        throw FormatException.getFormatInstance();\n      }\n      result.append(toAlphaNumericChar(digitBits));\n    }\n  }\n\n  private static int parseECIValue(BitSource bits) throws FormatException {\n    int firstByte = bits.readBits(8);\n    if ((firstByte & 0x80) == 0) {\n      // just one byte\n      return firstByte & 0x7F;\n    }\n    if ((firstByte & 0xC0) == 0x80) {\n      // two bytes\n      int secondByte = bits.readBits(8);\n      return ((firstByte & 0x3F) << 8) | secondByte;\n    }\n    if ((firstByte & 0xE0) == 0xC0) {\n      // three bytes\n      int secondThirdBytes = bits.readBits(16);\n      return ((firstByte & 0x1F) << 16) | secondThirdBytes;\n    }\n    throw FormatException.getFormatInstance();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/Decoder.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DecoderResult;\nimport com.google.zxing.common.reedsolomon.GenericGF;\nimport com.google.zxing.common.reedsolomon.ReedSolomonDecoder;\nimport com.google.zxing.common.reedsolomon.ReedSolomonException;\n\nimport java.util.Map;\n\n/**\n * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting\n * the QR Code from an image.</p>\n *\n * @author Sean Owen\n */\npublic final class Decoder {\n\n  private final ReedSolomonDecoder rsDecoder;\n\n  public Decoder() {\n    rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256);\n  }\n\n  public DecoderResult decode(boolean[][] image) throws ChecksumException, FormatException {\n    return decode(image, null);\n  }\n\n  /**\n   * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.\n   * \"true\" is taken to mean a black module.</p>\n   *\n   * @param image booleans representing white/black QR Code modules\n   * @param hints decoding hints that should be used to influence decoding\n   * @return text and bytes encoded within the QR Code\n   * @throws FormatException if the QR Code cannot be decoded\n   * @throws ChecksumException if error correction fails\n   */\n  public DecoderResult decode(boolean[][] image, Map<DecodeHintType,?> hints)\n      throws ChecksumException, FormatException {\n    return decode(BitMatrix.parse(image), hints);\n  }\n\n  public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException {\n    return decode(bits, null);\n  }\n\n  /**\n   * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or \"true\" is taken to mean a black module.</p>\n   *\n   * @param bits booleans representing white/black QR Code modules\n   * @param hints decoding hints that should be used to influence decoding\n   * @return text and bytes encoded within the QR Code\n   * @throws FormatException if the QR Code cannot be decoded\n   * @throws ChecksumException if error correction fails\n   */\n  public DecoderResult decode(BitMatrix bits, Map<DecodeHintType,?> hints)\n      throws FormatException, ChecksumException {\n\n    // Construct a parser and read version, error-correction level\n    BitMatrixParser parser = new BitMatrixParser(bits);\n    FormatException fe = null;\n    ChecksumException ce = null;\n    try {\n      return decode(parser, hints);\n    } catch (FormatException e) {\n      fe = e;\n    } catch (ChecksumException e) {\n      ce = e;\n    }\n\n    try {\n\n      // Revert the bit matrix\n      parser.remask();\n\n      // Will be attempting a mirrored reading of the version and format info.\n      parser.setMirror(true);\n\n      // Preemptively read the version.\n      parser.readVersion();\n\n      // Preemptively read the format information.\n      parser.readFormatInformation();\n\n      /*\n       * Since we're here, this means we have successfully detected some kind\n       * of version and format information when mirrored. This is a good sign,\n       * that the QR code may be mirrored, and we should try once more with a\n       * mirrored content.\n       */\n      // Prepare for a mirrored reading.\n      parser.mirror();\n\n      DecoderResult result = decode(parser, hints);\n\n      // Success! Notify the caller that the code was mirrored.\n      result.setOther(new QRCodeDecoderMetaData(true));\n\n      return result;\n\n    } catch (FormatException | ChecksumException e) {\n      // Throw the exception from the original reading\n      if (fe != null) {\n        throw fe;\n      }\n      throw ce; // If fe is null, this can't be\n    }\n  }\n\n  private DecoderResult decode(BitMatrixParser parser, Map<DecodeHintType,?> hints)\n      throws FormatException, ChecksumException {\n    Version version = parser.readVersion();\n    ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();\n\n    // Read codewords\n    byte[] codewords = parser.readCodewords();\n    // Separate into data blocks\n    DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);\n\n    // Count total number of data bytes\n    int totalBytes = 0;\n    for (DataBlock dataBlock : dataBlocks) {\n      totalBytes += dataBlock.getNumDataCodewords();\n    }\n    byte[] resultBytes = new byte[totalBytes];\n    int resultOffset = 0;\n\n    // Error-correct and copy data blocks together into a stream of bytes\n    for (DataBlock dataBlock : dataBlocks) {\n      byte[] codewordBytes = dataBlock.getCodewords();\n      int numDataCodewords = dataBlock.getNumDataCodewords();\n      correctErrors(codewordBytes, numDataCodewords);\n      for (int i = 0; i < numDataCodewords; i++) {\n        resultBytes[resultOffset++] = codewordBytes[i];\n      }\n    }\n\n    // Decode the contents of that stream of bytes\n    return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints);\n  }\n\n  /**\n   * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to\n   * correct the errors in-place using Reed-Solomon error correction.</p>\n   *\n   * @param codewordBytes data and error correction codewords\n   * @param numDataCodewords number of codewords that are data bytes\n   * @throws ChecksumException if error correction fails\n   */\n  private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException {\n    int numCodewords = codewordBytes.length;\n    // First read into an array of ints\n    int[] codewordsInts = new int[numCodewords];\n    for (int i = 0; i < numCodewords; i++) {\n      codewordsInts[i] = codewordBytes[i] & 0xFF;\n    }\n    try {\n      rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords);\n    } catch (ReedSolomonException ignored) {\n      throw ChecksumException.getChecksumInstance();\n    }\n    // Copy back into array of bytes -- only need to worry about the bytes that were data\n    // We don't care about errors in the error-correction codewords\n    for (int i = 0; i < numDataCodewords; i++) {\n      codewordBytes[i] = (byte) codewordsInts[i];\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\n/**\n * <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels\n * defined by the QR code standard.</p>\n *\n * @author Sean Owen\n */\npublic enum ErrorCorrectionLevel {\n\n  /** L = ~7% correction */\n  L(0x01),\n  /** M = ~15% correction */\n  M(0x00),\n  /** Q = ~25% correction */\n  Q(0x03),\n  /** H = ~30% correction */\n  H(0x02);\n\n  private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};\n\n  private final int bits;\n\n  ErrorCorrectionLevel(int bits) {\n    this.bits = bits;\n  }\n\n  public int getBits() {\n    return bits;\n  }\n\n  /**\n   * @param bits int containing the two bits encoding a QR Code's error correction level\n   * @return ErrorCorrectionLevel representing the encoded error correction level\n   */\n  public static ErrorCorrectionLevel forBits(int bits) {\n    if (bits < 0 || bits >= FOR_BITS.length) {\n      throw new IllegalArgumentException();\n    }\n    return FOR_BITS[bits];\n  }\n\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\n/**\n * <p>Encapsulates a QR Code's format information, including the data mask used and\n * error correction level.</p>\n *\n * @author Sean Owen\n * @see DataMask\n * @see ErrorCorrectionLevel\n */\nfinal class FormatInformation {\n\n  private static final int FORMAT_INFO_MASK_QR = 0x5412;\n\n  /**\n   * See ISO 18004:2006, Annex C, Table C.1\n   */\n  private static final int[][] FORMAT_INFO_DECODE_LOOKUP = {\n      {0x5412, 0x00},\n      {0x5125, 0x01},\n      {0x5E7C, 0x02},\n      {0x5B4B, 0x03},\n      {0x45F9, 0x04},\n      {0x40CE, 0x05},\n      {0x4F97, 0x06},\n      {0x4AA0, 0x07},\n      {0x77C4, 0x08},\n      {0x72F3, 0x09},\n      {0x7DAA, 0x0A},\n      {0x789D, 0x0B},\n      {0x662F, 0x0C},\n      {0x6318, 0x0D},\n      {0x6C41, 0x0E},\n      {0x6976, 0x0F},\n      {0x1689, 0x10},\n      {0x13BE, 0x11},\n      {0x1CE7, 0x12},\n      {0x19D0, 0x13},\n      {0x0762, 0x14},\n      {0x0255, 0x15},\n      {0x0D0C, 0x16},\n      {0x083B, 0x17},\n      {0x355F, 0x18},\n      {0x3068, 0x19},\n      {0x3F31, 0x1A},\n      {0x3A06, 0x1B},\n      {0x24B4, 0x1C},\n      {0x2183, 0x1D},\n      {0x2EDA, 0x1E},\n      {0x2BED, 0x1F},\n  };\n\n  private final ErrorCorrectionLevel errorCorrectionLevel;\n  private final byte dataMask;\n\n  private FormatInformation(int formatInfo) {\n    // Bits 3,4\n    errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);\n    // Bottom 3 bits\n    dataMask = (byte) (formatInfo & 0x07);\n  }\n\n  static int numBitsDiffering(int a, int b) {\n    return Integer.bitCount(a ^ b);\n  }\n\n  /**\n   * @param maskedFormatInfo1 format info indicator, with mask still applied\n   * @param maskedFormatInfo2 second copy of same info; both are checked at the same time\n   *  to establish best match\n   * @return information about the format it specifies, or {@code null}\n   *  if doesn't seem to match any known pattern\n   */\n  static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {\n    FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);\n    if (formatInfo != null) {\n      return formatInfo;\n    }\n    // Should return null, but, some QR codes apparently\n    // do not mask this info. Try again by actually masking the pattern\n    // first\n    return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,\n                                     maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);\n  }\n\n  private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {\n    // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing\n    int bestDifference = Integer.MAX_VALUE;\n    int bestFormatInfo = 0;\n    for (int[] decodeInfo : FORMAT_INFO_DECODE_LOOKUP) {\n      int targetInfo = decodeInfo[0];\n      if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {\n        // Found an exact match\n        return new FormatInformation(decodeInfo[1]);\n      }\n      int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);\n      if (bitsDifference < bestDifference) {\n        bestFormatInfo = decodeInfo[1];\n        bestDifference = bitsDifference;\n      }\n      if (maskedFormatInfo1 != maskedFormatInfo2) {\n        // also try the other option\n        bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);\n        if (bitsDifference < bestDifference) {\n          bestFormatInfo = decodeInfo[1];\n          bestDifference = bitsDifference;\n        }\n      }\n    }\n    // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits\n    // differing means we found a match\n    if (bestDifference <= 3) {\n      return new FormatInformation(bestFormatInfo);\n    }\n    return null;\n  }\n\n  ErrorCorrectionLevel getErrorCorrectionLevel() {\n    return errorCorrectionLevel;\n  }\n\n  byte getDataMask() {\n    return dataMask;\n  }\n\n  @Override\n  public int hashCode() {\n    return (errorCorrectionLevel.ordinal() << 3) | dataMask;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (!(o instanceof FormatInformation)) {\n      return false;\n    }\n    FormatInformation other = (FormatInformation) o;\n    return this.errorCorrectionLevel == other.errorCorrectionLevel &&\n        this.dataMask == other.dataMask;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/Mode.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\n/**\n * <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which\n * data can be encoded to bits in the QR code standard.</p>\n *\n * @author Sean Owen\n */\npublic enum Mode {\n\n  TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode...\n  NUMERIC(new int[]{10, 12, 14}, 0x01),\n  ALPHANUMERIC(new int[]{9, 11, 13}, 0x02),\n  STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported\n  BYTE(new int[]{8, 16, 16}, 0x04),\n  ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply\n  KANJI(new int[]{8, 10, 12}, 0x08),\n  FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05),\n  FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09),\n  /** See GBT 18284-2000; \"Hanzi\" is a transliteration of this mode name. */\n  HANZI(new int[]{8, 10, 12}, 0x0D);\n\n  private final int[] characterCountBitsForVersions;\n  private final int bits;\n\n  Mode(int[] characterCountBitsForVersions, int bits) {\n    this.characterCountBitsForVersions = characterCountBitsForVersions;\n    this.bits = bits;\n  }\n\n  /**\n   * @param bits four bits encoding a QR Code data mode\n   * @return Mode encoded by these bits\n   * @throws IllegalArgumentException if bits do not correspond to a known mode\n   */\n  public static Mode forBits(int bits) {\n    switch (bits) {\n      case 0x0:\n        return TERMINATOR;\n      case 0x1:\n        return NUMERIC;\n      case 0x2:\n        return ALPHANUMERIC;\n      case 0x3:\n        return STRUCTURED_APPEND;\n      case 0x4:\n        return BYTE;\n      case 0x5:\n        return FNC1_FIRST_POSITION;\n      case 0x7:\n        return ECI;\n      case 0x8:\n        return KANJI;\n      case 0x9:\n        return FNC1_SECOND_POSITION;\n      case 0xD:\n        // 0xD is defined in GBT 18284-2000, may not be supported in foreign country\n        return HANZI;\n      default:\n        throw new IllegalArgumentException();\n    }\n  }\n\n  /**\n   * @param version version in question\n   * @return number of bits used, in this QR Code symbol {@link Version}, to encode the\n   *         count of characters that will follow encoded in this Mode\n   */\n  public int getCharacterCountBits(Version version) {\n    int number = version.getVersionNumber();\n    int offset;\n    if (number <= 9) {\n      offset = 0;\n    } else if (number <= 26) {\n      offset = 1;\n    } else {\n      offset = 2;\n    }\n    return characterCountBitsForVersions[offset];\n  }\n\n  public int getBits() {\n    return bits;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/QRCodeDecoderMetaData.java",
    "content": "/*\n * Copyright 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\nimport com.google.zxing.ResultPoint;\n\n/**\n * Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the\n * decoding caller. Callers are expected to process this.\n *\n * @see com.google.zxing.common.DecoderResult#getOther()\n */\npublic final class QRCodeDecoderMetaData {\n\n  private final boolean mirrored;\n\n  QRCodeDecoderMetaData(boolean mirrored) {\n    this.mirrored = mirrored;\n  }\n\n  /**\n   * @return true if the QR Code was mirrored.\n   */\n  public boolean isMirrored() {\n    return mirrored;\n  }\n\n  /**\n   * Apply the result points' order correction due to mirroring.\n   *\n   * @param points Array of points to apply mirror correction to.\n   */\n  public void applyMirroredCorrection(ResultPoint[] points) {\n    if (!mirrored || points == null || points.length < 3) {\n      return;\n    }\n    ResultPoint bottomLeft = points[0];\n    points[0] = points[2];\n    points[2] = bottomLeft;\n    // No need to 'fix' top-left and alignment pattern.\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/decoder/Version.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.decoder;\n\nimport com.google.zxing.FormatException;\nimport com.google.zxing.common.BitMatrix;\n\n/**\n * See ISO 18004:2006 Annex D\n *\n * @author Sean Owen\n */\npublic final class Version {\n\n  /**\n   * See ISO 18004:2006 Annex D.\n   * Element i represents the raw version bits that specify version i + 7\n   */\n  private static final int[] VERSION_DECODE_INFO = {\n      0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,\n      0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,\n      0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,\n      0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,\n      0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,\n      0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,\n      0x2542E, 0x26A64, 0x27541, 0x28C69\n  };\n\n  private static final Version[] VERSIONS = buildVersions();\n\n  private final int versionNumber;\n  private final int[] alignmentPatternCenters;\n  private final ECBlocks[] ecBlocks;\n  private final int totalCodewords;\n\n  private Version(int versionNumber,\n                  int[] alignmentPatternCenters,\n                  ECBlocks... ecBlocks) {\n    this.versionNumber = versionNumber;\n    this.alignmentPatternCenters = alignmentPatternCenters;\n    this.ecBlocks = ecBlocks;\n    int total = 0;\n    int ecCodewords = ecBlocks[0].getECCodewordsPerBlock();\n    ECB[] ecbArray = ecBlocks[0].getECBlocks();\n    for (ECB ecBlock : ecbArray) {\n      total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);\n    }\n    this.totalCodewords = total;\n  }\n\n  public int getVersionNumber() {\n    return versionNumber;\n  }\n\n  public int[] getAlignmentPatternCenters() {\n    return alignmentPatternCenters;\n  }\n\n  public int getTotalCodewords() {\n    return totalCodewords;\n  }\n\n  public int getDimensionForVersion() {\n    return 17 + 4 * versionNumber;\n  }\n\n  public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) {\n    return ecBlocks[ecLevel.ordinal()];\n  }\n\n  /**\n   * <p>Deduces version information purely from QR Code dimensions.</p>\n   *\n   * @param dimension dimension in modules\n   * @return Version for a QR Code of that dimension\n   * @throws FormatException if dimension is not 1 mod 4\n   */\n  public static Version getProvisionalVersionForDimension(int dimension) throws FormatException {\n    if (dimension % 4 != 1) {\n      throw FormatException.getFormatInstance();\n    }\n    try {\n      return getVersionForNumber((dimension - 17) / 4);\n    } catch (IllegalArgumentException ignored) {\n      throw FormatException.getFormatInstance();\n    }\n  }\n\n  public static Version getVersionForNumber(int versionNumber) {\n    if (versionNumber < 1 || versionNumber > 40) {\n      throw new IllegalArgumentException();\n    }\n    return VERSIONS[versionNumber - 1];\n  }\n\n  static Version decodeVersionInformation(int versionBits) {\n    int bestDifference = Integer.MAX_VALUE;\n    int bestVersion = 0;\n    for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {\n      int targetVersion = VERSION_DECODE_INFO[i];\n      // Do the version info bits match exactly? done.\n      if (targetVersion == versionBits) {\n        return getVersionForNumber(i + 7);\n      }\n      // Otherwise see if this is the closest to a real version info bit string\n      // we have seen so far\n      int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);\n      if (bitsDifference < bestDifference) {\n        bestVersion = i + 7;\n        bestDifference = bitsDifference;\n      }\n    }\n    // We can tolerate up to 3 bits of error since no two version info codewords will\n    // differ in less than 8 bits.\n    if (bestDifference <= 3) {\n      return getVersionForNumber(bestVersion);\n    }\n    // If we didn't find a close enough match, fail\n    return null;\n  }\n\n  /**\n   * See ISO 18004:2006 Annex E\n   */\n  BitMatrix buildFunctionPattern() {\n    int dimension = getDimensionForVersion();\n    BitMatrix bitMatrix = new BitMatrix(dimension);\n\n    // Top left finder pattern + separator + format\n    bitMatrix.setRegion(0, 0, 9, 9);\n    // Top right finder pattern + separator + format\n    bitMatrix.setRegion(dimension - 8, 0, 8, 9);\n    // Bottom left finder pattern + separator + format\n    bitMatrix.setRegion(0, dimension - 8, 9, 8);\n\n    // Alignment patterns\n    int max = alignmentPatternCenters.length;\n    for (int x = 0; x < max; x++) {\n      int i = alignmentPatternCenters[x] - 2;\n      for (int y = 0; y < max; y++) {\n        if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) {\n          // No alignment patterns near the three finder patterns\n          continue;\n        }\n        bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);\n      }\n    }\n\n    // Vertical timing pattern\n    bitMatrix.setRegion(6, 9, 1, dimension - 17);\n    // Horizontal timing pattern\n    bitMatrix.setRegion(9, 6, dimension - 17, 1);\n\n    if (versionNumber > 6) {\n      // Version info, top right\n      bitMatrix.setRegion(dimension - 11, 0, 3, 6);\n      // Version info, bottom left\n      bitMatrix.setRegion(0, dimension - 11, 6, 3);\n    }\n\n    return bitMatrix;\n  }\n\n  /**\n   * <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will\n   * use blocks of differing sizes within one version, so, this encapsulates the parameters for\n   * each set of blocks. It also holds the number of error-correction codewords per block since it\n   * will be the same across all blocks within one version.</p>\n   */\n  public static final class ECBlocks {\n    private final int ecCodewordsPerBlock;\n    private final ECB[] ecBlocks;\n\n    ECBlocks(int ecCodewordsPerBlock, ECB... ecBlocks) {\n      this.ecCodewordsPerBlock = ecCodewordsPerBlock;\n      this.ecBlocks = ecBlocks;\n    }\n\n    public int getECCodewordsPerBlock() {\n      return ecCodewordsPerBlock;\n    }\n\n    public int getNumBlocks() {\n      int total = 0;\n      for (ECB ecBlock : ecBlocks) {\n        total += ecBlock.getCount();\n      }\n      return total;\n    }\n\n    public int getTotalECCodewords() {\n      return ecCodewordsPerBlock * getNumBlocks();\n    }\n\n    public ECB[] getECBlocks() {\n      return ecBlocks;\n    }\n  }\n\n  /**\n   * <p>Encapsulates the parameters for one error-correction block in one symbol version.\n   * This includes the number of data codewords, and the number of times a block with these\n   * parameters is used consecutively in the QR code version's format.</p>\n   */\n  public static final class ECB {\n    private final int count;\n    private final int dataCodewords;\n\n    ECB(int count, int dataCodewords) {\n      this.count = count;\n      this.dataCodewords = dataCodewords;\n    }\n\n    public int getCount() {\n      return count;\n    }\n\n    public int getDataCodewords() {\n      return dataCodewords;\n    }\n  }\n\n  @Override\n  public String toString() {\n    return String.valueOf(versionNumber);\n  }\n\n  /**\n   * See ISO 18004:2006 6.5.1 Table 9\n   */\n  private static Version[] buildVersions() {\n    return new Version[]{\n        new Version(1, new int[]{},\n            new ECBlocks(7, new ECB(1, 19)),\n            new ECBlocks(10, new ECB(1, 16)),\n            new ECBlocks(13, new ECB(1, 13)),\n            new ECBlocks(17, new ECB(1, 9))),\n        new Version(2, new int[]{6, 18},\n            new ECBlocks(10, new ECB(1, 34)),\n            new ECBlocks(16, new ECB(1, 28)),\n            new ECBlocks(22, new ECB(1, 22)),\n            new ECBlocks(28, new ECB(1, 16))),\n        new Version(3, new int[]{6, 22},\n            new ECBlocks(15, new ECB(1, 55)),\n            new ECBlocks(26, new ECB(1, 44)),\n            new ECBlocks(18, new ECB(2, 17)),\n            new ECBlocks(22, new ECB(2, 13))),\n        new Version(4, new int[]{6, 26},\n            new ECBlocks(20, new ECB(1, 80)),\n            new ECBlocks(18, new ECB(2, 32)),\n            new ECBlocks(26, new ECB(2, 24)),\n            new ECBlocks(16, new ECB(4, 9))),\n        new Version(5, new int[]{6, 30},\n            new ECBlocks(26, new ECB(1, 108)),\n            new ECBlocks(24, new ECB(2, 43)),\n            new ECBlocks(18, new ECB(2, 15),\n                new ECB(2, 16)),\n            new ECBlocks(22, new ECB(2, 11),\n                new ECB(2, 12))),\n        new Version(6, new int[]{6, 34},\n            new ECBlocks(18, new ECB(2, 68)),\n            new ECBlocks(16, new ECB(4, 27)),\n            new ECBlocks(24, new ECB(4, 19)),\n            new ECBlocks(28, new ECB(4, 15))),\n        new Version(7, new int[]{6, 22, 38},\n            new ECBlocks(20, new ECB(2, 78)),\n            new ECBlocks(18, new ECB(4, 31)),\n            new ECBlocks(18, new ECB(2, 14),\n                new ECB(4, 15)),\n            new ECBlocks(26, new ECB(4, 13),\n                new ECB(1, 14))),\n        new Version(8, new int[]{6, 24, 42},\n            new ECBlocks(24, new ECB(2, 97)),\n            new ECBlocks(22, new ECB(2, 38),\n                new ECB(2, 39)),\n            new ECBlocks(22, new ECB(4, 18),\n                new ECB(2, 19)),\n            new ECBlocks(26, new ECB(4, 14),\n                new ECB(2, 15))),\n        new Version(9, new int[]{6, 26, 46},\n            new ECBlocks(30, new ECB(2, 116)),\n            new ECBlocks(22, new ECB(3, 36),\n                new ECB(2, 37)),\n            new ECBlocks(20, new ECB(4, 16),\n                new ECB(4, 17)),\n            new ECBlocks(24, new ECB(4, 12),\n                new ECB(4, 13))),\n        new Version(10, new int[]{6, 28, 50},\n            new ECBlocks(18, new ECB(2, 68),\n                new ECB(2, 69)),\n            new ECBlocks(26, new ECB(4, 43),\n                new ECB(1, 44)),\n            new ECBlocks(24, new ECB(6, 19),\n                new ECB(2, 20)),\n            new ECBlocks(28, new ECB(6, 15),\n                new ECB(2, 16))),\n        new Version(11, new int[]{6, 30, 54},\n            new ECBlocks(20, new ECB(4, 81)),\n            new ECBlocks(30, new ECB(1, 50),\n                new ECB(4, 51)),\n            new ECBlocks(28, new ECB(4, 22),\n                new ECB(4, 23)),\n            new ECBlocks(24, new ECB(3, 12),\n                new ECB(8, 13))),\n        new Version(12, new int[]{6, 32, 58},\n            new ECBlocks(24, new ECB(2, 92),\n                new ECB(2, 93)),\n            new ECBlocks(22, new ECB(6, 36),\n                new ECB(2, 37)),\n            new ECBlocks(26, new ECB(4, 20),\n                new ECB(6, 21)),\n            new ECBlocks(28, new ECB(7, 14),\n                new ECB(4, 15))),\n        new Version(13, new int[]{6, 34, 62},\n            new ECBlocks(26, new ECB(4, 107)),\n            new ECBlocks(22, new ECB(8, 37),\n                new ECB(1, 38)),\n            new ECBlocks(24, new ECB(8, 20),\n                new ECB(4, 21)),\n            new ECBlocks(22, new ECB(12, 11),\n                new ECB(4, 12))),\n        new Version(14, new int[]{6, 26, 46, 66},\n            new ECBlocks(30, new ECB(3, 115),\n                new ECB(1, 116)),\n            new ECBlocks(24, new ECB(4, 40),\n                new ECB(5, 41)),\n            new ECBlocks(20, new ECB(11, 16),\n                new ECB(5, 17)),\n            new ECBlocks(24, new ECB(11, 12),\n                new ECB(5, 13))),\n        new Version(15, new int[]{6, 26, 48, 70},\n            new ECBlocks(22, new ECB(5, 87),\n                new ECB(1, 88)),\n            new ECBlocks(24, new ECB(5, 41),\n                new ECB(5, 42)),\n            new ECBlocks(30, new ECB(5, 24),\n                new ECB(7, 25)),\n            new ECBlocks(24, new ECB(11, 12),\n                new ECB(7, 13))),\n        new Version(16, new int[]{6, 26, 50, 74},\n            new ECBlocks(24, new ECB(5, 98),\n                new ECB(1, 99)),\n            new ECBlocks(28, new ECB(7, 45),\n                new ECB(3, 46)),\n            new ECBlocks(24, new ECB(15, 19),\n                new ECB(2, 20)),\n            new ECBlocks(30, new ECB(3, 15),\n                new ECB(13, 16))),\n        new Version(17, new int[]{6, 30, 54, 78},\n            new ECBlocks(28, new ECB(1, 107),\n                new ECB(5, 108)),\n            new ECBlocks(28, new ECB(10, 46),\n                new ECB(1, 47)),\n            new ECBlocks(28, new ECB(1, 22),\n                new ECB(15, 23)),\n            new ECBlocks(28, new ECB(2, 14),\n                new ECB(17, 15))),\n        new Version(18, new int[]{6, 30, 56, 82},\n            new ECBlocks(30, new ECB(5, 120),\n                new ECB(1, 121)),\n            new ECBlocks(26, new ECB(9, 43),\n                new ECB(4, 44)),\n            new ECBlocks(28, new ECB(17, 22),\n                new ECB(1, 23)),\n            new ECBlocks(28, new ECB(2, 14),\n                new ECB(19, 15))),\n        new Version(19, new int[]{6, 30, 58, 86},\n            new ECBlocks(28, new ECB(3, 113),\n                new ECB(4, 114)),\n            new ECBlocks(26, new ECB(3, 44),\n                new ECB(11, 45)),\n            new ECBlocks(26, new ECB(17, 21),\n                new ECB(4, 22)),\n            new ECBlocks(26, new ECB(9, 13),\n                new ECB(16, 14))),\n        new Version(20, new int[]{6, 34, 62, 90},\n            new ECBlocks(28, new ECB(3, 107),\n                new ECB(5, 108)),\n            new ECBlocks(26, new ECB(3, 41),\n                new ECB(13, 42)),\n            new ECBlocks(30, new ECB(15, 24),\n                new ECB(5, 25)),\n            new ECBlocks(28, new ECB(15, 15),\n                new ECB(10, 16))),\n        new Version(21, new int[]{6, 28, 50, 72, 94},\n            new ECBlocks(28, new ECB(4, 116),\n                new ECB(4, 117)),\n            new ECBlocks(26, new ECB(17, 42)),\n            new ECBlocks(28, new ECB(17, 22),\n                new ECB(6, 23)),\n            new ECBlocks(30, new ECB(19, 16),\n                new ECB(6, 17))),\n        new Version(22, new int[]{6, 26, 50, 74, 98},\n            new ECBlocks(28, new ECB(2, 111),\n                new ECB(7, 112)),\n            new ECBlocks(28, new ECB(17, 46)),\n            new ECBlocks(30, new ECB(7, 24),\n                new ECB(16, 25)),\n            new ECBlocks(24, new ECB(34, 13))),\n        new Version(23, new int[]{6, 30, 54, 78, 102},\n            new ECBlocks(30, new ECB(4, 121),\n                new ECB(5, 122)),\n            new ECBlocks(28, new ECB(4, 47),\n                new ECB(14, 48)),\n            new ECBlocks(30, new ECB(11, 24),\n                new ECB(14, 25)),\n            new ECBlocks(30, new ECB(16, 15),\n                new ECB(14, 16))),\n        new Version(24, new int[]{6, 28, 54, 80, 106},\n            new ECBlocks(30, new ECB(6, 117),\n                new ECB(4, 118)),\n            new ECBlocks(28, new ECB(6, 45),\n                new ECB(14, 46)),\n            new ECBlocks(30, new ECB(11, 24),\n                new ECB(16, 25)),\n            new ECBlocks(30, new ECB(30, 16),\n                new ECB(2, 17))),\n        new Version(25, new int[]{6, 32, 58, 84, 110},\n            new ECBlocks(26, new ECB(8, 106),\n                new ECB(4, 107)),\n            new ECBlocks(28, new ECB(8, 47),\n                new ECB(13, 48)),\n            new ECBlocks(30, new ECB(7, 24),\n                new ECB(22, 25)),\n            new ECBlocks(30, new ECB(22, 15),\n                new ECB(13, 16))),\n        new Version(26, new int[]{6, 30, 58, 86, 114},\n            new ECBlocks(28, new ECB(10, 114),\n                new ECB(2, 115)),\n            new ECBlocks(28, new ECB(19, 46),\n                new ECB(4, 47)),\n            new ECBlocks(28, new ECB(28, 22),\n                new ECB(6, 23)),\n            new ECBlocks(30, new ECB(33, 16),\n                new ECB(4, 17))),\n        new Version(27, new int[]{6, 34, 62, 90, 118},\n            new ECBlocks(30, new ECB(8, 122),\n                new ECB(4, 123)),\n            new ECBlocks(28, new ECB(22, 45),\n                new ECB(3, 46)),\n            new ECBlocks(30, new ECB(8, 23),\n                new ECB(26, 24)),\n            new ECBlocks(30, new ECB(12, 15),\n                new ECB(28, 16))),\n        new Version(28, new int[]{6, 26, 50, 74, 98, 122},\n            new ECBlocks(30, new ECB(3, 117),\n                new ECB(10, 118)),\n            new ECBlocks(28, new ECB(3, 45),\n                new ECB(23, 46)),\n            new ECBlocks(30, new ECB(4, 24),\n                new ECB(31, 25)),\n            new ECBlocks(30, new ECB(11, 15),\n                new ECB(31, 16))),\n        new Version(29, new int[]{6, 30, 54, 78, 102, 126},\n            new ECBlocks(30, new ECB(7, 116),\n                new ECB(7, 117)),\n            new ECBlocks(28, new ECB(21, 45),\n                new ECB(7, 46)),\n            new ECBlocks(30, new ECB(1, 23),\n                new ECB(37, 24)),\n            new ECBlocks(30, new ECB(19, 15),\n                new ECB(26, 16))),\n        new Version(30, new int[]{6, 26, 52, 78, 104, 130},\n            new ECBlocks(30, new ECB(5, 115),\n                new ECB(10, 116)),\n            new ECBlocks(28, new ECB(19, 47),\n                new ECB(10, 48)),\n            new ECBlocks(30, new ECB(15, 24),\n                new ECB(25, 25)),\n            new ECBlocks(30, new ECB(23, 15),\n                new ECB(25, 16))),\n        new Version(31, new int[]{6, 30, 56, 82, 108, 134},\n            new ECBlocks(30, new ECB(13, 115),\n                new ECB(3, 116)),\n            new ECBlocks(28, new ECB(2, 46),\n                new ECB(29, 47)),\n            new ECBlocks(30, new ECB(42, 24),\n                new ECB(1, 25)),\n            new ECBlocks(30, new ECB(23, 15),\n                new ECB(28, 16))),\n        new Version(32, new int[]{6, 34, 60, 86, 112, 138},\n            new ECBlocks(30, new ECB(17, 115)),\n            new ECBlocks(28, new ECB(10, 46),\n                new ECB(23, 47)),\n            new ECBlocks(30, new ECB(10, 24),\n                new ECB(35, 25)),\n            new ECBlocks(30, new ECB(19, 15),\n                new ECB(35, 16))),\n        new Version(33, new int[]{6, 30, 58, 86, 114, 142},\n            new ECBlocks(30, new ECB(17, 115),\n                new ECB(1, 116)),\n            new ECBlocks(28, new ECB(14, 46),\n                new ECB(21, 47)),\n            new ECBlocks(30, new ECB(29, 24),\n                new ECB(19, 25)),\n            new ECBlocks(30, new ECB(11, 15),\n                new ECB(46, 16))),\n        new Version(34, new int[]{6, 34, 62, 90, 118, 146},\n            new ECBlocks(30, new ECB(13, 115),\n                new ECB(6, 116)),\n            new ECBlocks(28, new ECB(14, 46),\n                new ECB(23, 47)),\n            new ECBlocks(30, new ECB(44, 24),\n                new ECB(7, 25)),\n            new ECBlocks(30, new ECB(59, 16),\n                new ECB(1, 17))),\n        new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150},\n            new ECBlocks(30, new ECB(12, 121),\n                new ECB(7, 122)),\n            new ECBlocks(28, new ECB(12, 47),\n                new ECB(26, 48)),\n            new ECBlocks(30, new ECB(39, 24),\n                new ECB(14, 25)),\n            new ECBlocks(30, new ECB(22, 15),\n                new ECB(41, 16))),\n        new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154},\n            new ECBlocks(30, new ECB(6, 121),\n                new ECB(14, 122)),\n            new ECBlocks(28, new ECB(6, 47),\n                new ECB(34, 48)),\n            new ECBlocks(30, new ECB(46, 24),\n                new ECB(10, 25)),\n            new ECBlocks(30, new ECB(2, 15),\n                new ECB(64, 16))),\n        new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158},\n            new ECBlocks(30, new ECB(17, 122),\n                new ECB(4, 123)),\n            new ECBlocks(28, new ECB(29, 46),\n                new ECB(14, 47)),\n            new ECBlocks(30, new ECB(49, 24),\n                new ECB(10, 25)),\n            new ECBlocks(30, new ECB(24, 15),\n                new ECB(46, 16))),\n        new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162},\n            new ECBlocks(30, new ECB(4, 122),\n                new ECB(18, 123)),\n            new ECBlocks(28, new ECB(13, 46),\n                new ECB(32, 47)),\n            new ECBlocks(30, new ECB(48, 24),\n                new ECB(14, 25)),\n            new ECBlocks(30, new ECB(42, 15),\n                new ECB(32, 16))),\n        new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166},\n            new ECBlocks(30, new ECB(20, 117),\n                new ECB(4, 118)),\n            new ECBlocks(28, new ECB(40, 47),\n                new ECB(7, 48)),\n            new ECBlocks(30, new ECB(43, 24),\n                new ECB(22, 25)),\n            new ECBlocks(30, new ECB(10, 15),\n                new ECB(67, 16))),\n        new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170},\n            new ECBlocks(30, new ECB(19, 118),\n                new ECB(6, 119)),\n            new ECBlocks(28, new ECB(18, 47),\n                new ECB(31, 48)),\n            new ECBlocks(30, new ECB(34, 24),\n                new ECB(34, 25)),\n            new ECBlocks(30, new ECB(20, 15),\n                new ECB(61, 16)))\n    };\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/detector/AlignmentPattern.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.detector;\n\nimport com.google.zxing.ResultPoint;\n\n/**\n * <p>Encapsulates an alignment pattern, which are the smaller square patterns found in\n * all but the simplest QR Codes.</p>\n *\n * @author Sean Owen\n */\npublic final class AlignmentPattern extends ResultPoint {\n\n  private final float estimatedModuleSize;\n\n  AlignmentPattern(float posX, float posY, float estimatedModuleSize) {\n    super(posX, posY);\n    this.estimatedModuleSize = estimatedModuleSize;\n  }\n\n  /**\n   * <p>Determines if this alignment pattern \"about equals\" an alignment pattern at the stated\n   * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>\n   */\n  boolean aboutEquals(float moduleSize, float i, float j) {\n    if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {\n      float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);\n      return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize;\n    }\n    return false;\n  }\n\n  /**\n   * Combines this object's current estimate of a finder pattern position and module size\n   * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.\n   */\n  AlignmentPattern combineEstimate(float i, float j, float newModuleSize) {\n    float combinedX = (getX() + j) / 2.0f;\n    float combinedY = (getY() + i) / 2.0f;\n    float combinedModuleSize = (estimatedModuleSize + newModuleSize) / 2.0f;\n    return new AlignmentPattern(combinedX, combinedY, combinedModuleSize);\n  }\n\n}"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.detector;\n\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder\n * patterns but are smaller and appear at regular intervals throughout the image.</p>\n *\n * <p>At the moment this only looks for the bottom-right alignment pattern.</p>\n *\n * <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,\n * pasted and stripped down here for maximum performance but does unfortunately duplicate\n * some code.</p>\n *\n * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p>\n *\n * @author Sean Owen\n */\nfinal class AlignmentPatternFinder {\n\n  private final BitMatrix image;\n  private final List<AlignmentPattern> possibleCenters;\n  private final int startX;\n  private final int startY;\n  private final int width;\n  private final int height;\n  private final float moduleSize;\n  private final int[] crossCheckStateCount;\n  private final ResultPointCallback resultPointCallback;\n\n  /**\n   * <p>Creates a finder that will look in a portion of the whole image.</p>\n   *\n   * @param image image to search\n   * @param startX left column from which to start searching\n   * @param startY top row from which to start searching\n   * @param width width of region to search\n   * @param height height of region to search\n   * @param moduleSize estimated module size so far\n   */\n  AlignmentPatternFinder(BitMatrix image,\n                         int startX,\n                         int startY,\n                         int width,\n                         int height,\n                         float moduleSize,\n                         ResultPointCallback resultPointCallback) {\n    this.image = image;\n    this.possibleCenters = new ArrayList<>(5);\n    this.startX = startX;\n    this.startY = startY;\n    this.width = width;\n    this.height = height;\n    this.moduleSize = moduleSize;\n    this.crossCheckStateCount = new int[3];\n    this.resultPointCallback = resultPointCallback;\n  }\n\n  /**\n   * <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since\n   * it's pretty performance-critical and so is written to be fast foremost.</p>\n   *\n   * @return {@link AlignmentPattern} if found\n   * @throws NotFoundException if not found\n   */\n  AlignmentPattern find() throws NotFoundException {\n    int startX = this.startX;\n    int height = this.height;\n    int maxJ = startX + width;\n    int middleI = startY + (height / 2);\n    // We are looking for black/white/black modules in 1:1:1 ratio;\n    // this tracks the number of black/white/black modules seen so far\n    int[] stateCount = new int[3];\n    for (int iGen = 0; iGen < height; iGen++) {\n      // Search from middle outwards\n      int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) / 2 : -((iGen + 1) / 2));\n      stateCount[0] = 0;\n      stateCount[1] = 0;\n      stateCount[2] = 0;\n      int j = startX;\n      // Burn off leading white pixels before anything else; if we start in the middle of\n      // a white run, it doesn't make sense to count its length, since we don't know if the\n      // white run continued to the left of the start point\n      while (j < maxJ && !image.get(j, i)) {\n        j++;\n      }\n      int currentState = 0;\n      while (j < maxJ) {\n        if (image.get(j, i)) {\n          // Black pixel\n          if (currentState == 1) { // Counting black pixels\n            stateCount[1]++;\n          } else { // Counting white pixels\n            if (currentState == 2) { // A winner?\n              if (foundPatternCross(stateCount)) { // Yes\n                AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j);\n                if (confirmed != null) {\n                  return confirmed;\n                }\n              }\n              stateCount[0] = stateCount[2];\n              stateCount[1] = 1;\n              stateCount[2] = 0;\n              currentState = 1;\n            } else {\n              stateCount[++currentState]++;\n            }\n          }\n        } else { // White pixel\n          if (currentState == 1) { // Counting black pixels\n            currentState++;\n          }\n          stateCount[currentState]++;\n        }\n        j++;\n      }\n      if (foundPatternCross(stateCount)) {\n        AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ);\n        if (confirmed != null) {\n          return confirmed;\n        }\n      }\n\n    }\n\n    // Hmm, nothing we saw was observed and confirmed twice. If we had\n    // any guess at all, return it.\n    if (!possibleCenters.isEmpty()) {\n      return possibleCenters.get(0);\n    }\n\n    throw NotFoundException.getNotFoundInstance();\n  }\n\n  /**\n   * Given a count of black/white/black pixels just seen and an end position,\n   * figures the location of the center of this black/white/black run.\n   */\n  private static float centerFromEnd(int[] stateCount, int end) {\n    return (end - stateCount[2]) - stateCount[1] / 2.0f;\n  }\n\n  /**\n   * @param stateCount count of black/white/black pixels just read\n   * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios\n   *         used by alignment patterns to be considered a match\n   */\n  private boolean foundPatternCross(int[] stateCount) {\n    float moduleSize = this.moduleSize;\n    float maxVariance = moduleSize / 2.0f;\n    for (int i = 0; i < 3; i++) {\n      if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  /**\n   * <p>After a horizontal scan finds a potential alignment pattern, this method\n   * \"cross-checks\" by scanning down vertically through the center of the possible\n   * alignment pattern to see if the same proportion is detected.</p>\n   *\n   * @param startI row where an alignment pattern was detected\n   * @param centerJ center of the section that appears to cross an alignment pattern\n   * @param maxCount maximum reasonable number of modules that should be\n   * observed in any reading state, based on the results of the horizontal scan\n   * @return vertical center of alignment pattern, or {@link Float#NaN} if not found\n   */\n  private float crossCheckVertical(int startI, int centerJ, int maxCount,\n      int originalStateCountTotal) {\n    BitMatrix image = this.image;\n\n    int maxI = image.getHeight();\n    int[] stateCount = crossCheckStateCount;\n    stateCount[0] = 0;\n    stateCount[1] = 0;\n    stateCount[2] = 0;\n\n    // Start counting up from center\n    int i = startI;\n    while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) {\n      stateCount[1]++;\n      i--;\n    }\n    // If already too many modules in this state or ran off the edge:\n    if (i < 0 || stateCount[1] > maxCount) {\n      return Float.NaN;\n    }\n    while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) {\n      stateCount[0]++;\n      i--;\n    }\n    if (stateCount[0] > maxCount) {\n      return Float.NaN;\n    }\n\n    // Now also count down from center\n    i = startI + 1;\n    while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) {\n      stateCount[1]++;\n      i++;\n    }\n    if (i == maxI || stateCount[1] > maxCount) {\n      return Float.NaN;\n    }\n    while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) {\n      stateCount[2]++;\n      i++;\n    }\n    if (stateCount[2] > maxCount) {\n      return Float.NaN;\n    }\n\n    int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];\n    if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {\n      return Float.NaN;\n    }\n\n    return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;\n  }\n\n  /**\n   * <p>This is called when a horizontal scan finds a possible alignment pattern. It will\n   * cross check with a vertical scan, and if successful, will see if this pattern had been\n   * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have\n   * found the alignment pattern.</p>\n   *\n   * @param stateCount reading state module counts from horizontal scan\n   * @param i row where alignment pattern may be found\n   * @param j end of possible alignment pattern in row\n   * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not\n   */\n  private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) {\n    int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];\n    float centerJ = centerFromEnd(stateCount, j);\n    float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal);\n    if (!Float.isNaN(centerI)) {\n      float estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;\n      for (AlignmentPattern center : possibleCenters) {\n        // Look for about the same center and module size:\n        if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {\n          return center.combineEstimate(centerI, centerJ, estimatedModuleSize);\n        }\n      }\n      // Hadn't found this before; save it\n      AlignmentPattern point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);\n      possibleCenters.add(point);\n      if (resultPointCallback != null) {\n        resultPointCallback.foundPossibleResultPoint(point);\n      }\n    }\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/detector/Detector.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.detector;\n\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.common.BitMatrix;\nimport com.google.zxing.common.DetectorResult;\nimport com.google.zxing.common.GridSampler;\nimport com.google.zxing.common.PerspectiveTransform;\nimport com.google.zxing.common.detector.MathUtils;\nimport com.google.zxing.qrcode.decoder.Version;\n\nimport java.util.Map;\n\n/**\n * <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code\n * is rotated or skewed, or partially obscured.</p>\n *\n * @author Sean Owen\n */\npublic class Detector {\n\n  private final BitMatrix image;\n  private ResultPointCallback resultPointCallback;\n\n  public Detector(BitMatrix image) {\n    this.image = image;\n  }\n\n  protected final BitMatrix getImage() {\n    return image;\n  }\n\n  protected final ResultPointCallback getResultPointCallback() {\n    return resultPointCallback;\n  }\n\n  /**\n   * <p>Detects a QR Code in an image.</p>\n   *\n   * @return {@link DetectorResult} encapsulating results of detecting a QR Code\n   * @throws NotFoundException if QR Code cannot be found\n   * @throws FormatException if a QR Code cannot be decoded\n   */\n  public DetectorResult detect() throws NotFoundException, FormatException {\n    return detect(null);\n  }\n\n  /**\n   * <p>Detects a QR Code in an image.</p>\n   *\n   * @param hints optional hints to detector\n   * @return {@link DetectorResult} encapsulating results of detecting a QR Code\n   * @throws NotFoundException if QR Code cannot be found\n   * @throws FormatException if a QR Code cannot be decoded\n   */\n  public final DetectorResult detect(Map<DecodeHintType,?> hints) throws NotFoundException, FormatException {\n\n    resultPointCallback = hints == null ? null :\n        (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);\n\n    FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback);\n    FinderPatternInfo info = finder.find(hints);\n\n    return processFinderPatternInfo(info);\n  }\n\n  protected final DetectorResult processFinderPatternInfo(FinderPatternInfo info)\n      throws NotFoundException, FormatException {\n\n    FinderPattern topLeft = info.getTopLeft();\n    FinderPattern topRight = info.getTopRight();\n    FinderPattern bottomLeft = info.getBottomLeft();\n\n    float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);\n    if (moduleSize < 1.0f) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n    int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);\n    Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension);\n    int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7;\n\n    AlignmentPattern alignmentPattern = null;\n    // Anything above version 1 has an alignment pattern\n    if (provisionalVersion.getAlignmentPatternCenters().length > 0) {\n\n      // Guess where a \"bottom right\" finder pattern would have been\n      float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX();\n      float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY();\n\n      // Estimate that alignment pattern is closer by 3 modules\n      // from \"bottom right\" to known top left location\n      float correctionToTopLeft = 1.0f - 3.0f / modulesBetweenFPCenters;\n      int estAlignmentX = (int) (topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX()));\n      int estAlignmentY = (int) (topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY()));\n\n      // Kind of arbitrary -- expand search radius before giving up\n      for (int i = 4; i <= 16; i <<= 1) {\n        try {\n          alignmentPattern = findAlignmentInRegion(moduleSize,\n              estAlignmentX,\n              estAlignmentY,\n              i);\n          break;\n        } catch (NotFoundException re) {\n          // try next round\n        }\n      }\n      // If we didn't find alignment pattern... well try anyway without it\n    }\n\n    PerspectiveTransform transform =\n        createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);\n\n    BitMatrix bits = sampleGrid(image, transform, dimension);\n\n    ResultPoint[] points;\n    if (alignmentPattern == null) {\n      points = new ResultPoint[]{bottomLeft, topLeft, topRight};\n    } else {\n      points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};\n    }\n    return new DetectorResult(bits, points);\n  }\n\n  private static PerspectiveTransform createTransform(ResultPoint topLeft,\n                                                      ResultPoint topRight,\n                                                      ResultPoint bottomLeft,\n                                                      ResultPoint alignmentPattern,\n                                                      int dimension) {\n    float dimMinusThree = dimension - 3.5f;\n    float bottomRightX;\n    float bottomRightY;\n    float sourceBottomRightX;\n    float sourceBottomRightY;\n    if (alignmentPattern != null) {\n      bottomRightX = alignmentPattern.getX();\n      bottomRightY = alignmentPattern.getY();\n      sourceBottomRightX = dimMinusThree - 3.0f;\n      sourceBottomRightY = sourceBottomRightX;\n    } else {\n      // Don't have an alignment pattern, just make up the bottom-right point\n      bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX();\n      bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY();\n      sourceBottomRightX = dimMinusThree;\n      sourceBottomRightY = dimMinusThree;\n    }\n\n    return PerspectiveTransform.quadrilateralToQuadrilateral(\n        3.5f,\n        3.5f,\n        dimMinusThree,\n        3.5f,\n        sourceBottomRightX,\n        sourceBottomRightY,\n        3.5f,\n        dimMinusThree,\n        topLeft.getX(),\n        topLeft.getY(),\n        topRight.getX(),\n        topRight.getY(),\n        bottomRightX,\n        bottomRightY,\n        bottomLeft.getX(),\n        bottomLeft.getY());\n  }\n\n  private static BitMatrix sampleGrid(BitMatrix image,\n                                      PerspectiveTransform transform,\n                                      int dimension) throws NotFoundException {\n\n    GridSampler sampler = GridSampler.getInstance();\n    return sampler.sampleGrid(image, dimension, dimension, transform);\n  }\n\n  /**\n   * <p>Computes the dimension (number of modules on a size) of the QR Code based on the position\n   * of the finder patterns and estimated module size.</p>\n   */\n  private static int computeDimension(ResultPoint topLeft,\n                                      ResultPoint topRight,\n                                      ResultPoint bottomLeft,\n                                      float moduleSize) throws NotFoundException {\n    int tltrCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleSize);\n    int tlblCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize);\n    int dimension = ((tltrCentersDimension + tlblCentersDimension) / 2) + 7;\n    switch (dimension & 0x03) { // mod 4\n      case 0:\n        dimension++;\n        break;\n        // 1? do nothing\n      case 2:\n        dimension--;\n        break;\n      case 3:\n        throw NotFoundException.getNotFoundInstance();\n    }\n    return dimension;\n  }\n\n  /**\n   * <p>Computes an average estimated module size based on estimated derived from the positions\n   * of the three finder patterns.</p>\n   *\n   * @param topLeft detected top-left finder pattern center\n   * @param topRight detected top-right finder pattern center\n   * @param bottomLeft detected bottom-left finder pattern center\n   * @return estimated module size\n   */\n  protected final float calculateModuleSize(ResultPoint topLeft,\n                                            ResultPoint topRight,\n                                            ResultPoint bottomLeft) {\n    // Take the average\n    return (calculateModuleSizeOneWay(topLeft, topRight) +\n        calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;\n  }\n\n  /**\n   * <p>Estimates module size based on two finder patterns -- it uses\n   * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the\n   * width of each, measuring along the axis between their centers.</p>\n   */\n  private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) {\n    float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.getX(),\n        (int) pattern.getY(),\n        (int) otherPattern.getX(),\n        (int) otherPattern.getY());\n    float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.getX(),\n        (int) otherPattern.getY(),\n        (int) pattern.getX(),\n        (int) pattern.getY());\n    if (Float.isNaN(moduleSizeEst1)) {\n      return moduleSizeEst2 / 7.0f;\n    }\n    if (Float.isNaN(moduleSizeEst2)) {\n      return moduleSizeEst1 / 7.0f;\n    }\n    // Average them, and divide by 7 since we've counted the width of 3 black modules,\n    // and 1 white and 1 black module on either side. Ergo, divide sum by 14.\n    return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;\n  }\n\n  /**\n   * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of\n   * a finder pattern by looking for a black-white-black run from the center in the direction\n   * of another point (another finder pattern center), and in the opposite direction too.\n   */\n  private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {\n\n    float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);\n\n    // Now count other way -- don't run off image though of course\n    float scale = 1.0f;\n    int otherToX = fromX - (toX - fromX);\n    if (otherToX < 0) {\n      scale = fromX / (float) (fromX - otherToX);\n      otherToX = 0;\n    } else if (otherToX >= image.getWidth()) {\n      scale = (image.getWidth() - 1 - fromX) / (float) (otherToX - fromX);\n      otherToX = image.getWidth() - 1;\n    }\n    int otherToY = (int) (fromY - (toY - fromY) * scale);\n\n    scale = 1.0f;\n    if (otherToY < 0) {\n      scale = fromY / (float) (fromY - otherToY);\n      otherToY = 0;\n    } else if (otherToY >= image.getHeight()) {\n      scale = (image.getHeight() - 1 - fromY) / (float) (otherToY - fromY);\n      otherToY = image.getHeight() - 1;\n    }\n    otherToX = (int) (fromX + (otherToX - fromX) * scale);\n\n    result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);\n\n    // Middle pixel is double-counted this way; subtract 1\n    return result - 1.0f;\n  }\n\n  /**\n   * <p>This method traces a line from a point in the image, in the direction towards another point.\n   * It begins in a black region, and keeps going until it finds white, then black, then white again.\n   * It reports the distance from the start to this point.</p>\n   *\n   * <p>This is used when figuring out how wide a finder pattern is, when the finder pattern\n   * may be skewed or rotated.</p>\n   */\n  private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) {\n    // Mild variant of Bresenham's algorithm;\n    // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm\n    boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);\n    if (steep) {\n      int temp = fromX;\n      fromX = fromY;\n      fromY = temp;\n      temp = toX;\n      toX = toY;\n      toY = temp;\n    }\n\n    int dx = Math.abs(toX - fromX);\n    int dy = Math.abs(toY - fromY);\n    int error = -dx / 2;\n    int xstep = fromX < toX ? 1 : -1;\n    int ystep = fromY < toY ? 1 : -1;\n\n    // In black pixels, looking for white, first or second time.\n    int state = 0;\n    // Loop up until x == toX, but not beyond\n    int xLimit = toX + xstep;\n    for (int x = fromX, y = fromY; x != xLimit; x += xstep) {\n      int realX = steep ? y : x;\n      int realY = steep ? x : y;\n\n      // Does current pixel mean we have moved white to black or vice versa?\n      // Scanning black in state 0,2 and white in state 1, so if we find the wrong\n      // color, advance to next state or end if we are in state 2 already\n      if ((state == 1) == image.get(realX, realY)) {\n        if (state == 2) {\n          return MathUtils.distance(x, y, fromX, fromY);\n        }\n        state++;\n      }\n\n      error += dy;\n      if (error > 0) {\n        if (y == toY) {\n          break;\n        }\n        y += ystep;\n        error -= dx;\n      }\n    }\n    // Found black-white-black; give the benefit of the doubt that the next pixel outside the image\n    // is \"white\" so this last point at (toX+xStep,toY) is the right ending. This is really a\n    // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.\n    if (state == 2) {\n      return MathUtils.distance(toX + xstep, toY, fromX, fromY);\n    }\n    // else we didn't find even black-white-black; no estimate is really possible\n    return Float.NaN;\n  }\n\n  /**\n   * <p>Attempts to locate an alignment pattern in a limited region of the image, which is\n   * guessed to contain it. This method uses {@link AlignmentPattern}.</p>\n   *\n   * @param overallEstModuleSize estimated module size so far\n   * @param estAlignmentX x coordinate of center of area probably containing alignment pattern\n   * @param estAlignmentY y coordinate of above\n   * @param allowanceFactor number of pixels in all directions to search from the center\n   * @return {@link AlignmentPattern} if found, or null otherwise\n   * @throws NotFoundException if an unexpected error occurs during detection\n   */\n  protected final AlignmentPattern findAlignmentInRegion(float overallEstModuleSize,\n                                                         int estAlignmentX,\n                                                         int estAlignmentY,\n                                                         float allowanceFactor)\n      throws NotFoundException {\n    // Look for an alignment pattern (3 modules in size) around where it\n    // should be\n    int allowance = (int) (allowanceFactor * overallEstModuleSize);\n    int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);\n    int alignmentAreaRightX = Math.min(image.getWidth() - 1, estAlignmentX + allowance);\n    if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);\n    int alignmentAreaBottomY = Math.min(image.getHeight() - 1, estAlignmentY + allowance);\n    if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) {\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    AlignmentPatternFinder alignmentFinder =\n        new AlignmentPatternFinder(\n            image,\n            alignmentAreaLeftX,\n            alignmentAreaTopY,\n            alignmentAreaRightX - alignmentAreaLeftX,\n            alignmentAreaBottomY - alignmentAreaTopY,\n            overallEstModuleSize,\n            resultPointCallback);\n    return alignmentFinder.find();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/detector/FinderPattern.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.detector;\n\nimport com.google.zxing.ResultPoint;\n\n/**\n * <p>Encapsulates a finder pattern, which are the three square patterns found in\n * the corners of QR Codes. It also encapsulates a count of similar finder patterns,\n * as a convenience to the finder's bookkeeping.</p>\n *\n * @author Sean Owen\n */\npublic final class FinderPattern extends ResultPoint {\n\n  private final float estimatedModuleSize;\n  private final int count;\n\n  FinderPattern(float posX, float posY, float estimatedModuleSize) {\n    this(posX, posY, estimatedModuleSize, 1);\n  }\n\n  private FinderPattern(float posX, float posY, float estimatedModuleSize, int count) {\n    super(posX, posY);\n    this.estimatedModuleSize = estimatedModuleSize;\n    this.count = count;\n  }\n\n  public float getEstimatedModuleSize() {\n    return estimatedModuleSize;\n  }\n\n  int getCount() {\n    return count;\n  }\n\n  /*\n  void incrementCount() {\n    this.count++;\n  }\n   */\n\n  /**\n   * <p>Determines if this finder pattern \"about equals\" a finder pattern at the stated\n   * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>\n   */\n  boolean aboutEquals(float moduleSize, float i, float j) {\n    if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {\n      float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);\n      return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize;\n    }\n    return false;\n  }\n\n  /**\n   * Combines this object's current estimate of a finder pattern position and module size\n   * with a new estimate. It returns a new {@code FinderPattern} containing a weighted average\n   * based on count.\n   */\n  FinderPattern combineEstimate(float i, float j, float newModuleSize) {\n    int combinedCount = count + 1;\n    float combinedX = (count * getX() + j) / combinedCount;\n    float combinedY = (count * getY() + i) / combinedCount;\n    float combinedModuleSize = (count * estimatedModuleSize + newModuleSize) / combinedCount;\n    return new FinderPattern(combinedX, combinedY, combinedModuleSize, combinedCount);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.detector;\n\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.ResultPointCallback;\nimport com.google.zxing.common.BitMatrix;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square\n * markers at three corners of a QR Code.</p>\n *\n * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.\n *\n * @author Sean Owen\n */\npublic class FinderPatternFinder {\n\n  private static final int CENTER_QUORUM = 2;\n  protected static final int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center\n  protected static final int MAX_MODULES = 97; // support up to version 20 for mobile clients\n\n  private final BitMatrix image;\n  private final List<FinderPattern> possibleCenters;\n  private boolean hasSkipped;\n  private final int[] crossCheckStateCount;\n  private final ResultPointCallback resultPointCallback;\n\n  /**\n   * <p>Creates a finder that will search the image for three finder patterns.</p>\n   *\n   * @param image image to search\n   */\n  public FinderPatternFinder(BitMatrix image) {\n    this(image, null);\n  }\n\n  public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {\n    this.image = image;\n    this.possibleCenters = new ArrayList<>();\n    this.crossCheckStateCount = new int[5];\n    this.resultPointCallback = resultPointCallback;\n  }\n\n  protected final BitMatrix getImage() {\n    return image;\n  }\n\n  protected final List<FinderPattern> getPossibleCenters() {\n    return possibleCenters;\n  }\n\n  final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException {\n    boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);\n    int maxI = image.getHeight();\n    int maxJ = image.getWidth();\n    // We are looking for black/white/black/white/black modules in\n    // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far\n\n    // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the\n    // image, and then account for the center being 3 modules in size. This gives the smallest\n    // number of pixels the center could be, so skip this often. When trying harder, look for all\n    // QR versions regardless of how dense they are.\n    int iSkip = (3 * maxI) / (4 * MAX_MODULES);\n    if (iSkip < MIN_SKIP || tryHarder) {\n      iSkip = MIN_SKIP;\n    }\n\n    boolean done = false;\n    int[] stateCount = new int[5];\n    for (int i = iSkip - 1; i < maxI && !done; i += iSkip) {\n      // Get a row of black/white values\n      clearCounts(stateCount);\n      int currentState = 0;\n      for (int j = 0; j < maxJ; j++) {\n        if (image.get(j, i)) {\n          // Black pixel\n          if ((currentState & 1) == 1) { // Counting white pixels\n            currentState++;\n          }\n          stateCount[currentState]++;\n        } else { // White pixel\n          if ((currentState & 1) == 0) { // Counting black pixels\n            if (currentState == 4) { // A winner?\n              if (foundPatternCross(stateCount)) { // Yes\n                boolean confirmed = handlePossibleCenter(stateCount, i, j);\n                if (confirmed) {\n                  // Start examining every other line. Checking each line turned out to be too\n                  // expensive and didn't improve performance.\n                  iSkip = 2;\n                  if (hasSkipped) {\n                    done = haveMultiplyConfirmedCenters();\n                  } else {\n                    int rowSkip = findRowSkip();\n                    if (rowSkip > stateCount[2]) {\n                      // Skip rows between row of lower confirmed center\n                      // and top of presumed third confirmed center\n                      // but back up a bit to get a full chance of detecting\n                      // it, entire width of center of finder pattern\n\n                      // Skip by rowSkip, but back off by stateCount[2] (size of last center\n                      // of pattern we saw) to be conservative, and also back off by iSkip which\n                      // is about to be re-added\n                      i += rowSkip - stateCount[2] - iSkip;\n                      j = maxJ - 1;\n                    }\n                  }\n                } else {\n                  shiftCounts2(stateCount);\n                  currentState = 3;\n                  continue;\n                }\n                // Clear state to start looking again\n                currentState = 0;\n                clearCounts(stateCount);\n              } else { // No, shift counts back by two\n                shiftCounts2(stateCount);\n                currentState = 3;\n              }\n            } else {\n              stateCount[++currentState]++;\n            }\n          } else { // Counting white pixels\n            stateCount[currentState]++;\n          }\n        }\n      }\n      if (foundPatternCross(stateCount)) {\n        boolean confirmed = handlePossibleCenter(stateCount, i, maxJ);\n        if (confirmed) {\n          iSkip = stateCount[0];\n          if (hasSkipped) {\n            // Found a third one\n            done = haveMultiplyConfirmedCenters();\n          }\n        }\n      }\n    }\n\n    FinderPattern[] patternInfo = selectBestPatterns();\n    ResultPoint.orderBestPatterns(patternInfo);\n\n    return new FinderPatternInfo(patternInfo);\n  }\n\n  /**\n   * Given a count of black/white/black/white/black pixels just seen and an end position,\n   * figures the location of the center of this run.\n   */\n  private static float centerFromEnd(int[] stateCount, int end) {\n    return (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;\n  }\n\n  /**\n   * @param stateCount count of black/white/black/white/black pixels just read\n   * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios\n   *         used by finder patterns to be considered a match\n   */\n  protected static boolean foundPatternCross(int[] stateCount) {\n    int totalModuleSize = 0;\n    for (int i = 0; i < 5; i++) {\n      int count = stateCount[i];\n      if (count == 0) {\n        return false;\n      }\n      totalModuleSize += count;\n    }\n    if (totalModuleSize < 7) {\n      return false;\n    }\n    float moduleSize = totalModuleSize / 7.0f;\n    float maxVariance = moduleSize / 2.0f;\n    // Allow less than 50% variance from 1-1-3-1-1 proportions\n    return\n        Math.abs(moduleSize - stateCount[0]) < maxVariance &&\n        Math.abs(moduleSize - stateCount[1]) < maxVariance &&\n        Math.abs(3.0f * moduleSize - stateCount[2]) < 3 * maxVariance &&\n        Math.abs(moduleSize - stateCount[3]) < maxVariance &&\n        Math.abs(moduleSize - stateCount[4]) < maxVariance;\n  }\n\n  /**\n   * @param stateCount count of black/white/black/white/black pixels just read\n   * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios\n   *         used by finder patterns to be considered a match\n   */\n  protected static boolean foundPatternDiagonal(int[] stateCount) {\n    int totalModuleSize = 0;\n    for (int i = 0; i < 5; i++) {\n      int count = stateCount[i];\n      if (count == 0) {\n        return false;\n      }\n      totalModuleSize += count;\n    }\n    if (totalModuleSize < 7) {\n      return false;\n    }\n    float moduleSize = totalModuleSize / 7.0f;\n    float maxVariance = moduleSize / 1.333f;\n    // Allow less than 75% variance from 1-1-3-1-1 proportions\n    return\n            Math.abs(moduleSize - stateCount[0]) < maxVariance &&\n                    Math.abs(moduleSize - stateCount[1]) < maxVariance &&\n                    Math.abs(3.0f * moduleSize - stateCount[2]) < 3 * maxVariance &&\n                    Math.abs(moduleSize - stateCount[3]) < maxVariance &&\n                    Math.abs(moduleSize - stateCount[4]) < maxVariance;\n  }\n\n  private int[] getCrossCheckStateCount() {\n    clearCounts(crossCheckStateCount);\n    return crossCheckStateCount;\n  }\n\n  protected final void clearCounts(int[] counts) {\n    for (int x = 0; x < counts.length; x++) {\n      counts[x] = 0;\n    }\n  }\n\n  protected final void shiftCounts2(int[] stateCount) {\n    stateCount[0] = stateCount[2];\n    stateCount[1] = stateCount[3];\n    stateCount[2] = stateCount[4];\n    stateCount[3] = 1;\n    stateCount[4] = 0;\n  }\n\n  /**\n   * After a vertical and horizontal scan finds a potential finder pattern, this method\n   * \"cross-cross-cross-checks\" by scanning down diagonally through the center of the possible\n   * finder pattern to see if the same proportion is detected.\n   * \n   * @param centerI row where a finder pattern was detected\n   * @param centerJ center of the section that appears to cross a finder pattern\n   * @return true if proportions are withing expected limits\n   */\n  private boolean crossCheckDiagonal(int centerI, int centerJ) {\n    int[] stateCount = getCrossCheckStateCount();\n\n    // Start counting up, left from center finding black center mass\n    int i = 0;\n    while (centerI >= i && centerJ >= i && image.get(centerJ - i, centerI - i)) {\n      stateCount[2]++;\n      i++;\n    }\n    if (stateCount[2] == 0) {\n      return false;\n    }\n\n    // Continue up, left finding white space\n    while (centerI >= i && centerJ >= i && !image.get(centerJ - i, centerI - i)) {\n      stateCount[1]++;\n      i++;\n    }\n    if (stateCount[1] == 0) {\n      return false;\n    }\n\n    // Continue up, left finding black border\n    while (centerI >= i && centerJ >= i && image.get(centerJ - i, centerI - i)) {\n      stateCount[0]++;\n      i++;\n    }\n    if (stateCount[0] == 0) {\n      return false;\n    }\n\n    int maxI = image.getHeight();\n    int maxJ = image.getWidth();\n\n    // Now also count down, right from center\n    i = 1;\n    while (centerI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, centerI + i)) {\n      stateCount[2]++;\n      i++;\n    }\n\n    while (centerI + i < maxI && centerJ + i < maxJ && !image.get(centerJ + i, centerI + i)) {\n      stateCount[3]++;\n      i++;\n    }\n    if (stateCount[3] == 0) {\n      return false;\n    }\n\n    while (centerI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, centerI + i)) {\n      stateCount[4]++;\n      i++;\n    }\n    if (stateCount[4] == 0) {\n      return false;\n    }\n\n    return foundPatternDiagonal(stateCount);\n  }\n\n  /**\n   * <p>After a horizontal scan finds a potential finder pattern, this method\n   * \"cross-checks\" by scanning down vertically through the center of the possible\n   * finder pattern to see if the same proportion is detected.</p>\n   *\n   * @param startI row where a finder pattern was detected\n   * @param centerJ center of the section that appears to cross a finder pattern\n   * @param maxCount maximum reasonable number of modules that should be\n   * observed in any reading state, based on the results of the horizontal scan\n   * @return vertical center of finder pattern, or {@link Float#NaN} if not found\n   */\n  private float crossCheckVertical(int startI, int centerJ, int maxCount,\n      int originalStateCountTotal) {\n    BitMatrix image = this.image;\n\n    int maxI = image.getHeight();\n    int[] stateCount = getCrossCheckStateCount();\n\n    // Start counting up from center\n    int i = startI;\n    while (i >= 0 && image.get(centerJ, i)) {\n      stateCount[2]++;\n      i--;\n    }\n    if (i < 0) {\n      return Float.NaN;\n    }\n    while (i >= 0 && !image.get(centerJ, i) && stateCount[1] <= maxCount) {\n      stateCount[1]++;\n      i--;\n    }\n    // If already too many modules in this state or ran off the edge:\n    if (i < 0 || stateCount[1] > maxCount) {\n      return Float.NaN;\n    }\n    while (i >= 0 && image.get(centerJ, i) && stateCount[0] <= maxCount) {\n      stateCount[0]++;\n      i--;\n    }\n    if (stateCount[0] > maxCount) {\n      return Float.NaN;\n    }\n\n    // Now also count down from center\n    i = startI + 1;\n    while (i < maxI && image.get(centerJ, i)) {\n      stateCount[2]++;\n      i++;\n    }\n    if (i == maxI) {\n      return Float.NaN;\n    }\n    while (i < maxI && !image.get(centerJ, i) && stateCount[3] < maxCount) {\n      stateCount[3]++;\n      i++;\n    }\n    if (i == maxI || stateCount[3] >= maxCount) {\n      return Float.NaN;\n    }\n    while (i < maxI && image.get(centerJ, i) && stateCount[4] < maxCount) {\n      stateCount[4]++;\n      i++;\n    }\n    if (stateCount[4] >= maxCount) {\n      return Float.NaN;\n    }\n\n    // If we found a finder-pattern-like section, but its size is more than 40% different than\n    // the original, assume it's a false positive\n    int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +\n        stateCount[4];\n    if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {\n      return Float.NaN;\n    }\n\n    return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;\n  }\n\n  /**\n   * <p>Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,\n   * except it reads horizontally instead of vertically. This is used to cross-cross\n   * check a vertical cross check and locate the real center of the alignment pattern.</p>\n   */\n  private float crossCheckHorizontal(int startJ, int centerI, int maxCount,\n      int originalStateCountTotal) {\n    BitMatrix image = this.image;\n\n    int maxJ = image.getWidth();\n    int[] stateCount = getCrossCheckStateCount();\n\n    int j = startJ;\n    while (j >= 0 && image.get(j, centerI)) {\n      stateCount[2]++;\n      j--;\n    }\n    if (j < 0) {\n      return Float.NaN;\n    }\n    while (j >= 0 && !image.get(j, centerI) && stateCount[1] <= maxCount) {\n      stateCount[1]++;\n      j--;\n    }\n    if (j < 0 || stateCount[1] > maxCount) {\n      return Float.NaN;\n    }\n    while (j >= 0 && image.get(j, centerI) && stateCount[0] <= maxCount) {\n      stateCount[0]++;\n      j--;\n    }\n    if (stateCount[0] > maxCount) {\n      return Float.NaN;\n    }\n\n    j = startJ + 1;\n    while (j < maxJ && image.get(j, centerI)) {\n      stateCount[2]++;\n      j++;\n    }\n    if (j == maxJ) {\n      return Float.NaN;\n    }\n    while (j < maxJ && !image.get(j, centerI) && stateCount[3] < maxCount) {\n      stateCount[3]++;\n      j++;\n    }\n    if (j == maxJ || stateCount[3] >= maxCount) {\n      return Float.NaN;\n    }\n    while (j < maxJ && image.get(j, centerI) && stateCount[4] < maxCount) {\n      stateCount[4]++;\n      j++;\n    }\n    if (stateCount[4] >= maxCount) {\n      return Float.NaN;\n    }\n\n    // If we found a finder-pattern-like section, but its size is significantly different than\n    // the original, assume it's a false positive\n    int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +\n        stateCount[4];\n    if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {\n      return Float.NaN;\n    }\n\n    return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : Float.NaN;\n  }\n\n  /**\n   * @param stateCount reading state module counts from horizontal scan\n   * @param i row where finder pattern may be found\n   * @param j end of possible finder pattern in row\n   * @param pureBarcode ignored\n   * @return true if a finder pattern candidate was found this time\n   * @deprecated only exists for backwards compatibility\n   * @see #handlePossibleCenter(int[], int, int)\n   */\n  @Deprecated\n  protected final boolean handlePossibleCenter(int[] stateCount, int i, int j, boolean pureBarcode) {\n    return handlePossibleCenter(stateCount, i, j);\n  }\n\n  /**\n   * <p>This is called when a horizontal scan finds a possible alignment pattern. It will\n   * cross check with a vertical scan, and if successful, will, ah, cross-cross-check\n   * with another horizontal scan. This is needed primarily to locate the real horizontal\n   * center of the pattern in cases of extreme skew.\n   * And then we cross-cross-cross check with another diagonal scan.</p>\n   *\n   * <p>If that succeeds the finder pattern location is added to a list that tracks\n   * the number of times each location has been nearly-matched as a finder pattern.\n   * Each additional find is more evidence that the location is in fact a finder\n   * pattern center\n   *\n   * @param stateCount reading state module counts from horizontal scan\n   * @param i row where finder pattern may be found\n   * @param j end of possible finder pattern in row\n   * @return true if a finder pattern candidate was found this time\n   */\n  protected final boolean handlePossibleCenter(int[] stateCount, int i, int j) {\n    int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +\n        stateCount[4];\n    float centerJ = centerFromEnd(stateCount, j);\n    float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2], stateCountTotal);\n    if (!Float.isNaN(centerI)) {\n      // Re-cross check\n      centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal);\n      if (!Float.isNaN(centerJ) && crossCheckDiagonal((int) centerI, (int) centerJ)) {\n        float estimatedModuleSize = stateCountTotal / 7.0f;\n        boolean found = false;\n        for (int index = 0; index < possibleCenters.size(); index++) {\n          FinderPattern center = possibleCenters.get(index);\n          // Look for about the same center and module size:\n          if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {\n            possibleCenters.set(index, center.combineEstimate(centerI, centerJ, estimatedModuleSize));\n            found = true;\n            break;\n          }\n        }\n        if (!found) {\n          FinderPattern point = new FinderPattern(centerJ, centerI, estimatedModuleSize);\n          possibleCenters.add(point);\n          if (resultPointCallback != null) {\n            resultPointCallback.foundPossibleResultPoint(point);\n          }\n        }\n        return true;\n      }\n    }\n    return false;\n  }\n\n  /**\n   * @return number of rows we could safely skip during scanning, based on the first\n   *         two finder patterns that have been located. In some cases their position will\n   *         allow us to infer that the third pattern must lie below a certain point farther\n   *         down in the image.\n   */\n  private int findRowSkip() {\n    int max = possibleCenters.size();\n    if (max <= 1) {\n      return 0;\n    }\n    ResultPoint firstConfirmedCenter = null;\n    for (FinderPattern center : possibleCenters) {\n      if (center.getCount() >= CENTER_QUORUM) {\n        if (firstConfirmedCenter == null) {\n          firstConfirmedCenter = center;\n        } else {\n          // We have two confirmed centers\n          // How far down can we skip before resuming looking for the next\n          // pattern? In the worst case, only the difference between the\n          // difference in the x / y coordinates of the two centers.\n          // This is the case where you find top left last.\n          hasSkipped = true;\n          return (int) (Math.abs(firstConfirmedCenter.getX() - center.getX()) -\n              Math.abs(firstConfirmedCenter.getY() - center.getY())) / 2;\n        }\n      }\n    }\n    return 0;\n  }\n\n  /**\n   * @return true iff we have found at least 3 finder patterns that have been detected\n   *         at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the\n   *         candidates is \"pretty similar\"\n   */\n  private boolean haveMultiplyConfirmedCenters() {\n    int confirmedCount = 0;\n    float totalModuleSize = 0.0f;\n    int max = possibleCenters.size();\n    for (FinderPattern pattern : possibleCenters) {\n      if (pattern.getCount() >= CENTER_QUORUM) {\n        confirmedCount++;\n        totalModuleSize += pattern.getEstimatedModuleSize();\n      }\n    }\n    if (confirmedCount < 3) {\n      return false;\n    }\n    // OK, we have at least 3 confirmed centers, but, it's possible that one is a \"false positive\"\n    // and that we need to keep looking. We detect this by asking if the estimated module sizes\n    // vary too much. We arbitrarily say that when the total deviation from average exceeds\n    // 5% of the total module size estimates, it's too much.\n    float average = totalModuleSize / max;\n    float totalDeviation = 0.0f;\n    for (FinderPattern pattern : possibleCenters) {\n      totalDeviation += Math.abs(pattern.getEstimatedModuleSize() - average);\n    }\n    return totalDeviation <= 0.05f * totalModuleSize;\n  }\n\n  /**\n   * @return the 3 best {@link FinderPattern}s from our list of candidates. The \"best\" are\n   *         those that have been detected at least {@link #CENTER_QUORUM} times, and whose module\n   *         size differs from the average among those patterns the least\n   * @throws NotFoundException if 3 such finder patterns do not exist\n   */\n  private FinderPattern[] selectBestPatterns() throws NotFoundException {\n\n    int startSize = possibleCenters.size();\n    if (startSize < 3) {\n      // Couldn't find enough finder patterns\n      throw NotFoundException.getNotFoundInstance();\n    }\n\n    // Filter outlier possibilities whose module size is too different\n    if (startSize > 3) {\n      // But we can only afford to do so if we have at least 4 possibilities to choose from\n      double totalModuleSize = 0.0;\n      double square = 0.0;\n      for (FinderPattern center : possibleCenters) {\n        float size = center.getEstimatedModuleSize();\n        totalModuleSize += size;\n        square += size * size;\n      }\n      double average = totalModuleSize / startSize;\n      float stdDev = (float) Math.sqrt(square / startSize - average * average);\n\n      Collections.sort(possibleCenters, new FurthestFromAverageComparator((float) average));\n\n      float limit = Math.max(0.2f * (float) average, stdDev);\n\n      for (int i = 0; i < possibleCenters.size() && possibleCenters.size() > 3; i++) {\n        FinderPattern pattern = possibleCenters.get(i);\n        if (Math.abs(pattern.getEstimatedModuleSize() - average) > limit) {\n          possibleCenters.remove(i);\n          i--;\n        }\n      }\n    }\n\n    if (possibleCenters.size() > 3) {\n      // Throw away all but those first size candidate points we found.\n\n      float totalModuleSize = 0.0f;\n      for (FinderPattern possibleCenter : possibleCenters) {\n        totalModuleSize += possibleCenter.getEstimatedModuleSize();\n      }\n\n      float average = totalModuleSize / possibleCenters.size();\n\n      Collections.sort(possibleCenters, new CenterComparator(average));\n\n      possibleCenters.subList(3, possibleCenters.size()).clear();\n    }\n\n    return new FinderPattern[]{\n        possibleCenters.get(0),\n        possibleCenters.get(1),\n        possibleCenters.get(2)\n    };\n  }\n\n  /**\n   * <p>Orders by furthest from average</p>\n   */\n  private static final class FurthestFromAverageComparator implements Comparator<FinderPattern>, Serializable {\n    private final float average;\n    private FurthestFromAverageComparator(float f) {\n      average = f;\n    }\n    @Override\n    public int compare(FinderPattern center1, FinderPattern center2) {\n      return Float.compare(Math.abs(center2.getEstimatedModuleSize() - average),\n                           Math.abs(center1.getEstimatedModuleSize() - average));\n    }\n  }\n\n  /**\n   * <p>Orders by {@link FinderPattern#getCount()}, descending.</p>\n   */\n  private static final class CenterComparator implements Comparator<FinderPattern>, Serializable {\n    private final float average;\n    private CenterComparator(float f) {\n      average = f;\n    }\n    @Override\n    public int compare(FinderPattern center1, FinderPattern center2) {\n      int countCompare = Integer.compare(center2.getCount(), center1.getCount());\n      if (countCompare == 0) {\n        return Float.compare(Math.abs(center1.getEstimatedModuleSize() - average),\n                             Math.abs(center2.getEstimatedModuleSize() - average));\n      }\n      return countCompare;\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/detector/FinderPatternInfo.java",
    "content": "/*\n * Copyright 2007 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.detector;\n\n/**\n * <p>Encapsulates information about finder patterns in an image, including the location of\n * the three finder patterns, and their estimated module size.</p>\n *\n * @author Sean Owen\n */\npublic final class FinderPatternInfo {\n\n  private final FinderPattern bottomLeft;\n  private final FinderPattern topLeft;\n  private final FinderPattern topRight;\n\n  public FinderPatternInfo(FinderPattern[] patternCenters) {\n    this.bottomLeft = patternCenters[0];\n    this.topLeft = patternCenters[1];\n    this.topRight = patternCenters[2];\n  }\n\n  public FinderPattern getBottomLeft() {\n    return bottomLeft;\n  }\n\n  public FinderPattern getTopLeft() {\n    return topLeft;\n  }\n\n  public FinderPattern getTopRight() {\n    return topRight;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.encoder;\n\nfinal class BlockPair {\n\n  private final byte[] dataBytes;\n  private final byte[] errorCorrectionBytes;\n\n  BlockPair(byte[] data, byte[] errorCorrection) {\n    dataBytes = data;\n    errorCorrectionBytes = errorCorrection;\n  }\n\n  public byte[] getDataBytes() {\n    return dataBytes;\n  }\n\n  public byte[] getErrorCorrectionBytes() {\n    return errorCorrectionBytes;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.encoder;\n\nimport java.util.Arrays;\n\n/**\n * JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned\n * -1, 0, and 1, I'm going to use less memory and go with bytes.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class ByteMatrix {\n\n  private final byte[][] bytes;\n  private final int width;\n  private final int height;\n\n  public ByteMatrix(int width, int height) {\n    bytes = new byte[height][width];\n    this.width = width;\n    this.height = height;\n  }\n\n  public int getHeight() {\n    return height;\n  }\n\n  public int getWidth() {\n    return width;\n  }\n\n  public byte get(int x, int y) {\n    return bytes[y][x];\n  }\n\n  /**\n   * @return an internal representation as bytes, in row-major order. array[y][x] represents point (x,y)\n   */\n  public byte[][] getArray() {\n    return bytes;\n  }\n\n  public void set(int x, int y, byte value) {\n    bytes[y][x] = value;\n  }\n\n  public void set(int x, int y, int value) {\n    bytes[y][x] = (byte) value;\n  }\n\n  public void set(int x, int y, boolean value) {\n    bytes[y][x] = (byte) (value ? 1 : 0);\n  }\n\n  public void clear(byte value) {\n    for (byte[] aByte : bytes) {\n      Arrays.fill(aByte, value);\n    }\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder result = new StringBuilder(2 * width * height + 2);\n    for (int y = 0; y < height; ++y) {\n      byte[] bytesY = bytes[y];\n      for (int x = 0; x < width; ++x) {\n        switch (bytesY[x]) {\n          case 0:\n            result.append(\" 0\");\n            break;\n          case 1:\n            result.append(\" 1\");\n            break;\n          default:\n            result.append(\"  \");\n            break;\n        }\n      }\n      result.append('\\n');\n    }\n    return result.toString();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.encoder;\n\nimport com.google.zxing.EncodeHintType;\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.common.CharacterSetECI;\nimport com.google.zxing.common.reedsolomon.GenericGF;\nimport com.google.zxing.common.reedsolomon.ReedSolomonEncoder;\nimport com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;\nimport com.google.zxing.qrcode.decoder.Mode;\nimport com.google.zxing.qrcode.decoder.Version;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * @author satorux@google.com (Satoru Takabayashi) - creator\n * @author dswitkin@google.com (Daniel Switkin) - ported from C++\n */\npublic final class Encoder {\n\n  // The original table is defined in the table 5 of JISX0510:2004 (p.19).\n  private static final int[] ALPHANUMERIC_TABLE = {\n      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 0x00-0x0f\n      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 0x10-0x1f\n      36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,  // 0x20-0x2f\n      0,   1,  2,  3,  4,  5,  6,  7,  8,  9, 44, -1, -1, -1, -1, -1,  // 0x30-0x3f\n      -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  // 0x40-0x4f\n      25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,  // 0x50-0x5f\n  };\n\n  static final String DEFAULT_BYTE_MODE_ENCODING = \"ISO-8859-1\";\n\n  private Encoder() {\n  }\n\n  // The mask penalty calculation is complicated.  See Table 21 of JISX0510:2004 (p.45) for details.\n  // Basically it applies four rules and summate all penalties.\n  private static int calculateMaskPenalty(ByteMatrix matrix) {\n    return MaskUtil.applyMaskPenaltyRule1(matrix)\n        + MaskUtil.applyMaskPenaltyRule2(matrix)\n        + MaskUtil.applyMaskPenaltyRule3(matrix)\n        + MaskUtil.applyMaskPenaltyRule4(matrix);\n  }\n\n  /**\n   * @param content text to encode\n   * @param ecLevel error correction level to use\n   * @return {@link QRCode} representing the encoded QR code\n   * @throws WriterException if encoding can't succeed, because of for example invalid content\n   *   or configuration\n   */\n  public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) throws WriterException {\n    return encode(content, ecLevel, null);\n  }\n\n  public static QRCode encode(String content,\n                              ErrorCorrectionLevel ecLevel,\n                              Map<EncodeHintType,?> hints) throws WriterException {\n\n    // Determine what character encoding has been specified by the caller, if any\n    String encoding = DEFAULT_BYTE_MODE_ENCODING;\n    boolean hasEncodingHint = hints != null && hints.containsKey(EncodeHintType.CHARACTER_SET);\n    if (hasEncodingHint) {\n      encoding = hints.get(EncodeHintType.CHARACTER_SET).toString();\n    }\n\n    // Pick an encoding mode appropriate for the content. Note that this will not attempt to use\n    // multiple modes / segments even if that were more efficient. Twould be nice.\n    Mode mode = chooseMode(content, encoding);\n\n    // This will store the header information, like mode and\n    // length, as well as \"header\" segments like an ECI segment.\n    BitArray headerBits = new BitArray();\n\n    // Append ECI segment if applicable\n    if (mode == Mode.BYTE && hasEncodingHint) {\n      CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);\n      if (eci != null) {\n        appendECI(eci, headerBits);\n      }\n    }\n\n    // Append the FNC1 mode header for GS1 formatted data if applicable\n    boolean hasGS1FormatHint = hints != null && hints.containsKey(EncodeHintType.GS1_FORMAT);\n    if (hasGS1FormatHint && Boolean.valueOf(hints.get(EncodeHintType.GS1_FORMAT).toString())) {\n      // GS1 formatted codes are prefixed with a FNC1 in first position mode header\n      appendModeInfo(Mode.FNC1_FIRST_POSITION, headerBits);\n    }\n\n    // (With ECI in place,) Write the mode marker\n    appendModeInfo(mode, headerBits);\n\n    // Collect data within the main segment, separately, to count its size if needed. Don't add it to\n    // main payload yet.\n    BitArray dataBits = new BitArray();\n    appendBytes(content, mode, dataBits, encoding);\n\n    Version version;\n    if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) {\n      int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION).toString());\n      version = Version.getVersionForNumber(versionNumber);\n      int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version);\n      if (!willFit(bitsNeeded, version, ecLevel)) {\n        throw new WriterException(\"Data too big for requested version\");\n      }\n    } else {\n      version = recommendVersion(ecLevel, mode, headerBits, dataBits);\n    }\n\n    BitArray headerAndDataBits = new BitArray();\n    headerAndDataBits.appendBitArray(headerBits);\n    // Find \"length\" of main segment and write it\n    int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length();\n    appendLengthInfo(numLetters, version, mode, headerAndDataBits);\n    // Put data together into the overall payload\n    headerAndDataBits.appendBitArray(dataBits);\n\n    Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);\n    int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords();\n\n    // Terminate the bits properly.\n    terminateBits(numDataBytes, headerAndDataBits);\n\n    // Interleave data bits with error correction code.\n    BitArray finalBits = interleaveWithECBytes(headerAndDataBits,\n                                               version.getTotalCodewords(),\n                                               numDataBytes,\n                                               ecBlocks.getNumBlocks());\n\n    QRCode qrCode = new QRCode();\n\n    qrCode.setECLevel(ecLevel);\n    qrCode.setMode(mode);\n    qrCode.setVersion(version);\n\n    //  Choose the mask pattern and set to \"qrCode\".\n    int dimension = version.getDimensionForVersion();\n    ByteMatrix matrix = new ByteMatrix(dimension, dimension);\n    int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);\n    qrCode.setMaskPattern(maskPattern);\n\n    // Build the matrix and set it to \"qrCode\".\n    MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);\n    qrCode.setMatrix(matrix);\n\n    return qrCode;\n  }\n\n  /**\n   * Decides the smallest version of QR code that will contain all of the provided data.\n   *\n   * @throws WriterException if the data cannot fit in any version\n   */\n  private static Version recommendVersion(ErrorCorrectionLevel ecLevel,\n                                          Mode mode,\n                                          BitArray headerBits,\n                                          BitArray dataBits) throws WriterException {\n    // Hard part: need to know version to know how many bits length takes. But need to know how many\n    // bits it takes to know version. First we take a guess at version by assuming version will be\n    // the minimum, 1:\n    int provisionalBitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, Version.getVersionForNumber(1));\n    Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);\n\n    // Use that guess to calculate the right version. I am still not sure this works in 100% of cases.\n    int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion);\n    return chooseVersion(bitsNeeded, ecLevel);\n  }\n\n  private static int calculateBitsNeeded(Mode mode,\n                                         BitArray headerBits,\n                                         BitArray dataBits,\n                                         Version version) {\n    return headerBits.getSize() + mode.getCharacterCountBits(version) + dataBits.getSize();\n  }\n\n  /**\n   * @return the code point of the table used in alphanumeric mode or\n   *  -1 if there is no corresponding code in the table.\n   */\n  static int getAlphanumericCode(int code) {\n    if (code < ALPHANUMERIC_TABLE.length) {\n      return ALPHANUMERIC_TABLE[code];\n    }\n    return -1;\n  }\n\n  public static Mode chooseMode(String content) {\n    return chooseMode(content, null);\n  }\n\n  /**\n   * Choose the best mode by examining the content. Note that 'encoding' is used as a hint;\n   * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.\n   */\n  private static Mode chooseMode(String content, String encoding) {\n    if (\"Shift_JIS\".equals(encoding) && isOnlyDoubleByteKanji(content)) {\n      // Choose Kanji mode if all input are double-byte characters\n      return Mode.KANJI;\n    }\n    boolean hasNumeric = false;\n    boolean hasAlphanumeric = false;\n    for (int i = 0; i < content.length(); ++i) {\n      char c = content.charAt(i);\n      if (c >= '0' && c <= '9') {\n        hasNumeric = true;\n      } else if (getAlphanumericCode(c) != -1) {\n        hasAlphanumeric = true;\n      } else {\n        return Mode.BYTE;\n      }\n    }\n    if (hasAlphanumeric) {\n      return Mode.ALPHANUMERIC;\n    }\n    if (hasNumeric) {\n      return Mode.NUMERIC;\n    }\n    return Mode.BYTE;\n  }\n\n  private static boolean isOnlyDoubleByteKanji(String content) {\n    byte[] bytes;\n    try {\n      bytes = content.getBytes(\"Shift_JIS\");\n    } catch (UnsupportedEncodingException ignored) {\n      return false;\n    }\n    int length = bytes.length;\n    if (length % 2 != 0) {\n      return false;\n    }\n    for (int i = 0; i < length; i += 2) {\n      int byte1 = bytes[i] & 0xFF;\n      if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private static int chooseMaskPattern(BitArray bits,\n                                       ErrorCorrectionLevel ecLevel,\n                                       Version version,\n                                       ByteMatrix matrix) throws WriterException {\n\n    int minPenalty = Integer.MAX_VALUE;  // Lower penalty is better.\n    int bestMaskPattern = -1;\n    // We try all mask patterns to choose the best one.\n    for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {\n      MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);\n      int penalty = calculateMaskPenalty(matrix);\n      if (penalty < minPenalty) {\n        minPenalty = penalty;\n        bestMaskPattern = maskPattern;\n      }\n    }\n    return bestMaskPattern;\n  }\n\n  private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException {\n    for (int versionNum = 1; versionNum <= 40; versionNum++) {\n      Version version = Version.getVersionForNumber(versionNum);\n      if (willFit(numInputBits, version, ecLevel)) {\n        return version;\n      }\n    }\n    throw new WriterException(\"Data too big\");\n  }\n  \n  /**\n   * @return true if the number of input bits will fit in a code with the specified version and\n   * error correction level.\n   */\n  private static boolean willFit(int numInputBits, Version version, ErrorCorrectionLevel ecLevel) {\n      // In the following comments, we use numbers of Version 7-H.\n      // numBytes = 196\n      int numBytes = version.getTotalCodewords();\n      // getNumECBytes = 130\n      Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);\n      int numEcBytes = ecBlocks.getTotalECCodewords();\n      // getNumDataBytes = 196 - 130 = 66\n      int numDataBytes = numBytes - numEcBytes;\n      int totalInputBytes = (numInputBits + 7) / 8;\n      return numDataBytes >= totalInputBytes;\n  }\n\n  /**\n   * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).\n   */\n  static void terminateBits(int numDataBytes, BitArray bits) throws WriterException {\n    int capacity = numDataBytes * 8;\n    if (bits.getSize() > capacity) {\n      throw new WriterException(\"data bits cannot fit in the QR Code\" + bits.getSize() + \" > \" +\n          capacity);\n    }\n    for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {\n      bits.appendBit(false);\n    }\n    // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.\n    // If the last byte isn't 8-bit aligned, we'll add padding bits.\n    int numBitsInLastByte = bits.getSize() & 0x07;    \n    if (numBitsInLastByte > 0) {\n      for (int i = numBitsInLastByte; i < 8; i++) {\n        bits.appendBit(false);\n      }\n    }\n    // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).\n    int numPaddingBytes = numDataBytes - bits.getSizeInBytes();\n    for (int i = 0; i < numPaddingBytes; ++i) {\n      bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);\n    }\n    if (bits.getSize() != capacity) {\n      throw new WriterException(\"Bits size does not equal capacity\");\n    }\n  }\n\n  /**\n   * Get number of data bytes and number of error correction bytes for block id \"blockID\". Store\n   * the result in \"numDataBytesInBlock\", and \"numECBytesInBlock\". See table 12 in 8.5.1 of\n   * JISX0510:2004 (p.30)\n   */\n  static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes,\n                                                     int numDataBytes,\n                                                     int numRSBlocks,\n                                                     int blockID,\n                                                     int[] numDataBytesInBlock,\n                                                     int[] numECBytesInBlock) throws WriterException {\n    if (blockID >= numRSBlocks) {\n      throw new WriterException(\"Block ID too large\");\n    }\n    // numRsBlocksInGroup2 = 196 % 5 = 1\n    int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;\n    // numRsBlocksInGroup1 = 5 - 1 = 4\n    int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;\n    // numTotalBytesInGroup1 = 196 / 5 = 39\n    int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;\n    // numTotalBytesInGroup2 = 39 + 1 = 40\n    int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;\n    // numDataBytesInGroup1 = 66 / 5 = 13\n    int numDataBytesInGroup1 = numDataBytes / numRSBlocks;\n    // numDataBytesInGroup2 = 13 + 1 = 14\n    int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;\n    // numEcBytesInGroup1 = 39 - 13 = 26\n    int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;\n    // numEcBytesInGroup2 = 40 - 14 = 26\n    int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;\n    // Sanity checks.\n    // 26 = 26\n    if (numEcBytesInGroup1 != numEcBytesInGroup2) {\n      throw new WriterException(\"EC bytes mismatch\");\n    }\n    // 5 = 4 + 1.\n    if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {\n      throw new WriterException(\"RS blocks mismatch\");\n    }\n    // 196 = (13 + 26) * 4 + (14 + 26) * 1\n    if (numTotalBytes !=\n        ((numDataBytesInGroup1 + numEcBytesInGroup1) *\n            numRsBlocksInGroup1) +\n            ((numDataBytesInGroup2 + numEcBytesInGroup2) *\n                numRsBlocksInGroup2)) {\n      throw new WriterException(\"Total bytes mismatch\");\n    }\n\n    if (blockID < numRsBlocksInGroup1) {\n      numDataBytesInBlock[0] = numDataBytesInGroup1;\n      numECBytesInBlock[0] = numEcBytesInGroup1;\n    } else {\n      numDataBytesInBlock[0] = numDataBytesInGroup2;\n      numECBytesInBlock[0] = numEcBytesInGroup2;\n    }\n  }\n\n  /**\n   * Interleave \"bits\" with corresponding error correction bytes. On success, store the result in\n   * \"result\". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.\n   */\n  static BitArray interleaveWithECBytes(BitArray bits,\n                                        int numTotalBytes,\n                                        int numDataBytes,\n                                        int numRSBlocks) throws WriterException {\n\n    // \"bits\" must have \"getNumDataBytes\" bytes of data.\n    if (bits.getSizeInBytes() != numDataBytes) {\n      throw new WriterException(\"Number of bits and data bytes does not match\");\n    }\n\n    // Step 1.  Divide data bytes into blocks and generate error correction bytes for them. We'll\n    // store the divided data bytes blocks and error correction bytes blocks into \"blocks\".\n    int dataBytesOffset = 0;\n    int maxNumDataBytes = 0;\n    int maxNumEcBytes = 0;\n\n    // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.\n    Collection<BlockPair> blocks = new ArrayList<>(numRSBlocks);\n\n    for (int i = 0; i < numRSBlocks; ++i) {\n      int[] numDataBytesInBlock = new int[1];\n      int[] numEcBytesInBlock = new int[1];\n      getNumDataBytesAndNumECBytesForBlockID(\n          numTotalBytes, numDataBytes, numRSBlocks, i,\n          numDataBytesInBlock, numEcBytesInBlock);\n\n      int size = numDataBytesInBlock[0];\n      byte[] dataBytes = new byte[size];\n      bits.toBytes(8 * dataBytesOffset, dataBytes, 0, size);\n      byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);\n      blocks.add(new BlockPair(dataBytes, ecBytes));\n\n      maxNumDataBytes = Math.max(maxNumDataBytes, size);\n      maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length);\n      dataBytesOffset += numDataBytesInBlock[0];\n    }\n    if (numDataBytes != dataBytesOffset) {\n      throw new WriterException(\"Data bytes does not match offset\");\n    }\n\n    BitArray result = new BitArray();\n\n    // First, place data blocks.\n    for (int i = 0; i < maxNumDataBytes; ++i) {\n      for (BlockPair block : blocks) {\n        byte[] dataBytes = block.getDataBytes();\n        if (i < dataBytes.length) {\n          result.appendBits(dataBytes[i], 8);\n        }\n      }\n    }\n    // Then, place error correction blocks.\n    for (int i = 0; i < maxNumEcBytes; ++i) {\n      for (BlockPair block : blocks) {\n        byte[] ecBytes = block.getErrorCorrectionBytes();\n        if (i < ecBytes.length) {\n          result.appendBits(ecBytes[i], 8);\n        }\n      }\n    }\n    if (numTotalBytes != result.getSizeInBytes()) {  // Should be same.\n      throw new WriterException(\"Interleaving error: \" + numTotalBytes + \" and \" +\n          result.getSizeInBytes() + \" differ.\");\n    }\n\n    return result;\n  }\n\n  static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) {\n    int numDataBytes = dataBytes.length;\n    int[] toEncode = new int[numDataBytes + numEcBytesInBlock];\n    for (int i = 0; i < numDataBytes; i++) {\n      toEncode[i] = dataBytes[i] & 0xFF;\n    }\n    new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock);\n\n    byte[] ecBytes = new byte[numEcBytesInBlock];\n    for (int i = 0; i < numEcBytesInBlock; i++) {\n      ecBytes[i] = (byte) toEncode[numDataBytes + i];\n    }\n    return ecBytes;\n  }\n\n  /**\n   * Append mode info. On success, store the result in \"bits\".\n   */\n  static void appendModeInfo(Mode mode, BitArray bits) {\n    bits.appendBits(mode.getBits(), 4);\n  }\n\n\n  /**\n   * Append length info. On success, store the result in \"bits\".\n   */\n  static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException {\n    int numBits = mode.getCharacterCountBits(version);\n    if (numLetters >= (1 << numBits)) {\n      throw new WriterException(numLetters + \" is bigger than \" + ((1 << numBits) - 1));\n    }\n    bits.appendBits(numLetters, numBits);\n  }\n\n  /**\n   * Append \"bytes\" in \"mode\" mode (encoding) into \"bits\". On success, store the result in \"bits\".\n   */\n  static void appendBytes(String content,\n                          Mode mode,\n                          BitArray bits,\n                          String encoding) throws WriterException {\n    switch (mode) {\n      case NUMERIC:\n        appendNumericBytes(content, bits);\n        break;\n      case ALPHANUMERIC:\n        appendAlphanumericBytes(content, bits);\n        break;\n      case BYTE:\n        append8BitBytes(content, bits, encoding);\n        break;\n      case KANJI:\n        appendKanjiBytes(content, bits);\n        break;\n      default:\n        throw new WriterException(\"Invalid mode: \" + mode);\n    }\n  }\n\n  static void appendNumericBytes(CharSequence content, BitArray bits) {\n    int length = content.length();\n    int i = 0;\n    while (i < length) {\n      int num1 = content.charAt(i) - '0';\n      if (i + 2 < length) {\n        // Encode three numeric letters in ten bits.\n        int num2 = content.charAt(i + 1) - '0';\n        int num3 = content.charAt(i + 2) - '0';\n        bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);\n        i += 3;\n      } else if (i + 1 < length) {\n        // Encode two numeric letters in seven bits.\n        int num2 = content.charAt(i + 1) - '0';\n        bits.appendBits(num1 * 10 + num2, 7);\n        i += 2;\n      } else {\n        // Encode one numeric letter in four bits.\n        bits.appendBits(num1, 4);\n        i++;\n      }\n    }\n  }\n\n  static void appendAlphanumericBytes(CharSequence content, BitArray bits) throws WriterException {\n    int length = content.length();\n    int i = 0;\n    while (i < length) {\n      int code1 = getAlphanumericCode(content.charAt(i));\n      if (code1 == -1) {\n        throw new WriterException();\n      }\n      if (i + 1 < length) {\n        int code2 = getAlphanumericCode(content.charAt(i + 1));\n        if (code2 == -1) {\n          throw new WriterException();\n        }\n        // Encode two alphanumeric letters in 11 bits.\n        bits.appendBits(code1 * 45 + code2, 11);\n        i += 2;\n      } else {\n        // Encode one alphanumeric letter in six bits.\n        bits.appendBits(code1, 6);\n        i++;\n      }\n    }\n  }\n\n  static void append8BitBytes(String content, BitArray bits, String encoding)\n      throws WriterException {\n    byte[] bytes;\n    try {\n      bytes = content.getBytes(encoding);\n    } catch (UnsupportedEncodingException uee) {\n      throw new WriterException(uee);\n    }\n    for (byte b : bytes) {\n      bits.appendBits(b, 8);\n    }\n  }\n\n  static void appendKanjiBytes(String content, BitArray bits) throws WriterException {\n    byte[] bytes;\n    try {\n      bytes = content.getBytes(\"Shift_JIS\");\n    } catch (UnsupportedEncodingException uee) {\n      throw new WriterException(uee);\n    }\n    if (bytes.length % 2 != 0) {\n      throw new WriterException(\"Kanji byte size not even\");\n    }\n    int maxI = bytes.length - 1; // bytes.length must be even\n    for (int i = 0; i < maxI; i += 2) {\n      int byte1 = bytes[i] & 0xFF;\n      int byte2 = bytes[i + 1] & 0xFF;\n      int code = (byte1 << 8) | byte2;\n      int subtracted = -1;\n      if (code >= 0x8140 && code <= 0x9ffc) {\n        subtracted = code - 0x8140;\n      } else if (code >= 0xe040 && code <= 0xebbf) {\n        subtracted = code - 0xc140;\n      }\n      if (subtracted == -1) {\n        throw new WriterException(\"Invalid byte sequence\");\n      }\n      int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);\n      bits.appendBits(encoded, 13);\n    }\n  }\n\n  private static void appendECI(CharacterSetECI eci, BitArray bits) {\n    bits.appendBits(Mode.ECI.getBits(), 4);\n    // This is correct for values up to 127, which is all we need now.\n    bits.appendBits(eci.getValue(), 8);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.encoder;\n\n/**\n * @author Satoru Takabayashi\n * @author Daniel Switkin\n * @author Sean Owen\n */\nfinal class MaskUtil {\n\n  // Penalty weights from section 6.8.2.1\n  private static final int N1 = 3;\n  private static final int N2 = 3;\n  private static final int N3 = 40;\n  private static final int N4 = 10;\n\n  private MaskUtil() {\n    // do nothing\n  }\n\n  /**\n   * Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and\n   * give penalty to them. Example: 00000 or 11111.\n   */\n  static int applyMaskPenaltyRule1(ByteMatrix matrix) {\n    return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);\n  }\n\n  /**\n   * Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give\n   * penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a\n   * penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.\n   */\n  static int applyMaskPenaltyRule2(ByteMatrix matrix) {\n    int penalty = 0;\n    byte[][] array = matrix.getArray();\n    int width = matrix.getWidth();\n    int height = matrix.getHeight();\n    for (int y = 0; y < height - 1; y++) {\n      byte[] arrayY = array[y];\n      for (int x = 0; x < width - 1; x++) {\n        int value = arrayY[x];\n        if (value == arrayY[x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {\n          penalty++;\n        }\n      }\n    }\n    return N2 * penalty;\n  }\n\n  /**\n   * Apply mask penalty rule 3 and return the penalty. Find consecutive runs of 1:1:3:1:1:4\n   * starting with black, or 4:1:1:3:1:1 starting with white, and give penalty to them.  If we\n   * find patterns like 000010111010000, we give penalty once.\n   */\n  static int applyMaskPenaltyRule3(ByteMatrix matrix) {\n    int numPenalties = 0;\n    byte[][] array = matrix.getArray();\n    int width = matrix.getWidth();\n    int height = matrix.getHeight();\n    for (int y = 0; y < height; y++) {\n      for (int x = 0; x < width; x++) {\n        byte[] arrayY = array[y];  // We can at least optimize this access\n        if (x + 6 < width &&\n            arrayY[x] == 1 &&\n            arrayY[x + 1] == 0 &&\n            arrayY[x + 2] == 1 &&\n            arrayY[x + 3] == 1 &&\n            arrayY[x + 4] == 1 &&\n            arrayY[x + 5] == 0 &&\n            arrayY[x + 6] == 1 &&\n            (isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11))) {\n          numPenalties++;\n        }\n        if (y + 6 < height &&\n            array[y][x] == 1 &&\n            array[y + 1][x] == 0 &&\n            array[y + 2][x] == 1 &&\n            array[y + 3][x] == 1 &&\n            array[y + 4][x] == 1 &&\n            array[y + 5][x] == 0 &&\n            array[y + 6][x] == 1 &&\n            (isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11))) {\n          numPenalties++;\n        }\n      }\n    }\n    return numPenalties * N3;\n  }\n\n  private static boolean isWhiteHorizontal(byte[] rowArray, int from, int to) {\n    from = Math.max(from, 0);\n    to = Math.min(to, rowArray.length);\n    for (int i = from; i < to; i++) {\n      if (rowArray[i] == 1) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private static boolean isWhiteVertical(byte[][] array, int col, int from, int to) {\n    from = Math.max(from, 0);\n    to = Math.min(to, array.length);\n    for (int i = from; i < to; i++) {\n      if (array[i][col] == 1) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  /**\n   * Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give\n   * penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance.\n   */\n  static int applyMaskPenaltyRule4(ByteMatrix matrix) {\n    int numDarkCells = 0;\n    byte[][] array = matrix.getArray();\n    int width = matrix.getWidth();\n    int height = matrix.getHeight();\n    for (int y = 0; y < height; y++) {\n      byte[] arrayY = array[y];\n      for (int x = 0; x < width; x++) {\n        if (arrayY[x] == 1) {\n          numDarkCells++;\n        }\n      }\n    }\n    int numTotalCells = matrix.getHeight() * matrix.getWidth();\n    int fivePercentVariances = Math.abs(numDarkCells * 2 - numTotalCells) * 10 / numTotalCells;\n    return fivePercentVariances * N4;\n  }\n\n  /**\n   * Return the mask bit for \"getMaskPattern\" at \"x\" and \"y\". See 8.8 of JISX0510:2004 for mask\n   * pattern conditions.\n   */\n  static boolean getDataMaskBit(int maskPattern, int x, int y) {\n    int intermediate;\n    int temp;\n    switch (maskPattern) {\n      case 0:\n        intermediate = (y + x) & 0x1;\n        break;\n      case 1:\n        intermediate = y & 0x1;\n        break;\n      case 2:\n        intermediate = x % 3;\n        break;\n      case 3:\n        intermediate = (y + x) % 3;\n        break;\n      case 4:\n        intermediate = ((y / 2) + (x / 3)) & 0x1;\n        break;\n      case 5:\n        temp = y * x;\n        intermediate = (temp & 0x1) + (temp % 3);\n        break;\n      case 6:\n        temp = y * x;\n        intermediate = ((temp & 0x1) + (temp % 3)) & 0x1;\n        break;\n      case 7:\n        temp = y * x;\n        intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1;\n        break;\n      default:\n        throw new IllegalArgumentException(\"Invalid mask pattern: \" + maskPattern);\n    }\n    return intermediate == 0;\n  }\n\n  /**\n   * Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both\n   * vertical and horizontal orders respectively.\n   */\n  private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {\n    int penalty = 0;\n    int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();\n    int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();\n    byte[][] array = matrix.getArray();\n    for (int i = 0; i < iLimit; i++) {\n      int numSameBitCells = 0;\n      int prevBit = -1;\n      for (int j = 0; j < jLimit; j++) {\n        int bit = isHorizontal ? array[i][j] : array[j][i];\n        if (bit == prevBit) {\n          numSameBitCells++;\n        } else {\n          if (numSameBitCells >= 5) {\n            penalty += N1 + (numSameBitCells - 5);\n          }\n          numSameBitCells = 1;  // Include the cell itself.\n          prevBit = bit;\n        }\n      }\n      if (numSameBitCells >= 5) {\n        penalty += N1 + (numSameBitCells - 5);\n      }\n    }\n    return penalty;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.encoder;\n\nimport com.google.zxing.WriterException;\nimport com.google.zxing.common.BitArray;\nimport com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;\nimport com.google.zxing.qrcode.decoder.Version;\n\n/**\n * @author satorux@google.com (Satoru Takabayashi) - creator\n * @author dswitkin@google.com (Daniel Switkin) - ported from C++\n */\nfinal class MatrixUtil {\n\n  private static final int[][] POSITION_DETECTION_PATTERN = {\n      {1, 1, 1, 1, 1, 1, 1},\n      {1, 0, 0, 0, 0, 0, 1},\n      {1, 0, 1, 1, 1, 0, 1},\n      {1, 0, 1, 1, 1, 0, 1},\n      {1, 0, 1, 1, 1, 0, 1},\n      {1, 0, 0, 0, 0, 0, 1},\n      {1, 1, 1, 1, 1, 1, 1},\n  };\n\n  private static final int[][] POSITION_ADJUSTMENT_PATTERN = {\n      {1, 1, 1, 1, 1},\n      {1, 0, 0, 0, 1},\n      {1, 0, 1, 0, 1},\n      {1, 0, 0, 0, 1},\n      {1, 1, 1, 1, 1},\n  };\n\n  // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.\n  private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = {\n      {-1, -1, -1, -1,  -1,  -1,  -1},  // Version 1\n      { 6, 18, -1, -1,  -1,  -1,  -1},  // Version 2\n      { 6, 22, -1, -1,  -1,  -1,  -1},  // Version 3\n      { 6, 26, -1, -1,  -1,  -1,  -1},  // Version 4\n      { 6, 30, -1, -1,  -1,  -1,  -1},  // Version 5\n      { 6, 34, -1, -1,  -1,  -1,  -1},  // Version 6\n      { 6, 22, 38, -1,  -1,  -1,  -1},  // Version 7\n      { 6, 24, 42, -1,  -1,  -1,  -1},  // Version 8\n      { 6, 26, 46, -1,  -1,  -1,  -1},  // Version 9\n      { 6, 28, 50, -1,  -1,  -1,  -1},  // Version 10\n      { 6, 30, 54, -1,  -1,  -1,  -1},  // Version 11\n      { 6, 32, 58, -1,  -1,  -1,  -1},  // Version 12\n      { 6, 34, 62, -1,  -1,  -1,  -1},  // Version 13\n      { 6, 26, 46, 66,  -1,  -1,  -1},  // Version 14\n      { 6, 26, 48, 70,  -1,  -1,  -1},  // Version 15\n      { 6, 26, 50, 74,  -1,  -1,  -1},  // Version 16\n      { 6, 30, 54, 78,  -1,  -1,  -1},  // Version 17\n      { 6, 30, 56, 82,  -1,  -1,  -1},  // Version 18\n      { 6, 30, 58, 86,  -1,  -1,  -1},  // Version 19\n      { 6, 34, 62, 90,  -1,  -1,  -1},  // Version 20\n      { 6, 28, 50, 72,  94,  -1,  -1},  // Version 21\n      { 6, 26, 50, 74,  98,  -1,  -1},  // Version 22\n      { 6, 30, 54, 78, 102,  -1,  -1},  // Version 23\n      { 6, 28, 54, 80, 106,  -1,  -1},  // Version 24\n      { 6, 32, 58, 84, 110,  -1,  -1},  // Version 25\n      { 6, 30, 58, 86, 114,  -1,  -1},  // Version 26\n      { 6, 34, 62, 90, 118,  -1,  -1},  // Version 27\n      { 6, 26, 50, 74,  98, 122,  -1},  // Version 28\n      { 6, 30, 54, 78, 102, 126,  -1},  // Version 29\n      { 6, 26, 52, 78, 104, 130,  -1},  // Version 30\n      { 6, 30, 56, 82, 108, 134,  -1},  // Version 31\n      { 6, 34, 60, 86, 112, 138,  -1},  // Version 32\n      { 6, 30, 58, 86, 114, 142,  -1},  // Version 33\n      { 6, 34, 62, 90, 118, 146,  -1},  // Version 34\n      { 6, 30, 54, 78, 102, 126, 150},  // Version 35\n      { 6, 24, 50, 76, 102, 128, 154},  // Version 36\n      { 6, 28, 54, 80, 106, 132, 158},  // Version 37\n      { 6, 32, 58, 84, 110, 136, 162},  // Version 38\n      { 6, 26, 54, 82, 110, 138, 166},  // Version 39\n      { 6, 30, 58, 86, 114, 142, 170},  // Version 40\n  };\n\n  // Type info cells at the left top corner.\n  private static final int[][] TYPE_INFO_COORDINATES = {\n      {8, 0},\n      {8, 1},\n      {8, 2},\n      {8, 3},\n      {8, 4},\n      {8, 5},\n      {8, 7},\n      {8, 8},\n      {7, 8},\n      {5, 8},\n      {4, 8},\n      {3, 8},\n      {2, 8},\n      {1, 8},\n      {0, 8},\n  };\n\n  // From Appendix D in JISX0510:2004 (p. 67)\n  private static final int VERSION_INFO_POLY = 0x1f25;  // 1 1111 0010 0101\n\n  // From Appendix C in JISX0510:2004 (p.65).\n  private static final int TYPE_INFO_POLY = 0x537;\n  private static final int TYPE_INFO_MASK_PATTERN = 0x5412;\n\n  private MatrixUtil() {\n    // do nothing\n  }\n  \n  // Set all cells to -1.  -1 means that the cell is empty (not set yet).\n  //\n  // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding\n  // with the ByteMatrix initialized all to zero.\n  static void clearMatrix(ByteMatrix matrix) {\n    matrix.clear((byte) -1);\n  }\n\n  // Build 2D matrix of QR Code from \"dataBits\" with \"ecLevel\", \"version\" and \"getMaskPattern\". On\n  // success, store the result in \"matrix\" and return true.\n  static void buildMatrix(BitArray dataBits,\n                          ErrorCorrectionLevel ecLevel,\n                          Version version,\n                          int maskPattern,\n                          ByteMatrix matrix) throws WriterException {\n    clearMatrix(matrix);\n    embedBasicPatterns(version, matrix);\n    // Type information appear with any version.\n    embedTypeInfo(ecLevel, maskPattern, matrix);\n    // Version info appear if version >= 7.\n    maybeEmbedVersionInfo(version, matrix);\n    // Data should be embedded at end.\n    embedDataBits(dataBits, maskPattern, matrix);\n  }\n\n  // Embed basic patterns. On success, modify the matrix and return true.\n  // The basic patterns are:\n  // - Position detection patterns\n  // - Timing patterns\n  // - Dark dot at the left bottom corner\n  // - Position adjustment patterns, if need be\n  static void embedBasicPatterns(Version version, ByteMatrix matrix) throws WriterException {\n    // Let's get started with embedding big squares at corners.\n    embedPositionDetectionPatternsAndSeparators(matrix);\n    // Then, embed the dark dot at the left bottom corner.\n    embedDarkDotAtLeftBottomCorner(matrix);\n\n    // Position adjustment patterns appear if version >= 2.\n    maybeEmbedPositionAdjustmentPatterns(version, matrix);\n    // Timing patterns should be embedded after position adj. patterns.\n    embedTimingPatterns(matrix);\n  }\n\n  // Embed type information. On success, modify the matrix.\n  static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)\n      throws WriterException {\n    BitArray typeInfoBits = new BitArray();\n    makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);\n\n    for (int i = 0; i < typeInfoBits.getSize(); ++i) {\n      // Place bits in LSB to MSB order.  LSB (least significant bit) is the last value in\n      // \"typeInfoBits\".\n      boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i);\n\n      // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).\n      int[] coordinates = TYPE_INFO_COORDINATES[i];\n      int x1 = coordinates[0];\n      int y1 = coordinates[1];\n      matrix.set(x1, y1, bit);\n\n      if (i < 8) {\n        // Right top corner.\n        int x2 = matrix.getWidth() - i - 1;\n        int y2 = 8;\n        matrix.set(x2, y2, bit);\n      } else {\n        // Left bottom corner.\n        int x2 = 8;\n        int y2 = matrix.getHeight() - 7 + (i - 8);\n        matrix.set(x2, y2, bit);\n      }\n    }\n  }\n\n  // Embed version information if need be. On success, modify the matrix and return true.\n  // See 8.10 of JISX0510:2004 (p.47) for how to embed version information.\n  static void maybeEmbedVersionInfo(Version version, ByteMatrix matrix) throws WriterException {\n    if (version.getVersionNumber() < 7) {  // Version info is necessary if version >= 7.\n      return;  // Don't need version info.\n    }\n    BitArray versionInfoBits = new BitArray();\n    makeVersionInfoBits(version, versionInfoBits);\n\n    int bitIndex = 6 * 3 - 1;  // It will decrease from 17 to 0.\n    for (int i = 0; i < 6; ++i) {\n      for (int j = 0; j < 3; ++j) {\n        // Place bits in LSB (least significant bit) to MSB order.\n        boolean bit = versionInfoBits.get(bitIndex);\n        bitIndex--;\n        // Left bottom corner.\n        matrix.set(i, matrix.getHeight() - 11 + j, bit);\n        // Right bottom corner.\n        matrix.set(matrix.getHeight() - 11 + j, i, bit);\n      }\n    }\n  }\n\n  // Embed \"dataBits\" using \"getMaskPattern\". On success, modify the matrix and return true.\n  // For debugging purposes, it skips masking process if \"getMaskPattern\" is -1.\n  // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.\n  static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix)\n      throws WriterException {\n    int bitIndex = 0;\n    int direction = -1;\n    // Start from the right bottom cell.\n    int x = matrix.getWidth() - 1;\n    int y = matrix.getHeight() - 1;\n    while (x > 0) {\n      // Skip the vertical timing pattern.\n      if (x == 6) {\n        x -= 1;\n      }\n      while (y >= 0 && y < matrix.getHeight()) {\n        for (int i = 0; i < 2; ++i) {\n          int xx = x - i;\n          // Skip the cell if it's not empty.\n          if (!isEmpty(matrix.get(xx, y))) {\n            continue;\n          }\n          boolean bit;\n          if (bitIndex < dataBits.getSize()) {\n            bit = dataBits.get(bitIndex);\n            ++bitIndex;\n          } else {\n            // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described\n            // in 8.4.9 of JISX0510:2004 (p. 24).\n            bit = false;\n          }\n\n          // Skip masking if mask_pattern is -1.\n          if (maskPattern != -1 && MaskUtil.getDataMaskBit(maskPattern, xx, y)) {\n            bit = !bit;\n          }\n          matrix.set(xx, y, bit);\n        }\n        y += direction;\n      }\n      direction = -direction;  // Reverse the direction.\n      y += direction;\n      x -= 2;  // Move to the left.\n    }\n    // All bits should be consumed.\n    if (bitIndex != dataBits.getSize()) {\n      throw new WriterException(\"Not all bits consumed: \" + bitIndex + '/' + dataBits.getSize());\n    }\n  }\n\n  // Return the position of the most significant bit set (to one) in the \"value\". The most\n  // significant bit is position 32. If there is no bit set, return 0. Examples:\n  // - findMSBSet(0) => 0\n  // - findMSBSet(1) => 1\n  // - findMSBSet(255) => 8\n  static int findMSBSet(int value) {\n    return 32 - Integer.numberOfLeadingZeros(value);\n  }\n\n  // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for \"value\" using polynomial \"poly\". The BCH\n  // code is used for encoding type information and version information.\n  // Example: Calculation of version information of 7.\n  // f(x) is created from 7.\n  //   - 7 = 000111 in 6 bits\n  //   - f(x) = x^2 + x^1 + x^0\n  // g(x) is given by the standard (p. 67)\n  //   - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1\n  // Multiply f(x) by x^(18 - 6)\n  //   - f'(x) = f(x) * x^(18 - 6)\n  //   - f'(x) = x^14 + x^13 + x^12\n  // Calculate the remainder of f'(x) / g(x)\n  //         x^2\n  //         __________________________________________________\n  //   g(x) )x^14 + x^13 + x^12\n  //         x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2\n  //         --------------------------------------------------\n  //                              x^11 + x^10 + x^7 + x^4 + x^2\n  //\n  // The remainder is x^11 + x^10 + x^7 + x^4 + x^2\n  // Encode it in binary: 110010010100\n  // The return value is 0xc94 (1100 1001 0100)\n  //\n  // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit\n  // operations. We don't care if coefficients are positive or negative.\n  static int calculateBCHCode(int value, int poly) {\n    if (poly == 0) {\n      throw new IllegalArgumentException(\"0 polynomial\");\n    }\n    // If poly is \"1 1111 0010 0101\" (version info poly), msbSetInPoly is 13. We'll subtract 1\n    // from 13 to make it 12.\n    int msbSetInPoly = findMSBSet(poly);\n    value <<= msbSetInPoly - 1;\n    // Do the division business using exclusive-or operations.\n    while (findMSBSet(value) >= msbSetInPoly) {\n      value ^= poly << (findMSBSet(value) - msbSetInPoly);\n    }\n    // Now the \"value\" is the remainder (i.e. the BCH code)\n    return value;\n  }\n\n  // Make bit vector of type information. On success, store the result in \"bits\" and return true.\n  // Encode error correction level and mask pattern. See 8.9 of\n  // JISX0510:2004 (p.45) for details.\n  static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)\n      throws WriterException {\n    if (!QRCode.isValidMaskPattern(maskPattern)) {\n      throw new WriterException(\"Invalid mask pattern\");\n    }\n    int typeInfo = (ecLevel.getBits() << 3) | maskPattern;\n    bits.appendBits(typeInfo, 5);\n\n    int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);\n    bits.appendBits(bchCode, 10);\n\n    BitArray maskBits = new BitArray();\n    maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);\n    bits.xor(maskBits);\n\n    if (bits.getSize() != 15) {  // Just in case.\n      throw new WriterException(\"should not happen but we got: \" + bits.getSize());\n    }\n  }\n\n  // Make bit vector of version information. On success, store the result in \"bits\" and return true.\n  // See 8.10 of JISX0510:2004 (p.45) for details.\n  static void makeVersionInfoBits(Version version, BitArray bits) throws WriterException {\n    bits.appendBits(version.getVersionNumber(), 6);\n    int bchCode = calculateBCHCode(version.getVersionNumber(), VERSION_INFO_POLY);\n    bits.appendBits(bchCode, 12);\n\n    if (bits.getSize() != 18) {  // Just in case.\n      throw new WriterException(\"should not happen but we got: \" + bits.getSize());\n    }\n  }\n\n  // Check if \"value\" is empty.\n  private static boolean isEmpty(int value) {\n    return value == -1;\n  }\n\n  private static void embedTimingPatterns(ByteMatrix matrix) {\n    // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical\n    // separation patterns (size 1). Thus, 8 = 7 + 1.\n    for (int i = 8; i < matrix.getWidth() - 8; ++i) {\n      int bit = (i + 1) % 2;\n      // Horizontal line.\n      if (isEmpty(matrix.get(i, 6))) {\n        matrix.set(i, 6, bit);\n      }\n      // Vertical line.\n      if (isEmpty(matrix.get(6, i))) {\n        matrix.set(6, i, bit);\n      }\n    }\n  }\n\n  // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)\n  private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {\n    if (matrix.get(8, matrix.getHeight() - 8) == 0) {\n      throw new WriterException();\n    }\n    matrix.set(8, matrix.getHeight() - 8, 1);\n  }\n\n  private static void embedHorizontalSeparationPattern(int xStart,\n                                                       int yStart,\n                                                       ByteMatrix matrix) throws WriterException {\n    for (int x = 0; x < 8; ++x) {\n      if (!isEmpty(matrix.get(xStart + x, yStart))) {\n        throw new WriterException();\n      }\n      matrix.set(xStart + x, yStart, 0);\n    }\n  }\n\n  private static void embedVerticalSeparationPattern(int xStart,\n                                                     int yStart,\n                                                     ByteMatrix matrix) throws WriterException {\n    for (int y = 0; y < 7; ++y) {\n      if (!isEmpty(matrix.get(xStart, yStart + y))) {\n        throw new WriterException();\n      }\n      matrix.set(xStart, yStart + y, 0);\n    }\n  }\n\n  private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix) {\n    for (int y = 0; y < 5; ++y) {\n      int[] patternY = POSITION_ADJUSTMENT_PATTERN[y];\n      for (int x = 0; x < 5; ++x) {\n        matrix.set(xStart + x, yStart + y, patternY[x]);\n      }\n    }\n  }\n\n  private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix) {\n    for (int y = 0; y < 7; ++y) {\n      int[] patternY = POSITION_DETECTION_PATTERN[y];\n      for (int x = 0; x < 7; ++x) {\n        matrix.set(xStart + x, yStart + y, patternY[x]);\n      }\n    }\n  }\n\n  // Embed position detection patterns and surrounding vertical/horizontal separators.\n  private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException {\n    // Embed three big squares at corners.\n    int pdpWidth = POSITION_DETECTION_PATTERN[0].length;\n    // Left top corner.\n    embedPositionDetectionPattern(0, 0, matrix);\n    // Right top corner.\n    embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix);\n    // Left bottom corner.\n    embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);\n\n    // Embed horizontal separation patterns around the squares.\n    int hspWidth = 8;\n    // Left top corner.\n    embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);\n    // Right top corner.\n    embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth,\n        hspWidth - 1, matrix);\n    // Left bottom corner.\n    embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);\n\n    // Embed vertical separation patterns around the squares.\n    int vspSize = 7;\n    // Left top corner.\n    embedVerticalSeparationPattern(vspSize, 0, matrix);\n    // Right top corner.\n    embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix);\n    // Left bottom corner.\n    embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize,\n        matrix);\n  }\n\n  // Embed position adjustment patterns if need be.\n  private static void maybeEmbedPositionAdjustmentPatterns(Version version, ByteMatrix matrix) {\n    if (version.getVersionNumber() < 2) {  // The patterns appear if version >= 2\n      return;\n    }\n    int index = version.getVersionNumber() - 1;\n    int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];\n    for (int y : coordinates) {\n      if (y >= 0) {\n        for (int x : coordinates) {\n          if (x >= 0 && isEmpty(matrix.get(x, y))) {\n            // If the cell is unset, we embed the position adjustment pattern here.\n            // -2 is necessary since the x/y coordinates point to the center of the pattern, not the\n            // left top corner.\n            embedPositionAdjustmentPattern(x - 2, y - 2, matrix);\n          }\n        }\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java",
    "content": "/*\n * Copyright 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.zxing.qrcode.encoder;\n\nimport com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;\nimport com.google.zxing.qrcode.decoder.Mode;\nimport com.google.zxing.qrcode.decoder.Version;\n\n/**\n * @author satorux@google.com (Satoru Takabayashi) - creator\n * @author dswitkin@google.com (Daniel Switkin) - ported from C++\n */\npublic final class QRCode {\n\n  public static final int NUM_MASK_PATTERNS = 8;\n\n  private Mode mode;\n  private ErrorCorrectionLevel ecLevel;\n  private Version version;\n  private int maskPattern;\n  private ByteMatrix matrix;\n\n  public QRCode() {\n    maskPattern = -1;\n  }\n\n  public Mode getMode() {\n    return mode;\n  }\n\n  public ErrorCorrectionLevel getECLevel() {\n    return ecLevel;\n  }\n\n  public Version getVersion() {\n    return version;\n  }\n\n  public int getMaskPattern() {\n    return maskPattern;\n  }\n\n  public ByteMatrix getMatrix() {\n    return matrix;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder result = new StringBuilder(200);\n    result.append(\"<<\\n\");\n    result.append(\" mode: \");\n    result.append(mode);\n    result.append(\"\\n ecLevel: \");\n    result.append(ecLevel);\n    result.append(\"\\n version: \");\n    result.append(version);\n    result.append(\"\\n maskPattern: \");\n    result.append(maskPattern);\n    if (matrix == null) {\n      result.append(\"\\n matrix: null\\n\");\n    } else {\n      result.append(\"\\n matrix:\\n\");\n      result.append(matrix);\n    }\n    result.append(\">>\\n\");\n    return result.toString();\n  }\n\n  public void setMode(Mode value) {\n    mode = value;\n  }\n\n  public void setECLevel(ErrorCorrectionLevel value) {\n    ecLevel = value;\n  }\n\n  public void setVersion(Version version) {\n    this.version = version;\n  }\n\n  public void setMaskPattern(int value) {\n    maskPattern = value;\n  }\n\n  public void setMatrix(ByteMatrix value) {\n    matrix = value;\n  }\n\n  // Check if \"mask_pattern\" is valid.\n  public static boolean isValidMaskPattern(int maskPattern) {\n    return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/module/CaptureActivity.java",
    "content": "package com.nanchen.scanner.module;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.content.res.Configuration;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.hardware.Camera;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.Bundle;\nimport android.provider.MediaStore;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.app.AlertDialog;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.Surface;\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceView;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.nanchen.scanner.R;\nimport com.nanchen.scanner.utils.PermissionConstants;\nimport com.nanchen.scanner.utils.PermissionUtils;\nimport com.nanchen.scanner.utils.QRUtils;\nimport com.nanchen.scanner.zxing.BaseCaptureActivity;\nimport com.nanchen.scanner.zxing.BeepManager;\nimport com.nanchen.scanner.zxing.CaptureActivityHandler;\nimport com.nanchen.scanner.zxing.FinishListener;\nimport com.nanchen.scanner.zxing.InactivityTimer;\nimport com.nanchen.scanner.zxing.Intents;\nimport com.nanchen.scanner.zxing.ViewfinderView;\nimport com.nanchen.scanner.zxing.camera.CameraManager;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.List;\n\n/**\n * @author nanchen\n * 拍照真正的 Activity，实际业务可以自己编写 UI\n */\npublic class CaptureActivity extends BaseCaptureActivity implements View.OnClickListener {\n    private static final String TAG = CaptureActivity.class.getSimpleName();\n    private CameraManager cameraManager;\n    private CaptureActivityHandler handler;\n    private ViewfinderView viewfinderView;\n    private SurfaceView surfaceView;\n    private boolean hasSurface;\n    private InactivityTimer inactivityTimer;\n    private BeepManager beepManager;\n\n    private static final int REQUEST_IMAGE_GET = 1001;\n\n    private boolean torchOpen = false;\n    private TextView tvTitle;\n    private GestureDetector gestureDetector;\n\n    public static void startForResult(final Activity activity, final int requestCode) {\n        PermissionUtils.permission(activity, PermissionConstants.CAMERA, PermissionConstants.STORAGE)\n                .rationale(new PermissionUtils.OnRationaleListener() {\n                    @Override\n                    public void rationale(final ShouldRequest shouldRequest) {\n                        shouldRequest.again(true);\n                    }\n                })\n                .callback(new PermissionUtils.FullCallback() {\n                    @Override\n                    public void onGranted(List<String> permissionsGranted) {\n                        Intent intent = new Intent(activity.getApplicationContext(), CaptureActivity.class);\n                        activity.startActivityForResult(intent, requestCode);\n                    }\n\n                    @Override\n                    public void onDenied(List<String> permissionsDeniedForever,\n                                         List<String> permissionsDenied) {\n                        Toast.makeText(activity.getApplicationContext(), activity.getResources().getString(R.string.scanner_camera_refuse), Toast.LENGTH_SHORT).show();\n\n                    }\n                }).request();\n\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        // 保持屏幕常亮\n        Window window = getWindow();\n        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n        setContentView(R.layout.activity_capture);\n        initView();\n    }\n\n    private void initView() {\n        findViewById(R.id.ivBack).setOnClickListener(this);\n        findViewById(R.id.tvGallery).setOnClickListener(this);\n        findViewById(R.id.tvFlash).setOnClickListener(this);\n\n        viewfinderView = findViewById(R.id.viewfinderView);\n        surfaceView = findViewById(R.id.surfaceView);\n        tvTitle = findViewById(R.id.tvTitle);\n\n        hasSurface = false;\n        inactivityTimer = new InactivityTimer(this);\n        beepManager = new BeepManager(this);\n\n        gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {\n            @Override\n            public boolean onDoubleTap(MotionEvent e) {\n                Log.d(TAG, \"双击放大\");\n                cameraManager.handleDoubleZoom();\n                return true;\n            }\n        });\n    }\n\n    @Override\n    public void onClick(View v) {\n        int i = v.getId();\n        if (i == R.id.ivBack) {\n            finish();\n\n        } else if (i == R.id.tvFlash) {\n            clickTorch();\n\n        } else if (i == R.id.tvGallery) {\n            openGallery();\n        }\n    }\n\n    private void openGallery() {\n        Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null);\n        // 如果限制上传到服务器的图片类型时可以直接写如：\"image/jpeg 、 image/png等的类型\" 所有类型则写 \"image/*\"\n        intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, \"image/jpeg\");\n        startActivityForResult(intentToPickPic, REQUEST_IMAGE_GET);\n    }\n\n    private void initCamera(SurfaceHolder surfaceHolder) {\n        if (surfaceHolder == null) {\n            throw new IllegalStateException(\"No SurfaceHolder provided\");\n        }\n        if (cameraManager.isOpen()) {\n            Log.w(TAG, \"initCamera() while already open -- late SurfaceView callback?\");\n            return;\n        }\n        try {\n            cameraManager.openDriver(surfaceHolder);\n            // Creating the handler starts the preview, which can also throw a RuntimeException.\n            if (handler == null) {\n                handler = new CaptureActivityHandler(this, cameraManager);\n            }\n        } catch (IOException ioe) {\n            Log.w(TAG, ioe);\n            displayFrameworkBugMessageAndExit();\n        } catch (RuntimeException e) {\n            // Barcode Scanner has seen crashes in the wild of this variety:\n            // java.?lang.?RuntimeException: Fail to connect to camera service\n            Log.w(TAG, \"Unexpected error initializing camera\", e);\n            displayFrameworkBugMessageAndExit();\n        }\n    }\n\n\n    private void displayFrameworkBugMessageAndExit() {\n        AlertDialog.Builder builder = new AlertDialog.Builder(this);\n        builder.setTitle(getString(R.string.app_name));\n        builder.setMessage(getString(R.string.msg_camera_framework_bug));\n        builder.setPositiveButton(R.string.common_ok, new FinishListener(this));\n        builder.setOnCancelListener(new FinishListener(this));\n        builder.show();\n    }\n\n    private void resetStatusView() {\n        viewfinderView.setVisibility(View.VISIBLE);\n    }\n\n    public void clickTorch() {\n        torchOpen = !torchOpen;\n        if (cameraManager != null) {\n            cameraManager.setTorch(torchOpen);\n        }\n    }\n\n    @Override\n    public ViewfinderView getViewfinderView() {\n        return viewfinderView;\n    }\n\n    @Override\n    public Handler getHandler() {\n        return handler;\n    }\n\n    @Override\n    public CameraManager getCameraManager() {\n        return cameraManager;\n    }\n\n    @Override\n    public void handleDecode(String result, Bitmap barcode, float scaleFactor) {\n        inactivityTimer.onActivity();\n\n        boolean fromLiveScan = barcode != null;\n        if (fromLiveScan) {\n            // Then not from history, so beep/vibrate and we have an image to draw on\n            beepManager.playBeepSoundAndVibrate();\n        }\n        doParseResult(result);\n    }\n\n    private void doParseResult(String result) {\n        if (result == null || TextUtils.isEmpty(result)) {\n            restartPreviewAfterDelay(0);\n        } else {\n            Intent data = new Intent();\n            data.putExtra(\"result\", result);\n            setResult(RESULT_OK, data);\n            finish();\n        }\n    }\n\n    @Override\n    public void drawViewfinder() {\n\n    }\n\n\n    @Override\n    public void surfaceCreated(SurfaceHolder holder) {\n        if (holder == null) {\n            Log.e(TAG, \"*** WARNING *** surfaceCreated() gave us a null surface!\");\n        }\n        if (!hasSurface) {\n            hasSurface = true;\n            initCamera(holder);\n        }\n    }\n\n    @Override\n    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {\n\n    }\n\n    @Override\n    public void surfaceDestroyed(SurfaceHolder holder) {\n        hasSurface = false;\n    }\n\n    private int getCurrentOrientation() {\n        int rotation = getWindowManager().getDefaultDisplay().getRotation();\n        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {\n            switch (rotation) {\n                case Surface.ROTATION_0:\n                case Surface.ROTATION_90:\n                    return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;\n                default:\n                    return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;\n            }\n        } else {\n            switch (rotation) {\n                case Surface.ROTATION_0:\n                case Surface.ROTATION_270:\n                    return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;\n                default:\n                    return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;\n            }\n        }\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n\n        // CameraManager must be initialized here, not in onCreate(). This is necessary because we don't\n        // want to open the camera driver and measure the screen size if we're going to pick the help on\n        // first launch. That led to bugs where the scanning rectangle was the wrong size and partially\n        // off screen.\n        cameraManager = new CameraManager(getApplication());\n\n        viewfinderView.setCameraManager(cameraManager);\n\n        handler = null;\n\n        setRequestedOrientation(getCurrentOrientation());\n\n        resetStatusView();\n\n\n        beepManager.updatePrefs();\n        //        ambientLightManager.startForResult(cameraManager);\n\n        inactivityTimer.onResume();\n\n        Intent intent = getIntent();\n\n        if (intent != null) {\n            String action = intent.getAction();\n\n            if (Intents.Scan.ACTION.equals(action)) {\n\n                // Scan the formats the intent requested, and return the result to the calling activity.\n//                decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);\n                // 仅仅支持二维码\n//                decodeFormats = DecodeFormatManager.onlyQrCode();\n//                decodeHints = DecodeHintManager.parseDecodeHints(intent);\n\n                if (intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {\n                    int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);\n                    int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);\n                    if (width > 0 && height > 0) {\n                        cameraManager.setManualFramingRect(width, height);\n                    }\n                }\n\n                if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {\n                    int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1);\n                    if (cameraId >= 0) {\n                        cameraManager.setManualCameraId(cameraId);\n                    }\n                }\n            }\n        }\n\n        SurfaceHolder surfaceHolder = surfaceView.getHolder();\n        if (hasSurface) {\n            // The activity was paused but not stopped, so the surface still exists. Therefore\n            // surfaceCreated() won't be called, so init the camera here.\n            initCamera(surfaceHolder);\n        } else {\n            // Install the callback and wait for surfaceCreated() to init the camera.\n            surfaceHolder.addCallback(this);\n        }\n    }\n\n\n    @Override\n    public void onPause() {\n        if (handler != null) {\n            handler.quitSynchronously();\n            handler = null;\n        }\n        inactivityTimer.onPause();\n        //        ambientLightManager.stop();\n        beepManager.close();\n        cameraManager.closeDriver();\n        //historyManager = null; // Keep for onActivityResult\n        if (!hasSurface) {\n            SurfaceHolder surfaceHolder = surfaceView.getHolder();\n            surfaceHolder.removeCallback(this);\n        }\n        super.onPause();\n    }\n\n    @Override\n    protected void onDestroy() {\n        inactivityTimer.shutdown();\n        super.onDestroy();\n    }\n\n    Bitmap bitmap;\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        if (requestCode == REQUEST_IMAGE_GET && resultCode == RESULT_OK && data.getData() != null) {\n            Uri uri = data.getData();\n            bitmap = null;\n            try {\n                bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));\n            } catch (FileNotFoundException e) {\n                bitmap = null;\n            }\n            if (bitmap == null) {\n                Toast.makeText(getApplicationContext(), getResources().getString(R.string.scanner_get_picture_error), Toast.LENGTH_SHORT).show();\n                return;\n            }\n            showProgressDialog(getResources().getString(R.string.scanner_waiting));\n            new Thread(new Runnable() {\n                @Override\n                public void run() {\n                    final String result = QRUtils.getInstance().decodeQRcodeByZxing(bitmap);\n                    if (!TextUtils.isEmpty(result)) {\n                        closeProgressDialog();\n                        doParseResult(result);\n                    } else {\n                        runOnUiThread(new Runnable() {\n                            @Override\n                            public void run() {\n                                Toast.makeText(CaptureActivity.this.getApplicationContext(), getResources().getString(R.string.scanner_failed), Toast.LENGTH_SHORT).show();\n                            }\n                        });\n                        closeProgressDialog();\n                    }\n                }\n            }).start();\n        }\n    }\n\n    private AlertDialog progressDialog;\n\n    public void showProgressDialog(String msg) {\n        AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialogStyle);\n        builder.setCancelable(false);\n        View view = View.inflate(this, R.layout.dialog_loading, null);\n        builder.setView(view);\n        ProgressBar pb_loading = view.findViewById(R.id.pb_loading);\n        TextView tv_hint = view.findViewById(R.id.tv_hint);\n        tv_hint.setText(msg);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            pb_loading.setIndeterminateTintList(ContextCompat.getColorStateList(this, R.color.dialog_pro_color));\n        }\n        progressDialog = builder.create();\n        progressDialog.show();\n    }\n\n    public void closeProgressDialog() {\n        try {\n            if (progressDialog != null) {\n                progressDialog.dismiss();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * 重新扫描\n     */\n    public void restartPreviewAfterDelay(long delayMS) {\n        if (handler != null) {\n            handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);\n        }\n        resetStatusView();\n    }\n\n    /**\n     * 计算手指间距\n     */\n    private float calculateFingerSpacing(MotionEvent event) {\n        float x = event.getX(0) - event.getX(1);\n        float y = event.getY(0) - event.getY(1);\n        return (float) Math.sqrt(x * x + y * y);\n    }\n\n    private float mOldDist = 1f;\n\n    private boolean isPreviewing() {\n        return cameraManager.isPreviewing() && hasSurface;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if (!isPreviewing()) {\n            return super.onTouchEvent(event);\n        }\n        gestureDetector.onTouchEvent(event);\n        int pointCount = event.getPointerCount();\n        if (pointCount >= 2) {\n            switch (event.getAction() & MotionEvent.ACTION_MASK) {\n                case MotionEvent.ACTION_POINTER_DOWN:\n                    mOldDist = calculateFingerSpacing(event);\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    float newDist = calculateFingerSpacing(event);\n                    if (newDist > mOldDist) {\n                        cameraManager.handleZoom(true);\n                    } else if (newDist < mOldDist) {\n                        cameraManager.handleZoom(false);\n                    }\n                    break;\n            }\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/utils/PermissionConstants.java",
    "content": "package com.nanchen.scanner.utils;\n\nimport android.Manifest;\nimport android.Manifest.permission;\nimport android.annotation.SuppressLint;\nimport android.support.annotation.StringDef;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/29\n *     desc  : constants of permission\n * </pre>\n */\n@SuppressLint(\"InlinedApi\")\npublic final class PermissionConstants {\n\n    public static final String CALENDAR   = Manifest.permission_group.CALENDAR;\n    public static final String CAMERA     = Manifest.permission_group.CAMERA;\n    public static final String CONTACTS   = Manifest.permission_group.CONTACTS;\n    public static final String LOCATION   = Manifest.permission_group.LOCATION;\n    public static final String MICROPHONE = Manifest.permission_group.MICROPHONE;\n    public static final String PHONE      = Manifest.permission_group.PHONE;\n    public static final String SENSORS    = Manifest.permission_group.SENSORS;\n    public static final String SMS        = Manifest.permission_group.SMS;\n    public static final String STORAGE    = Manifest.permission_group.STORAGE;\n\n    private static final String[] GROUP_CALENDAR   = {\n            permission.READ_CALENDAR, permission.WRITE_CALENDAR\n    };\n    private static final String[] GROUP_CAMERA     = {\n            permission.CAMERA\n    };\n    private static final String[] GROUP_CONTACTS   = {\n            permission.READ_CONTACTS, permission.WRITE_CONTACTS, permission.GET_ACCOUNTS\n    };\n    private static final String[] GROUP_LOCATION   = {\n            permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION\n    };\n    private static final String[] GROUP_MICROPHONE = {\n            permission.RECORD_AUDIO\n    };\n    private static final String[] GROUP_PHONE      = {\n            permission.READ_PHONE_STATE, permission.CALL_PHONE\n            , permission.READ_CALL_LOG, permission.WRITE_CALL_LOG,\n            permission.ADD_VOICEMAIL, permission.USE_SIP, permission.PROCESS_OUTGOING_CALLS\n    };\n    private static final String[] GROUP_SENSORS    = {\n            permission.BODY_SENSORS\n    };\n    private static final String[] GROUP_SMS        = {\n            permission.SEND_SMS, permission.RECEIVE_SMS, permission.READ_SMS,\n            permission.RECEIVE_WAP_PUSH, permission.RECEIVE_MMS,\n    };\n    private static final String[] GROUP_STORAGE    = {\n            permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE\n    };\n\n    @StringDef({CALENDAR, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE,})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Permission {\n    }\n\n    public static String[] getPermissions(@Permission final String permission) {\n        switch (permission) {\n            case CALENDAR:\n                return GROUP_CALENDAR;\n            case CAMERA:\n                return GROUP_CAMERA;\n            case CONTACTS:\n                return GROUP_CONTACTS;\n            case LOCATION:\n                return GROUP_LOCATION;\n            case MICROPHONE:\n                return GROUP_MICROPHONE;\n            case PHONE:\n                return GROUP_PHONE;\n            case SENSORS:\n                return GROUP_SENSORS;\n            case SMS:\n                return GROUP_SMS;\n            case STORAGE:\n                return GROUP_STORAGE;\n        }\n        return new String[]{permission};\n    }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/utils/PermissionUtils.java",
    "content": "package com.nanchen.scanner.utils;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.graphics.Color;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.annotation.RequiresApi;\nimport android.support.v4.content.ContextCompat;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.WindowManager;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/29\n *     desc  : utils about permission\n * </pre>\n */\npublic final class PermissionUtils {\n\n    private static List<String> PERMISSIONS = null;\n\n    private static PermissionUtils sInstance;\n\n    private OnRationaleListener mOnRationaleListener;\n    private SimpleCallback mSimpleCallback;\n    private FullCallback mFullCallback;\n    private ThemeCallback mThemeCallback;\n    private Set<String> mPermissions;\n    private List<String> mPermissionsRequest;\n    private List<String> mPermissionsGranted;\n    private List<String> mPermissionsDenied;\n    private List<String> mPermissionsDeniedForever;\n    private static Context mApp;\n\n    /**\n     * Return the permissions used in application.\n     *\n     * @return the permissions used in application\n     */\n    public static List<String> getPermissions() {\n        return getPermissions(mApp.getPackageName());\n    }\n\n\n    /**\n     * Return the permissions used in application.\n     *\n     * @param packageName The name of the package.\n     * @return the permissions used in application\n     */\n    public static List<String> getPermissions(final String packageName) {\n        PackageManager pm = mApp.getPackageManager();\n        try {\n            return Arrays.asList(\n                    pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)\n                            .requestedPermissions\n            );\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return Collections.emptyList();\n        }\n    }\n\n    /**\n     * Return whether <em>you</em> have granted the permissions.\n     *\n     * @param permissions The permissions.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isGranted(final String... permissions) {\n        for (String permission : permissions) {\n            if (!isGranted(permission)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private static boolean isGranted(final String permission) {\n        return Build.VERSION.SDK_INT < Build.VERSION_CODES.M\n                || PackageManager.PERMISSION_GRANTED\n                == ContextCompat.checkSelfPermission(mApp, permission);\n    }\n\n    /**\n     * Launch the application's details settings.\n     */\n    public static void launchAppDetailsSettings() {\n        Intent intent = new Intent(\"android.settings.APPLICATION_DETAILS_SETTINGS\");\n        intent.setData(Uri.parse(\"package:\" + mApp.getPackageName()));\n        mApp.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));\n    }\n\n    /**\n     * Set the permissions.\n     *\n     * @param permissions The permissions.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public static PermissionUtils permission(Context mContext, @PermissionConstants.Permission final String...\n            permissions\n    ) {\n        mApp = mContext;\n        PERMISSIONS = getPermissions();\n        return new PermissionUtils(permissions);\n    }\n\n    private PermissionUtils(final String... permissions) {\n        mPermissions = new LinkedHashSet<>();\n        for (String permission : permissions) {\n            for (String aPermission : PermissionConstants.getPermissions(permission)) {\n                if (PERMISSIONS.contains(aPermission)) {\n                    mPermissions.add(aPermission);\n                }\n            }\n        }\n        sInstance = this;\n    }\n\n    /**\n     * Set rationale listener.\n     *\n     * @param listener The rationale listener.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils rationale(final OnRationaleListener listener) {\n        mOnRationaleListener = listener;\n        return this;\n    }\n\n    /**\n     * Set the simple call back.\n     *\n     * @param callback the simple call back\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils callback(final SimpleCallback callback) {\n        mSimpleCallback = callback;\n        return this;\n    }\n\n    /**\n     * Set the full call back.\n     *\n     * @param callback the full call back\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils callback(final FullCallback callback) {\n        mFullCallback = callback;\n        return this;\n    }\n\n    /**\n     * Set the theme callback.\n     *\n     * @param callback The theme callback.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils theme(final ThemeCallback callback) {\n        mThemeCallback = callback;\n        return this;\n    }\n\n    /**\n     * Start request.\n     */\n    public void request() {\n        mPermissionsGranted = new ArrayList<>();\n        mPermissionsRequest = new ArrayList<>();\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            mPermissionsGranted.addAll(mPermissions);\n            requestCallback();\n        } else {\n            for (String permission : mPermissions) {\n                if (isGranted(permission)) {\n                    mPermissionsGranted.add(permission);\n                } else {\n                    mPermissionsRequest.add(permission);\n                }\n            }\n            if (mPermissionsRequest.isEmpty()) {\n                requestCallback();\n            } else {\n                startPermissionActivity();\n            }\n        }\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private void startPermissionActivity() {\n        mPermissionsDenied = new ArrayList<>();\n        mPermissionsDeniedForever = new ArrayList<>();\n        PermissionActivity.start(mApp);\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private boolean rationale(final Activity activity) {\n        boolean isRationale = false;\n        if (mOnRationaleListener != null) {\n            for (String permission : mPermissionsRequest) {\n                if (activity.shouldShowRequestPermissionRationale(permission)) {\n                    getPermissionsStatus(activity);\n                    mOnRationaleListener.rationale(new OnRationaleListener.ShouldRequest() {\n                        @Override\n                        public void again(boolean again) {\n                            if (again) {\n                                startPermissionActivity();\n                            } else {\n                                requestCallback();\n                            }\n                        }\n                    });\n                    isRationale = true;\n                    break;\n                }\n            }\n            mOnRationaleListener = null;\n        }\n        return isRationale;\n    }\n\n    private void getPermissionsStatus(final Activity activity) {\n        for (String permission : mPermissionsRequest) {\n            if (isGranted(permission)) {\n                mPermissionsGranted.add(permission);\n            } else {\n                mPermissionsDenied.add(permission);\n                if (!activity.shouldShowRequestPermissionRationale(permission)) {\n                    mPermissionsDeniedForever.add(permission);\n                }\n            }\n        }\n    }\n\n    private void requestCallback() {\n        if (mSimpleCallback != null) {\n            if (mPermissionsRequest.size() == 0\n                    || mPermissions.size() == mPermissionsGranted.size()) {\n                mSimpleCallback.onGranted();\n            } else {\n                if (!mPermissionsDenied.isEmpty()) {\n                    mSimpleCallback.onDenied();\n                }\n            }\n            mSimpleCallback = null;\n        }\n        if (mFullCallback != null) {\n            if (mPermissionsRequest.size() == 0\n                    || mPermissions.size() == mPermissionsGranted.size()) {\n                mFullCallback.onGranted(mPermissionsGranted);\n            } else {\n                if (!mPermissionsDenied.isEmpty()) {\n                    mFullCallback.onDenied(mPermissionsDeniedForever, mPermissionsDenied);\n                }\n            }\n            mFullCallback = null;\n        }\n        mOnRationaleListener = null;\n        mThemeCallback = null;\n    }\n\n    private void onRequestPermissionsResult(final Activity activity) {\n        getPermissionsStatus(activity);\n        requestCallback();\n    }\n\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static class PermissionActivity extends Activity {\n\n        public static void start(final Context context) {\n            Intent starter = new Intent(context, PermissionActivity.class);\n            starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            context.startActivity(starter);\n        }\n\n        @Override\n        protected void onCreate(@Nullable Bundle savedInstanceState) {\n            getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE\n                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);\n            getWindow().setStatusBarColor(Color.TRANSPARENT);\n            if (sInstance == null) {\n                super.onCreate(savedInstanceState);\n                Log.e(\"PermissionUtils\", \"request permissions failed\");\n                finish();\n                return;\n            }\n            if (sInstance.mThemeCallback != null) {\n                sInstance.mThemeCallback.onActivityCreate(this);\n            }\n            super.onCreate(savedInstanceState);\n\n            if (sInstance.rationale(this)) {\n                finish();\n                return;\n            }\n            if (sInstance.mPermissionsRequest != null) {\n                int size = sInstance.mPermissionsRequest.size();\n                if (size <= 0) {\n                    finish();\n                    return;\n                }\n                requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1);\n            }\n        }\n\n        @Override\n        public void onRequestPermissionsResult(int requestCode,\n                                               @NonNull String[] permissions,\n                                               @NonNull int[] grantResults) {\n            sInstance.onRequestPermissionsResult(this);\n            finish();\n        }\n\n        @Override\n        public boolean dispatchTouchEvent(MotionEvent ev) {\n            finish();\n            return true;\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    public interface OnRationaleListener {\n\n        void rationale(ShouldRequest shouldRequest);\n\n        interface ShouldRequest {\n            void again(boolean again);\n        }\n    }\n\n    public interface SimpleCallback {\n        void onGranted();\n\n        void onDenied();\n    }\n\n    public interface FullCallback {\n        void onGranted(List<String> permissionsGranted);\n\n        void onDenied(List<String> permissionsDeniedForever, List<String> permissionsDenied);\n    }\n\n    public interface ThemeCallback {\n        void onActivityCreate(Activity activity);\n    }\n}"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/utils/QRUtils.java",
    "content": "package com.nanchen.scanner.utils;\n\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.text.TextUtils;\n\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.ChecksumException;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.FormatException;\nimport com.google.zxing.NotFoundException;\nimport com.google.zxing.RGBLuminanceSource;\nimport com.google.zxing.Result;\nimport com.google.zxing.common.GlobalHistogramBinarizer;\nimport com.google.zxing.qrcode.QRCodeReader;\n\nimport java.util.Hashtable;\n\n\npublic class QRUtils {\n    private static QRUtils instance;\n\n\n    public static QRUtils getInstance() {\n        if (instance == null)\n            instance = new QRUtils();\n        return instance;\n    }\n\n    /**\n     * 扫描二维码图片的方法\n     */\n    public String decodeQRcodeByZxing(Bitmap scanBitmap) {\n        if (scanBitmap == null) {\n            return null;\n        }\n        Hashtable<DecodeHintType, String> hints = new Hashtable<>();\n        hints.put(DecodeHintType.CHARACTER_SET, \"UTF-8\"); // 设置二维码内容的编码\n        int[] data = new int[scanBitmap.getWidth() * scanBitmap.getHeight()];\n        scanBitmap.getPixels(data, 0, scanBitmap.getWidth(), 0, 0, scanBitmap.getWidth(), scanBitmap.getHeight());\n        RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(scanBitmap.getWidth(), scanBitmap.getHeight(), data);\n        BinaryBitmap binaryBitmap = new BinaryBitmap(new GlobalHistogramBinarizer(rgbLuminanceSource));\n        QRCodeReader reader = new QRCodeReader();\n        try {\n            Result result = reader.decode(binaryBitmap, hints);\n            return result.getText();\n        } catch (NotFoundException e) {\n            return \"\";\n        } catch (ChecksumException e) {\n            return \"\";\n        } catch (FormatException e) {\n            return \"\";\n        }\n\n    }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/utils/ScreenUtils.java",
    "content": "package com.nanchen.scanner.utils;\n\nimport android.app.Activity;\nimport android.app.KeyguardManager;\nimport android.content.Context;\nimport android.content.pm.ActivityInfo;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Point;\nimport android.os.Build;\nimport android.provider.Settings;\nimport android.support.annotation.NonNull;\nimport android.util.DisplayMetrics;\nimport android.view.Surface;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\n\n/**\n * Author: nanchen\n * Email: liusl@codoon.com\n * Date: 2019/1/15 14:14\n */\npublic final class ScreenUtils {\n    private ScreenUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the width of screen, in pixel.\n     *\n     * @return the width of screen, in pixel\n     */\n    public static int getScreenWidth(Context context) {\n        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        Point point = new Point();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            //noinspection ConstantConditions\n            wm.getDefaultDisplay().getRealSize(point);\n        } else {\n            //noinspection ConstantConditions\n            wm.getDefaultDisplay().getSize(point);\n        }\n        return point.x;\n    }\n\n    /**\n     * Return the height of screen, in pixel.\n     *\n     * @return the height of screen, in pixel\n     */\n    public static int getScreenHeight(Context context) {\n        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        Point point = new Point();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            //noinspection ConstantConditions\n            wm.getDefaultDisplay().getRealSize(point);\n        } else {\n            //noinspection ConstantConditions\n            wm.getDefaultDisplay().getSize(point);\n        }\n        return point.y;\n    }\n\n    /**\n     * Return the density of screen.\n     *\n     * @return the density of screen\n     */\n    public static float getScreenDensity() {\n        return Resources.getSystem().getDisplayMetrics().density;\n    }\n\n    /**\n     * Return the screen density expressed as dots-per-inch.\n     *\n     * @return the screen density expressed as dots-per-inch\n     */\n    public static int getScreenDensityDpi() {\n        return Resources.getSystem().getDisplayMetrics().densityDpi;\n    }\n\n    /**\n     * Set full screen.\n     *\n     * @param activity The activity.\n     */\n    public static void setFullScreen(@NonNull final Activity activity) {\n        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n    }\n\n    /**\n     * Set non full screen.\n     *\n     * @param activity The activity.\n     */\n    public static void setNonFullScreen(@NonNull final Activity activity) {\n        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n    }\n\n    /**\n     * Toggle full screen.\n     *\n     * @param activity The activity.\n     */\n    public static void toggleFullScreen(@NonNull final Activity activity) {\n        int fullScreenFlag = WindowManager.LayoutParams.FLAG_FULLSCREEN;\n        Window window = activity.getWindow();\n        if ((window.getAttributes().flags & fullScreenFlag) == fullScreenFlag) {\n            window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN\n                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);\n        } else {\n            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN\n                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);\n        }\n    }\n\n    /**\n     * Return whether screen is full.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFullScreen(@NonNull final Activity activity) {\n        int fullScreenFlag = WindowManager.LayoutParams.FLAG_FULLSCREEN;\n        return (activity.getWindow().getAttributes().flags & fullScreenFlag) == fullScreenFlag;\n    }\n\n    /**\n     * Set the screen to landscape.\n     *\n     * @param activity The activity.\n     */\n    public static void setLandscape(@NonNull final Activity activity) {\n        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n    }\n\n    /**\n     * Set the screen to portrait.\n     *\n     * @param activity The activity.\n     */\n    public static void setPortrait(@NonNull final Activity activity) {\n        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);\n    }\n\n    /**\n     * Return whether screen is landscape.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLandscape(Context context) {\n        return context.getResources().getConfiguration().orientation\n                == Configuration.ORIENTATION_LANDSCAPE;\n    }\n\n    /**\n     * Return whether screen is portrait.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPortrait(Context context) {\n        return context.getResources().getConfiguration().orientation\n                == Configuration.ORIENTATION_PORTRAIT;\n    }\n\n    /**\n     * Return the rotation of screen.\n     *\n     * @param activity The activity.\n     * @return the rotation of screen\n     */\n    public static int getScreenRotation(@NonNull final Activity activity) {\n        switch (activity.getWindowManager().getDefaultDisplay().getRotation()) {\n            case Surface.ROTATION_0:\n                return 0;\n            case Surface.ROTATION_90:\n                return 90;\n            case Surface.ROTATION_180:\n                return 180;\n            case Surface.ROTATION_270:\n                return 270;\n            default:\n                return 0;\n        }\n    }\n\n    /**\n     * Return the bitmap of screen.\n     *\n     * @param activity The activity.\n     * @return the bitmap of screen\n     */\n    public static Bitmap screenShot(@NonNull final Activity activity) {\n        return screenShot(activity, false);\n    }\n\n    /**\n     * Return the bitmap of screen.\n     *\n     * @param activity          The activity.\n     * @param isDeleteStatusBar True to delete status bar, false otherwise.\n     * @return the bitmap of screen\n     */\n    public static Bitmap screenShot(@NonNull final Activity activity, boolean isDeleteStatusBar) {\n        View decorView = activity.getWindow().getDecorView();\n        decorView.setDrawingCacheEnabled(true);\n        decorView.setWillNotCacheDrawing(false);\n        Bitmap bmp = decorView.getDrawingCache();\n        if (bmp == null) return null;\n        DisplayMetrics dm = new DisplayMetrics();\n        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);\n        Bitmap ret;\n        if (isDeleteStatusBar) {\n            Resources resources = activity.getResources();\n            int resourceId = resources.getIdentifier(\"status_bar_height\", \"dimen\", \"android\");\n            int statusBarHeight = resources.getDimensionPixelSize(resourceId);\n            ret = Bitmap.createBitmap(\n                    bmp,\n                    0,\n                    statusBarHeight,\n                    dm.widthPixels,\n                    dm.heightPixels - statusBarHeight\n            );\n        } else {\n            ret = Bitmap.createBitmap(bmp, 0, 0, dm.widthPixels, dm.heightPixels);\n        }\n        decorView.destroyDrawingCache();\n        return ret;\n    }\n\n    /**\n     * Return whether screen is locked.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isScreenLock(Context context) {\n        KeyguardManager km =\n                (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);\n        //noinspection ConstantConditions\n        return km.inKeyguardRestrictedInputMode();\n    }\n\n\n    /**\n     * Return the duration of sleep.\n     *\n     * @return the duration of sleep.\n     */\n    public static int getSleepDuration(Context context) {\n        try {\n            return Settings.System.getInt(\n                    context.getContentResolver(),\n                    Settings.System.SCREEN_OFF_TIMEOUT\n            );\n        } catch (Settings.SettingNotFoundException e) {\n            e.printStackTrace();\n            return -123;\n        }\n    }\n\n    /**\n     * Return whether device is tablet.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isTablet(Context context) {\n        return (context.getResources().getConfiguration().screenLayout\n                & Configuration.SCREENLAYOUT_SIZE_MASK)\n                >= Configuration.SCREENLAYOUT_SIZE_LARGE;\n    }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/AmbientLightManager.java",
    "content": "/*\n * Copyright (C) 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.hardware.Sensor;\nimport android.hardware.SensorEvent;\nimport android.hardware.SensorEventListener;\nimport android.hardware.SensorManager;\nimport android.preference.PreferenceManager;\n\nimport com.nanchen.scanner.zxing.camera.CameraManager;\nimport com.nanchen.scanner.zxing.camera.FrontLightMode;\n\n/**\n * Detects ambient light and switches on the front light when very dark, and off again when sufficiently light.\n *\n * @author Sean Owen\n * @author Nikolaus Huber\n */\nfinal class AmbientLightManager implements SensorEventListener {\n\n  private static final float TOO_DARK_LUX = 45.0f;\n  private static final float BRIGHT_ENOUGH_LUX = 450.0f;\n\n  private final Context context;\n  private CameraManager cameraManager;\n  private Sensor lightSensor;\n\n  AmbientLightManager(Context context) {\n    this.context = context;\n  }\n\n  void start(CameraManager cameraManager) {\n    this.cameraManager = cameraManager;\n    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);\n    if (FrontLightMode.readPref(sharedPrefs) == FrontLightMode.AUTO) {\n      SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);\n      lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);\n      if (lightSensor != null) {\n        sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);\n      }\n    }\n  }\n\n  void stop() {\n    if (lightSensor != null) {\n      SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);\n      sensorManager.unregisterListener(this);\n      cameraManager = null;\n      lightSensor = null;\n    }\n  }\n\n  @Override\n  public void onSensorChanged(SensorEvent sensorEvent) {\n    float ambientLightLux = sensorEvent.values[0];\n    if (cameraManager != null) {\n      if (ambientLightLux <= TOO_DARK_LUX) {\n        cameraManager.setTorch(true);\n      } else if (ambientLightLux >= BRIGHT_ENOUGH_LUX) {\n        cameraManager.setTorch(false);\n      }\n    }\n  }\n\n  @Override\n  public void onAccuracyChanged(Sensor sensor, int accuracy) {\n    // do nothing\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/BaseCaptureActivity.java",
    "content": "/*\n * Copyright (C) 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport com.nanchen.scanner.zxing.camera.CameraManager;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.os.Handler;\nimport android.view.SurfaceHolder;\n\n/**\n * This activity opens the camera and does the actual scanning on a background thread. It draws a\n * viewfinder to help the user place the barcode correctly, shows feedback as the image processing\n * is happening, and then overlays the results when a scan is successful.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n * @author Sean Owen\n */\npublic abstract class BaseCaptureActivity extends Activity implements SurfaceHolder.Callback {\n\n    public abstract ViewfinderView getViewfinderView();\n\n    public abstract Handler getHandler();\n\n    public abstract CameraManager getCameraManager();\n\n    public abstract void handleDecode(String result, Bitmap barcode, float scaleFactor);\n\n    public abstract void drawViewfinder();\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/BeepManager.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.res.AssetFileDescriptor;\nimport android.media.AudioManager;\nimport android.media.MediaPlayer;\nimport android.os.Build;\nimport android.os.Vibrator;\nimport android.preference.PreferenceManager;\nimport android.util.Log;\n\nimport com.nanchen.scanner.R;\n\nimport java.io.Closeable;\nimport java.io.IOException;\n\n/**\n * Manages beeps and vibrations for {@link BaseCaptureActivity}.\n */\npublic final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {\n\n    private static final String TAG = BeepManager.class.getSimpleName();\n\n    private static final float BEEP_VOLUME = 0.10f;\n    private static final long VIBRATE_DURATION = 200L;\n\n    private final Activity activity;\n    private MediaPlayer mediaPlayer;\n    private boolean playBeep;\n    private boolean vibrate;\n\n    public BeepManager(Activity activity) {\n        this.activity = activity;\n        this.mediaPlayer = null;\n        updatePrefs();\n    }\n\n    public synchronized void updatePrefs() {\n        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);\n        playBeep = shouldBeep(prefs, activity);\n        // vibrate = prefs.getBoolean(PreferencesActivity.KEY_VIBRATE, false);\n        vibrate = true;\n        if (playBeep && mediaPlayer == null) {\n            // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,\n            // so we now play on the music stream.\n            activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);\n            mediaPlayer = buildMediaPlayer(activity);\n        }\n    }\n\n    public synchronized void playBeepSoundAndVibrate() {\n        if (playBeep && mediaPlayer != null) {\n            mediaPlayer.start();\n        }\n        if (vibrate) {\n            Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);\n            vibrator.vibrate(VIBRATE_DURATION);\n        }\n    }\n\n    private static boolean shouldBeep(SharedPreferences prefs, Context activity) {\n//    boolean shouldPlayBeep = prefs.getBoolean(PreferencesActivity.KEY_PLAY_BEEP, true);\n        // 直接true\n        boolean shouldPlayBeep = true;\n        if (shouldPlayBeep) {\n            // See if sound settings overrides this\n            AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);\n            if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {\n                shouldPlayBeep = false;\n            }\n        }\n        return shouldPlayBeep;\n    }\n\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    private MediaPlayer buildMediaPlayer(Context activity) {\n        MediaPlayer mediaPlayer = new MediaPlayer();\n        try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep)) {\n            mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());\n            mediaPlayer.setOnErrorListener(this);\n            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);\n            mediaPlayer.setLooping(false);\n            mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);\n            mediaPlayer.prepare();\n            return mediaPlayer;\n        } catch (IOException ioe) {\n            Log.w(TAG, ioe);\n            mediaPlayer.release();\n            return null;\n        }\n    }\n\n    @Override\n    public synchronized boolean onError(MediaPlayer mp, int what, int extra) {\n        if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {\n            // we are finished, so put up an appropriate error toast if required and finish\n            activity.finish();\n        } else {\n            // possibly media player error, so release and recreate\n            close();\n            updatePrefs();\n        }\n        return true;\n    }\n\n    @Override\n    public synchronized void close() {\n        if (mediaPlayer != null) {\n            mediaPlayer.release();\n            mediaPlayer = null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/CaptureActivityHandler.java",
    "content": "/*\n * Copyright (C) 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.content.ActivityNotFoundException;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.graphics.BitmapFactory;\nimport android.provider.Browser;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.nanchen.scanner.R;\nimport com.nanchen.scanner.zxing.camera.CameraManager;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.Log;\n\n\n/**\n * This class handles all the messaging which comprises the state machine for capture.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class CaptureActivityHandler extends Handler {\n\n    private static final String TAG = CaptureActivityHandler.class.getSimpleName();\n\n    private final BaseCaptureActivity activity;\n    private final DecodeThread decodeThread;\n    private State state;\n    private final CameraManager cameraManager;\n\n    private enum State {\n        PREVIEW,\n        SUCCESS,\n        DONE\n    }\n\n    public CaptureActivityHandler(BaseCaptureActivity activity,\n                                  CameraManager cameraManager) {\n        this.activity = activity;\n        decodeThread = new DecodeThread(activity);\n        decodeThread.start();\n        state = State.SUCCESS;\n\n        // Start ourselves capturing previews and decoding.\n        this.cameraManager = cameraManager;\n        cameraManager.startPreview();\n        restartPreviewAndDecode();\n    }\n\n    @Override\n    public void handleMessage(Message message) {\n        if (message.what == R.id.restart_preview) {\n            restartPreviewAndDecode();\n        } else if (message.what == R.id.decode_succeeded) {\n            state = State.SUCCESS;\n            Bundle bundle = message.getData();\n            Bitmap barcode = null;\n            float scaleFactor = 1.0f;\n            if (bundle != null) {\n                byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);\n                if (compressedBitmap != null) {\n                    barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);\n                    // Mutable copy:\n                    barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);\n                }\n                scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);\n            }\n            activity.handleDecode((String) message.obj, barcode, scaleFactor);\n\n        } else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one decode fails, start another.\n            state = State.PREVIEW;\n            cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);\n        } else if (message.what == R.id.return_scan_result) {\n            activity.setResult(Activity.RESULT_OK, (Intent) message.obj);\n            activity.finish();\n\n        } else if (message.what == R.id.launch_product_query) {\n            String url = (String) message.obj;\n\n            Intent intent = new Intent(Intent.ACTION_VIEW);\n            intent.addFlags(Intents.FLAG_NEW_DOC);\n            intent.setData(Uri.parse(url));\n\n            ResolveInfo resolveInfo =\n                    activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);\n            String browserPackageName = null;\n            if (resolveInfo != null && resolveInfo.activityInfo != null) {\n                browserPackageName = resolveInfo.activityInfo.packageName;\n                Log.d(TAG, \"Using browser in package \" + browserPackageName);\n            }\n\n            // Needed for default Android browser / Chrome only apparently\n            if (browserPackageName != null) {\n                switch (browserPackageName) {\n                    case \"com.android.browser\":\n                    case \"com.android.chrome\":\n                        intent.setPackage(browserPackageName);\n                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                        intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);\n                        break;\n                }\n            }\n\n            try {\n                activity.startActivity(intent);\n            } catch (ActivityNotFoundException ignored) {\n                Log.w(TAG, \"Can't find anything to handle VIEW of URI\");\n            }\n\n        }\n    }\n\n    public void quitSynchronously() {\n        state = State.DONE;\n        cameraManager.stopPreview();\n        Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);\n        quit.sendToTarget();\n        try {\n            // Wait at most half a second; should be enough time, and onPause() will timeout quickly\n            decodeThread.join(500L);\n        } catch (InterruptedException e) {\n            // continue\n        }\n\n        // Be absolutely sure we don't send any queued up messages\n        removeMessages(R.id.decode_succeeded);\n        removeMessages(R.id.decode_failed);\n    }\n\n    private void restartPreviewAndDecode() {\n        if (state == State.SUCCESS) {\n            state = State.PREVIEW;\n            cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);\n            activity.drawViewfinder();\n        }\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/Contents.java",
    "content": "/*\n * Copyright (C) 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.provider.ContactsContract;\n\n/**\n * The set of constants to use when sending Barcode Scanner an Intent which requests a barcode\n * to be encoded.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class Contents {\n  private Contents() {\n  }\n\n  /**\n   * Contains type constants used when sending Intents.\n   */\n  public static final class Type {\n    /**\n     * Plain text. Use Intent.putExtra(DATA, string). This can be used for URLs too, but string\n     * must include \"http://\" or \"https://\".\n     */\n    public static final String TEXT = \"TEXT_TYPE\";\n\n    /**\n     * An email type. Use Intent.putExtra(DATA, string) where string is the email address.\n     */\n    public static final String EMAIL = \"EMAIL_TYPE\";\n\n    /**\n     * Use Intent.putExtra(DATA, string) where string is the phone number to call.\n     */\n    public static final String PHONE = \"PHONE_TYPE\";\n\n    /**\n     * An SMS type. Use Intent.putExtra(DATA, string) where string is the number to SMS.\n     */\n    public static final String SMS = \"SMS_TYPE\";\n\n    /**\n     * A contact. Send a request to encode it as follows:\n     * {@code\n     * import android.provider.Contacts;\n     *\n     * Intent intent = new Intent(Intents.Encode.ACTION);\n     * intent.putExtra(Intents.Encode.TYPE, CONTACT);\n     * Bundle bundle = new Bundle();\n     * bundle.putString(ContactsContract.Intents.Insert.NAME, \"Jenny\");\n     * bundle.putString(ContactsContract.Intents.Insert.PHONE, \"8675309\");\n     * bundle.putString(ContactsContract.Intents.Insert.EMAIL, \"jenny@the80s.com\");\n     * bundle.putString(ContactsContract.Intents.Insert.POSTAL, \"123 Fake St. San Francisco, CA 94102\");\n     * intent.putExtra(Intents.Encode.DATA, bundle);\n     * }\n     */\n    public static final String CONTACT = \"CONTACT_TYPE\";\n\n    /**\n     * A geographic location. Use as follows:\n     * Bundle bundle = new Bundle();\n     * bundle.putFloat(\"LAT\", latitude);\n     * bundle.putFloat(\"LONG\", longitude);\n     * intent.putExtra(Intents.Encode.DATA, bundle);\n     */\n    public static final String LOCATION = \"LOCATION_TYPE\";\n\n    private Type() {\n    }\n  }\n\n  public static final String URL_KEY = \"URL_KEY\";\n\n  public static final String NOTE_KEY = \"NOTE_KEY\";\n\n  /**\n   * When using Type.CONTACT, these arrays provide the keys for adding or retrieving multiple\n   * phone numbers and addresses.\n   */\n  public static final String[] PHONE_KEYS = {\n      ContactsContract.Intents.Insert.PHONE,\n      ContactsContract.Intents.Insert.SECONDARY_PHONE,\n      ContactsContract.Intents.Insert.TERTIARY_PHONE\n  };\n\n  public static final String[] PHONE_TYPE_KEYS = {\n      ContactsContract.Intents.Insert.PHONE_TYPE,\n      ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE,\n      ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE\n  };\n\n  public static final String[] EMAIL_KEYS = {\n      ContactsContract.Intents.Insert.EMAIL,\n      ContactsContract.Intents.Insert.SECONDARY_EMAIL,\n      ContactsContract.Intents.Insert.TERTIARY_EMAIL\n  };\n\n  public static final String[] EMAIL_TYPE_KEYS = {\n      ContactsContract.Intents.Insert.EMAIL_TYPE,\n      ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE,\n      ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE\n  };\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/DecodeFormatManager.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.util.ArraySet;\n\nimport com.google.zxing.BarcodeFormat;\n\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\npublic final class DecodeFormatManager {\n\n    private static final Pattern COMMA_PATTERN = Pattern.compile(\",\");\n\n    static final Set<BarcodeFormat> PRODUCT_FORMATS;\n    static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;\n    private static final Set<BarcodeFormat> ONE_D_FORMATS;\n    static final Set<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);\n    static final Set<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);\n    static final Set<BarcodeFormat> AZTEC_FORMATS = EnumSet.of(BarcodeFormat.AZTEC);\n    static final Set<BarcodeFormat> PDF417_FORMATS = EnumSet.of(BarcodeFormat.PDF_417);\n\n    static {\n        PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,\n                BarcodeFormat.UPC_E,\n                BarcodeFormat.EAN_13,\n                BarcodeFormat.EAN_8,\n                BarcodeFormat.RSS_14,\n                BarcodeFormat.RSS_EXPANDED);\n        INDUSTRIAL_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,\n                BarcodeFormat.CODE_93,\n                BarcodeFormat.CODE_128,\n                BarcodeFormat.ITF,\n                BarcodeFormat.CODABAR);\n//        ONE_D_FORMATS = EnumSet.copyOf(PRODUCT_FORMATS);\n//        ONE_D_FORMATS.addAll(INDUSTRIAL_FORMATS);\n        // 这里默认值支持二维码和一维码，如果要支持其他格式，请使用上面的代码。\n        ONE_D_FORMATS = EnumSet.of(BarcodeFormat.CODE_128);\n    }\n\n    private static final Map<String, Set<BarcodeFormat>> FORMATS_FOR_MODE;\n\n    static {\n        FORMATS_FOR_MODE = new HashMap<>();\n        FORMATS_FOR_MODE.put(Intents.Scan.ONE_D_MODE, ONE_D_FORMATS);\n        FORMATS_FOR_MODE.put(Intents.Scan.PRODUCT_MODE, PRODUCT_FORMATS);\n        FORMATS_FOR_MODE.put(Intents.Scan.QR_CODE_MODE, QR_CODE_FORMATS);\n        FORMATS_FOR_MODE.put(Intents.Scan.DATA_MATRIX_MODE, DATA_MATRIX_FORMATS);\n        FORMATS_FOR_MODE.put(Intents.Scan.AZTEC_MODE, AZTEC_FORMATS);\n        FORMATS_FOR_MODE.put(Intents.Scan.PDF417_MODE, PDF417_FORMATS);\n    }\n\n    private DecodeFormatManager() {\n    }\n\n    public static Set<BarcodeFormat> onlyQrCode(){\n        return EnumSet.of(BarcodeFormat.QR_CODE,BarcodeFormat.CODE_128);\n    }\n\n\n    public static Set<BarcodeFormat> parseDecodeFormats(Intent intent) {\n        Iterable<String> scanFormats = null;\n        CharSequence scanFormatsString = intent.getStringExtra(Intents.Scan.FORMATS);\n        if (scanFormatsString != null) {\n            scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));\n        }\n        return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));\n    }\n\n    static Set<BarcodeFormat> parseDecodeFormats(Uri inputUri) {\n        List<String> formats = inputUri.getQueryParameters(Intents.Scan.FORMATS);\n        if (formats != null && formats.size() == 1 && formats.get(0) != null) {\n            formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));\n        }\n        return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));\n    }\n\n    private static Set<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats, String decodeMode) {\n        if (scanFormats != null) {\n            Set<BarcodeFormat> formats = EnumSet.noneOf(BarcodeFormat.class);\n            try {\n                for (String format : scanFormats) {\n                    formats.add(BarcodeFormat.valueOf(format));\n                }\n                return formats;\n            } catch (IllegalArgumentException iae) {\n                // ignore it then\n            }\n        }\n        if (decodeMode != null) {\n            return FORMATS_FOR_MODE.get(decodeMode);\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/DecodeHandler.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.graphics.Bitmap;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.BinaryBitmap;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.PlanarYUVLuminanceSource;\nimport com.google.zxing.ReaderException;\nimport com.google.zxing.Result;\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.common.GlobalHistogramBinarizer;\nimport com.google.zxing.common.HybridBinarizer;\nimport com.google.zxing.qrcode.QRCodeReader;\nimport com.nanchen.scanner.R;\nimport com.yanzhenjie.zbar.Image;\nimport com.yanzhenjie.zbar.ImageScanner;\nimport com.yanzhenjie.zbar.Symbol;\nimport com.yanzhenjie.zbar.SymbolSet;\n\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.util.Log;\n\nimport java.io.ByteArrayOutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\n\nfinal class DecodeHandler extends Handler {\n\n    private static final String TAG = DecodeHandler.class.getSimpleName();\n\n    private final BaseCaptureActivity activity;\n    private QRCodeReader qrCodeReader;\n    private ViewfinderResultPointCallback callback;\n    private boolean running = true;\n\n    DecodeHandler(BaseCaptureActivity activity) {\n        qrCodeReader = new QRCodeReader();\n        this.activity = activity;\n        this.callback = new ViewfinderResultPointCallback(activity.getViewfinderView());\n    }\n\n    @Override\n    public void handleMessage(Message message) {\n        if (message == null || !running) {\n            return;\n        }\n        if (message.what == R.id.decode) {\n            decode((byte[]) message.obj, message.arg1, message.arg2);\n\n        } else if (message.what == R.id.quit) {\n            running = false;\n            Looper.myLooper().quit();\n\n        }\n    }\n\n    /**\n     * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,\n     * reuse the same reader objects from one decode to the next.\n     *\n     * @param data   The YUV preview frame.\n     * @param width  The width of the preview frame.\n     * @param height The height of the preview frame.\n     */\n    private void decode(byte[] data, int width, int height) {\n        Map<DecodeHintType, Object> hints = new HashMap<>();\n        hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);\n        hints.put(DecodeHintType.CHARACTER_SET, \"utf-8\");\n        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);\n        hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, callback);\n\n        long start = System.currentTimeMillis();\n        Result rawResult = null;\n\n        String strResult = null;\n\n        // zxing\n        PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);\n        Log.d(TAG, \"width:\" + width + \",height:\" + height + \",newWidth:\" + source.getWidth() + \",newHeight:\" + source.getHeight());\n        BinaryBitmap globalBitmap = new BinaryBitmap(new GlobalHistogramBinarizer(source));\n        try {\n            rawResult = qrCodeReader.decode(globalBitmap, hints);\n            if (rawResult != null)\n                strResult = rawResult.getText();\n        } catch (ReaderException re) {\n            // continue\n        } finally {\n            qrCodeReader.reset();\n        }\n        // hybrid 解码\n//        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));\n//        try {\n//            rawResult = qrCodeReader.decode(bitmap, hints);\n//            if (rawResult != null)\n//                strResult = rawResult.getText();\n//        } catch (ReaderException re) {\n//            // continue\n//        } finally {\n//            qrCodeReader.reset();\n//        }\n//\n//        // zbar 解码\n//        Image barcode = new Image(width, height, \"Y800\");\n//        barcode.setData(data);\n//        Rect rect = activity.getCameraManager().getFramingRectInPreview();\n//        if (rect != null) {\n//            /* zbar 解码库,不需要将数据进行旋转,因此设置裁剪区域是的x为 top, y为left 设置了裁剪区域,解码速度快了近5倍左右 */\n//            barcode.setCrop(rect.top, rect.left, rect.width(), rect.height()); // 设置截取区域，也就是你的扫描框在图片上的区域.\n//        }\n//        ImageScanner mImageScanner = new ImageScanner();\n//        int result = mImageScanner.scanImage(barcode);\n//        if (result != 0) {\n//            SymbolSet symSet = mImageScanner.getResults();\n//            for (Symbol sym : symSet) {\n//                // 未能识别的格式继续遍历\n//                if (sym.getType() == Symbol.NONE) {\n//                    continue;\n//                }\n//                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {\n//                    strResult = new String(sym.getDataBytes(), StandardCharsets.UTF_8);\n//                } else {\n//                    strResult = sym.getData();\n//                }\n//            }\n//        }\n\n        Handler handler = activity.getHandler();\n        if (strResult != null) {\n            // Don't log the barcode contents for security.\n            long end = System.currentTimeMillis();\n            Log.d(TAG, \"Found barcode in \" + (end - start) + \" ms\");\n            if (handler != null) {\n                Message message = Message.obtain(handler, R.id.decode_succeeded, strResult);\n                if (rawResult != null) {\n                    Bundle bundle = new Bundle();\n                    bundleThumbnail(source, bundle);\n                    // 绘制点点\n                    for (ResultPoint points : rawResult.getResultPoints()) {\n                        callback.foundPossibleResultPoint(points);\n                    }\n                    message.setData(bundle);\n                }\n                message.sendToTarget();\n            }\n        } else {\n            if (handler != null) {\n                Message message = Message.obtain(handler, R.id.decode_failed);\n                message.sendToTarget();\n            }\n        }\n    }\n\n\n    private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {\n        int[] pixels = source.renderThumbnail();\n        int width = source.getThumbnailWidth();\n        int height = source.getThumbnailHeight();\n        Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);\n        bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());\n        bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width / source.getWidth());\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/DecodeHintManager.java",
    "content": "/*\n * Copyright (C) 2013 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport java.util.EnumMap;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport com.google.zxing.DecodeHintType;\n\n/**\n * @author Lachezar Dobrev\n */\npublic final class DecodeHintManager {\n\n    private static final String TAG = DecodeHintManager.class.getSimpleName();\n\n    // This pattern is used in decoding integer arrays.\n    private static final Pattern COMMA = Pattern.compile(\",\");\n\n    private DecodeHintManager() {\n    }\n\n    /**\n     * <p>Split a query string into a list of name-value pairs.</p>\n     *\n     * <p>This is an alternative to the {@link Uri#getQueryParameterNames()} and\n     * {@link Uri#getQueryParameters(String)}, which are quirky and not suitable\n     * for exist-only Uri parameters.</p>\n     *\n     * <p>This method ignores multiple parameters with the same name and returns the\n     * first one only. This is technically incorrect, but should be acceptable due\n     * to the method of processing Hints: no multiple values for a hint.</p>\n     *\n     * @param query query to split\n     * @return name-value pairs\n     */\n    private static Map<String, String> splitQuery(String query) {\n        Map<String, String> map = new HashMap<>();\n        int pos = 0;\n        while (pos < query.length()) {\n            if (query.charAt(pos) == '&') {\n                // Skip consecutive ampersand separators.\n                pos++;\n                continue;\n            }\n            int amp = query.indexOf('&', pos);\n            int equ = query.indexOf('=', pos);\n            if (amp < 0) {\n                // This is the last element in the query, no more ampersand elements.\n                String name;\n                String text;\n                if (equ < 0) {\n                    // No equal sign\n                    name = query.substring(pos);\n                    name = name.replace('+', ' '); // Preemptively decode +\n                    name = Uri.decode(name);\n                    text = \"\";\n                } else {\n                    // Split name and text.\n                    name = query.substring(pos, equ);\n                    name = name.replace('+', ' '); // Preemptively decode +\n                    name = Uri.decode(name);\n                    text = query.substring(equ + 1);\n                    text = text.replace('+', ' '); // Preemptively decode +\n                    text = Uri.decode(text);\n                }\n                if (!map.containsKey(name)) {\n                    map.put(name, text);\n                }\n                break;\n            }\n            if (equ < 0 || equ > amp) {\n                // No equal sign until the &: this is a simple parameter with no value.\n                String name = query.substring(pos, amp);\n                name = name.replace('+', ' '); // Preemptively decode +\n                name = Uri.decode(name);\n                if (!map.containsKey(name)) {\n                    map.put(name, \"\");\n                }\n                pos = amp + 1;\n                continue;\n            }\n            String name = query.substring(pos, equ);\n            name = name.replace('+', ' '); // Preemptively decode +\n            name = Uri.decode(name);\n            String text = query.substring(equ + 1, amp);\n            text = text.replace('+', ' '); // Preemptively decode +\n            text = Uri.decode(text);\n            if (!map.containsKey(name)) {\n                map.put(name, text);\n            }\n            pos = amp + 1;\n        }\n        return map;\n    }\n\n    static Map<DecodeHintType, ?> parseDecodeHints(Uri inputUri) {\n        String query = inputUri.getEncodedQuery();\n        if (query == null || query.isEmpty()) {\n            return null;\n        }\n\n        // Extract parameters\n        Map<String, String> parameters = splitQuery(query);\n\n        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);\n\n        for (DecodeHintType hintType : DecodeHintType.values()) {\n\n            if (hintType == DecodeHintType.CHARACTER_SET ||\n                    hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||\n                    hintType == DecodeHintType.POSSIBLE_FORMATS) {\n                continue; // This hint is specified in another way\n            }\n\n            String parameterName = hintType.name();\n            String parameterText = parameters.get(parameterName);\n            if (parameterText == null) {\n                continue;\n            }\n            if (hintType.getValueType().equals(Object.class)) {\n                // This is an unspecified type of hint content. Use the value as is.\n                // TODO: Can we make a different assumption on this?\n                hints.put(hintType, parameterText);\n                continue;\n            }\n            if (hintType.getValueType().equals(Void.class)) {\n                // Void hints are just flags: use the constant specified by DecodeHintType\n                hints.put(hintType, Boolean.TRUE);\n                continue;\n            }\n            if (hintType.getValueType().equals(String.class)) {\n                // A string hint: use the decoded value.\n                hints.put(hintType, parameterText);\n                continue;\n            }\n            if (hintType.getValueType().equals(Boolean.class)) {\n                // A boolean hint: a few values for false, everything else is true.\n                // An empty parameter is simply a flag-style parameter, assuming true\n                if (parameterText.isEmpty()) {\n                    hints.put(hintType, Boolean.TRUE);\n                } else if (\"0\".equals(parameterText) ||\n                        \"false\".equalsIgnoreCase(parameterText) ||\n                        \"no\".equalsIgnoreCase(parameterText)) {\n                    hints.put(hintType, Boolean.FALSE);\n                } else {\n                    hints.put(hintType, Boolean.TRUE);\n                }\n\n                continue;\n            }\n            if (hintType.getValueType().equals(int[].class)) {\n                // An integer array. Used to specify valid lengths.\n                // Strip a trailing comma as in Java style array initialisers.\n                if (!parameterText.isEmpty() && parameterText.charAt(parameterText.length() - 1) == ',') {\n                    parameterText = parameterText.substring(0, parameterText.length() - 1);\n                }\n                String[] values = COMMA.split(parameterText);\n                int[] array = new int[values.length];\n                for (int i = 0; i < values.length; i++) {\n                    try {\n                        array[i] = Integer.parseInt(values[i]);\n                    } catch (NumberFormatException ignored) {\n                        Log.w(TAG, \"Skipping array of integers hint \" + hintType + \" due to invalid numeric value\");\n                        array = null;\n                        break;\n                    }\n                }\n                if (array != null) {\n                    hints.put(hintType, array);\n                }\n                continue;\n            }\n            Log.w(TAG, \"Unsupported hint type '\" + hintType + \"' of type \" + hintType.getValueType());\n        }\n\n        return hints;\n    }\n\n    public static Map<DecodeHintType, Object> parseDecodeHints(Intent intent) {\n        Bundle extras = intent.getExtras();\n        if (extras == null || extras.isEmpty()) {\n            return null;\n        }\n        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);\n\n        for (DecodeHintType hintType : DecodeHintType.values()) {\n\n            if (hintType == DecodeHintType.CHARACTER_SET ||\n                    hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||\n                    hintType == DecodeHintType.POSSIBLE_FORMATS) {\n                continue; // This hint is specified in another way\n            }\n\n            String hintName = hintType.name();\n            if (extras.containsKey(hintName)) {\n                if (hintType.getValueType().equals(Void.class)) {\n                    // Void hints are just flags: use the constant specified by the DecodeHintType\n                    hints.put(hintType, Boolean.TRUE);\n                } else {\n                    Object hintData = extras.get(hintName);\n                    if (hintType.getValueType().isInstance(hintData)) {\n                        hints.put(hintType, hintData);\n                    } else {\n                        Log.w(TAG, \"Ignoring hint \" + hintType + \" because it is not a \" + hintType.getValueType());\n                    }\n                }\n            }\n        }\n\n        return hints;\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/DecodeThread.java",
    "content": "/*\n * Copyright (C) 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport com.google.zxing.BarcodeFormat;\nimport com.google.zxing.DecodeHintType;\nimport com.google.zxing.ResultPointCallback;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\nimport java.util.EnumMap;\nimport java.util.Map;\nimport java.util.concurrent.CountDownLatch;\n\n/**\n * This thread does all the heavy lifting of decoding the images.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\nfinal class DecodeThread extends Thread {\n\n    public static final String BARCODE_BITMAP = \"barcode_bitmap\";\n    public static final String BARCODE_SCALED_FACTOR = \"barcode_scaled_factor\";\n\n    private final BaseCaptureActivity activity;\n    private Handler handler;\n    private final CountDownLatch handlerInitLatch;\n\n    DecodeThread(BaseCaptureActivity activity) {\n        this.activity = activity;\n        handlerInitLatch = new CountDownLatch(1);\n    }\n\n    Handler getHandler() {\n        try {\n            handlerInitLatch.await();\n        } catch (InterruptedException ie) {\n            // continue?\n        }\n        return handler;\n    }\n\n    @Override\n    public void run() {\n        Looper.prepare();\n        handler = new DecodeHandler(activity);\n        handlerInitLatch.countDown();\n        Looper.loop();\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/FinishListener.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.app.Activity;\nimport android.content.DialogInterface;\n\n/**\n * Simple listener used to exit the app in a few cases.\n *\n * @author Sean Owen\n */\npublic final class FinishListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {\n\n  private final Activity activityToFinish;\n\n  public FinishListener(Activity activityToFinish) {\n    this.activityToFinish = activityToFinish;\n  }\n\n  @Override\n  public void onCancel(DialogInterface dialogInterface) {\n    run();\n  }\n\n  @Override\n  public void onClick(DialogInterface dialogInterface, int i) {\n    run();\n  }\n\n  private void run() {\n    activityToFinish.finish();\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/InactivityTimer.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.app.Activity;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.AsyncTask;\nimport android.os.BatteryManager;\nimport android.util.Log;\n\nimport java.util.concurrent.RejectedExecutionException;\n\n/**\n * Finishes an activity after a period of inactivity if the device is on battery power.\n */\npublic final class InactivityTimer {\n\n  private static final String TAG = InactivityTimer.class.getSimpleName();\n\n  private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;\n\n  private final Activity activity;\n  private final BroadcastReceiver powerStatusReceiver;\n  private boolean registered;\n  private AsyncTask<Object,Object,Object> inactivityTask;\n\n  public InactivityTimer(Activity activity) {\n    this.activity = activity;\n    powerStatusReceiver = new PowerStatusReceiver();\n    registered = false;\n    onActivity();\n  }\n\n  public synchronized void onActivity() {\n    cancel();\n    inactivityTask = new InactivityAsyncTask();\n    try {\n      inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);\n    } catch (RejectedExecutionException ree) {\n      Log.w(TAG, \"Couldn't schedule inactivity task; ignoring\"); \n    }\n  }\n\n  public synchronized void onPause() {\n    cancel();\n    if (registered) {\n      activity.unregisterReceiver(powerStatusReceiver);\n      registered = false;\n    } else {\n      Log.w(TAG, \"PowerStatusReceiver was never registered?\");\n    }\n  }\n\n  public synchronized void onResume() {\n    if (registered) {\n      Log.w(TAG, \"PowerStatusReceiver was already registered?\");\n    } else {\n      activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));\n      registered = true;\n    }\n    onActivity();\n  }\n\n  private synchronized void cancel() {\n    AsyncTask<?,?,?> task = inactivityTask;\n    if (task != null) {\n      task.cancel(true);\n      inactivityTask = null;\n    }\n  }\n\n  public void shutdown() {\n    cancel();\n  }\n\n  private final class PowerStatusReceiver extends BroadcastReceiver {\n    @Override\n    public void onReceive(Context context, Intent intent) {\n      if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {\n        // 0 indicates that we're on battery\n        boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;\n        if (onBatteryNow) {\n          InactivityTimer.this.onActivity();\n        } else {\n          InactivityTimer.this.cancel();\n        }\n      }\n    }\n  }\n\n  private final class InactivityAsyncTask extends AsyncTask<Object,Object,Object> {\n    @Override\n    protected Object doInBackground(Object... objects) {\n      try {\n        Thread.sleep(INACTIVITY_DELAY_MS);\n        Log.i(TAG, \"Finishing activity due to inactivity\");\n        activity.finish();\n      } catch (InterruptedException e) {\n        // continue without killing\n      }\n      return null;\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/Intents.java",
    "content": "/*\n * Copyright (C) 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport android.content.Intent;\n\n/**\n * This class provides the constants to use when sending an Intent to Barcode Scanner.\n * These strings are effectively API and cannot be changed.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class Intents {\n  private Intents() {\n  }\n\n  /**\n   * Constants related to the {@link Scan#ACTION} Intent.\n   */\n  public static final class Scan {\n    /**\n     * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return\n     * the results.\n     */\n    public static final String ACTION = \"com.google.zxing.client.android.SCAN\";\n\n    /**\n     * By default, sending this will decode all barcodes that we understand. However it\n     * may be useful to limit scanning to certain formats. Use\n     * {@link Intent#putExtra(String, String)} with one of the values below.\n     *\n     * Setting this is effectively shorthand for setting explicit formats with {@link #FORMATS}.\n     * It is overridden by that setting.\n     */\n    public static final String MODE = \"SCAN_MODE\";\n\n    /**\n     * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get\n     * prices, reviews, etc. for products.\n     */\n    public static final String PRODUCT_MODE = \"PRODUCT_MODE\";\n\n    /**\n     * Decode only 1D barcodes.\n     */\n    public static final String ONE_D_MODE = \"ONE_D_MODE\";\n\n    /**\n     * Decode only QR codes.\n     */\n    public static final String QR_CODE_MODE = \"QR_CODE_MODE\";\n\n    /**\n     * Decode only Data Matrix codes.\n     */\n    public static final String DATA_MATRIX_MODE = \"DATA_MATRIX_MODE\";\n\n    /**\n     * Decode only Aztec.\n     */\n    public static final String AZTEC_MODE = \"AZTEC_MODE\";\n\n    /**\n     * Decode only PDF417.\n     */\n    public static final String PDF417_MODE = \"PDF417_MODE\";\n\n    /**\n     * Comma-separated list of formats to scan for. The values must match the names of\n     * {@link com.google.zxing.BarcodeFormat}s, e.g. {@link com.google.zxing.BarcodeFormat#EAN_13}.\n     * Example: \"EAN_13,EAN_8,QR_CODE\". This overrides {@link #MODE}.\n     */\n    public static final String FORMATS = \"SCAN_FORMATS\";\n\n    /**\n     * Optional parameter to specify the id of the camera from which to recognize barcodes.\n     * Overrides the default camera that would otherwise would have been selected.\n     * If provided, should be an int.\n     */\n    public static final String CAMERA_ID = \"SCAN_CAMERA_ID\";\n\n    /**\n     * @see com.google.zxing.DecodeHintType#CHARACTER_SET\n     */\n    public static final String CHARACTER_SET = \"CHARACTER_SET\";\n\n    /**\n     * Optional parameters to specify the width and height of the scanning rectangle in pixels.\n     * The app will try to honor these, but will clamp them to the size of the preview frame.\n     * You should specify both or neither, and pass the size as an int.\n     */\n    public static final String WIDTH = \"SCAN_WIDTH\";\n    public static final String HEIGHT = \"SCAN_HEIGHT\";\n\n    /**\n     * Desired duration in milliseconds for which to pause after a successful scan before\n     * returning to the calling intent. Specified as a long, not an integer!\n     * For example: 1000L, not 1000.\n     */\n    public static final String RESULT_DISPLAY_DURATION_MS = \"RESULT_DISPLAY_DURATION_MS\";\n\n    /**\n     * Prompt to show on-screen when scanning by intent. Specified as a {@link String}.\n     */\n    public static final String PROMPT_MESSAGE = \"PROMPT_MESSAGE\";\n\n    /**\n     * If a barcode is found, Barcodes returns {@link android.app.Activity#RESULT_OK} to\n     * {@link android.app.Activity#onActivityResult(int, int, Intent)}\n     * of the app which requested the scan via\n     * {@link android.app.Activity#startActivityForResult(Intent, int)}\n     * The barcodes contents can be retrieved with\n     * {@link Intent#getStringExtra(String)}.\n     * If the user presses Back, the result code will be {@link android.app.Activity#RESULT_CANCELED}.\n     */\n    public static final String RESULT = \"SCAN_RESULT\";\n\n    /**\n     * Call {@link Intent#getStringExtra(String)} with {@code RESULT_FORMAT}\n     * to determine which barcode format was found.\n     * See {@link com.google.zxing.BarcodeFormat} for possible values.\n     */\n    public static final String RESULT_FORMAT = \"SCAN_RESULT_FORMAT\";\n\n    /**\n     * Call {@link Intent#getStringExtra(String)} with {@code RESULT_UPC_EAN_EXTENSION}\n     * to return the content of any UPC extension barcode that was also found. Only applicable\n     * to {@link com.google.zxing.BarcodeFormat#UPC_A} and {@link com.google.zxing.BarcodeFormat#EAN_13}\n     * formats.\n     */\n    public static final String RESULT_UPC_EAN_EXTENSION = \"SCAN_RESULT_UPC_EAN_EXTENSION\";\n\n    /**\n     * Call {@link Intent#getByteArrayExtra(String)} with {@code RESULT_BYTES}\n     * to get a {@code byte[]} of raw bytes in the barcode, if available.\n     */\n    public static final String RESULT_BYTES = \"SCAN_RESULT_BYTES\";\n\n    /**\n     * Key for the value of {@link com.google.zxing.ResultMetadataType#ORIENTATION}, if available.\n     * Call {@link Intent#getIntArrayExtra(String)} with {@code RESULT_ORIENTATION}.\n     */\n    public static final String RESULT_ORIENTATION = \"SCAN_RESULT_ORIENTATION\";\n\n    /**\n     * Key for the value of {@link com.google.zxing.ResultMetadataType#ERROR_CORRECTION_LEVEL}, if available.\n     * Call {@link Intent#getStringExtra(String)} with {@code RESULT_ERROR_CORRECTION_LEVEL}.\n     */\n    public static final String RESULT_ERROR_CORRECTION_LEVEL = \"SCAN_RESULT_ERROR_CORRECTION_LEVEL\";\n\n    /**\n     * Prefix for keys that map to the values of {@link com.google.zxing.ResultMetadataType#BYTE_SEGMENTS},\n     * if available. The actual values will be set under a series of keys formed by adding 0, 1, 2, ...\n     * to this prefix. So the first byte segment is under key \"SCAN_RESULT_BYTE_SEGMENTS_0\" for example.\n     * Call {@link Intent#getByteArrayExtra(String)} with these keys.\n     */\n    public static final String RESULT_BYTE_SEGMENTS_PREFIX = \"SCAN_RESULT_BYTE_SEGMENTS_\";\n\n    /**\n     * Setting this to false will not save scanned codes in the history. Specified as a {@code boolean}.\n     */\n    public static final String SAVE_HISTORY = \"SAVE_HISTORY\";\n\n    private Scan() {\n    }\n  }\n\n  /**\n   * Constants related to the scan history and retrieving history items.\n   */\n  public static final class History {\n\n    public static final String ITEM_NUMBER = \"ITEM_NUMBER\";\n\n    private History() {\n    }\n  }\n\n  /**\n   * Constants related to the {@link Encode#ACTION} Intent.\n   */\n  public static final class Encode {\n    /**\n     * Send this intent to encode a piece of data as a QR code and display it full screen, so\n     * that another person can scan the barcode from your screen.\n     */\n    public static final String ACTION = \"com.google.zxing.client.android.ENCODE\";\n\n    /**\n     * The data to encode. Use {@link Intent#putExtra(String, String)} or\n     * {@link Intent#putExtra(String, android.os.Bundle)},\n     * depending on the type and format specified. Non-QR Code formats should\n     * just use a String here. For QR Code, see Contents for details.\n     */\n    public static final String DATA = \"ENCODE_DATA\";\n\n    /**\n     * The type of data being supplied if the format is QR Code. Use\n     * {@link Intent#putExtra(String, String)} with one of {@link Contents.Type}.\n     */\n    public static final String TYPE = \"ENCODE_TYPE\";\n\n    /**\n     * The barcode format to be displayed. If this isn't specified or is blank,\n     * it defaults to QR Code. Use {@link Intent#putExtra(String, String)}, where\n     * format is one of {@link com.google.zxing.BarcodeFormat}.\n     */\n    public static final String FORMAT = \"ENCODE_FORMAT\";\n\n    /**\n     * Normally the contents of the barcode are displayed to the user in a TextView. Setting this\n     * boolean to false will hide that TextView, showing only the encode barcode.\n     */\n    public static final String SHOW_CONTENTS = \"ENCODE_SHOW_CONTENTS\";\n\n    private Encode() {\n    }\n  }\n\n  /**\n   * Constants related to the {@link SearchBookContents#ACTION} Intent.\n   */\n  public static final class SearchBookContents {\n    /**\n     * Use Google Book Search to search the contents of the book provided.\n     */\n    public static final String ACTION = \"com.google.zxing.client.android.SEARCH_BOOK_CONTENTS\";\n\n    /**\n     * The book to search, identified by ISBN number.\n     */\n    public static final String ISBN = \"ISBN\";\n\n    /**\n     * An optional field which is the text to search for.\n     */\n    public static final String QUERY = \"QUERY\";\n\n    private SearchBookContents() {\n    }\n  }\n\n  /**\n   * Constants related to the {@link WifiConnect#ACTION} Intent.\n   */\n  public static final class WifiConnect {\n    /**\n     * Internal intent used to trigger connection to a wi-fi network.\n     */\n    public static final String ACTION = \"com.google.zxing.client.android.WIFI_CONNECT\";\n\n    /**\n     * The network to connect to, all the configuration provided here.\n     */\n    public static final String SSID = \"SSID\";\n\n    /**\n     * The network to connect to, all the configuration provided here.\n     */\n    public static final String TYPE = \"TYPE\";\n\n    /**\n     * The network to connect to, all the configuration provided here.\n     */\n    public static final String PASSWORD = \"PASSWORD\";\n\n    private WifiConnect() {\n    }\n  }\n\n  /**\n   * Constants related to the {@link Share#ACTION} Intent.\n   */\n  public static final class Share {\n    /**\n     * Give the user a choice of items to encode as a barcode, then render it as a QR Code and\n     * display onscreen for a friend to scan with their phone.\n     */\n    public static final String ACTION = \"com.google.zxing.client.android.SHARE\";\n\n    private Share() {\n    }\n  }\n\n  // Not the best place for this, but, better than a new class\n  // Should be FLAG_ACTIVITY_NEW_DOCUMENT in API 21+.\n  // Defined once here because the current value is deprecated, so generates just one warning\n  public static final int FLAG_NEW_DOC = Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/ViewfinderResultPointCallback.java",
    "content": "/*\n * Copyright (C) 2009 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport com.google.zxing.ResultPoint;\nimport com.google.zxing.ResultPointCallback;\n\nfinal class ViewfinderResultPointCallback implements ResultPointCallback {\n\n  private final ViewfinderView viewfinderView;\n\n  ViewfinderResultPointCallback(ViewfinderView viewfinderView) {\n    this.viewfinderView = viewfinderView;\n  }\n\n  @Override\n  public void foundPossibleResultPoint(ResultPoint point) {\n    viewfinderView.addPossibleResultPoint(point);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/ViewfinderView.java",
    "content": "/*\n * Copyright (C) 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing;\n\nimport com.google.zxing.ResultPoint;\nimport com.nanchen.scanner.R;\nimport com.nanchen.scanner.zxing.camera.CameraManager;\n\nimport android.animation.ValueAnimator;\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.animation.AccelerateDecelerateInterpolator;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial\n * transparency outside it, as well as the laser scanner animation and result points.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\npublic final class ViewfinderView extends View {\n\n    private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};\n    private static final long ANIMATION_DELAY = 80L;\n    private static final int CURRENT_POINT_OPACITY = 0xA0;\n    private static final int MAX_RESULT_POINTS = 20;\n    private static final int POINT_SIZE = 6;\n\n    private CameraManager cameraManager;\n    private final Paint paint;\n    private final int maskColor;\n    private final int laserColor;\n    private final int resultPointColor;\n    private int scannerAlpha;\n    private List<ResultPoint> possibleResultPoints;\n    private List<ResultPoint> lastPossibleResultPoints;\n    private int mCornerBarW;\n    private int mCornerBarH;\n    private int mCornerBarColor;\n    private int mLaserH;\n    private int testColor;\n    private int mLaserPos;\n    private ValueAnimator mAnimator;\n\n    // This constructor is used when the class is built from an XML resource.\n    public ViewfinderView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n\n        // Initialize these once for performance rather than calling them every time in onDraw().\n        paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        Resources resources = getResources();\n        maskColor = resources.getColor(R.color.viewfinder_mask);\n        laserColor = resources.getColor(R.color.viewfinder_laser);\n        resultPointColor = resources.getColor(R.color.possible_result_points);\n        mCornerBarW = resources.getDimensionPixelSize(R.dimen.scan_frame_corner_w);\n        mCornerBarH = resources.getDimensionPixelSize(R.dimen.scan_frame_corner_h);\n        mCornerBarColor = resources.getColor(R.color.viewfinder_corner_bar);\n        testColor = resources.getColor(R.color.viewfinder_corner_bar2);\n        mLaserH = resources.getDimensionPixelSize(R.dimen.laser_bar_h);\n        scannerAlpha = 0;\n        possibleResultPoints = new ArrayList<>(5);\n        lastPossibleResultPoints = null;\n        mAnimator = null;\n    }\n\n    public void setCameraManager(CameraManager cameraManager) {\n        this.cameraManager = cameraManager;\n    }\n\n    @SuppressLint(\"DrawAllocation\")\n    @Override\n    public void onDraw(Canvas canvas) {\n        if (cameraManager == null) {\n            return; // not ready yet, early draw before done configuring\n        }\n        Rect frame = cameraManager.getFramingRect();\n        Rect previewFrame = cameraManager.getFramingRectInPreview();\n        if (frame == null || previewFrame == null) {\n            return;\n        }\n        /** FIXME:调试绘制代码\n         paint.setColor(testColor);\n         canvas.drawRect(previewFrame, paint);\n         */\n\n        int width = canvas.getWidth();\n        int height = canvas.getHeight();\n\n        // Draw the exterior (i.e. outside the framing rect) darkened\n        paint.setColor(maskColor);\n        canvas.drawRect(0, 0, width, frame.top, paint);\n        canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);\n        canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);\n        canvas.drawRect(0, frame.bottom + 1, width, height, paint);\n        // 画扫描框的4个角\n        paint.setColor(mCornerBarColor);\n        // 左上角\n        canvas.drawRect(\n                frame.left - mCornerBarH,\n                frame.top - mCornerBarH,\n                frame.left - mCornerBarH + mCornerBarW,\n                frame.top,\n                paint);\n        canvas.drawRect(\n                frame.left - mCornerBarH,\n                frame.top,\n                frame.left,\n                frame.top + mCornerBarW - mCornerBarH,\n                paint);\n        // 右上角\n        canvas.drawRect(\n                frame.right - mCornerBarW + mCornerBarH,\n                frame.top - mCornerBarH,\n                frame.right,\n                frame.top,\n                paint);\n        canvas.drawRect(\n                frame.right,\n                frame.top - mCornerBarH,\n                frame.right + mCornerBarH,\n                frame.top + mCornerBarW - mCornerBarH,\n                paint);\n        // 左下角\n        canvas.drawRect(\n                frame.left - mCornerBarH,\n                frame.bottom - mCornerBarW + mCornerBarH,\n                frame.left,\n                frame.bottom,\n                paint);\n        canvas.drawRect(\n                frame.left - mCornerBarH,\n                frame.bottom,\n                frame.left - mCornerBarH + mCornerBarW,\n                frame.bottom + mCornerBarH,\n                paint);\n        // 右下角\n        canvas.drawRect(\n                frame.right - mCornerBarW + mCornerBarH,\n                frame.bottom,\n                frame.right + mCornerBarH,\n                frame.bottom + mCornerBarH,\n                paint);\n        canvas.drawRect(\n                frame.right,\n                frame.bottom - mCornerBarW + mCornerBarH,\n                frame.right + mCornerBarH,\n                frame.bottom,\n                paint);\n\n        // Draw a red \"laser scanner\" line through the middle to show decoding is active\n        paint.setColor(laserColor);\n//    paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);\n//    scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;\n        int middle = frame.height() / 2 + frame.top;\n        if (mAnimator == null) {\n            initAnimator(frame);\n        }\n//      int step = 4;\n//    mLaserPos = mLaserPos == 0 ? frame.top : mLaserPos + step;\n//      mLaserPos = mLaserPos > frame.bottom ? frame.top : mLaserPos + step;\n//    canvas.drawRect(frame.left, middle - mLaserH / 2, frame.right, middle + mLaserH / 2, paint);\n        canvas.drawRect(frame.left, mLaserPos - mLaserH / 2, frame.right, mLaserPos + mLaserH / 2, paint);\n\n        float scaleX = frame.width() / (float) previewFrame.width();\n        float scaleY = frame.height() / (float) previewFrame.height();\n\n        List<ResultPoint> currentPossible = possibleResultPoints;\n        List<ResultPoint> currentLast = lastPossibleResultPoints;\n        int frameLeft = frame.left;\n        int frameTop = frame.top;\n        if (currentPossible.isEmpty()) {\n            lastPossibleResultPoints = null;\n        } else {\n            possibleResultPoints = new ArrayList<>(5);\n            lastPossibleResultPoints = currentPossible;\n            paint.setAlpha(CURRENT_POINT_OPACITY);\n            paint.setColor(resultPointColor);\n            synchronized (currentPossible) {\n                for (ResultPoint point : currentPossible) {\n                    canvas.drawCircle(\n                            frameLeft + (int) (point.getX() * scaleX),\n                            frameTop + (int) (point.getY() * scaleY),\n                            POINT_SIZE,\n                            paint);\n                }\n            }\n        }\n        if (currentLast != null) {\n            paint.setAlpha(CURRENT_POINT_OPACITY / 2);\n            paint.setColor(resultPointColor);\n            synchronized (currentLast) {\n                float radius = POINT_SIZE / 2.0f;\n                for (ResultPoint point : currentLast) {\n                    canvas.drawCircle(\n                            frameLeft + (int) (point.getX() * scaleX),\n                            frameTop + (int) (point.getY() * scaleY),\n                            radius,\n                            paint);\n                }\n            }\n        }\n\n        // Request another update at the animation interval, but only repaint the laser line,\n        // not the entire viewfinder mask.\n        postInvalidateDelayed(\n                ANIMATION_DELAY,\n                frame.left - POINT_SIZE,\n                frame.top - POINT_SIZE,\n                frame.right + POINT_SIZE,\n                frame.bottom + POINT_SIZE);\n    }\n\n    private void initAnimator(Rect frame) {\n        mAnimator = ValueAnimator.ofInt(frame.top, frame.bottom);\n        mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());\n        mAnimator.setDuration(2500);\n        mAnimator.setRepeatMode(ValueAnimator.RESTART);\n        mAnimator.setRepeatCount(ValueAnimator.INFINITE);\n        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                mLaserPos = (int) animation.getAnimatedValue();\n                invalidate();\n            }\n        });\n        mAnimator.start();\n    }\n\n    public void drawViewfinder() {\n        invalidate();\n    }\n\n    /**\n     * Draw a bitmap with the result points highlighted instead of the live scanning display.\n     *\n     * @param barcode An image of the decoded barcode.\n     */\n    public void drawResultBitmap(Bitmap barcode) {\n        invalidate();\n    }\n\n    public void addPossibleResultPoint(ResultPoint point) {\n        List<ResultPoint> points = possibleResultPoints;\n        synchronized (points) {\n            points.add(point);\n            int size = points.size();\n            if (size > MAX_RESULT_POINTS) {\n                // trim it\n                points.subList(0, size - MAX_RESULT_POINTS / 2).clear();\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/AutoFocusManager.java",
    "content": "/*\n * Copyright (C) 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.hardware.Camera;\nimport android.os.AsyncTask;\nimport android.preference.PreferenceManager;\nimport android.util.Log;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.concurrent.RejectedExecutionException;\n\n\n@SuppressWarnings(\"deprecation\") // camera APIs\nfinal class AutoFocusManager implements Camera.AutoFocusCallback {\n\n    private static final String TAG = AutoFocusManager.class.getSimpleName();\n\n    // 修改默认聚焦时间为 1 秒\n    private static final long AUTO_FOCUS_INTERVAL_MS = 1000L;\n    //    private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;\n    private static final Collection<String> FOCUS_MODES_CALLING_AF;\n\n    static {\n        FOCUS_MODES_CALLING_AF = new ArrayList<>(2);\n        FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);\n        FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);\n    }\n\n    private boolean stopped;\n    private boolean focusing;\n    private final boolean useAutoFocus;\n    private final Camera camera;\n    private AsyncTask<?, ?, ?> outstandingTask;\n\n    AutoFocusManager(Context context, Camera camera) {\n        this.camera = camera;\n        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);\n        String currentFocusMode = camera.getParameters().getFocusMode();\n        useAutoFocus = true;\n        Log.i(TAG, \"Current focus mode '\" + currentFocusMode + \"'; use auto focus? \" + useAutoFocus);\n        start();\n    }\n\n    @Override\n    public synchronized void onAutoFocus(boolean success, Camera theCamera) {\n        focusing = false;\n        autoFocusAgainLater();\n    }\n\n    private synchronized void autoFocusAgainLater() {\n        if (!stopped && outstandingTask == null) {\n            AutoFocusTask newTask = new AutoFocusTask();\n            try {\n                newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);\n                outstandingTask = newTask;\n            } catch (RejectedExecutionException ree) {\n                Log.w(TAG, \"Could not request auto focus\", ree);\n            }\n        }\n    }\n\n    synchronized void start() {\n        if (useAutoFocus) {\n            outstandingTask = null;\n            if (!stopped && !focusing) {\n                try {\n                    camera.autoFocus(this);\n                    focusing = true;\n                } catch (RuntimeException re) {\n                    // Have heard RuntimeException reported in Android 4.0.x+; continue?\n                    Log.w(TAG, \"Unexpected exception while focusing\", re);\n                    // Try again later to keep cycle going\n                    autoFocusAgainLater();\n                }\n            }\n        }\n    }\n\n    private synchronized void cancelOutstandingTask() {\n        if (outstandingTask != null) {\n            if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {\n                outstandingTask.cancel(true);\n            }\n            outstandingTask = null;\n        }\n    }\n\n    synchronized void stop() {\n        stopped = true;\n        if (useAutoFocus) {\n            cancelOutstandingTask();\n            // Doesn't hurt to call this even if not focusing\n            try {\n                camera.cancelAutoFocus();\n            } catch (RuntimeException re) {\n                // Have heard RuntimeException reported in Android 4.0.x+; continue?\n                Log.w(TAG, \"Unexpected exception while cancelling focusing\", re);\n            }\n        }\n    }\n\n    private final class AutoFocusTask extends AsyncTask<Object, Object, Object> {\n        @Override\n        protected Object doInBackground(Object... voids) {\n            try {\n                Thread.sleep(AUTO_FOCUS_INTERVAL_MS);\n            } catch (InterruptedException e) {\n                // continue\n            }\n            start();\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/CameraConfigurationManager.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.graphics.Point;\nimport android.hardware.Camera;\nimport android.preference.PreferenceManager;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.view.Display;\nimport android.view.Surface;\nimport android.view.WindowManager;\n\nimport com.nanchen.scanner.zxing.camera.open.CameraFacing;\nimport com.nanchen.scanner.zxing.camera.open.OpenCamera;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\n/**\n * A class which deals with reading, parsing, and setting the camera parameters which are used to\n * configure the camera hardware.\n */\n@SuppressWarnings(\"deprecation\") // camera APIs\nfinal class CameraConfigurationManager {\n\n    private static final String TAG = \"CameraConfiguration\";\n\n    private final Context context;\n    private int cwNeededRotation;\n    private int cwRotationFromDisplayToCamera;\n    private Point screenResolution;\n    private Point cameraResolution;\n    private Point bestPreviewSize;\n    private Point previewSizeOnScreen;\n\n    CameraConfigurationManager(Context context) {\n        this.context = context;\n    }\n\n    /**\n     * Reads, one time, values from the camera that are needed by the app.\n     */\n    void initFromCameraParameters(OpenCamera camera) {\n        Camera.Parameters parameters = camera.getCamera().getParameters();\n        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n        Display display = manager.getDefaultDisplay();\n\n        int displayRotation = display.getRotation();\n        int cwRotationFromNaturalToDisplay;\n        switch (displayRotation) {\n            case Surface.ROTATION_0:\n                cwRotationFromNaturalToDisplay = 0;\n                break;\n            case Surface.ROTATION_90:\n                cwRotationFromNaturalToDisplay = 90;\n                break;\n            case Surface.ROTATION_180:\n                cwRotationFromNaturalToDisplay = 180;\n                break;\n            case Surface.ROTATION_270:\n                cwRotationFromNaturalToDisplay = 270;\n                break;\n            default:\n                // Have seen this return incorrect values like -90\n                if (displayRotation % 90 == 0) {\n                    cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360;\n                } else {\n                    throw new IllegalArgumentException(\"Bad rotation: \" + displayRotation);\n                }\n        }\n        Log.i(TAG, \"Display at: \" + cwRotationFromNaturalToDisplay);\n\n        int cwRotationFromNaturalToCamera = camera.getOrientation();\n        Log.i(TAG, \"Camera at: \" + cwRotationFromNaturalToCamera);\n\n        // Still not 100% sure about this. But acts like we need to flip this:\n        if (camera.getFacing() == CameraFacing.FRONT) {\n            cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360;\n            Log.i(TAG, \"Front camera overriden to: \" + cwRotationFromNaturalToCamera);\n        }\n\n    /*\n    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n    String overrideRotationString;\n    if (camera.getFacing() == CameraFacing.FRONT) {\n      overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION_FRONT, null);\n    } else {\n      overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION, null);\n    }\n    if (overrideRotationString != null && !\"-\".equals(overrideRotationString)) {\n      Log.i(TAG, \"Overriding camera manually to \" + overrideRotationString);\n      cwRotationFromNaturalToCamera = Integer.parseInt(overrideRotationString);\n    }\n     */\n\n        cwRotationFromDisplayToCamera =\n                (360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360;\n        Log.i(TAG, \"Final display orientation: \" + cwRotationFromDisplayToCamera);\n        if (camera.getFacing() == CameraFacing.FRONT) {\n            Log.i(TAG, \"Compensating rotation for front camera\");\n            cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360;\n        } else {\n            cwNeededRotation = cwRotationFromDisplayToCamera;\n        }\n        Log.i(TAG, \"Clockwise rotation from display to camera: \" + cwNeededRotation);\n\n        Point theScreenResolution = new Point();\n        display.getSize(theScreenResolution);\n        screenResolution = theScreenResolution;\n\n        Log.i(TAG, \"Screen resolution in current orientation: \" + screenResolution);\n        cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);\n        Log.i(TAG, \"Camera resolution: \" + cameraResolution);\n        bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);\n        Log.i(TAG, \"Best available preview size: \" + bestPreviewSize);\n\n        boolean isScreenPortrait = screenResolution.x < screenResolution.y;\n        boolean isPreviewSizePortrait = bestPreviewSize.x < bestPreviewSize.y;\n\n        if (isScreenPortrait == isPreviewSizePortrait) {\n            previewSizeOnScreen = bestPreviewSize;\n        } else {\n            previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);\n        }\n        Log.i(TAG, \"Preview size on screen: \" + previewSizeOnScreen);\n    }\n\n    void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) {\n\n        Camera theCamera = camera.getCamera();\n        Camera.Parameters parameters = theCamera.getParameters();\n\n\n        if (parameters == null) {\n            Log.w(TAG, \"Device error: no camera parameters are available. Proceeding without configuration.\");\n            return;\n        }\n\n        setZoom(parameters);\n\n        Log.i(TAG, \"Initial camera parameters: \" + parameters.flatten());\n\n        if (safeMode) {\n            Log.w(TAG, \"In camera config safe mode -- most settings will not be honored\");\n        }\n\n        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n\n        initializeTorch(parameters, prefs, safeMode);\n\n        CameraConfigurationUtils.setFocus(\n                parameters,\n                true,\n                true,\n                safeMode);\n\n//        if (!safeMode) {\n//            if (prefs.getBoolean(PreferencesActivity.KEY_INVERT_SCAN, false)) {\n//                CameraConfigurationUtils.setInvertColor(parameters);\n//            }\n//\n//            if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_BARCODE_SCENE_MODE, true)) {\n//                CameraConfigurationUtils.setBarcodeSceneMode(parameters);\n//            }\n//\n//            if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_METERING, true)) {\n//                CameraConfigurationUtils.setVideoStabilization(parameters);\n//                CameraConfigurationUtils.setFocusArea(parameters);\n//                CameraConfigurationUtils.setMetering(parameters);\n//            }\n//\n//            //SetRecordingHint to true also a workaround for low framerate on Nexus 4\n//            //https://stackoverflow.com/questions/14131900/extreme-camera-lag-on-nexus-4\n//            parameters.setRecordingHint(true);\n//\n//        }\n\n        parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y);\n\n        // 设置合适的最佳预览尺寸/图片尺寸\n        DisplayMetrics displayMetrics = new DisplayMetrics();\n        Camera.Size mCameraResolution = findCloselySize(displayMetrics.widthPixels, displayMetrics.heightPixels,\n                parameters.getSupportedPreviewSizes());\n        Camera.Size pictureSize = findCloselySize(displayMetrics.widthPixels, displayMetrics.heightPixels,\n                parameters.getSupportedPictureSizes());\n//        parameters.setPreviewSize(mCameraResolution.width, mCameraResolution.height);\n        parameters.setPictureSize(pictureSize.width, pictureSize.height);\n\n        theCamera.setParameters(parameters);\n\n        theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera);\n\n        Camera.Parameters afterParameters = theCamera.getParameters();\n        Camera.Size afterSize = afterParameters.getPreviewSize();\n        if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) {\n            Log.w(TAG, \"Camera said it supported preview size \" + bestPreviewSize.x + 'x' + bestPreviewSize.y +\n                    \", but after setting it, preview size is \" + afterSize.width + 'x' + afterSize.height);\n            bestPreviewSize.x = afterSize.width;\n            bestPreviewSize.y = afterSize.height;\n        }\n    }\n\n    Point getBestPreviewSize() {\n        return bestPreviewSize;\n    }\n\n    Point getPreviewSizeOnScreen() {\n        return previewSizeOnScreen;\n    }\n\n    Point getCameraResolution() {\n        return cameraResolution;\n    }\n\n    Point getScreenResolution() {\n        return screenResolution;\n    }\n\n    int getCWNeededRotation() {\n        return cwNeededRotation;\n    }\n\n    boolean getTorchState(Camera camera) {\n        if (camera != null) {\n            Camera.Parameters parameters = camera.getParameters();\n            if (parameters != null) {\n                String flashMode = parameters.getFlashMode();\n                return\n                        Camera.Parameters.FLASH_MODE_ON.equals(flashMode) ||\n                                Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode);\n            }\n        }\n        return false;\n    }\n\n    void setTorch(Camera camera, boolean newSetting) {\n        Camera.Parameters parameters = camera.getParameters();\n        doSetTorch(parameters, newSetting, false);\n        camera.setParameters(parameters);\n    }\n\n    private void initializeTorch(Camera.Parameters parameters, SharedPreferences prefs, boolean safeMode) {\n        boolean currentSetting = FrontLightMode.readPref(prefs) == FrontLightMode.ON;\n        doSetTorch(parameters, currentSetting, safeMode);\n    }\n\n    private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {\n        CameraConfigurationUtils.setTorch(parameters, newSetting);\n//        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);\n//        if (!safeMode && !prefs.getBoolean(PreferencesActivity.KEY_DISABLE_EXPOSURE, true)) {\n//            CameraConfigurationUtils.setBestExposure(parameters, newSetting);\n//        }\n    }\n\n    /**\n     * 通过对比得到与宽高比最接近的尺寸（如果有相同尺寸，优先选择）\n     *\n     * @param surfaceWidth  需要被进行对比的原宽\n     * @param surfaceHeight 需要被进行对比的原高\n     * @param preSizeList   需要对比的预览尺寸列表\n     * @return 得到与原宽高比例最接近的尺寸\n     */\n    private Camera.Size findCloselySize(int surfaceWidth, int surfaceHeight, List<Camera.Size> preSizeList) {\n        Collections.sort(preSizeList, new SizeComparator(surfaceWidth, surfaceHeight));\n        return preSizeList.get(0);\n    }\n\n    private static final double MAX_ASPECT_DISTORTION = 0.15;\n    private static final int TEN_DESIRED_ZOOM = 27;\n    private static final Pattern COMMA_PATTERN = Pattern.compile(\",\");\n\n    private void setZoom(Camera.Parameters parameters) {\n\n        String zoomSupportedString = parameters.get(\"zoom-supported\");\n        if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {\n            return;\n        }\n\n        int tenDesiredZoom = TEN_DESIRED_ZOOM;\n\n        String maxZoomString = parameters.get(\"max-zoom\");\n        if (maxZoomString != null) {\n            try {\n                int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));\n                if (tenDesiredZoom > tenMaxZoom) {\n                    tenDesiredZoom = tenMaxZoom;\n                }\n            } catch (NumberFormatException nfe) {\n                Log.e(TAG, \"Bad max-zoom: \" + maxZoomString);\n            }\n        }\n\n        String takingPictureZoomMaxString = parameters.get(\"taking-picture-zoom-max\");\n        if (takingPictureZoomMaxString != null) {\n            try {\n                int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);\n                if (tenDesiredZoom > tenMaxZoom) {\n                    tenDesiredZoom = tenMaxZoom;\n                }\n            } catch (NumberFormatException nfe) {\n                Log.e(TAG, \"Bad taking-picture-zoom-max: \" + takingPictureZoomMaxString);\n            }\n        }\n\n        String motZoomValuesString = parameters.get(\"mot-zoom-values\");\n        if (motZoomValuesString != null) {\n            tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);\n        }\n\n        String motZoomStepString = parameters.get(\"mot-zoom-step\");\n        if (motZoomStepString != null) {\n            try {\n                double motZoomStep = Double.parseDouble(motZoomStepString.trim());\n                int tenZoomStep = (int) (10.0 * motZoomStep);\n                if (tenZoomStep > 1) {\n                    tenDesiredZoom -= tenDesiredZoom % tenZoomStep;\n                }\n            } catch (NumberFormatException nfe) {\n                // continue\n            }\n        }\n\n        // Set zoom. This helps encourage the user to pull back.\n        // Some devices like the Behold have a zoom parameter\n        // if (maxZoomString != null || motZoomValuesString != null) {\n        // parameters.set(\"zoom\", String.valueOf(tenDesiredZoom / 10.0));\n        // }\n        if (parameters.isZoomSupported()) {\n            Log.e(TAG, \"max-zoom:\" + parameters.getMaxZoom());\n            Log.i(\"0000\", \"tenDesiredZoom:\" + tenDesiredZoom);\n            parameters.setZoom(parameters.getMaxZoom() / tenDesiredZoom);\n        } else {\n            Log.e(TAG, \"Unsupported zoom.\");\n        }\n\n        // Most devices, like the Hero, appear to expose this zoom parameter.\n        // It takes on values like \"27\" which appears to mean 2.7x zoom\n        // if (takingPictureZoomMaxString != null) {\n        // parameters.set(\"taking-picture-zoom\", tenDesiredZoom);\n        // }\n    }\n\n    private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) {\n        int tenBestValue = 0;\n        for (String stringValue : COMMA_PATTERN.split(stringValues)) {\n            stringValue = stringValue.trim();\n            double value;\n            try {\n                value = Double.parseDouble(stringValue);\n            } catch (NumberFormatException nfe) {\n                return tenDesiredZoom;\n            }\n            int tenValue = (int) (10.0 * value);\n            if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {\n                tenBestValue = tenValue;\n            }\n        }\n        return tenBestValue;\n    }\n\n\n    /**\n     * 预览尺寸与给定的宽高尺寸比较器。首先比较宽高的比例，在宽高比相同的情况下，根据宽和高的最小差进行比较。\n     */\n    private static class SizeComparator implements Comparator<Camera.Size> {\n\n        private final int width;\n        private final int height;\n        private final float ratio;\n\n        SizeComparator(int width, int height) {\n            if (width < height) {\n                this.width = height;\n                this.height = width;\n            } else {\n                this.width = width;\n                this.height = height;\n            }\n            this.ratio = (float) this.height / this.width;\n        }\n\n        @Override\n        public int compare(Camera.Size size1, Camera.Size size2) {\n            int width1 = size1.width;\n            int height1 = size1.height;\n            int width2 = size2.width;\n            int height2 = size2.height;\n\n            float ratio1 = Math.abs((float) height1 / width1 - ratio);\n            float ratio2 = Math.abs((float) height2 / width2 - ratio);\n            int result = Float.compare(ratio1, ratio2);\n            if (result != 0) {\n                return result;\n            } else {\n                int minGap1 = Math.abs(width - width1) + Math.abs(height - height1);\n                int minGap2 = Math.abs(width - width2) + Math.abs(height - height2);\n                return minGap1 - minGap2;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/CameraConfigurationUtils.java",
    "content": "/*\n * Copyright (C) 2014 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera;\n\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.hardware.Camera;\nimport android.os.Build;\nimport android.util.Log;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\n/**\n * Utility methods for configuring the Android camera.\n *\n * @author Sean Owen\n */\n@SuppressWarnings(\"deprecation\") // camera APIs\npublic final class CameraConfigurationUtils {\n\n    private static final String TAG = \"CameraConfiguration\";\n\n    private static final Pattern SEMICOLON = Pattern.compile(\";\");\n\n    private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen\n    private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;\n    private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;\n    private static final double MAX_ASPECT_DISTORTION = 0.15;\n    private static final int MIN_FPS = 10;\n    private static final int MAX_FPS = 20;\n    private static final int AREA_PER_1000 = 400;\n\n    private CameraConfigurationUtils() {\n    }\n\n    public static void setFocus(Camera.Parameters parameters,\n                                boolean autoFocus,\n                                boolean disableContinuous,\n                                boolean safeMode) {\n        List<String> supportedFocusModes = parameters.getSupportedFocusModes();\n        String focusMode = null;\n        if (autoFocus) {\n            if (safeMode || disableContinuous) {\n                focusMode = findSettableValue(\"focus mode\",\n                        supportedFocusModes,\n                        Camera.Parameters.FOCUS_MODE_AUTO);\n            } else {\n                focusMode = findSettableValue(\"focus mode\",\n                        supportedFocusModes,\n                        Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,\n                        Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,\n                        Camera.Parameters.FOCUS_MODE_AUTO);\n            }\n        }\n        // Maybe selected auto-focus but not available, so fall through here:\n        if (!safeMode && focusMode == null) {\n            focusMode = findSettableValue(\"focus mode\",\n                    supportedFocusModes,\n                    Camera.Parameters.FOCUS_MODE_MACRO,\n                    Camera.Parameters.FOCUS_MODE_EDOF);\n        }\n        if (focusMode != null) {\n            if (focusMode.equals(parameters.getFocusMode())) {\n                Log.i(TAG, \"Focus mode already set to \" + focusMode);\n            } else {\n                parameters.setFocusMode(focusMode);\n            }\n        }\n    }\n\n    public static void setTorch(Camera.Parameters parameters, boolean on) {\n        List<String> supportedFlashModes = parameters.getSupportedFlashModes();\n        String flashMode;\n        if (on) {\n            flashMode = findSettableValue(\"flash mode\",\n                    supportedFlashModes,\n                    Camera.Parameters.FLASH_MODE_TORCH,\n                    Camera.Parameters.FLASH_MODE_ON);\n        } else {\n            flashMode = findSettableValue(\"flash mode\",\n                    supportedFlashModes,\n                    Camera.Parameters.FLASH_MODE_OFF);\n        }\n        if (flashMode != null) {\n            if (flashMode.equals(parameters.getFlashMode())) {\n                Log.i(TAG, \"Flash mode already set to \" + flashMode);\n            } else {\n                Log.i(TAG, \"Setting flash mode to \" + flashMode);\n                parameters.setFlashMode(flashMode);\n            }\n        }\n    }\n\n    public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) {\n        int minExposure = parameters.getMinExposureCompensation();\n        int maxExposure = parameters.getMaxExposureCompensation();\n        float step = parameters.getExposureCompensationStep();\n        if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) {\n            // Set low when light is on\n            float targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION;\n            int compensationSteps = Math.round(targetCompensation / step);\n            float actualCompensation = step * compensationSteps;\n            // Clamp value:\n            compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure);\n            if (parameters.getExposureCompensation() == compensationSteps) {\n                Log.i(TAG, \"Exposure compensation already set to \" + compensationSteps + \" / \" + actualCompensation);\n            } else {\n                Log.i(TAG, \"Setting exposure compensation to \" + compensationSteps + \" / \" + actualCompensation);\n                parameters.setExposureCompensation(compensationSteps);\n            }\n        } else {\n            Log.i(TAG, \"Camera does not support exposure compensation\");\n        }\n    }\n\n    public static void setBestPreviewFPS(Camera.Parameters parameters) {\n        setBestPreviewFPS(parameters, MIN_FPS, MAX_FPS);\n    }\n\n    public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS, int maxFPS) {\n        List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();\n        Log.i(TAG, \"Supported FPS ranges: \" + toString(supportedPreviewFpsRanges));\n        if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {\n            int[] suitableFPSRange = null;\n            for (int[] fpsRange : supportedPreviewFpsRanges) {\n                int thisMin = fpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];\n                int thisMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];\n                if (thisMin >= minFPS * 1000 && thisMax <= maxFPS * 1000) {\n                    suitableFPSRange = fpsRange;\n                    break;\n                }\n            }\n            if (suitableFPSRange == null) {\n                Log.i(TAG, \"No suitable FPS range?\");\n            } else {\n                int[] currentFpsRange = new int[2];\n                parameters.getPreviewFpsRange(currentFpsRange);\n                if (Arrays.equals(currentFpsRange, suitableFPSRange)) {\n                    Log.i(TAG, \"FPS range already set to \" + Arrays.toString(suitableFPSRange));\n                } else {\n                    Log.i(TAG, \"Setting FPS range to \" + Arrays.toString(suitableFPSRange));\n                    parameters.setPreviewFpsRange(suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],\n                            suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);\n                }\n            }\n        }\n    }\n\n    public static void setFocusArea(Camera.Parameters parameters) {\n        if (parameters.getMaxNumFocusAreas() > 0) {\n            Log.i(TAG, \"Old focus areas: \" + toString(parameters.getFocusAreas()));\n            List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);\n            Log.i(TAG, \"Setting focus area to : \" + toString(middleArea));\n            parameters.setFocusAreas(middleArea);\n        } else {\n            Log.i(TAG, \"Device does not support focus areas\");\n        }\n    }\n\n    public static void setMetering(Camera.Parameters parameters) {\n        if (parameters.getMaxNumMeteringAreas() > 0) {\n            Log.i(TAG, \"Old metering areas: \" + parameters.getMeteringAreas());\n            List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);\n            Log.i(TAG, \"Setting metering area to : \" + toString(middleArea));\n            parameters.setMeteringAreas(middleArea);\n        } else {\n            Log.i(TAG, \"Device does not support metering areas\");\n        }\n    }\n\n    private static List<Camera.Area> buildMiddleArea(int areaPer1000) {\n        return Collections.singletonList(\n                new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1));\n    }\n\n    public static void setVideoStabilization(Camera.Parameters parameters) {\n        if (parameters.isVideoStabilizationSupported()) {\n            if (parameters.getVideoStabilization()) {\n                Log.i(TAG, \"Video stabilization already enabled\");\n            } else {\n                Log.i(TAG, \"Enabling video stabilization...\");\n                parameters.setVideoStabilization(true);\n            }\n        } else {\n            Log.i(TAG, \"This device does not support video stabilization\");\n        }\n    }\n\n    public static void setBarcodeSceneMode(Camera.Parameters parameters) {\n        if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) {\n            Log.i(TAG, \"Barcode scene mode already set\");\n            return;\n        }\n        String sceneMode = findSettableValue(\"scene mode\",\n                parameters.getSupportedSceneModes(),\n                Camera.Parameters.SCENE_MODE_BARCODE);\n        if (sceneMode != null) {\n            parameters.setSceneMode(sceneMode);\n        }\n    }\n\n    public static void setZoom(Camera.Parameters parameters, double targetZoomRatio) {\n        if (parameters.isZoomSupported()) {\n            Integer zoom = indexOfClosestZoom(parameters, targetZoomRatio);\n            if (zoom == null) {\n                return;\n            }\n            if (parameters.getZoom() == zoom) {\n                Log.i(TAG, \"Zoom is already set to \" + zoom);\n            } else {\n                Log.i(TAG, \"Setting zoom to \" + zoom);\n                parameters.setZoom(zoom);\n            }\n        } else {\n            Log.i(TAG, \"Zoom is not supported\");\n        }\n    }\n\n    private static Integer indexOfClosestZoom(Camera.Parameters parameters, double targetZoomRatio) {\n        List<Integer> ratios = parameters.getZoomRatios();\n        Log.i(TAG, \"Zoom ratios: \" + ratios);\n        int maxZoom = parameters.getMaxZoom();\n        if (ratios == null || ratios.isEmpty() || ratios.size() != maxZoom + 1) {\n            Log.w(TAG, \"Invalid zoom ratios!\");\n            return null;\n        }\n        double target100 = 100.0 * targetZoomRatio;\n        double smallestDiff = Double.POSITIVE_INFINITY;\n        int closestIndex = 0;\n        for (int i = 0; i < ratios.size(); i++) {\n            double diff = Math.abs(ratios.get(i) - target100);\n            if (diff < smallestDiff) {\n                smallestDiff = diff;\n                closestIndex = i;\n            }\n        }\n        Log.i(TAG, \"Chose zoom ratio of \" + (ratios.get(closestIndex) / 100.0));\n        return closestIndex;\n    }\n\n    public static void setInvertColor(Camera.Parameters parameters) {\n        if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) {\n            Log.i(TAG, \"Negative effect already set\");\n            return;\n        }\n        String colorMode = findSettableValue(\"color effect\",\n                parameters.getSupportedColorEffects(),\n                Camera.Parameters.EFFECT_NEGATIVE);\n        if (colorMode != null) {\n            parameters.setColorEffect(colorMode);\n        }\n    }\n\n    public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {\n\n        List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();\n        if (rawSupportedSizes == null) {\n            Log.w(TAG, \"Device returned no supported preview sizes; using default\");\n            Camera.Size defaultSize = parameters.getPreviewSize();\n            if (defaultSize == null) {\n                throw new IllegalStateException(\"Parameters contained no preview size!\");\n            }\n            return new Point(defaultSize.width, defaultSize.height);\n        }\n\n        if (Log.isLoggable(TAG, Log.INFO)) {\n            StringBuilder previewSizesString = new StringBuilder();\n            for (Camera.Size size : rawSupportedSizes) {\n                previewSizesString.append(size.width).append('x').append(size.height).append(' ');\n            }\n            Log.i(TAG, \"Supported preview sizes: \" + previewSizesString);\n        }\n\n        double screenAspectRatio = screenResolution.x / (double) screenResolution.y;\n\n        // Find a suitable size, with max resolution\n        int maxResolution = 0;\n        Camera.Size maxResPreviewSize = null;\n        for (Camera.Size size : rawSupportedSizes) {\n            int realWidth = size.width;\n            int realHeight = size.height;\n            int resolution = realWidth * realHeight;\n            if (resolution < MIN_PREVIEW_PIXELS) {\n                continue;\n            }\n\n            boolean isCandidatePortrait = realWidth < realHeight;\n            int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;\n            int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;\n            double aspectRatio = maybeFlippedWidth / (double) maybeFlippedHeight;\n            double distortion = Math.abs(aspectRatio - screenAspectRatio);\n            if (distortion > MAX_ASPECT_DISTORTION) {\n                continue;\n            }\n\n            if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {\n                Point exactPoint = new Point(realWidth, realHeight);\n                Log.i(TAG, \"Found preview size exactly matching screen size: \" + exactPoint);\n                return exactPoint;\n            }\n\n            // Resolution is suitable; record the one with max resolution\n            if (resolution > maxResolution) {\n                maxResolution = resolution;\n                maxResPreviewSize = size;\n            }\n        }\n\n        // If no exact match, use largest preview size. This was not a great idea on older devices because\n        // of the additional computation needed. We're likely to get here on newer Android 4+ devices, where\n        // the CPU is much more powerful.\n        if (maxResPreviewSize != null) {\n            Point largestSize = new Point(maxResPreviewSize.width, maxResPreviewSize.height);\n            Log.i(TAG, \"Using largest suitable preview size: \" + largestSize);\n            return largestSize;\n        }\n\n        // If there is nothing at all suitable, return current preview size\n        Camera.Size defaultPreview = parameters.getPreviewSize();\n        if (defaultPreview == null) {\n            throw new IllegalStateException(\"Parameters contained no preview size!\");\n        }\n        Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);\n        Log.i(TAG, \"No suitable preview sizes, using default: \" + defaultSize);\n        return defaultSize;\n    }\n\n    private static String findSettableValue(String name,\n                                            Collection<String> supportedValues,\n                                            String... desiredValues) {\n        Log.i(TAG, \"Requesting \" + name + \" value from among: \" + Arrays.toString(desiredValues));\n        Log.i(TAG, \"Supported \" + name + \" values: \" + supportedValues);\n        if (supportedValues != null) {\n            for (String desiredValue : desiredValues) {\n                if (supportedValues.contains(desiredValue)) {\n                    Log.i(TAG, \"Can set \" + name + \" to: \" + desiredValue);\n                    return desiredValue;\n                }\n            }\n        }\n        Log.i(TAG, \"No supported values match\");\n        return null;\n    }\n\n    private static String toString(Collection<int[]> arrays) {\n        if (arrays == null || arrays.isEmpty()) {\n            return \"[]\";\n        }\n        StringBuilder buffer = new StringBuilder();\n        buffer.append('[');\n        Iterator<int[]> it = arrays.iterator();\n        while (it.hasNext()) {\n            buffer.append(Arrays.toString(it.next()));\n            if (it.hasNext()) {\n                buffer.append(\", \");\n            }\n        }\n        buffer.append(']');\n        return buffer.toString();\n    }\n\n    private static String toString(Iterable<Camera.Area> areas) {\n        if (areas == null) {\n            return null;\n        }\n        StringBuilder result = new StringBuilder();\n        for (Camera.Area area : areas) {\n            result.append(area.rect).append(':').append(area.weight).append(' ');\n        }\n        return result.toString();\n    }\n\n    public static String collectStats(Camera.Parameters parameters) {\n        return collectStats(parameters.flatten());\n    }\n\n    public static String collectStats(CharSequence flattenedParams) {\n        StringBuilder result = new StringBuilder(1000);\n\n        result.append(\"BOARD=\").append(Build.BOARD).append('\\n');\n        result.append(\"BRAND=\").append(Build.BRAND).append('\\n');\n        result.append(\"CPU_ABI=\").append(Build.CPU_ABI).append('\\n');\n        result.append(\"DEVICE=\").append(Build.DEVICE).append('\\n');\n        result.append(\"DISPLAY=\").append(Build.DISPLAY).append('\\n');\n        result.append(\"FINGERPRINT=\").append(Build.FINGERPRINT).append('\\n');\n        result.append(\"HOST=\").append(Build.HOST).append('\\n');\n        result.append(\"ID=\").append(Build.ID).append('\\n');\n        result.append(\"MANUFACTURER=\").append(Build.MANUFACTURER).append('\\n');\n        result.append(\"MODEL=\").append(Build.MODEL).append('\\n');\n        result.append(\"PRODUCT=\").append(Build.PRODUCT).append('\\n');\n        result.append(\"TAGS=\").append(Build.TAGS).append('\\n');\n        result.append(\"TIME=\").append(Build.TIME).append('\\n');\n        result.append(\"TYPE=\").append(Build.TYPE).append('\\n');\n        result.append(\"USER=\").append(Build.USER).append('\\n');\n        result.append(\"VERSION.CODENAME=\").append(Build.VERSION.CODENAME).append('\\n');\n        result.append(\"VERSION.INCREMENTAL=\").append(Build.VERSION.INCREMENTAL).append('\\n');\n        result.append(\"VERSION.RELEASE=\").append(Build.VERSION.RELEASE).append('\\n');\n        result.append(\"VERSION.SDK_INT=\").append(Build.VERSION.SDK_INT).append('\\n');\n\n        if (flattenedParams != null) {\n            String[] params = SEMICOLON.split(flattenedParams);\n            Arrays.sort(params);\n            for (String param : params) {\n                result.append(param).append('\\n');\n            }\n        }\n\n        return result.toString();\n    }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/CameraManager.java",
    "content": "/*\n * Copyright (C) 2008 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera;\n\nimport android.content.Context;\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.hardware.Camera;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.SurfaceHolder;\n\nimport com.google.zxing.PlanarYUVLuminanceSource;\nimport com.nanchen.scanner.zxing.camera.open.OpenCamera;\nimport com.nanchen.scanner.zxing.camera.open.OpenCameraInterface;\n\nimport java.io.IOException;\n\n/**\n * This object wraps the Camera service object and expects to be the only one talking to it. The\n * implementation encapsulates the steps needed to take preview-sized images, which are used for\n * both preview and decoding.\n *\n * @author dswitkin@google.com (Daniel Switkin)\n */\n@SuppressWarnings(\"deprecation\") // camera APIs\npublic final class CameraManager {\n\n    private static final String TAG = CameraManager.class.getSimpleName();\n\n    private static final int MIN_FRAME_WIDTH = 240;\n    private static final int MIN_FRAME_HEIGHT = 240;\n    private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920\n    private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080\n\n    private final Context context;\n    private final CameraConfigurationManager configManager;\n    private OpenCamera camera;\n    private AutoFocusManager autoFocusManager;\n    private Rect framingRect;\n    private Rect framingRectInPreview;\n    private boolean initialized;\n    private boolean previewing;\n    private int requestedCameraId = OpenCameraInterface.NO_REQUESTED_CAMERA;\n    private int requestedFramingRectWidth;\n    private int requestedFramingRectHeight;\n    /**\n     * Preview frames are delivered here, which we pass on to the registered handler. Make sure to\n     * clear the handler so it will only receive one message.\n     */\n    private final PreviewCallback previewCallback;\n\n    public CameraManager(Context context) {\n        this.context = context;\n        this.configManager = new CameraConfigurationManager(context);\n        previewCallback = new PreviewCallback(configManager);\n    }\n\n    /**\n     * Opens the camera driver and initializes the hardware parameters.\n     *\n     * @param holder The surface object which the camera will draw preview frames into.\n     * @throws IOException Indicates the camera driver failed to open.\n     */\n    public synchronized void openDriver(SurfaceHolder holder) throws IOException {\n        OpenCamera theCamera = camera;\n        if (theCamera == null) {\n            theCamera = OpenCameraInterface.open(requestedCameraId);\n            if (theCamera == null) {\n                throw new IOException(\"Camera.open() failed to return object from driver\");\n            }\n            camera = theCamera;\n        }\n\n        if (!initialized) {\n            initialized = true;\n            configManager.initFromCameraParameters(theCamera);\n            if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {\n                setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);\n                requestedFramingRectWidth = 0;\n                requestedFramingRectHeight = 0;\n            }\n        }\n\n        Camera cameraObject = theCamera.getCamera();\n        Camera.Parameters parameters = cameraObject.getParameters();\n        String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these, temporarily\n        try {\n            configManager.setDesiredCameraParameters(theCamera, false);\n        } catch (RuntimeException re) {\n            // Driver failed\n            Log.w(TAG, \"Camera rejected parameters. Setting only minimal safe-mode parameters\");\n            Log.i(TAG, \"Resetting to saved camera params: \" + parametersFlattened);\n            // Reset:\n            if (parametersFlattened != null) {\n                parameters = cameraObject.getParameters();\n                parameters.unflatten(parametersFlattened);\n                try {\n                    cameraObject.setParameters(parameters);\n                    configManager.setDesiredCameraParameters(theCamera, true);\n                } catch (RuntimeException re2) {\n                    // Well, darn. Give up\n                    Log.w(TAG, \"Camera rejected even safe-mode parameters! No configuration\");\n                }\n            }\n        }\n        cameraObject.setPreviewDisplay(holder);\n\n    }\n\n    public synchronized boolean isOpen() {\n        return camera != null;\n    }\n\n    /**\n     * Closes the camera driver if still in use.\n     */\n    public synchronized void closeDriver() {\n        if (camera != null) {\n            camera.getCamera().release();\n            camera = null;\n            // Make sure to clear these each time we close the camera, so that any scanning rect\n            // requested by intent is forgotten.\n            framingRect = null;\n            framingRectInPreview = null;\n        }\n    }\n\n    /**\n     * Asks the camera hardware to begin drawing preview frames to the screen.\n     */\n    public synchronized void startPreview() {\n        OpenCamera theCamera = camera;\n        if (theCamera != null && !previewing) {\n            theCamera.getCamera().startPreview();\n            previewing = true;\n            autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());\n        }\n    }\n\n    /**\n     * Tells the camera to stop drawing preview frames.\n     */\n    public synchronized void stopPreview() {\n        if (autoFocusManager != null) {\n            autoFocusManager.stop();\n            autoFocusManager = null;\n        }\n        if (camera != null && previewing) {\n            camera.getCamera().stopPreview();\n            previewCallback.setHandler(null, 0);\n            previewing = false;\n        }\n    }\n\n    /**\n     * Convenience method for\n     *\n     * @param newSetting if {@code true}, light should be turned on if currently off. And vice versa.\n     */\n    public synchronized void setTorch(boolean newSetting) {\n        OpenCamera theCamera = camera;\n        if (theCamera != null && newSetting != configManager.getTorchState(theCamera.getCamera())) {\n            boolean wasAutoFocusManager = autoFocusManager != null;\n            if (wasAutoFocusManager) {\n                autoFocusManager.stop();\n                autoFocusManager = null;\n            }\n            configManager.setTorch(theCamera.getCamera(), newSetting);\n            if (wasAutoFocusManager) {\n                autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());\n                autoFocusManager.start();\n            }\n        }\n    }\n\n    /**\n     * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]\n     * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,\n     * respectively.\n     *\n     * @param handler The handler to send the message to.\n     * @param message The what field of the message to be sent.\n     */\n    public synchronized void requestPreviewFrame(Handler handler, int message) {\n        OpenCamera theCamera = camera;\n        if (theCamera != null && previewing) {\n            previewCallback.setHandler(handler, message);\n            theCamera.getCamera().setOneShotPreviewCallback(previewCallback);\n        }\n    }\n\n    /**\n     * Calculates the framing rect which the UI should draw to show the user where to place the\n     * barcode. This target helps with alignment as well as forces the user to hold the device\n     * far enough away to ensure the image will be in focus.\n     *\n     * @return The rectangle to draw on screen in window coordinates.\n     */\n    public synchronized Rect getFramingRect() {\n        if (framingRect == null) {\n            if (camera == null) {\n                return null;\n            }\n            Point screenResolution = configManager.getScreenResolution();\n            if (screenResolution == null) {\n                // Called early, before init even finished\n                return null;\n            }\n\n            int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);\n            int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);\n\n            int leftOffset = (screenResolution.x - width) / 2;\n            int topOffset = (screenResolution.y - height) / 2;\n            framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);\n            Log.d(TAG, \"Calculated framing rect: \" + framingRect);\n        }\n        return framingRect;\n    }\n\n    private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) {\n        int dim = 5 * resolution / 8; // Target 5/8 of each dimension\n        if (dim < hardMin) {\n            return hardMin;\n        }\n        if (dim > hardMax) {\n            return hardMax;\n        }\n        return dim;\n    }\n\n    /**\n     * Like {@link #getFramingRect} but coordinates are in terms of the preview frame,\n     * not UI / screen.\n     *\n     * @return {@link Rect} expressing barcode scan area in terms of the preview size\n     */\n    public synchronized Rect getFramingRectInPreview() {\n        if (framingRectInPreview == null) {\n            Rect framingRect = getFramingRect();\n            if (framingRect == null) {\n                return null;\n            }\n            Rect rect = new Rect(framingRect);\n            Point cameraResolution = configManager.getCameraResolution();\n            Point screenResolution = configManager.getScreenResolution();\n            if (cameraResolution == null || screenResolution == null) {\n                // Called early, before init even finished\n                return null;\n            }\n            rect.left = rect.left * cameraResolution.x / screenResolution.x;\n            rect.right = rect.right * cameraResolution.x / screenResolution.x;\n            rect.top = rect.top * cameraResolution.y / screenResolution.y;\n            rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;\n            framingRectInPreview = rect;\n        }\n        return framingRectInPreview;\n    }\n\n\n    /**\n     * Allows third party apps to specify the camera ID, rather than determine\n     * it automatically based on available cameras and their orientation.\n     *\n     * @param cameraId camera ID of the camera to use. A negative value means \"no preference\".\n     */\n    public synchronized void setManualCameraId(int cameraId) {\n        requestedCameraId = cameraId;\n    }\n\n    /**\n     * Allows third party apps to specify the scanning rectangle dimensions, rather than determine\n     * them automatically based on screen resolution.\n     *\n     * @param width  The width in pixels to scan.\n     * @param height The height in pixels to scan.\n     */\n    public synchronized void setManualFramingRect(int width, int height) {\n        if (initialized) {\n            Point screenResolution = configManager.getScreenResolution();\n            if (width > screenResolution.x) {\n                width = screenResolution.x;\n            }\n            if (height > screenResolution.y) {\n                height = screenResolution.y;\n            }\n            int leftOffset = (screenResolution.x - width) / 2;\n            int topOffset = (screenResolution.y - height) / 2;\n            framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);\n            Log.d(TAG, \"Calculated manual framing rect: \" + framingRect);\n            framingRectInPreview = null;\n        } else {\n            requestedFramingRectWidth = width;\n            requestedFramingRectHeight = height;\n        }\n    }\n\n    /**\n     * A factory method to build the appropriate LuminanceSource object based on the format\n     * of the preview buffers, as described by Camera.Parameters.\n     *\n     * @param data   A preview frame.\n     * @param width  The width of the image.\n     * @param height The height of the image.\n     * @return A PlanarYUVLuminanceSource instance.\n     */\n    public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {\n        // 直接返回整幅图像的数据，而不需要计算聚焦框的大小。\n//        return new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);\n        Rect rect = getFramingRectInPreview();\n        if (rect == null) {\n            return null;\n        }\n        // Go ahead and assume it's YUV rather than die.\n        return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,\n                rect.width(), rect.height(), false);\n    }\n\n\n    public void handleZoom(boolean isZoomIn) {\n        Camera theCamera = camera.getCamera();\n        if (theCamera != null && previewing) {\n            Camera.Parameters params = theCamera.getParameters();\n            if (params != null && params.isZoomSupported()) {\n                int zoom = params.getZoom();\n                if (isZoomIn && zoom < params.getMaxZoom()) {\n                    Log.d(TAG, \"放大\");\n                    zoom++;\n                } else if (!isZoomIn && zoom > 0) {\n                    Log.d(TAG, \"缩小\");\n                    zoom--;\n                }\n                params.setZoom(zoom);\n                theCamera.setParameters(params);\n            } else {\n                Log.d(TAG, \"不支持缩放\");\n            }\n        } else {\n            Log.d(TAG, \"camera is not previewing.\");\n        }\n    }\n\n\n    public void handleDoubleZoom() {\n        Camera theCamera = camera.getCamera();\n        if (theCamera != null && previewing) {\n            Camera.Parameters params = theCamera.getParameters();\n            if (params != null && params.isZoomSupported()) {\n                int curZoom = params.getZoom();\n                int maxZoom = params.getMaxZoom();\n                if (curZoom >= maxZoom / 2) {\n                    params.setZoom(0);\n                } else if (curZoom < maxZoom / 2) {\n                    params.setZoom(maxZoom / 2);\n                }\n                theCamera.setParameters(params);\n            } else {\n                Log.d(TAG, \"不支持缩放\");\n            }\n        } else {\n            Log.d(TAG, \"camera is not previewing.\");\n        }\n    }\n\n\n    public boolean isPreviewing() {\n        return previewing;\n    }\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/FrontLightMode.java",
    "content": "/*\n * Copyright (C) 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera;\n\nimport android.content.SharedPreferences;\n\n/**\n * Enumerates settings of the preference controlling the front light.\n */\npublic enum FrontLightMode {\n\n  /** Always on. */\n  ON,\n  /** On only when ambient light is low. */\n  AUTO,\n  /** Always off. */\n  OFF;\n\n  private static FrontLightMode parse(String modeString) {\n    return modeString == null ? OFF : valueOf(modeString);\n  }\n\n  public static FrontLightMode readPref(SharedPreferences sharedPrefs) {\n    return parse(null);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/PreviewCallback.java",
    "content": "/*\n * Copyright (C) 2010 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera;\n\nimport android.graphics.Point;\nimport android.hardware.Camera;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.Log;\n\n@SuppressWarnings(\"deprecation\") // camera APIs\nfinal class PreviewCallback implements Camera.PreviewCallback {\n\n  private static final String TAG = PreviewCallback.class.getSimpleName();\n\n  private final CameraConfigurationManager configManager;\n  private Handler previewHandler;\n  private int previewMessage;\n\n  PreviewCallback(CameraConfigurationManager configManager) {\n    this.configManager = configManager;\n  }\n\n  void setHandler(Handler previewHandler, int previewMessage) {\n    this.previewHandler = previewHandler;\n    this.previewMessage = previewMessage;\n  }\n\n  @Override\n  public void onPreviewFrame(byte[] data, Camera camera) {\n    Point cameraResolution = configManager.getCameraResolution();\n    Handler thePreviewHandler = previewHandler;\n    if (cameraResolution != null && thePreviewHandler != null) {\n      Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,\n          cameraResolution.y, data);\n      message.sendToTarget();\n      previewHandler = null;\n    } else {\n      Log.d(TAG, \"Got preview callback, but no handler or resolution available\");\n    }\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/open/CameraFacing.java",
    "content": "/*\n * Copyright (C) 2015 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera.open;\n\n/**\n * Enumeration of directions a camera may face: front or back.\n */\npublic enum CameraFacing {\n\n  BACK,  // must be value 0!\n  FRONT, // must be value 1!\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/open/OpenCamera.java",
    "content": "/*\n * Copyright (C) 2015 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera.open;\n\nimport android.hardware.Camera;\n\n/**\n * Represents an open {@link Camera} and its metadata, like facing direction and orientation.\n */\n@SuppressWarnings(\"deprecation\") // camera APIs\npublic final class OpenCamera {\n  \n  private final int index;\n  private final Camera camera;\n  private final CameraFacing facing;\n  private final int orientation;\n  \n  public OpenCamera(int index, Camera camera, CameraFacing facing, int orientation) {\n    this.index = index;\n    this.camera = camera;\n    this.facing = facing;\n    this.orientation = orientation;\n  }\n\n  public Camera getCamera() {\n    return camera;\n  }\n\n  public CameraFacing getFacing() {\n    return facing;\n  }\n\n  public int getOrientation() {\n    return orientation;\n  }\n\n  @Override\n  public String toString() {\n    return \"Camera #\" + index + \" : \" + facing + ',' + orientation;\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/java/com/nanchen/scanner/zxing/camera/open/OpenCameraInterface.java",
    "content": "/*\n * Copyright (C) 2012 ZXing authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.nanchen.scanner.zxing.camera.open;\n\nimport android.hardware.Camera;\nimport android.util.Log;\n\n/**\n * Abstraction over the {@link Camera} API that helps open them and return their metadata.\n */\n@SuppressWarnings(\"deprecation\") // camera APIs\npublic final class OpenCameraInterface {\n\n  private static final String TAG = OpenCameraInterface.class.getName();\n\n  /** For {@link #open(int)}, means no preference for which camera to open. */\n  public static final int NO_REQUESTED_CAMERA = -1;\n\n  private OpenCameraInterface() {\n  }\n\n  /**\n   * Opens the requested camera with {@link Camera#open(int)}, if one exists.\n   *\n   * @param cameraId camera ID of the camera to use. A negative value\n   *  or {@link #NO_REQUESTED_CAMERA} means \"no preference\", in which case a rear-facing\n   *  camera is returned if possible or else any camera\n   * @return handle to {@link OpenCamera} that was opened\n   */\n  public static OpenCamera open(int cameraId) {\n\n    int numCameras = Camera.getNumberOfCameras();\n    if (numCameras == 0) {\n      Log.w(TAG, \"No cameras!\");\n      return null;\n    }\n    if (cameraId >= numCameras) {\n      Log.w(TAG, \"Requested camera does not exist: \" + cameraId);\n      return null;\n    }\n\n    if (cameraId <= NO_REQUESTED_CAMERA) {\n      cameraId = 0;\n      while (cameraId < numCameras) {\n        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();\n        Camera.getCameraInfo(cameraId, cameraInfo);\n        if (CameraFacing.values()[cameraInfo.facing] == CameraFacing.BACK) {\n          break;\n        }\n        cameraId++;\n      }\n      if (cameraId == numCameras) {\n        Log.i(TAG, \"No camera facing \" + CameraFacing.BACK + \"; returning camera #0\");\n        cameraId = 0;\n      }\n    }\n\n    Log.i(TAG, \"Opening camera #\" + cameraId);\n    Camera.CameraInfo cameraInfo = new Camera.CameraInfo();\n    Camera.getCameraInfo(cameraId, cameraInfo);\n    Camera camera = Camera.open(cameraId);\n    if (camera == null) {\n      return null;\n    }\n    return new OpenCamera(cameraId,\n                          camera,\n                          CameraFacing.values()[cameraInfo.facing],\n                          cameraInfo.orientation);\n  }\n\n}\n"
  },
  {
    "path": "scanner/src/main/res/color/dialog_pro_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:color=\"@android:color/white\"/>\n\n</selector>"
  },
  {
    "path": "scanner/src/main/res/drawable/shape_dialog_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <solid android:color=\"#c1333333\" />\n\n    <corners android:radius=\"5dip\" />\n\n\n</shape>"
  },
  {
    "path": "scanner/src/main/res/layout/activity_capture.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.constraint.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"com.nanchen.scanner.module.CaptureActivity\">\n\n    <SurfaceView\n        android:id=\"@+id/surfaceView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <com.nanchen.scanner.zxing.ViewfinderView\n        android:id=\"@+id/viewfinderView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n\n    <RelativeLayout\n        android:id=\"@+id/layoutTitle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/title_bar_height\">\n\n        <ImageView\n            android:id=\"@+id/ivBack\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:paddingTop=\"8dp\"\n            android:paddingBottom=\"8dp\"\n            android:src=\"@drawable/ic_common_back_white\" />\n\n\n        <TextView\n            android:id=\"@+id/tvTitle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@string/scanner\"\n            android:textColor=\"@color/white\" />\n\n        <TextView\n            android:id=\"@+id/tvGallery\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginEnd=\"10dp\"\n            android:layout_marginRight=\"10dp\"\n            android:background=\"@null\"\n            android:padding=\"8dp\"\n            android:text=\"@string/gallery\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"16sp\" />\n    </RelativeLayout>\n\n    <TextView\n        android:id=\"@+id/tvTips\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"20dp\"\n        android:text=\"@string/scan_qr_desc\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/text_size_default\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/viewfinderView\" />\n\n    <android.support.constraint.Guideline\n        android:id=\"@+id/guideline6\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"\n        app:layout_constraintGuide_percent=\"0.8\" />\n\n    <TextView\n        android:id=\"@+id/tvFlash\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"@dimen/margin_20dp\"\n        android:drawableTop=\"@mipmap/icon_torch\"\n        android:drawablePadding=\"@dimen/margin_small\"\n        android:text=\"@string/scan_qr_torch\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/text_size_smallest\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"@+id/guideline6\" />\n\n</android.support.constraint.ConstraintLayout>"
  },
  {
    "path": "scanner/src/main/res/layout/dialog_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/shape_dialog_bg\"\n    android:orientation=\"vertical\"\n    android:gravity=\"center\">\n\n\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:gravity=\"center\"\n        android:layout_margin=\"30dp\"\n        >\n        <ProgressBar\n            android:id=\"@+id/pb_loading\"\n            android:layout_width=\"40dp\"\n            android:layout_height=\"40dp\"\n            />\n        <TextView\n            android:id=\"@+id/tv_hint\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:singleLine=\"true\"\n            android:textSize=\"14sp\"\n            android:textColor=\"#fff\"\n            android:text=\"请稍后...\"\n            android:layout_marginTop=\"10dp\"\n            />\n\n    </LinearLayout>\n\n\n</LinearLayout>"
  },
  {
    "path": "scanner/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"viewfinder_mask\">#60000000</color>\n    <color name=\"viewfinder_corner_bar\">#009FDE</color>\n    <color name=\"viewfinder_corner_bar2\">#30FF0000</color>\n    <color name=\"viewfinder_laser\">@color/viewfinder_corner_bar</color>\n    <color name=\"possible_result_points\">#c0ffbd21</color>\n    <color name=\"result_view\">#b0000000</color>\n    <color name=\"white\">#ffffff</color>\n</resources>"
  },
  {
    "path": "scanner/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <dimen name=\"scan_qr_frame_size\">215dp</dimen>\n    <dimen name=\"scan_qr_frame_top_margin\">110dp</dimen>\n    <dimen name=\"scan_frame_corner_w\">32dp</dimen>\n    <dimen name=\"scan_frame_corner_h\">4dp</dimen>\n    <dimen name=\"laser_bar_h\">4dp</dimen>\n    <!-- 字体大小 -->\n    <dimen name=\"text_size_smallest\">10sp</dimen>\n    <dimen name=\"text_size_11sp\">11sp</dimen>\n    <dimen name=\"text_size_small\">12sp</dimen>\n    <dimen name=\"text_size_13sp\">13sp</dimen>\n    <dimen name=\"text_size_default\">14sp</dimen>\n    <dimen name=\"text_size_15sp\">15sp</dimen>\n    <dimen name=\"text_size_big\">16sp</dimen>\n    <dimen name=\"text_size_17sp\">17sp</dimen>\n    <dimen name=\"text_size_bigger\">18sp</dimen>\n    <dimen name=\"text_size_biggest\">20sp</dimen>\n\n    <dimen name=\"tool_bar_h\">48dp</dimen>\n    <dimen name=\"btn_common_h\">45dp</dimen>\n    <dimen name=\"tab_bar_h\">50dp</dimen>\n    <dimen name=\"margin_smallest\">2dp</dimen>\n    <dimen name=\"margin_small\">4dp</dimen>\n    <dimen name=\"margin_5dp\">5dp</dimen>\n    <dimen name=\"margin_6dp\">6dp</dimen>\n    <dimen name=\"margin_mid\">8dp</dimen>\n    <dimen name=\"margin_9dp\">9dp</dimen>\n    <dimen name=\"margin_10dp\">10dp</dimen>\n    <dimen name=\"margin_big\">12dp</dimen>\n    <dimen name=\"margin_13dp\">13dp</dimen>\n    <dimen name=\"margin_bigger\">14dp</dimen>\n    <dimen name=\"margin_15dp\">15dp</dimen>\n    <dimen name=\"margin_large\">16dp</dimen>\n    <dimen name=\"margin_largger\">18dp</dimen>\n    <dimen name=\"margin_20dp\">20dp</dimen>\n    <dimen name=\"title_bar_height\">40dp</dimen>\n</resources>"
  },
  {
    "path": "scanner/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright (C) 2008 ZXing authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n -->\n<resources>\n  <item type=\"id\" name=\"decode\"/>\n  <item type=\"id\" name=\"decode_failed\"/>\n  <item type=\"id\" name=\"decode_succeeded\"/>\n  <item type=\"id\" name=\"launch_product_query\"/>\n  <item type=\"id\" name=\"quit\"/>\n  <item type=\"id\" name=\"restart_preview\"/>\n  <item type=\"id\" name=\"return_scan_result\"/>\n</resources>\n"
  },
  {
    "path": "scanner/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">scanner</string>\n    <string name=\"scan_qr_desc\">将二维码放入框内，即可自动扫描</string>\n    <string name=\"scan_qr_torch\">手电筒</string>\n    <string name=\"scanner\">扫一扫</string>\n    <string name=\"gallery\">相册</string>\n\n    <string name=\"msg_camera_framework_bug\">很抱歉，Android 相机出现问题。你可能需要重启设备。</string>\n    <string name=\"common_ok\">确定</string>\n    <string name=\"scanner_camera_refuse\">摄像头权限被拒绝！</string>\n    <string name=\"scanner_get_picture_error\">获取图片失败！</string>\n    <string name=\"scanner_waiting\">请稍后...</string>\n    <string name=\"scanner_failed\">识别失败！</string>\n</resources>\n"
  },
  {
    "path": "scanner/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"AlertDialogStyle\" parent=\"@android:style/Theme.Dialog\">\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:backgroundDimEnabled\">true</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n    </style>\n\n    <style name=\"ActivityTranslucent\">\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:activityOpenEnterAnimation\">@null</item>\n        <item name=\"android:activityOpenExitAnimation\">@null</item>\n        <item name=\"android:activityCloseEnterAnimation\">@null</item>\n        <item name=\"android:activityCloseExitAnimation\">@null</item>\n    </style>\n</resources>"
  },
  {
    "path": "scanner/src/main/res/values-ja/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">scanner</string>\n    <string name=\"scan_qr_desc\">箱にQRコードを入れれば自動的にそれをスキャンすることができます</string>\n    <string name=\"scan_qr_torch\">懐中電灯</string>\n    <string name=\"scanner\">スイープ</string>\n    <string name=\"gallery\">アルバム</string>\n\n    <string name=\"msg_camera_framework_bug\">申し訳ありませんが、Androidカメラに問題があります。 デバイスを再起動する必要があります。</string>\n    <string name=\"common_ok\">決定する</string>\n    <string name=\"scanner_camera_refuse\">カメラの許可が拒否されました</string>\n    <string name=\"scanner_get_picture_error\">写真を取得できませんでした。</string>\n    <string name=\"scanner_waiting\">お待ちください...</string>\n    <string name=\"scanner_failed\">識別に失敗しました。</string>\n</resources>\n"
  },
  {
    "path": "scanner/src/main/res/xml/preferences.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Copyright (C) 2008 ZXing authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n -->\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <PreferenceCategory>\n    <CheckBoxPreference\n        android:key=\"preferences_decode_1D_product\"\n        android:defaultValue=\"false\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_decode_1D_industrial\"\n        android:defaultValue=\"false\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_decode_QR\"\n        android:defaultValue=\"true\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_decode_Data_Matrix\"\n        android:defaultValue=\"false\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_decode_Aztec\"\n        android:defaultValue=\"false\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_decode_PDF417\"\n        android:defaultValue=\"false\"/>\n  </PreferenceCategory>\n\n  <PreferenceCategory>\n    <CheckBoxPreference\n      android:key=\"preferences_play_beep\"\n      android:defaultValue=\"true\"/>\n    <CheckBoxPreference\n      android:key=\"preferences_vibrate\"\n      android:defaultValue=\"false\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_auto_focus\"\n        android:defaultValue=\"true\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_disable_continuous_focus\"\n        android:defaultValue=\"true\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_disable_exposure\"\n        android:defaultValue=\"true\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_disable_metering\"\n        android:defaultValue=\"true\"/>\n    <CheckBoxPreference\n        android:key=\"preferences_disable_barcode_scene_mode\"\n        android:defaultValue=\"true\"/>\n    <CheckBoxPreference\n      android:key=\"preferences_invert_scan\"\n      android:defaultValue=\"false\"/>\n  </PreferenceCategory>\n</PreferenceScreen>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app', ':scanner'\n"
  }
]