Browse Source

zxing-cpp: strip to core library only (remove tests/wrappers/examples/docs); .gitignore: fix /core to allow zxing-cpp/core/ dir

feature/x25519-migration
Evgeny 3 months ago
parent
commit
8051dfe4e5
  1. 2
      .gitignore
  2. 43
      tools/chatgui/zxing-cpp/.clang-format
  3. 58
      tools/chatgui/zxing-cpp/.clang-tidy
  4. 19
      tools/chatgui/zxing-cpp/.editorconfig
  5. 3
      tools/chatgui/zxing-cpp/.gitattributes
  6. 2
      tools/chatgui/zxing-cpp/.github/FUNDING.yml
  7. 102
      tools/chatgui/zxing-cpp/.github/create_release_archive.py
  8. 435
      tools/chatgui/zxing-cpp/.github/workflows/ci.yml
  9. 59
      tools/chatgui/zxing-cpp/.github/workflows/codeql-analysis.yml
  10. 71
      tools/chatgui/zxing-cpp/.github/workflows/msvc-analysis.yml
  11. 41
      tools/chatgui/zxing-cpp/.github/workflows/publish-android.yml
  12. 130
      tools/chatgui/zxing-cpp/.github/workflows/publish-dotnet.yml
  13. 131
      tools/chatgui/zxing-cpp/.github/workflows/publish-gh-pages.yml
  14. 97
      tools/chatgui/zxing-cpp/.github/workflows/publish-kn.yml
  15. 100
      tools/chatgui/zxing-cpp/.github/workflows/publish-python.yml
  16. 39
      tools/chatgui/zxing-cpp/.github/workflows/publish-source.yml
  17. 82
      tools/chatgui/zxing-cpp/.github/workflows/publish-winrt.yml
  18. 27
      tools/chatgui/zxing-cpp/.gitignore
  19. 4
      tools/chatgui/zxing-cpp/.gitmodules
  20. 38
      tools/chatgui/zxing-cpp/Package.swift
  21. 154
      tools/chatgui/zxing-cpp/README.md
  22. 783
      tools/chatgui/zxing-cpp/core/CMakeLists.txt
  23. 28
      tools/chatgui/zxing-cpp/core/Version.h.in
  24. 9
      tools/chatgui/zxing-cpp/core/ZXingConfig.cmake.in
  25. 255
      tools/chatgui/zxing-cpp/core/src/Barcode.cpp
  26. 191
      tools/chatgui/zxing-cpp/core/src/Barcode.h
  27. 100
      tools/chatgui/zxing-cpp/core/src/BarcodeData.h
  28. 286
      tools/chatgui/zxing-cpp/core/src/BarcodeFormat.cpp
  29. 272
      tools/chatgui/zxing-cpp/core/src/BarcodeFormat.h
  30. 111
      tools/chatgui/zxing-cpp/core/src/BinaryBitmap.cpp
  31. 66
      tools/chatgui/zxing-cpp/core/src/BinaryBitmap.h
  32. 38
      tools/chatgui/zxing-cpp/core/src/BitArray.cpp
  33. 201
      tools/chatgui/zxing-cpp/core/src/BitArray.h
  34. 175
      tools/chatgui/zxing-cpp/core/src/BitMatrix.cpp
  35. 199
      tools/chatgui/zxing-cpp/core/src/BitMatrix.h
  36. 245
      tools/chatgui/zxing-cpp/core/src/BitMatrixCursor.h
  37. 101
      tools/chatgui/zxing-cpp/core/src/BitMatrixIO.cpp
  38. 21
      tools/chatgui/zxing-cpp/core/src/BitMatrixIO.h
  39. 77
      tools/chatgui/zxing-cpp/core/src/BitSource.cpp
  40. 72
      tools/chatgui/zxing-cpp/core/src/BitSource.h
  41. 42
      tools/chatgui/zxing-cpp/core/src/ByteArray.h
  42. 78
      tools/chatgui/zxing-cpp/core/src/CharacterSet.cpp
  43. 58
      tools/chatgui/zxing-cpp/core/src/CharacterSet.h
  44. 270
      tools/chatgui/zxing-cpp/core/src/ConcentricFinder.cpp
  45. 150
      tools/chatgui/zxing-cpp/core/src/ConcentricFinder.h
  46. 471
      tools/chatgui/zxing-cpp/core/src/Content.cpp
  47. 82
      tools/chatgui/zxing-cpp/core/src/Content.h
  48. 19
      tools/chatgui/zxing-cpp/core/src/ContentType.h
  49. 490
      tools/chatgui/zxing-cpp/core/src/CreateBarcode.cpp
  50. 132
      tools/chatgui/zxing-cpp/core/src/CreateBarcode.h
  51. 92
      tools/chatgui/zxing-cpp/core/src/DecoderResult.h
  52. 44
      tools/chatgui/zxing-cpp/core/src/DetectorResult.h
  53. 81
      tools/chatgui/zxing-cpp/core/src/ECI.cpp
  54. 78
      tools/chatgui/zxing-cpp/core/src/ECI.h
  55. 29
      tools/chatgui/zxing-cpp/core/src/Error.cpp
  56. 79
      tools/chatgui/zxing-cpp/core/src/Error.h
  57. 165
      tools/chatgui/zxing-cpp/core/src/Flags.h
  58. 253
      tools/chatgui/zxing-cpp/core/src/GTIN.cpp
  59. 58
      tools/chatgui/zxing-cpp/core/src/GTIN.h
  60. 197
      tools/chatgui/zxing-cpp/core/src/GlobalHistogramBinarizer.cpp
  61. 34
      tools/chatgui/zxing-cpp/core/src/GlobalHistogramBinarizer.h
  62. 124
      tools/chatgui/zxing-cpp/core/src/GridSampler.cpp
  63. 59
      tools/chatgui/zxing-cpp/core/src/GridSampler.h
  64. 343
      tools/chatgui/zxing-cpp/core/src/HRI.cpp
  65. 16
      tools/chatgui/zxing-cpp/core/src/HRI.h
  66. 315
      tools/chatgui/zxing-cpp/core/src/HybridBinarizer.cpp
  67. 40
      tools/chatgui/zxing-cpp/core/src/HybridBinarizer.h
  68. 168
      tools/chatgui/zxing-cpp/core/src/ImageView.h
  69. 154
      tools/chatgui/zxing-cpp/core/src/JSON.cpp
  70. 66
      tools/chatgui/zxing-cpp/core/src/JSON.h
  71. 242
      tools/chatgui/zxing-cpp/core/src/LocalGrid.cpp
  72. 91
      tools/chatgui/zxing-cpp/core/src/LocalGrid.h
  73. 104
      tools/chatgui/zxing-cpp/core/src/LogMatrix.h
  74. 125
      tools/chatgui/zxing-cpp/core/src/Matrix.h
  75. 107
      tools/chatgui/zxing-cpp/core/src/MultiFormatReader.cpp
  76. 34
      tools/chatgui/zxing-cpp/core/src/MultiFormatReader.h
  77. 109
      tools/chatgui/zxing-cpp/core/src/MultiFormatWriter.cpp
  78. 62
      tools/chatgui/zxing-cpp/core/src/MultiFormatWriter.h
  79. 427
      tools/chatgui/zxing-cpp/core/src/Pattern.h
  80. 81
      tools/chatgui/zxing-cpp/core/src/PerspectiveTransform.cpp
  81. 47
      tools/chatgui/zxing-cpp/core/src/PerspectiveTransform.h
  82. 173
      tools/chatgui/zxing-cpp/core/src/Point.h
  83. 215
      tools/chatgui/zxing-cpp/core/src/Quadrilateral.h
  84. 112
      tools/chatgui/zxing-cpp/core/src/Range.h
  85. 335
      tools/chatgui/zxing-cpp/core/src/ReadBarcode.cpp
  86. 33
      tools/chatgui/zxing-cpp/core/src/ReadBarcode.h
  87. 32
      tools/chatgui/zxing-cpp/core/src/Reader.h
  88. 210
      tools/chatgui/zxing-cpp/core/src/ReaderOptions.h
  89. 101
      tools/chatgui/zxing-cpp/core/src/ReedSolomon.cpp
  90. 65
      tools/chatgui/zxing-cpp/core/src/ReedSolomon.h
  91. 161
      tools/chatgui/zxing-cpp/core/src/RegressionLine.h
  92. 20
      tools/chatgui/zxing-cpp/core/src/ResultPoint.cpp
  93. 35
      tools/chatgui/zxing-cpp/core/src/ResultPoint.h
  94. 121
      tools/chatgui/zxing-cpp/core/src/StdGenerator.h
  95. 38
      tools/chatgui/zxing-cpp/core/src/StdPrint.h
  96. 55
      tools/chatgui/zxing-cpp/core/src/StdScope.h
  97. 19
      tools/chatgui/zxing-cpp/core/src/StructuredAppend.h
  98. 29
      tools/chatgui/zxing-cpp/core/src/SymbologyIdentifier.h
  99. 54
      tools/chatgui/zxing-cpp/core/src/TextDecoder.cpp
  100. 23
      tools/chatgui/zxing-cpp/core/src/TextDecoder.h
  101. Some files were not shown because too many files have changed in this diff Show More

2
.gitignore vendored

@ -60,7 +60,7 @@ Makefile.in
.cache/
# Debug outputs
core
/core
vgcore.*
callgrind.out.*

43
tools/chatgui/zxing-cpp/.clang-format

@ -1,43 +0,0 @@
---
Language: Cpp
Standard: c++17
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: ForContinuationAndIndentation # ForIndentation
AccessModifierOffset: -4
ColumnLimit: 135
#AlignConsecutiveAssignments: true
AlignConsecutiveBitFields: true
AlignEscapedNewlines: DontAlign
AlignTrailingComments: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
#AllowShortLambdasOnASingleLine: Inline
AllowShortEnumsOnASingleLine: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterEnum: true
AfterStruct: true
AfterUnion: true
AfterFunction: true
SplitEmptyFunction: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
FixNamespaceComments: true
IncludeBlocks: Regroup
KeepEmptyLinesAtTheStartOfBlocks: false
PointerAlignment: Left
ReflowComments: true
SortIncludes: true

58
tools/chatgui/zxing-cpp/.clang-tidy

@ -1,58 +0,0 @@
---
Checks: >
-*,
bugprone-*,
-bugprone-easily-swappable-parameters,
-bugprone-exception-escape,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-integer-division,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-signed-char-misuse,
-bugprone-use-after-move,
-bugprone-assignment-in-if-condition,
performance-*,
-performance-enum-size,
-performance-move-const-arg,
portability-*,
-portability-avoid-pragma-once,
readability-avoid-const-params-in-decls,
readability-const-return-type,
readability-delete-null-pointer,
readability-deleted-default,
readability-inconsistent-declaration-parameter-name,
readability-misleading-indentation,
readability-misplaced-array-index,
readability-non-const-parameter,
readability-redundant-*,
readability-simplify-*,
-readability-simplify-boolean-expr,
readability-static-*,
-readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-suspicious-call-argument,
readability-uniqueptr-delete-release,
readability-use-anyofallof,
-readability-redundant-inline-specifier,
-readability-redundant-member-init,
modernize-deprecated-*,
modernize-loop-convert,
modernize-make-*,
modernize-pass-by-value,
modernize-raw-string-literal,
modernize-redundant-void-arg,
modernize-replace-*,
modernize-return-braced-init-list,
modernize-shrink-to-fit,
modernize-unary-static-assert,
modernize-use-*,
-modernize-use-constraints,
-modernize-use-designated-initializers,
-modernize-use-nodiscard,
-modernize-use-ranges,
-modernize-use-trailing-return-type,
-modernize-use-integer-sign-comparison,
WarningsAsErrors: ''
HeaderFilterRegex: '^(?!.*zint\.h).*$'
FormatStyle: file

19
tools/chatgui/zxing-cpp/.editorconfig

@ -1,19 +0,0 @@
root = true
[*]
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{cpp,c,h,html,py,kt,cs,qlm}]
indent_style = tab
indent_size = 4
max_line_length = 135
[CMakeLists.txt]
indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false

3
tools/chatgui/zxing-cpp/.gitattributes vendored

@ -1,3 +0,0 @@
test/samples export-ignore
zint export-ignore
.git* export-ignore

2
tools/chatgui/zxing-cpp/.github/FUNDING.yml

@ -1,2 +0,0 @@
github: axxel
custom: 'www.paypal.com/donate/?hosted_button_id=8LDF4NNQF8Z3L'

102
tools/chatgui/zxing-cpp/.github/create_release_archive.py

@ -1,102 +0,0 @@
#!/usr/bin/env python3
import subprocess
import tempfile
import shutil
import sys
import re
from pathlib import Path
def run_command(cmd, cwd=None):
try:
return subprocess.run(cmd, check=True, capture_output=True, text=True, cwd=cwd)
except subprocess.CalledProcessError as e:
print(f"✗ Error {cmd}: {e.stderr}")
sys.exit(1)
def main(repo_root=None, output_dir=None):
repo_root = Path.cwd() if repo_root is None else Path(repo_root).resolve()
output_dir = repo_root if output_dir is None else Path(output_dir).resolve()
output_dir.mkdir(parents=True, exist_ok=True)
print(f"Working in repository: {repo_root}")
# Extract version and optional suffix from core/CMakeLists.txt
cmake_txt = (repo_root / "core" / "CMakeLists.txt").read_text()
version = re.search(r'ZXing VERSION "?([0-9]+\.[0-9]+\.[0-9]+)"?', cmake_txt).group(1)
suffix = re.search(r'ZXING_VERSION_SUFFIX "?([^\)\s\"]*)"?', cmake_txt).group(1)
ver_tag = f"{version}{suffix}"
print(f"Project version from CMake: {ver_tag}")
# Create output filenames with git tag
output_stem = f"zxing-cpp-{ver_tag}"
output_tgz = output_dir / f"{output_stem}.tar.gz"
output_zip = output_dir / f"{output_stem}.zip"
# Step 1: Create initial tar ball via git archive
print("\n[1/4] Creating git archive...")
run_command(["git", "archive", f"--prefix={output_stem}/", f"--output={output_tgz}", "HEAD", repo_root])
print(f"✓ Created git archive: {output_tgz}")
# Step 2: Extract tar ball into temporary folder
print("\n[2/4] Extracting archive to temporary folder...")
with tempfile.TemporaryDirectory() as temp_dir:
print(f"✓ Created temporary directory: {temp_dir}")
run_command(["tar", "-xf", str(output_tgz), "-C", temp_dir])
print(f"✓ Extracted archive to temporary folder")
# Step 3: Replace files in core/src/libzint/ with files from zint/backend/
print("\n[3/4] Replacing files in core/src/libzint/ with zint/backend/ files...")
dst_dir = Path(temp_dir) / output_stem / "core" / "src" / "libzint"
src_dir = repo_root / "zint" / "backend" # Use actual repo directory
# Find all files/symlinks currently in core/src/libzint/ (including subdirectories)
libzint_files = list(dst_dir.rglob("*"))
# Include both regular files and symlinks
libzint_files = [f for f in libzint_files if f.is_file() or f.is_symlink()]
replaced_count = 0
not_found_count = 0
for libzint_file in libzint_files:
# Get relative path from core/src/libzint/
rel_path = libzint_file.relative_to(dst_dir)
# Source path in zint/backend/
source_file = src_dir / rel_path
# Only replace if the file exists in zint/backend/
if source_file.exists() and source_file.is_file():
# Remove symlink if it exists
if libzint_file.is_symlink():
libzint_file.unlink()
# Copy the actual file
shutil.copy2(source_file, libzint_file)
replaced_count += 1
else:
not_found_count += 1
print(f" - Skipped (not in zint/backend): {rel_path}")
print(f"✓ Replaced {replaced_count} files, skipped {not_found_count} files")
# Step 4: Create tar.gz and zip archives
print("\n[4/4] Creating archives (tar.gz and zip)...")
run_command(["tar", "-czf", str(output_tgz), "-C", temp_dir, output_stem])
if (output_zip.exists()):
output_zip.unlink()
run_command(["zip", "-q", "-r", str(output_zip), output_stem], cwd=temp_dir)
# Cleanup initial tar file
print(f"\n✅ Done! Source archives created:")
print(f" - {output_tgz}")
print(f" - {output_zip}")
if __name__ == "__main__":
repo_root = sys.argv[1] if len(sys.argv) > 1 else None
output_dir = sys.argv[2] if len(sys.argv) > 2 else None
main(repo_root=repo_root, output_dir=output_dir)

435
tools/chatgui/zxing-cpp/.github/workflows/ci.yml

@ -1,435 +0,0 @@
name: CI
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
# don't cancel all jobs just because one of them failed
fail-fast: false
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Setup Python 3
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: Configure
run: >
cmake -S . -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_SHARED_LIBS=ON
-DZXING_BLACKBOX_TESTS=ON -DZXING_TEST_DOTNET=ON -DZXING_TEST_GO=ON -DZXING_TEST_INSTALL=ON -DZXING_UNIT_TESTS=ON
-DZXING_PYTHON_MODULE=ON
- name: Build
run: cmake --build build --parallel --config ${{env.BUILD_TYPE}}
- name: Set PATH for Tests
shell: bash # to make the $GITHUB_PATH update work
if: runner.os == 'Windows'
run: |
echo "${GITHUB_WORKSPACE}/build/core/${BUILD_TYPE}" >> $GITHUB_PATH
echo "${GITHUB_WORKSPACE}/build/bin/${BUILD_TYPE}" >> $GITHUB_PATH
- name: Test
run: ctest --test-dir build -V -C ${{env.BUILD_TYPE}}
- name: Install
run: |
cmake -E make_directory install
cmake --install build --config ${{env.BUILD_TYPE}} --prefix ${{github.workspace}}/install
- uses: actions/upload-artifact@v6
with:
name: ${{matrix.os}}-artifacts
path: install
build-exp:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest, ubuntu-24.04-arm, macos-15-intel, windows-11-arm]
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Setup Python 3
id: setup-python
if: matrix.os == 'windows-11-arm' # the default python on the arm runner is an x64 build
uses: actions/setup-python@v6
with:
python-version: '3.13'
architecture: arm64
- name: Configure
run: >
cmake -S . -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_SHARED_LIBS=ON
-DZXING_READERS=ON -DZXING_WRITERS=BOTH -DZXING_EXPERIMENTAL_API=ON
-DZXING_BLACKBOX_TESTS=ON -DZXING_TEST_INSTALL=ON -DZXING_UNIT_TESTS=OFF -DZXING_PYTHON_MODULE=ON
${{ steps.setup-python.outputs.python-path != '' && format('-DPython_EXECUTABLE={0}', steps.setup-python.outputs.python-path) || '' }}
- name: Build
run: cmake --build build --parallel --config ${{env.BUILD_TYPE}}
- name: Set PATH for Tests
shell: bash # to make the $GITHUB_PATH update work
if: runner.os == 'Windows'
run: |
echo "${GITHUB_WORKSPACE}/build/core/${BUILD_TYPE}" >> $GITHUB_PATH
echo "${GITHUB_WORKSPACE}/build/bin/${BUILD_TYPE}" >> $GITHUB_PATH
- name: Test
run: ctest --test-dir build -V -C ${{env.BUILD_TYPE}}
- name: Install
run: |
cmake -E make_directory install
cmake --install build --config ${{env.BUILD_TYPE}} --prefix ${{github.workspace}}/install
- uses: actions/upload-artifact@v6
with:
name: ${{matrix.os}}-exp-artifacts
path: install
build-ubuntu-gcc-12: # keep homebrew happy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Configure
run: >
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
-DZXING_WRITERS=BOTH
-DCMAKE_CXX_COMPILER=g++-12 -DCMAKE_C_COMPILER=gcc-12
- name: Build
run: cmake --build build --parallel
- name: Test
run: ctest -V --test-dir build
build-ubuntu-asan-ubsan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Configure
run: >
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DZXING_WRITERS=BOTH
-DZXING_BLACKBOX_TESTS=ON -DZXING_UNIT_TESTS=ON -DZXING_PYTHON_MODULE=OFF -DZXING_C_API=OFF
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_FLAGS="-march=native -fsanitize=address,undefined -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer"
-DCMAKE_C_COMPILER=clang
-DCMAKE_C_FLAGS="-march=native -fsanitize=address,undefined -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer"
- name: Build
run: cmake --build build --parallel
- name: Test
run: ctest -V --test-dir build
build-ubuntu-tsan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Configure
run: >
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DZXING_WRITERS=OFF -DZXING_EXAMPLES=OFF
-DZXING_BLACKBOX_TESTS=ON -DZXING_UNIT_TESTS=OFF -DZXING_PYTHON_MODULE=OFF -DZXING_C_API=OFF
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_FLAGS="-march=native -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer -g"
-DCMAKE_C_COMPILER=clang
-DCMAKE_C_FLAGS="-march=native -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer -g"
- name: Build
run: cmake --build build --parallel
- name: Test
run: ctest -V --test-dir build
build-old-writers:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Configure
run: >
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
-DZXING_READERS=ON -DZXING_WRITERS=OLD -DZXING_PYTHON_MODULE=ON -DZXING_C_API=ON
-DZXING_BLACKBOX_TESTS=ON -DZXING_TEST_INSTALL=ON -DZXING_UNIT_TESTS=ON -DZXING_PYTHON_MODULE=ON
- name: Build
run: cmake --build build --parallel
- name: Test
run: ctest -V --test-dir build
build-ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Build the swift package
run: swift build
- name: Build the demo app
working-directory: wrappers/ios/demo
run: xcodebuild build -scheme demo -sdk "iphonesimulator"
# - name: Validate the Pod
# run: pod lib lint --allow-warnings
build-android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
- name: Build the lib/app
working-directory: wrappers/android
run: ./gradlew assembleRelease
- uses: actions/upload-artifact@v6
with:
name: android-artifacts
path: "wrappers/android/zxingcpp/build/outputs/aar/zxingcpp-release.aar"
- name: Publish Library Snapshot
if: github.repository == 'zxing-cpp/zxing-cpp' && github.event_name != 'pull_request'
working-directory: wrappers/android
env:
ORG_GRADLE_PROJECT_publishSnapshot: true
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALUSERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALPASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }}
run: ./gradlew publishToMavenCentral --no-configuration-cache
build-kn:
runs-on: ubuntu-latest
defaults:
run:
working-directory: wrappers/kn
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: Prepare Zig toolchain
uses: mlugg/setup-zig@v2
with:
version: 0.13.0
- name: Checkout toolchain initializer repository
uses: actions/checkout@v6
with:
repository: ISNing/kn-toolchain-initializer
path: wrappers/kn/.kn-toolchain-initializer
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 17
distribution: temurin
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v5
- name: Initialize Kotlin/Native toolchain
working-directory: wrappers/kn/.kn-toolchain-initializer
run: ./gradlew build -DkotlinVersion=2.2.0
- name: Run test for linuxX64 target
run: |
echo -e "konan.dir=$HOME/.konan/kotlin-native-prebuilt-linux-x86_64-2.2.0" >> local.properties
./gradlew linuxX64Test
build-python:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ['3.12']
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install numpy pillow build
- name: Build module
working-directory: wrappers/python
run: python -m pip -v install -e .
- name: Test module
working-directory: wrappers/python
run: python -m unittest -v
- name: Build wheel
working-directory: wrappers/python
run: python -m build --wheel
- uses: actions/upload-artifact@v6
with:
name: ${{matrix.os}}-python-artifacts
path: wrappers/python/dist/*
build-rust:
runs-on: ubuntu-latest
defaults:
run:
working-directory: wrappers/rust
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Lint
run: |
cargo fmt --check
cargo clippy -- -Dwarnings
- name: Build
run: cargo build --release --verbose --all-features --examples
- name: Test
run: cargo test --release --all-features
- name: Package
# --allow-dirty is required on the windows build (but not the ubuntu build?!)
run: |
cargo package --all-features
cargo publish --dry-run
- uses: actions/upload-artifact@v6
with:
name: rust-artifacts
path: wrappers/rust/target/package/*.crate
build-wasm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- uses: emscripten-core/setup-emsdk@v15
- name: Configure
run: emcmake cmake -Swrappers/wasm -Bbuild
- name: Build
run: cmake --build build --parallel
# - name: Test
# run: node build/EmGlueTests.js
- uses: actions/upload-artifact@v6
with:
name: wasm-artifacts
path: |
build/zxing*
build/demo*
# disable WinRT build for now, as it does not compile with C++20 (see wrappers/winrt/CMakeLists.txt)
# build-winrt:
# runs-on: windows-latest
# steps:
# - uses: actions/checkout@v6
# with:
# submodules: true
# - name: Configure
# shell: cmd # powershell messes up the arguments containing a '.' ?!?
# run: >
# cmake -S wrappers/winrt -B build -A ARM64
# -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_BUILD_TYPE=Release
# -DBUILD_WINRT_LIB=ON -DZXING_EXAMPLES=OFF -DZXING_BLACKBOX_TESTS=OFF -DZXING_C_API=OFF
# -DEXTENSION_SDK_OUTPUT=dist/UAP/v0.8.0.0/ExtensionSDKs/ZXingWinRT/1.0.0.0
# - name: Build
# run: cmake --build build -j8 --config Release
# - uses: actions/upload-artifact@v6
# with:
# name: winrt-ARM64-artifacts
# path: build/dist
build-tarball:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Build
run: python .github/create_release_archive.py
- name: Find generated archives
id: find
run: |
tgz=$(ls zxing-cpp-*.tar.gz 2>/dev/null | head -n1 || true)
zip=$(ls zxing-cpp-*.zip 2>/dev/null | head -n1 || true)
echo "tgz=$tgz" >> $GITHUB_OUTPUT
echo "zip=$zip" >> $GITHUB_OUTPUT
- name: Upload source_artifacts
uses: actions/upload-artifact@v6
with:
name: source_artifacts
path: |
${{ steps.find.outputs.tgz }}
${{ steps.find.outputs.zip }}

59
tools/chatgui/zxing-cpp/.github/workflows/codeql-analysis.yml

@ -1,59 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: CodeQL
on:
workflow_dispatch:
# push:
# branches: [ master ]
# pull_request:
# # The branches below must be a subset of the branches above
# branches: [ master ]
# schedule:
# - cron: '18 21 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ] # , 'java-kotlin', 'swift' currently fail with autobuild, 'python' is not worth the extra build time
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: manual
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Build
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DZXING_EXPERIMENTAL_API=ON
cmake --build build -j
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4

71
tools/chatgui/zxing-cpp/.github/workflows/msvc-analysis.yml

@ -1,71 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
#
# Find more information at:
# https://github.com/microsoft/msvc-code-analysis-action
name: Microsoft C++ Code Analysis
on: workflow_dispatch
# on:
# push:
# branches: [ "master" ]
# pull_request:
# branches: [ "master" ]
# schedule:
# - cron: '34 6 * * 1'
env:
# Path to the CMake build directory.
build: '${{ github.workspace }}/build'
permissions:
contents: read
jobs:
analyze:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
name: Analyze
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: Configure CMake
run: cmake -B ${{ env.build }} -DZXING_EXPERIMENTAL_API=ON -DZXING_DISABLE_PCH=ON
# Build is not required unless generated source files are used
# - name: Build CMake
# run: cmake --build ${{ env.build }}
- name: Initialize MSVC Code Analysis
uses: microsoft/msvc-code-analysis-action@v0.1.1
# Provide a unique ID to access the sarif output path
id: run-analysis
with:
cmakeBuildDirectory: ${{ env.build }}
# Ruleset file that will determine what checks will be run
ruleset: NativeRecommendedRules.ruleset
# Upload SARIF file to GitHub Code Scanning Alerts
# this currently fails with the Error: The CodeQL Action does not support uploading multiple SARIF runs with the same category.
# - name: Upload SARIF to GitHub
# uses: github/codeql-action/upload-sarif@v4
# with:
# sarif_file: ${{ steps.run-analysis.outputs.sarif }}
# Upload SARIF file as an Artifact to download and view
- name: Upload SARIF as an Artifact
uses: actions/upload-artifact@v6
with:
name: sarif-file
path: ${{ steps.run-analysis.outputs.sarif }}

41
tools/chatgui/zxing-cpp/.github/workflows/publish-android.yml

@ -1,41 +0,0 @@
name: publish-android
on:
# release:
# types: [published]
workflow_dispatch:
inputs:
publish:
description: 'Publish package (y/n)'
default: 'n'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
# working-directory: wrappers/android
# run: ./gradlew versionDisplay
- name: Build Library
working-directory: wrappers/android
run: ./gradlew assembleRelease
- name: Publish Library
if: ${{ github.event.inputs.publish == 'y' }}
working-directory: wrappers/android
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALUSERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALPASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }}
run: ./gradlew publishToMavenCentral --no-configuration-cache
# To actually publish the the blob, login to https://central.sonatype.com/publishing/deployments.

130
tools/chatgui/zxing-cpp/.github/workflows/publish-dotnet.yml

@ -1,130 +0,0 @@
name: publish-dotnet
on:
# release:
# types: [published]
workflow_dispatch:
inputs:
publish:
description: 'Publish package (y/n)'
default: 'n'
jobs:
build-native:
name: build-native ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
lib_name: ZXing.dll
rid: win-x64
lib_path: build/core/Release/ZXing.dll
- os: windows-11-arm
lib_name: ZXing.dll
rid: win-arm64
lib_path: build/core/Release/ZXing.dll
- os: ubuntu-latest
lib_name: libZXing.so
rid: linux-x64
lib_path: build/core/libZXing.so
- os: ubuntu-24.04-arm
lib_name: libZXing.so
rid: linux-arm64
lib_path: build/core/libZXing.so
- os: macos-15-intel
lib_name: libZXing.dylib
rid: osx-x64
lib_path: build/core/libZXing.dylib
- os: macos-latest
lib_name: libZXing.dylib
rid: osx-arm64
lib_path: build/core/libZXing.dylib
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Configure CMake
run: >
cmake -S . -B build
-DCMAKE_BUILD_TYPE=Release
-DBUILD_SHARED_LIBS=ON
-DZXING_EXAMPLES=OFF
-DZXING_TEST_DOTNET=ON
- name: Build
run: cmake --build build --config Release --parallel
- name: Test
run: ctest --test-dir build -V -C Release -R DotNetTest
- name: Stage native library
run: |
cmake -E make_directory artifact/runtimes/${{ matrix.rid }}/native
cmake -E copy_if_different ${{ matrix.lib_path }} artifact/runtimes/${{ matrix.rid }}/native/${{ matrix.lib_name }}
- name: Upload native library
uses: actions/upload-artifact@v6
with:
name: native-${{ matrix.rid }}
path: artifact
publish-nuget:
needs: build-native
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Download native libraries
uses: actions/download-artifact@v8
with:
pattern: native-*
path: wrappers/dotnet
merge-multiple: true
- name: Validate runtime files
shell: bash
run: |
test -f wrappers/dotnet/runtimes/win-x64/native/ZXing.dll
test -f wrappers/dotnet/runtimes/win-arm64/native/ZXing.dll
test -f wrappers/dotnet/runtimes/linux-x64/native/libZXing.so
test -f wrappers/dotnet/runtimes/linux-arm64/native/libZXing.so
test -f wrappers/dotnet/runtimes/osx-x64/native/libZXing.dylib
test -f wrappers/dotnet/runtimes/osx-arm64/native/libZXing.dylib
- name: Pack NuGet package
run: dotnet pack wrappers/dotnet/ZXingCpp/ZXingCpp.csproj --configuration Release --output wrappers/dotnet/artifacts
- name: Smoke test local NuGet package
shell: bash
run: |
set -euo pipefail
pkg_file="$(ls wrappers/dotnet/artifacts/ZXingCpp.*.nupkg | head -n 1)"
pkg_version="$(basename "$pkg_file" | sed -E 's/^ZXingCpp\.//; s/\.nupkg$//')"
tmp_dir="$(mktemp -d)"
dotnet new console -n ZxingPkgTest -o "$tmp_dir/ZxingPkgTest"
echo "Console.WriteLine(global::ZXingCpp.BarcodeFormat.QRCode.ToString());" > "$tmp_dir/ZxingPkgTest/Program.cs"
dotnet add "$tmp_dir/ZxingPkgTest/ZxingPkgTest.csproj" package ZXingCpp --version "$pkg_version" --no-restore
dotnet restore "$tmp_dir/ZxingPkgTest/ZxingPkgTest.csproj" \
--source "${{ github.workspace }}/wrappers/dotnet/artifacts" \
--source https://api.nuget.org/v3/index.json
dotnet run --project "$tmp_dir/ZxingPkgTest/ZxingPkgTest.csproj" --no-restore
- name: Upload NuGet package artifact
uses: actions/upload-artifact@v6
with:
name: dotnet-nuget-package
path: wrappers/dotnet/artifacts/*.nupkg
- name: Push NuGet package
if: ${{ github.event_name == 'release' || github.event.inputs.publish == 'y' }}
run: dotnet nuget push wrappers/dotnet/artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate

131
tools/chatgui/zxing-cpp/.github/workflows/publish-gh-pages.yml

@ -1,131 +0,0 @@
name: publish-gh-pages
on:
# Runs on pushes targeting the default branch
# push:
# branches: ["master"]
# Runs on new releases
release:
types: [published]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build-wasm:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: true
- name: Setup emsdk
uses: emscripten-core/setup-emsdk@v15
# with:
# # Make sure to set a version number!
# version: 1.38.40
# # This is the name of the cache folder.
# # The cache folder will be placed in the build directory,
# # so make sure it doesn't conflict with anything!
# actions-cache-folder: 'emsdk-cache'
- name: Configure
run: emcmake cmake -Swrappers/wasm -Bbuild
- name: Build
run: cmake --build build --parallel
- name: Prepare Archive
shell: sh
run: |
mkdir -p public/demo
mv build/zxing_* build/*.html public/demo/
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
path: 'public'
name: 'artifact-wasm'
build-docs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install dependencies
run: brew install doxygen
- name: Configure
run: cmake -S . -B build
- name: Build docs
run: cmake --build build --target docs
- name: Prepare deploy folder
run: |
mkdir -p public/docs
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/}
mkdir -p public/docs/$VERSION
cp -r build/docs/html/* public/docs/$VERSION/
else
mkdir -p public/docs/latest
cp -r build/docs/html/* public/docs/latest/
fi
- uses: actions/upload-artifact@v7
with:
path: public
name: 'artifact-docs'
deploy:
needs: [build-wasm, build-docs]
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
steps:
- name: Download WASM artifact
uses: actions/download-artifact@v8
with:
name: artifact-wasm
path: wasm
- name: Download Docs artifact
uses: actions/download-artifact@v8
with:
name: artifact-docs
path: docs
- name: Merge artifacts
run: |
mkdir public
cp -r wasm/* public/
cp -r docs/* public/
- name: Upload merged artifact
uses: actions/upload-pages-artifact@v4
with:
path: public
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5

97
tools/chatgui/zxing-cpp/.github/workflows/publish-kn.yml

@ -1,97 +0,0 @@
name: publish-kn
on:
workflow_dispatch:
inputs:
publish:
description: 'Publish package (y/n)'
default: 'n'
jobs:
publish:
name: Library Publish
runs-on: macos-latest # at least macos-13 is required to enable c++20 support
defaults:
run:
working-directory: ./wrappers/kn
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: Prepare Zig toolchain
uses: mlugg/setup-zig@v2
with:
version: 0.13.0
- name: Checkout toolchain initializer repository
uses: actions/checkout@v6
with:
repository: ISNing/kn-toolchain-initializer
path: wrappers/kn/.kn-toolchain-initializer
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 17
distribution: temurin
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v5
- name: Initialize Kotlin/Native toolchain
working-directory: wrappers/kn/.kn-toolchain-initializer
run: ./gradlew build -DkotlinVersion=2.2.0
- name: Export Toolchain properties
run: |
echo -e "konan.dir=$HOME/.konan/kotlin-native-prebuilt-macos-aarch64-2.2.0" >> local.properties
- name: Build Library
run: |
export PATH="$PATH:$(brew --prefix llvm@15)/bin"
./gradlew assemble
- name: Run All Library Tests
run: |
export PATH="$PATH:$(brew --prefix llvm@15)/bin"
./gradlew allTests
- name: Upload Library Test Reports
uses: actions/upload-artifact@v6
with:
name: allTests-reports
path: wrappers/kn/build/reports/tests/allTests
- name: Upload Built LinuxArm64 shared library
uses: actions/upload-artifact@v6
with:
name: linuxArm64-shared
path: wrappers/kn/build/cmake/libZXing/linuxArm64
- name: Upload Built LinuxX64 shared library
uses: actions/upload-artifact@v6
with:
name: linuxX64-shared
path: wrappers/kn/build/cmake/libZXing/linuxX64
- name: Publish Library
if: ${{ github.event.inputs.publish == 'y' }}
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALUSERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_MAVENCENTRALPASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.GPG_KEY_ID }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }}
run: |
export PATH="$PATH:$(brew --prefix llvm@15)/bin"
./gradlew publishToMavenCentral
# To actually publish the blob, login to https://central.sonatype.com/publishing/deployments.

100
tools/chatgui/zxing-cpp/.github/workflows/publish-python.yml

@ -1,100 +0,0 @@
name: publish-python
on:
# release:
# types: [published]
workflow_dispatch:
inputs:
publish:
description: 'Publish package (y/n)'
default: 'n'
# push:
# branches: [master]
# tags: ["v*.*.*"]
# pull_request:
# branches: [master]
jobs:
build-wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest, macos-15-intel, ubuntu-24.04-arm, windows-11-arm]
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install cibuildwheel
run: python3 -m pip install cibuildwheel==3.3.0
- name: Build wheels
run: python3 -m cibuildwheel --output-dir wheelhouse wrappers/python
- name: Upload wheels
uses: actions/upload-artifact@v6
with:
name: cibw-wheels-${{ matrix.os }}
path: ./wheelhouse/*.whl
build-sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.14'
- name: Install dependencies
working-directory: wrappers/python
run: python -m pip install --upgrade build
- name: Build sdist
working-directory: wrappers/python
run: python3 -m build --sdist
- name: Upload sdist
uses: actions/upload-artifact@v6
with:
name: cibw-sdist
path: wrappers/python/dist/*.tar.gz
upload-pypi:
name: Upload to PyPI
needs: [build-wheels, build-sdist]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/zxing-cpp
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
# only run if the commit is tagged...
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
if: github.event_name == 'release' || github.event.inputs.publish == 'y'
steps:
- uses: actions/download-artifact@v8
with:
pattern: cibw-*
path: dist
merge-multiple: true
- name: List wheels
run: ls dist
- uses: pypa/gh-action-pypi-publish@release/v1
# with:
# repository-url: https://test.pypi.org/legacy/

39
tools/chatgui/zxing-cpp/.github/workflows/publish-source.yml

@ -1,39 +0,0 @@
name: publish-source
on:
release:
types: [published]
workflow_dispatch:
jobs:
publish-source:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create source archives
run: python .github/create_release_archive.py
- name: Create test samples tarball
run: tar -czf test_samples.tar.gz test/samples
- name: Upload workflow artifact
if: github.event_name != 'release'
uses: actions/upload-artifact@v6
with:
name: source-archives
path: |
*.zip
*.gz
if-no-files-found: error
- name: Upload to release
if: github.event_name == 'release'
run: |
tag='${{ github.event.release.tag_name }}'
version="${tag#v}"
gh release upload --clobber "$tag" "$GITHUB_WORKSPACE/zxing-cpp-$version.tar.gz" "$GITHUB_WORKSPACE/zxing-cpp-$version.zip" "$GITHUB_WORKSPACE/test_samples.tar.gz"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

82
tools/chatgui/zxing-cpp/.github/workflows/publish-winrt.yml

@ -1,82 +0,0 @@
name: publish-winrt
on:
# release:
# types: [published]
workflow_dispatch:
inputs:
publish:
description: 'Publish package (y/n)'
default: 'n'
package_version:
description: 'Package version (major.minor.patch)'
default: '1.0.0'
jobs:
build:
runs-on: windows-latest
strategy:
matrix:
target: [Win32, x64, ARM64]
steps:
- uses: actions/checkout@v6
- name: Create build environment
shell: cmd
run: |
cmake -E make_directory ${{runner.workspace}}/build
cmake -E copy_directory ${{github.workspace}}/wrappers/winrt/UAP ${{runner.workspace}}/build/dist/UAP
cmake -E copy_directory ${{github.workspace}}/wrappers/winrt/nuget ${{runner.workspace}}/build/dist/nuget
- name: Configure CMake
shell: cmd
working-directory: ${{runner.workspace}}/build
run: cmake ${{github.workspace}}/wrappers/winrt -A ${{matrix.target}} -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_BUILD_TYPE=Release -DBUILD_WINRT_LIB=ON -DZXING_EXAMPLES=OFF -DZXING_BLACKBOX_TESTS=OFF -DEXTENSION_SDK_OUTPUT=dist/UAP/v0.8.0.0/ExtensionSDKs/ZXingWinRT/1.0.0.0
- name: Build
shell: cmd
working-directory: ${{runner.workspace}}/build
run: cmake --build . -j8 --config Release
- uses: actions/upload-artifact@v6
with:
name: winrt-${{matrix.target}}-artifacts
path: ${{runner.workspace}}/build/dist
publish-package:
needs: build
runs-on: windows-latest
if: ${{ github.event_name == 'release' || github.event.inputs.publish == 'y' }}
steps:
- uses: actions/download-artifact@v8
with:
name: winrt-Win32-artifacts
- uses: actions/download-artifact@v8
with:
name: winrt-x64-artifacts
- uses: actions/download-artifact@v8
with:
name: winrt-ARM64-artifacts
- name: Create NuGet package
if: ${{ github.event_name != 'release' }}
shell: cmd
run: nuget pack -Version ${{ github.event.inputs.package_version }} -OutputFileNamesWithoutVersion nuget/ZXingWinRT.nuspec
- name: Create NuGet package
if: ${{ github.event_name == 'release' }}
shell: bash
env:
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
run: nuget pack -Version ${RELEASE_TAG_NAME:1} -OutputFileNamesWithoutVersion nuget/ZXingWinRT.nuspec
- name: Publish NuGet package
shell: cmd
run: nuget push huycn.zxingcpp.winrt.nupkg -ApiKey ${{ secrets.NUGET_API_KEY }} -Source https://api.nuget.org/v3/index.json
- uses: actions/upload-artifact@v6
with:
name: nuget-package
path: huycn.zxingcpp.winrt.nupkg

27
tools/chatgui/zxing-cpp/.gitignore vendored

@ -1,27 +0,0 @@
*.o
*.so
*.lib
*.d
*.a
compile_commands.json
/build
# QtCreator
CMakeLists.txt.user
# Vim
*.swp
# XCode/Swift
.swiftpm
.build
# JetBrain (AndroidStudio, clion)
.idea
# Visual Studio
/.vs
/CMakeUserPresets.json
# Visual Studio Code
/.vscode

4
tools/chatgui/zxing-cpp/.gitmodules vendored

@ -1,4 +0,0 @@
[submodule "zint"]
path = zint
url = https://github.com/zint/zint.git
shallow = true

38
tools/chatgui/zxing-cpp/Package.swift

@ -1,38 +0,0 @@
// swift-tools-version:5.7.1
import PackageDescription
let package = Package(
name: "ZXingCpp",
platforms: [
.macOS(.v13), .iOS(.v12)
],
products: [
.library(
name: "ZXingCpp",
targets: ["ZXingCpp"])
],
targets: [
.target(
name: "ZXingCppCore",
path: "core/src",
exclude: ["libzint", "ZXingC.cpp", "ZXingCpp.cpp"],
publicHeadersPath: ".",
cxxSettings: [
.headerSearchPath("../../wrappers/ios/Sources/Wrapper"),
.define("ZXING_INTERNAL")
]
),
.target(
name: "ZXingCpp",
dependencies: ["ZXingCppCore"],
path: "wrappers/ios/Sources/Wrapper",
publicHeadersPath: ".",
linkerSettings: [
.linkedFramework("CoreGraphics"),
.linkedFramework("CoreImage"),
.linkedFramework("CoreVideo")
]
)
],
cxxLanguageStandard: CXXLanguageStandard.cxx20
)

154
tools/chatgui/zxing-cpp/README.md

@ -1,154 +0,0 @@
[![Build Status](https://github.com/zxing-cpp/zxing-cpp/actions/workflows/ci.yml/badge.svg)](https://github.com/zxing-cpp/zxing-cpp/actions?query=workflow%3ACI)
# ZXing-C++
ZXing-C++ ("zebra crossing") is an open-source, multi-format linear/matrix barcode image processing library implemented in C++.
It was originally ported from the Java [ZXing library](https://github.com/zxing/zxing) but has been developed further and now includes many improvements in terms of runtime and detection performance. It can both read and write barcodes in a number of formats. Since version 3.0 the default writing backend is provided by the [zint library](https://sourceforge.net/projects/zint/).
## Features
* Written in pure C++20 (public API is C++17 compatible), no third-party dependencies (for the library itself)
* Thread safe
* Wrappers/Bindings for:
| | | |
|:-|:-|:-|
| [Android](wrappers/android/README.md) | [C](wrappers/c/README.md) | [Go](wrappers/go/README.md) |
| [iOS](wrappers/ios/README.md) | [Kotlin/Native](wrappers/kn/README.md) | [.NET](wrappers/dotnet/README.md) |
| [Python](wrappers/python/README.md) | [Qt](wrappers/qt/README.md) | [Rust](wrappers/rust/README.md) |
| [WebAssembly](wrappers/wasm/README.md) | [WinRT](wrappers/winrt/README.md) | |
| External projects: |
| [Flutter](https://pub.dev/packages/flutter_zxing) | [Julia](https://github.com/rakeshksr/ZXingCPP.jl) | |
## Supported Formats
| Symbology | Variants |
|:----------|:---------|
| ***Retail:*** | *(Point-of-Sale, Coupons)*
| EAN/UPC | EAN-13, EAN-8, EAN-5ᵂ, EAN-2ᵂ, UPC-A, UPC-E, ISBN
| DataBar | Omnidirectional, Stacked, Limited, Expanded, Expanded Stacked
| ***Industrial:*** | *(Logistics, Tracking, Pharma)*
| Code39 | Standard, Extended, PZN, Code32, (VIN, LOGMARS)
| Code93 |
| Code128 |
| ITF | ITF-14, (DHL Leitcode, DHL Identcode)
| Telepen | Telepen Alpha, Telepen Numeric
| ***Matrix:*** | *(Documents, Tickets, Logistics, IDs)*
| Aztec Code | Aztec Code, Aztec Rune
| Data Matrix | ECC200
| MaxiCode | (partial read support)
| PDF417 | PDF417, Compact PDF417, MicroPDF417
| QR Code | Model 1ᴿ, Model 2, Micro QR Code, rMQR
| ***Other:*** | *(Legacy, Niche)*
| Codabar |
| DXFilmEdge |
[Note:]
* ᵂ : write support only
* ᴿ : read support only
* DataBar used to be called RSS.
* DataBar, DX Film Edge, MaxiCode, Micro QR Code and rMQR Code are not supported for writing if the library is configured with `ZXING_WRITERS=OLD`.
## Sponsors
You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/axxel).
| | Named Sponsors: |
|:-:|:-|
| [![ArAllAll](https://www.alliedvision.com/favicon.ico)](https://www.alliedvision.com/) | [Allied Vision Konstanz GmbH](https://www.alliedvision.com/) |
| [![jknlsn](https://avatars.githubusercontent.com/u/9172090?s=32)](https://github.com/jknlsn) | [Jake Nelson](https://github.com/jknlsn) |
| [![KURZ](https://avatars.githubusercontent.com/u/25196688?s=32)](https://github.com/kurzdigital) | [KURZ Digital Solutions GmbH & Co. KG](https://github.com/kurzdigital) |
| [![Moonshine AI](https://avatars.githubusercontent.com/u/98664891?s=32)](https://github.com/moonshine-ai) | [Moonshine AI](https://github.com/moonshine-ai) |
| [![SAP](https://avatars.githubusercontent.com/u/2531208?s=32)](https://www.sap.com/germany/about/company/innovation/open-source.html) | [SAP (Open Source Program Office)](https://www.sap.com/germany/about/company/innovation/open-source.html) |
| [![Somco Software](https://avatars.githubusercontent.com/u/41477867?s=32)](https://github.com/somcosoftware) | [Somco Software](https://somcosoftware.com/en) |
| [![synedra](https://www.synedra.com/favicon.ico)](https://synedra.com/) | [synedra information technologies GmbH](https://synedra.com/) |
Thanks a lot for your contribution!
## Getting Started
### To read barcodes:
1. Load your image into memory (3rd-party library required).
2. Call `ReadBarcodes()` from [`ReadBarcode.h`](core/src/ReadBarcode.h), the simplest API to get a list of `Barcode` objects.
A very simple example looks like this:
```c++
#include "ZXing/ZXingCpp.h"
#include <iostream>
int main(int argc, char** argv)
{
int width, height;
unsigned char* data;
// load your image data from somewhere. ImageFormat::Lum assumes grey scale image data.
auto image = ZXing::ImageView(data, width, height, ZXing::ImageFormat::Lum);
auto options = ZXing::ReaderOptions().formats(ZXing::BarcodeFormat::QRCode);
auto barcodes = ZXing::ReadBarcodes(image, options);
for (const auto& b : barcodes)
std::cout << ZXing::ToString(b.format()) << ": " << b.text() << "\n";
return 0;
}
```
To see the full capability of the API, have a look at [`ZXingReader.cpp`](example/ZXingReader.cpp).
### To write barcodes:
1. Create a `Barcode` object with `CreateBarcodeFrom...()` from [`CreateBarcode.h`](core/src/CreateBarcode.h).
2. The `Barcode::symbol()` can be used to get access to the bit matrix (1 module == 1 pixel, no quiet zone)
3. Alternatively the 3 `WriteBarcodeTo...()` functions from [`WriteBarcode.h`](core/src/WriteBarcode.h) can be used to create an `Image`, a SVG string or a UTF-8 string representation.
A very simple example looks like this:
```c++
#include "ZXing/ZXingCpp.h"
#include <iostream>
int main(int argc, char** argv)
{
auto barcode = ZXing::CreateBarcodeFromText("some text", ZXing::BarcodeFormat::QRCode);
auto svg = ZXing::WriteBarcodeToSVG(barcode);
// see also ZXing::WriteBarcodeToImage()
std::cout << svg << "\n";
return 0;
}
```
As an example for how to parameterize the process with `CreatorOptions` and `WriterOptions`, have a look at [`ZXingWriter.cpp`](example/ZXingWriter.cpp).
### Online API documentation
The latest API documentation can be found here: https://zxing-cpp.github.io/zxing-cpp/docs/latest
## Web Demos
- [Read barcodes](https://zxing-cpp.github.io/zxing-cpp/demo/demo_reader.html)
- [Write barcodes](https://zxing-cpp.github.io/zxing-cpp/demo/demo_writer.html)
- [Read barcodes from camera](https://zxing-cpp.github.io/zxing-cpp/demo/demo_cam_reader.html)
[Note: those live demos are not necessarily fully up-to-date at all times.]
## Build Instructions
These are the generic instructions to build the library on Windows/macOS/Linux. For details on how to build the individual wrappers, follow the links above.
1. Make sure [CMake](https://cmake.org) version 3.16 or newer is installed. The python module requires 3.18 or higher.
2. Make sure a sufficiently C++20 compliant compiler is installed (minimum VS 2019 16.10? / gcc 11 / clang 12?).
3. See the cmake `ZXING_...` options to enable the testing code, python wrapper, etc.
```
git clone https://github.com/zxing-cpp/zxing-cpp.git --recursive --depth 1
cmake -S zxing-cpp -B zxing-cpp/build -DCMAKE_BUILD_TYPE=Release
cmake --build zxing-cpp/build --parallel --config Release
```
[Note: ready to use packages are available for/as
[vcpkg](https://github.com/Microsoft/vcpkg/tree/master/ports/nu-book-zxing-cpp),
[conan](https://github.com/conan-io/conan-center-index/tree/master/recipes/zxing-cpp),
[mingw](https://github.com/msys2/MINGW-packages/tree/master/mingw-w64-zxing-cpp),
[homebrew](https://formulae.brew.sh/formula/zxing-cpp) and a bunch of
[linux distributions](https://repology.org/project/zxing-cpp/versions).]

783
tools/chatgui/zxing-cpp/core/CMakeLists.txt

@ -0,0 +1,783 @@
cmake_minimum_required(VERSION 3.16)
project (ZXing VERSION "3.1.0")
set (ZXING_VERSION_SUFFIX "-rc1")
set (ZXING_SONAME 4) # see https://github.com/zxing-cpp/zxing-cpp/issues/333
option (ZXING_USE_BUNDLED_ZINT "Use the bundled libzint for barcode creation/writing" ON)
foreach(format 1D AZTEC DATAMATRIX MAXICODE PDF417 QRCODE)
option ("ZXING_ENABLE_${format}" "Enable support for ${format} barcodes" ON)
endforeach()
if (NOT DEFINED CMAKE_CXX_STANDARD)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
endif()
if (BUILD_SHARED_LIBS)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if (NOT DEFINED ZXING_WRITERS)
set (ZXING_WRITERS OFF)
endif()
if (NOT DEFINED ZXING_READERS)
set (ZXING_READERS ON)
endif()
set (ZXING_WRITERS_NEW OFF)
set (ZXING_WRITERS_OLD OFF)
if (ZXING_WRITERS MATCHES "OLD")
set (ZXING_WRITERS ON)
set (ZXING_WRITERS_OLD ON)
elseif (ZXING_WRITERS MATCHES "NEW|ON")
set (ZXING_WRITERS ON)
set (ZXING_WRITERS_NEW ON)
elseif (ZXING_WRITERS MATCHES "BOTH")
set (ZXING_WRITERS ON)
set (ZXING_WRITERS_NEW ON)
set (ZXING_WRITERS_OLD ON)
endif()
if (BUILD_SHARED_LIBS)
set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
set (ZXING_USE_ZINT ${ZXING_WRITERS_NEW}) # see Version.h.in
set (ZXING_PRIVATE_FLAGS
-DZXING_INTERNAL
$<$<BOOL:${ZXING_UNIT_TESTS}>:-DZXING_BUILD_FOR_TEST>
)
if (WINRT)
set (ZXING_PUBLIC_FLAGS -DWINRT )
endif()
if (MSVC)
list (APPEND ZXING_PRIVATE_FLAGS
-D_SCL_SECURE_NO_WARNINGS
-D_CRT_SECURE_NO_WARNINGS
-D_CRT_NONSTDC_NO_WARNINGS
-DNOMINMAX
/utf-8 # see https://github.com/zxing-cpp/zxing-cpp/issues/757
)
else()
list (APPEND ZXING_PRIVATE_FLAGS
-Wall -Wextra -Wno-missing-braces -Werror=undef -Werror=return-type)
endif()
include (CheckCXXCompilerFlag)
# This is needed for reproducible builds across different build directories.
# Without this, the usage of the __FILE__ macro leaves the build directory in
# the binary. When building the Python extension with build isolation enabled
# this would lead to random paths in the binary.
set(FILE_PREFIX_ARG "-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}/=")
check_cxx_compiler_flag("${FILE_PREFIX_ARG}" HAS_FILE_PREFIX_ARG)
if(HAS_FILE_PREFIX_ARG)
list (APPEND ZXING_PRIVATE_FLAGS "${FILE_PREFIX_ARG}")
endif()
# MARK: - COMMON_FILES
set (COMMON_FILES
src/Barcode.h
src/Barcode.cpp
src/BarcodeData.h
src/BarcodeFormat.h
src/BarcodeFormat.cpp
src/BitMatrix.h
src/BitMatrix.cpp
src/BitMatrixIO.h
src/BitMatrixIO.cpp
src/ByteArray.h
src/CharacterSet.h
src/CharacterSet.cpp
src/CreateBarcode.h
src/CreateBarcode.cpp
src/Content.h
src/Content.cpp
src/ContentType.h
src/DecoderResult.h
src/DetectorResult.h
src/ECI.h
src/ECI.cpp
src/Error.h
src/Error.cpp
src/Flags.h
src/GTIN.h
src/GTIN.cpp
src/ImageView.h
src/JSON.h
src/JSON.cpp
src/Matrix.h
src/Point.h
src/Quadrilateral.h
src/Range.h
src/ReaderOptions.h
src/ReadBarcode.h
src/ReadBarcode.cpp
src/ReedSolomon.h
src/ReedSolomon.cpp
src/SymbologyIdentifier.h
src/Utf.h
src/Utf.cpp
src/WriteBarcode.h
src/WriteBarcode.cpp
src/ZXingCpp.h
src/ZXingCpp.cpp
src/ZXingQt.h
src/ZXAlgorithms.h
src/ZXConfig.h
src/ZXTestSupport.h
src/ZXVersion.h
$<$<BOOL:${ZXING_C_API}>:src/ZXingC.h>
$<$<BOOL:${ZXING_C_API}>:src/ZXingC.cpp>
)
if (ZXING_READERS OR ZXING_WRITERS_OLD)
set (COMMON_FILES ${COMMON_FILES}
src/BitArray.h
src/BitArray.cpp
)
endif()
if (ZXING_READERS OR ZXING_WRITERS_NEW)
set (COMMON_FILES ${COMMON_FILES}
src/HRI.h
src/HRI.cpp
)
endif()
if (ZXING_READERS)
set (COMMON_FILES ${COMMON_FILES}
src/BinaryBitmap.h
src/BinaryBitmap.cpp
src/BitMatrixCursor.h
src/BitSource.h
src/BitSource.cpp
src/ConcentricFinder.h
src/ConcentricFinder.cpp
src/GlobalHistogramBinarizer.h
src/GlobalHistogramBinarizer.cpp
src/GridSampler.h
src/GridSampler.cpp
src/LogMatrix.h
src/LocalGrid.h
src/LocalGrid.cpp
src/HybridBinarizer.h
src/HybridBinarizer.cpp
src/MultiFormatReader.h
src/MultiFormatReader.cpp
src/Pattern.h
src/PerspectiveTransform.h
src/PerspectiveTransform.cpp
src/Reader.h
src/RegressionLine.h
src/ResultPoint.h
src/ResultPoint.cpp
src/StructuredAppend.h
src/StdGenerator.h
src/StdPrint.h
src/StdScope.h
src/TextDecoder.h
src/TextDecoder.cpp
src/ThresholdBinarizer.h
src/TritMatrix.h # QRCode
src/WhiteRectDetector.h
src/WhiteRectDetector.cpp
)
endif()
if (ZXING_WRITERS)
set (COMMON_FILES ${COMMON_FILES}
src/TextEncoder.h
src/TextEncoder.cpp
)
endif()
if (ZXING_WRITERS_OLD)
set (COMMON_FILES ${COMMON_FILES}
src/MultiFormatWriter.h
src/MultiFormatWriter.cpp
)
endif()
# MARK: - PUBLIC_HEADERS
# define subset of public headers that get distributed with the binaries
set (PUBLIC_HEADERS
src/Barcode.h
src/BarcodeFormat.h
src/CharacterSet.h
src/ContentType.h
src/CreateBarcode.h
src/Error.h
src/GTIN.h
src/ImageView.h
src/Point.h
src/Quadrilateral.h
src/ReadBarcode.h
src/ReaderOptions.h
src/WriteBarcode.h
src/ZXingCpp.h
${CMAKE_CURRENT_SOURCE_DIR}/src/ZXVersion.h # deprecated, but keep it for now to not break old clients
${CMAKE_CURRENT_SOURCE_DIR}/src/ZXingQt.h
$<$<BOOL:${ZXING_C_API}>:${CMAKE_CURRENT_SOURCE_DIR}/src/ZXingC.h>
)
if (ZXING_READERS)
set (PUBLIC_HEADERS ${PUBLIC_HEADERS}
)
endif()
if (ZXING_WRITERS_OLD)
set (PUBLIC_HEADERS ${PUBLIC_HEADERS}
src/BitMatrix.h
src/BitMatrixIO.h
src/Matrix.h
src/MultiFormatWriter.h
)
endif()
# end of public header set
# MARK: - READERS/WRITERS
if (ZXING_READERS AND ZXING_ENABLE_AZTEC)
set (AZTEC_FILES ${AZTEC_FILES}
src/aztec/AZDecoder.h
src/aztec/AZDecoder.cpp
src/aztec/AZDetector.h
src/aztec/AZDetector.cpp
src/aztec/AZDetectorResult.h
src/aztec/AZReader.h
src/aztec/AZReader.cpp
)
endif()
if (ZXING_WRITERS_OLD AND ZXING_ENABLE_AZTEC)
set (AZTEC_FILES ${AZTEC_FILES}
src/aztec/AZEncodingState.h
src/aztec/AZEncoder.h
src/aztec/AZEncoder.cpp
src/aztec/AZHighLevelEncoder.h
src/aztec/AZHighLevelEncoder.cpp
src/aztec/AZToken.h
src/aztec/AZToken.cpp
src/aztec/AZWriter.h
src/aztec/AZWriter.cpp
)
endif()
if ((ZXING_READERS OR ZXING_WRITERS_OLD) AND ZXING_ENABLE_DATAMATRIX)
set (DATAMATRIX_FILES
src/datamatrix/DMBitLayout.h
src/datamatrix/DMBitLayout.cpp
src/datamatrix/DMVersion.h
src/datamatrix/DMVersion.cpp
)
endif()
if (ZXING_READERS AND ZXING_ENABLE_DATAMATRIX)
set (DATAMATRIX_FILES ${DATAMATRIX_FILES}
src/datamatrix/DMDataBlock.h
src/datamatrix/DMDataBlock.cpp
src/datamatrix/DMDecoder.h
src/datamatrix/DMDecoder.cpp
src/datamatrix/DMDetector.h
src/datamatrix/DMDetector.cpp
src/datamatrix/DMReader.h
src/datamatrix/DMReader.cpp
)
endif()
if (ZXING_WRITERS_OLD AND ZXING_ENABLE_DATAMATRIX)
set (DATAMATRIX_FILES ${DATAMATRIX_FILES}
src/datamatrix/DMECEncoder.h
src/datamatrix/DMECEncoder.cpp
src/datamatrix/DMEncoderContext.h
src/datamatrix/DMHighLevelEncoder.h
src/datamatrix/DMHighLevelEncoder.cpp
src/datamatrix/DMSymbolInfo.h
src/datamatrix/DMSymbolInfo.cpp
src/datamatrix/DMSymbolShape.h
src/datamatrix/DMWriter.h
src/datamatrix/DMWriter.cpp
)
endif()
if (ZXING_READERS AND ZXING_ENABLE_MAXICODE)
set (MAXICODE_FILES ${MAXICODE_FILES}
src/maxicode/MCBitMatrixParser.h
src/maxicode/MCBitMatrixParser.cpp
src/maxicode/MCDecoder.h
src/maxicode/MCDecoder.cpp
src/maxicode/MCReader.h
src/maxicode/MCReader.cpp
)
endif()
if ((ZXING_READERS OR ZXING_WRITERS_OLD) AND ZXING_ENABLE_1D)
set (ONED_FILES
src/oned/ODUPCEANCommon.h
src/oned/ODUPCEANCommon.cpp
src/oned/ODCode93Patterns.h
src/oned/ODCode128Patterns.h
src/oned/ODCode128Patterns.cpp
)
endif()
if (ZXING_READERS AND ZXING_ENABLE_1D)
set (ONED_FILES ${ONED_FILES}
src/oned/ODCodabarReader.h
src/oned/ODCodabarReader.cpp
src/oned/ODCode39Reader.h
src/oned/ODCode39Reader.cpp
src/oned/ODCode93Reader.h
src/oned/ODCode93Reader.cpp
src/oned/ODCode128Reader.h
src/oned/ODCode128Reader.cpp
src/oned/ODDataBarCommon.h
src/oned/ODDataBarCommon.cpp
src/oned/ODDataBarReader.h
src/oned/ODDataBarReader.cpp
src/oned/ODDataBarExpandedBitDecoder.h
src/oned/ODDataBarExpandedBitDecoder.cpp
src/oned/ODDataBarExpandedReader.h
src/oned/ODDataBarExpandedReader.cpp
src/oned/ODDataBarLimitedReader.h
src/oned/ODDataBarLimitedReader.cpp
src/oned/ODDXFilmEdgeReader.h
src/oned/ODDXFilmEdgeReader.cpp
src/oned/ODITFReader.h
src/oned/ODITFReader.cpp
src/oned/ODTelepenReader.h
src/oned/ODTelepenReader.cpp
src/oned/ODMultiUPCEANReader.h
src/oned/ODMultiUPCEANReader.cpp
src/oned/ODReader.h
src/oned/ODReader.cpp
src/oned/ODRowReader.h
)
endif()
if (ZXING_WRITERS_OLD AND ZXING_ENABLE_1D)
set (ONED_FILES ${ONED_FILES}
src/oned/ODCodabarWriter.h
src/oned/ODCodabarWriter.cpp
src/oned/ODCode39Writer.h
src/oned/ODCode39Writer.cpp
src/oned/ODCode93Writer.h
src/oned/ODCode93Writer.cpp
src/oned/ODCode128Writer.h
src/oned/ODCode128Writer.cpp
src/oned/ODEAN8Writer.h
src/oned/ODEAN8Writer.cpp
src/oned/ODEAN13Writer.h
src/oned/ODEAN13Writer.cpp
src/oned/ODITFWriter.h
src/oned/ODITFWriter.cpp
src/oned/ODUPCEWriter.h
src/oned/ODUPCEWriter.cpp
src/oned/ODUPCAWriter.h
src/oned/ODUPCAWriter.cpp
src/oned/ODWriterHelper.h
src/oned/ODWriterHelper.cpp
)
endif()
if ((ZXING_READERS OR ZXING_WRITERS_OLD) AND ZXING_ENABLE_PDF417)
set (PDF417_FILES
src/pdf417/ZXBigInteger.h
src/pdf417/ZXBigInteger.cpp
)
endif()
if (ZXING_READERS AND ZXING_ENABLE_PDF417)
set (PDF417_FILES ${PDF417_FILES}
src/pdf417/PDFBarcodeMetadata.h
src/pdf417/PDFBarcodeValue.h
src/pdf417/PDFBarcodeValue.cpp
src/pdf417/PDFBoundingBox.h
src/pdf417/PDFBoundingBox.cpp
src/pdf417/PDFCodeword.h
src/pdf417/PDFCodewordDecoder.h
src/pdf417/PDFCodewordDecoder.cpp
src/pdf417/PDFCustomData.h
src/pdf417/PDFDecoder.h
src/pdf417/PDFDecoder.cpp
src/pdf417/PDFDetectionResult.h
src/pdf417/PDFDetectionResult.cpp
src/pdf417/PDFDetectionResultColumn.h
src/pdf417/PDFDetectionResultColumn.cpp
src/pdf417/PDFDetector.h
src/pdf417/PDFDetector.cpp
src/pdf417/PDFReader.h
src/pdf417/PDFReader.cpp
src/pdf417/PDFScanningDecoder.h
src/pdf417/PDFScanningDecoder.cpp
src/pdf417/ZXNullable.h
src/pdf417/MicroPDFReader.h
src/pdf417/MicroPDFReader.cpp
)
endif()
if (ZXING_WRITERS_OLD AND ZXING_ENABLE_PDF417)
set (PDF417_FILES ${PDF417_FILES}
src/pdf417/PDFCompaction.h
src/pdf417/PDFEncoder.h
src/pdf417/PDFEncoder.cpp
src/pdf417/PDFHighLevelEncoder.h
src/pdf417/PDFHighLevelEncoder.cpp
src/pdf417/PDFWriter.h
src/pdf417/PDFWriter.cpp
)
endif()
if ((ZXING_READERS OR ZXING_WRITERS_OLD) AND ZXING_ENABLE_QRCODE)
set (QRCODE_FILES
src/qrcode/QRCodecMode.h
src/qrcode/QRCodecMode.cpp
src/qrcode/QRErrorCorrectionLevel.h
src/qrcode/QRErrorCorrectionLevel.cpp
src/qrcode/QRVersion.h
src/qrcode/QRVersion.cpp
)
endif()
if (ZXING_READERS AND ZXING_ENABLE_QRCODE)
set (QRCODE_FILES ${QRCODE_FILES}
src/qrcode/QRBitMatrixParser.h
src/qrcode/QRBitMatrixParser.cpp
src/qrcode/QRDataBlock.h
src/qrcode/QRDataBlock.cpp
src/qrcode/QRDataMask.h
src/qrcode/QRDecoder.h
src/qrcode/QRDecoder.cpp
src/qrcode/QRDetector.h
src/qrcode/QRDetector.cpp
src/qrcode/QRECB.h
src/qrcode/QRFormatInformation.h
src/qrcode/QRFormatInformation.cpp
src/qrcode/QRReader.h
src/qrcode/QRReader.cpp
)
endif()
if (ZXING_WRITERS_OLD AND ZXING_ENABLE_QRCODE)
set (QRCODE_FILES ${QRCODE_FILES}
src/qrcode/QREncoder.h
src/qrcode/QREncoder.cpp
src/qrcode/QREncodeResult.h
src/qrcode/QRMaskUtil.h
src/qrcode/QRMaskUtil.cpp
src/qrcode/QRMatrixUtil.h
src/qrcode/QRMatrixUtil.cpp
src/qrcode/QRWriter.h
src/qrcode/QRWriter.cpp
)
endif()
source_group (Sources FILES ${COMMON_FILES})
source_group (Sources\\aztec FILES ${AZTEC_FILES})
source_group (Sources\\datamatrix FILES ${DATAMATRIX_FILES})
source_group (Sources\\maxicode FILES ${MAXICODE_FILES})
source_group (Sources\\oned FILES ${ONED_FILES})
source_group (Sources\\pdf417 FILES ${PDF417_FILES})
source_group (Sources\\qrcode FILES ${QRCODE_FILES})
# MARK: - Library target
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
add_library (ZXing
${COMMON_FILES}
${AZTEC_FILES}
${DATAMATRIX_FILES}
${MAXICODE_FILES}
${ONED_FILES}
${PDF417_FILES}
${QRCODE_FILES}
)
target_include_directories (ZXing
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
INTERFACE "$<INSTALL_INTERFACE:include>"
)
target_compile_options (ZXing
PUBLIC ${ZXING_PUBLIC_FLAGS}
PRIVATE ${ZXING_PRIVATE_FLAGS}
)
target_compile_features(ZXing PUBLIC cxx_std_17)
target_link_libraries (ZXing PRIVATE Threads::Threads)
if (ZXING_READERS OR ZXING_WRITERS_OLD)
set (LIBZUECI_FILES
src/libzueci/zueci.c
src/libzueci/zueci.h
)
set_source_files_properties(${LIBZUECI_FILES} PROPERTIES
COMPILE_FLAGS "$<$<NOT:$<BOOL:${ZXING_READERS}>>:-DZUECI_EMBED_NO_TO_UTF> $<$<NOT:$<BOOL:${ZXING_WRITERS_OLD}>>:-DZUECI_EMBED_NO_TO_ECI>"
SKIP_PRECOMPILE_HEADERS ON
)
target_sources(ZXing PRIVATE ${LIBZUECI_FILES})
source_group (Sources\\libzueci FILES ${LIBZUECI_FILES})
set (LIBRSCPP_FILES
src/librscpp/field.h
src/librscpp/poly.h
src/librscpp/encode.h
src/librscpp/decode.h
)
target_sources(ZXing PRIVATE ${LIBRSCPP_FILES})
source_group (Sources\\librscpp FILES ${LIBRSCPP_FILES})
endif()
# MARK: - ZINT
if (ZXING_WRITERS_NEW)
if (ZXING_USE_BUNDLED_ZINT)
set (LIBZINT_FILES
src/libzint/stubs.c
src/libzint/common.c
src/libzint/common.h
src/libzint/big5.h
src/libzint/eci.c
src/libzint/eci.h
src/libzint/eci_sb.h
src/libzint/filemem.c
src/libzint/filemem.h
src/libzint/fonts/normal_woff2.h
src/libzint/fonts/upcean_woff2.h
src/libzint/gb18030.h
src/libzint/gb2312.h
src/libzint/gbk.h
src/libzint/general_field.c
src/libzint/general_field.h
src/libzint/gs1.c
src/libzint/gs1.h
src/libzint/gs1_lint.h
src/libzint/iso3166.h
src/libzint/iso4217.h
src/libzint/ksx1001.h
src/libzint/large.c
src/libzint/large.h
src/libzint/library.c
src/libzint/output.c
src/libzint/output.h
src/libzint/raster.c
src/libzint/raster_font.h
src/libzint/reedsol.c
src/libzint/reedsol.h
src/libzint/reedsol_logs.h
src/libzint/sjis.h
src/libzint/svg.c
src/libzint/vector.c
src/libzint/zfiletypes.h
src/libzint/zintconfig.h
src/libzint/zint.h
)
if (ZXING_ENABLE_1D)
list(APPEND LIBZINT_FILES
src/libzint/2of5inter.c
src/libzint/2of5inter_based.c
src/libzint/telepen.c
src/libzint/codabar.c
src/libzint/code128.c
src/libzint/code128.h
src/libzint/code.c
src/libzint/dxfilmedge.c
src/libzint/medical.c
src/libzint/rss.c
src/libzint/rss.h
src/libzint/upcean.c
)
endif()
if (ZXING_ENABLE_AZTEC)
list(APPEND LIBZINT_FILES
src/libzint/aztec.c
src/libzint/aztec.h
)
endif()
if (ZXING_ENABLE_DATAMATRIX)
list(APPEND LIBZINT_FILES
src/libzint/dmatrix.c
src/libzint/dmatrix.h
)
endif()
if (ZXING_ENABLE_MAXICODE)
list(APPEND LIBZINT_FILES
src/libzint/maxicode.c
src/libzint/maxicode.h
)
endif()
if (ZXING_ENABLE_PDF417)
list(APPEND LIBZINT_FILES
src/libzint/pdf417.c
src/libzint/pdf417.h
src/libzint/pdf417_tabs.h
)
endif()
if (ZXING_ENABLE_QRCODE)
list(APPEND LIBZINT_FILES
src/libzint/qr.c
src/libzint/qr.h
)
endif()
set_source_files_properties(${LIBZINT_FILES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON COMPILE_OPTIONS -DZINT_NO_PNG)
target_sources(ZXing PRIVATE ${LIBZINT_FILES})
source_group (Sources\\libzint FILES ${LIBZINT_FILES})
target_include_directories (ZXing PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/libzint>")
else()
include(../zxing.cmake)
zxing_add_package(zint zint https://github.com/zint/zint.git 2.16.0)
target_link_libraries (ZXing PRIVATE $<IF:$<BOOL:${ZINT_STATIC}>,zint::zint-static,zint::zint>)
endif()
endif()
add_library(ZXing::ZXing ALIAS ZXing)
# add the old alias as well, to keep old clients compiling [[deprecated]]
# note: this only affects client code that includes ZXing via sub_directory.
# for clients using the exported target, see ZXingConfig.cmake.in
add_library(ZXing::Core ALIAS ZXing)
set_target_properties(ZXing PROPERTIES EXPORT_NAME ZXing)
# force position independent code to be able to link it as static lib into a DLL (e.g. the python module)
set_target_properties(ZXing PROPERTIES POSITION_INDEPENDENT_CODE ON)
if (PROJECT_VERSION)
set_target_properties(ZXing PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(ZXing PROPERTIES SOVERSION ${ZXING_SONAME})
endif()
# Add Windows resource file for version information
if (WIN32 AND BUILD_SHARED_LIBS)
# Convert version to comma-separated format for Windows resources
string(REPLACE "." "," FILE_VERSION_COMMA "${PROJECT_VERSION}.0")
set(FILE_VERSION_STR "${PROJECT_VERSION}.0")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in
${CMAKE_CURRENT_BINARY_DIR}/version.rc
@ONLY
)
target_sources(ZXing PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
# TODO: use FILE_SET HEADERS instead of PUBLIC_HEADER and target_sources, once CMake 3.23+ is required
set_target_properties(ZXing PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}")
# MARK: - Precompiled headers
set(PRECOMPILE_HEADERS ${PUBLIC_HEADERS})
list(REMOVE_ITEM PRECOMPILE_HEADERS
"$<$<BOOL:${ZXING_C_API}>:${CMAKE_CURRENT_SOURCE_DIR}/src/ZXingC.h>"
"${CMAKE_CURRENT_SOURCE_DIR}/src/ZXingQt.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/ZXVersion.h")
list(APPEND PRECOMPILE_HEADERS "src/BarcodeData.h" "src/Content.h" "src/Range.h")
if (NOT ZXING_DISABLE_PCH)
target_precompile_headers(ZXing PRIVATE ${PRECOMPILE_HEADERS})
endif()
if(CMAKE_BUILD_TYPE MATCHES "Release|RelWithDebInfo")
# The following is a list of translation units that fulfill two criteria regarding the use of -Os vs -O3:
# 1. their binary size decreases significantly
# 2. the runtime of ReaderTest is not (measurably) affected
# Compiling them with -Os saves about 40kB (3%) with clang and 190kB (12%) with gcc.
check_cxx_compiler_flag("-Os" COMPILER_KNOWS_Os)
if(COMPILER_KNOWS_Os)
set_source_files_properties(
src/Barcode.cpp
src/BarcodeFormat.cpp
src/BitMatrixIO.cpp
src/CreateBarcode.cpp
src/Error.cpp
src/GTIN.cpp
src/HRI.cpp
src/MultiFormatReader.cpp
src/WriteBarcode.cpp
src/ZXingC.cpp
src/ZXingCpp.cpp
src/aztec/AZHighLevelEncoder.cpp
src/datamatrix/DMDataBlock.cpp
src/datamatrix/DMHighLevelEncoder.cpp
src/oned/ODDataBarExpandedBitDecoder.cpp
src/pdf417/PDFHighLevelEncoder.cpp
src/qrcode/QRBitMatrixParser.cpp
src/qrcode/QRDataBlock.cpp
src/qrcode/QRDecoder.cpp
src/qrcode/QREncoder.cpp
src/qrcode/QRMaskUtil.cpp
src/qrcode/QRReader.cpp
src/qrcode/QRVersion.cpp
${LIBZINT_FILES}
PROPERTIES SKIP_PRECOMPILE_HEADERS ON COMPILE_FLAGS -Os)
endif()
endif()
# MARK: - Installation
include (GNUInstallDirs)
set(ZX_INSTALL_TARGETS ZXing)
install (
TARGETS ${ZX_INSTALL_TARGETS} EXPORT ZXingTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR}
# INCLUDES DESTINATION include
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ZXing"
)
configure_file (Version.h.in Version.h)
install (
FILES "${CMAKE_CURRENT_BINARY_DIR}/Version.h"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ZXing"
)
if (MSVC)
set_target_properties(ZXing PROPERTIES
COMPILE_PDB_NAME ZXing
COMPILE_PDB_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ZXing.pdb
DESTINATION ${CMAKE_INSTALL_LIBDIR}
CONFIGURATIONS Debug RelWithDebInfo
OPTIONAL)
endif()
set (CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/ZXing")
install (
EXPORT ZXingTargets
DESTINATION ${CMAKECONFIG_INSTALL_DIR} NAMESPACE ZXing::
)
IF (NOT WIN32 OR MINGW)
# if (${ZXING_EXPERIMENTAL_API})
# set(COMPILE_FLAGS "-DZXING_EXPERIMENTAL_API")
# endif()
configure_file(zxing.pc.in zxing.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/zxing.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
ENDIF()
include (CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/ZXingConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
configure_package_config_file (
ZXingConfig.cmake.in
ZXingConfig.cmake
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
)
install (
FILES
"${CMAKE_CURRENT_BINARY_DIR}/ZXingConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/ZXingConfigVersion.cmake"
DESTINATION ${CMAKECONFIG_INSTALL_DIR}
)

28
tools/chatgui/zxing-cpp/core/Version.h.in

@ -0,0 +1,28 @@
/*
* Copyright 2019 Nu-book Inc.
* Copyright 2023 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#cmakedefine ZXING_READERS
#cmakedefine ZXING_WRITERS
#cmakedefine01 ZXING_ENABLE_1D
#cmakedefine01 ZXING_ENABLE_AZTEC
#cmakedefine01 ZXING_ENABLE_DATAMATRIX
#cmakedefine01 ZXING_ENABLE_MAXICODE
#cmakedefine01 ZXING_ENABLE_PDF417
#cmakedefine01 ZXING_ENABLE_QRCODE
#cmakedefine ZXING_EXPERIMENTAL_API
#cmakedefine ZXING_USE_ZINT
// Version numbering
#define ZXING_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define ZXING_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define ZXING_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define ZXING_VERSION_SUFFIX "@ZXING_VERSION_SUFFIX@"
#define ZXING_VERSION_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@@ZXING_VERSION_SUFFIX@"

9
tools/chatgui/zxing-cpp/core/ZXingConfig.cmake.in

@ -0,0 +1,9 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/ZXingTargets.cmake")
# this does not work: add_library(ZXing::Core ALIAS ZXing::ZXing)
# this is a workaround available since 3.11 :
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.11 AND NOT TARGET ZXing::Core)
add_library(ZXing::Core INTERFACE IMPORTED)
target_link_libraries(ZXing::Core INTERFACE ZXing::ZXing)
endif()

255
tools/chatgui/zxing-cpp/core/src/Barcode.cpp

@ -0,0 +1,255 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "Barcode.h"
#include "BarcodeData.h"
#include "JSON.h"
#include "ZXAlgorithms.h"
#include "BitMatrix.h"
#include <cmath>
#include <list>
#include <map>
#include <numbers>
#include <utility>
#ifdef ZXING_USE_ZINT
#include <zint.h>
void zint_symbol_deleter::operator()(zint_symbol* p) const noexcept
{
ZBarcode_Delete(p);
}
#endif
namespace ZXing {
Barcode::Barcode() : d(std::make_shared<Data>()) {}
Barcode::Barcode(Data&& data) : d(std::make_shared<Data>(std::move(data))) {}
bool Barcode::isValid() const
{
return d->isValid();
}
const Error& Barcode::error() const
{
return d->error;
}
BarcodeFormat Barcode::format() const
{
return d->format;
}
const Position& Barcode::position() const
{
return d->position;
}
const std::vector<uint8_t>& Barcode::bytes() const
{
return d->content.bytes;
}
std::vector<uint8_t> Barcode::bytesECI() const
{
return d->content.bytesECI();
}
std::string Barcode::text(TextMode mode) const
{
return d->content.text(mode);
}
std::string Barcode::text() const
{
return text(d->defaultTextMode);
}
ContentType Barcode::contentType() const
{
return d->content.type();
}
bool Barcode::hasECI() const
{
return d->content.hasECI;
}
int Barcode::orientation() const
{
return narrow_cast<int>(std::lround(d->position.orientation() * 180 / std::numbers::pi));
}
bool Barcode::isMirrored() const
{
return d->isMirrored;
}
bool Barcode::isInverted() const
{
return d->isInverted;
}
std::string Barcode::symbologyIdentifier() const
{
return d->content.symbology.toString();
}
int Barcode::sequenceSize() const
{
return d->sai.count;
}
int Barcode::sequenceIndex() const
{
return d->sai.index;
}
std::string Barcode::sequenceId() const
{
return d->sai.id;
}
int Barcode::lineCount() const
{
return d->lineCount;
}
Barcode& Barcode::setReaderOptions(const ReaderOptions& opts)
{
if (opts.characterSet() != CharacterSet::Unknown)
d->content.defaultCharset = opts.characterSet();
d->defaultTextMode = opts.textMode();
return *this;
}
ImageView Barcode::symbol() const
{
return !d->symbol.empty() ? ImageView{d->symbol.row(0).begin(), d->symbol.width(), d->symbol.height(), ImageFormat::Lum}
: ImageView{};
}
#if defined(ZXING_USE_ZINT) && defined(ZXING_EXPERIMENTAL_API)
zint_symbol* Barcode::zint() const
{
return d->zint.get();
}
#endif
std::string Barcode::extra(std::string_view key) const
{
if (key == "ALL") {
if (format() == BarcodeFormat::None)
return {};
auto res =
StrCat("{", JsonProp("Text", text(TextMode::Plain)), JsonProp("HRI", text(TextMode::HRI)),
JsonProp("TextECI", text(TextMode::ECI)), JsonProp("Bytes", text(TextMode::Hex)),
JsonProp("Identifier", symbologyIdentifier()), JsonProp("Format", Name(format())),
JsonProp("Symbology", Name(Symbology(format()))), JsonProp("ContentType", isValid() ? ToString(contentType()) : ""),
JsonProp("Position", ToString(position())), JsonProp("HasECI", hasECI()), JsonProp("IsMirrored", isMirrored()),
JsonProp("IsInverted", isInverted()), d->extra, JsonProp("Error", ToString(error())));
res.back() = '}';
return res;
}
return d->extra.empty() ? ""
: key.empty() ? StrCat("{", std::string_view(d->extra).substr(0, d->extra.size() - 1), "}") // remove trailing ','
: JsonGet<std::string>(d->extra, key).value_or(""); // make sure JsonUnescape() is called
}
bool Barcode::operator==(const Barcode& o) const
{
return *d == *o.d;
}
bool BarcodeData::operator==(const BarcodeData& o) const
{
if (format != o.format)
return false;
// handle MatrixCodes first
if (!(format & BarcodeFormat::AllLinear)) {
if (content.bytes != o.content.bytes)
return false;
// At this point both are valid with the same content or both are in error.
// Treat them as equal if their positions are about the same (center of one is inside the other).
return IsInside(Center(o.position), position);
}
if (content.bytes != o.content.bytes || error != o.error || orientation() != o.orientation())
return false;
if (lineCount > 1 && o.lineCount > 1)
return HaveIntersectingBoundingBoxes(o.position, position);
// the following code is only meant for this or other lineCount == 1
assert(lineCount == 1 || o.lineCount == 1);
// sl == single line, ml = multi line
const auto& sl = lineCount == 1 ? *this : o;
const auto& ml = lineCount == 1 ? o : *this;
// If one line is less than half the length of the other away from the
// latter, we consider it to belong to the same symbol.
// Additionally, both need to have roughly the same length (see #367).
auto dTop = maxAbsComponent(ml.position.topLeft() - sl.position.topLeft());
auto dBot = maxAbsComponent(ml.position.bottomLeft() - sl.position.topLeft());
auto slLength = maxAbsComponent(sl.position.topLeft() - sl.position.bottomRight());
bool isHorizontal = sl.position.topLeft().y == sl.position.bottomRight().y;
// Measure the multi line length in the same direction as the single line one (not diagonally)
// to make sure overly tall symbols don't get segmented (see #769).
auto mlLength = isHorizontal ? std::abs(ml.position.topLeft().x - ml.position.bottomRight().x)
: std::abs(ml.position.topLeft().y - ml.position.bottomRight().y);
return std::min(dTop, dBot) < slLength / 2 && std::abs(slLength - mlLength) < slLength / 5;
}
Barcode MergeStructuredAppendSequence(const Barcodes& barcodes)
{
if (barcodes.empty())
return {};
std::list<Barcode> allBarcodes(barcodes.begin(), barcodes.end());
allBarcodes.sort([](const Barcode& r1, const Barcode& r2) { return r1.sequenceIndex() < r2.sequenceIndex(); });
Barcode res = allBarcodes.front();
for (auto i = std::next(allBarcodes.begin()); i != allBarcodes.end(); ++i)
res.d->content.append(i->d->content);
res.d->position = {};
res.d->sai.index = -1;
if (allBarcodes.back().sequenceSize() != Size(allBarcodes) ||
!std::all_of(allBarcodes.begin(), allBarcodes.end(),
[&](Barcode& it) { return it.sequenceId() == allBarcodes.front().sequenceId(); }))
res.d->error = FormatError("sequenceIDs not matching during structured append sequence merging");
return res;
}
Barcodes MergeStructuredAppendSequences(const Barcodes& barcodes)
{
std::map<std::string, Barcodes> sas;
for (auto& barcode : barcodes) {
if (barcode.isPartOfSequence())
sas[barcode.sequenceId()].push_back(barcode);
}
Barcodes res;
for (auto& [id, seq] : sas) {
auto barcode = MergeStructuredAppendSequence(seq);
if (barcode.isValid())
res.push_back(std::move(barcode));
}
return res;
}
} // namespace ZXing

191
tools/chatgui/zxing-cpp/core/src/Barcode.h

@ -0,0 +1,191 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BarcodeFormat.h"
#include "ContentType.h"
#include "Error.h"
#include "ImageView.h"
#include "Quadrilateral.h"
#include "ReaderOptions.h" // for TextMode
#include "Version.h" // ZXING_... macros
#include <memory>
#include <string>
#include <vector>
#ifdef ZXING_USE_ZINT
extern "C" struct zint_symbol;
#endif
namespace ZXing {
class CreatorOptions;
class ReaderOptions;
class WriterOptions;
class Barcode;
struct BarcodeData;
using Position = QuadrilateralI;
using Barcodes = std::vector<Barcode>;
/**
* @brief String keys for the extra() metadata of Barcode. The values depend on the symbology and may not be present for all barcodes.
*/
namespace BarcodeExtra {
#define ZX_EXTRA(NAME) static constexpr auto NAME = #NAME
ZX_EXTRA(DataMask); ///< QRCodes
ZX_EXTRA(Version); ///< QRCodes, DataMatrix, MaxiCode (e.g. "12x12" for DataMatrix)
ZX_EXTRA(EanAddOn); ///< The EAN/UPC add-on content if detected
ZX_EXTRA(ECLevel); ///< Error correction level (e.g. "L", "M", "Q", "H" for QRCode)
ZX_EXTRA(UEC); ///< Unused error correction margin in the range [0, 1]
ZX_EXTRA(UPCE); ///< The original (non-normalized) UPC-E code if UPC-E is detected
ZX_EXTRA(ReaderInit);
#undef ZX_EXTRA
} // namespace BarcodeExtra
/**
* @brief The Barcode class encapsulates a decoded or created barcode symbol.
*
* Barcode represents a decoded or created barcode symbol, providing access to its content, format,
* position, and other metadata. It serves as the primary interface for working with barcodes in the library.
*
* Barcode objects are obtained from the ReadBarcodes function when scanning an image, or from the
* CreateBarcodeFrom... functions when generating a new barcode. To convert a Barcode to an image or SVG,
* use the WriteBarcodeTo... functions.
*
* @see ReadBarcodes, CreateBarcodeFromText, CreateBarcodeFromBytes, WriteBarcodeToImage, WriteBarcodeToSVG
*/
class Barcode
{
using Data = BarcodeData;
std::shared_ptr<Data> d;
Barcode& setReaderOptions(const ReaderOptions& opts);
friend Barcode MergeStructuredAppendSequence(const Barcodes&);
friend Barcodes ReadBarcodes(const ImageView&, const ReaderOptions&);
friend Barcode CreateBarcode(const void*, int, int, const CreatorOptions&);
friend Image WriteBarcodeToImage(const Barcode&, const WriterOptions&);
friend std::string WriteBarcodeToSVG(const Barcode&, const WriterOptions&);
public:
Barcode();
Barcode(Barcode::Data&& data);
/// Returns whether the barcode is valid, i.e. it contains a successfully decoded or created symbol.
bool isValid() const;
/// Returns the error associated with the barcode, if any.
const Error& error() const;
/// Returns the BarcodeFormat of the barcode.
BarcodeFormat format() const;
/// Returns the symbology of the barcode format (e.g. EAN/UPC for EAN13, EAN8, UPCA, etc.).
BarcodeFormat symbology() const { return Symbology(format()); }
/// Returns the raw / standard content without any modifications like character set conversions.
const std::vector<uint8_t>& bytes() const;
/// Returns the raw / standard content following the ECI protocol.
std::vector<uint8_t> bytesECI() const;
/// Returns the bytes() content rendered to unicode/utf8 text according to specified TextMode.
std::string text(TextMode mode) const;
/// Returns the bytes() content rendered to unicode/utf8 text according to the TextMode set in the ReaderOptions.
std::string text() const;
/// Returns the content type, giving a hint to the type of content found (Text/Binary/GS1/etc.).
ContentType contentType() const;
/// Returns whether or not an ECI tag was found.
bool hasECI() const;
/// Returns the position of the barcode in the image as a quadrilateral of 4 points (topLeft, topRight, bottomRight,
/// bottomLeft).
const Position& position() const;
/// Returns the orientation of the barcode in degrees, see also Position::orientation()
int orientation() const;
/// Returns whether the symbol is mirrored (currently only supported by QRCode and DataMatrix).
bool isMirrored() const;
/// Returns whether the symbol is inverted / has reversed reflectance (see ReaderOptions::tryInvert).
bool isInverted() const;
/// Returns the symbology identifier "]cm" where "c" is symbology code character, "m" the modifier.
std::string symbologyIdentifier() const;
/// @brief Returns the number of symbols in a structured append sequence.
///
/// If this is not part of a structured append sequence, the returned value is -1.
/// If it is a structured append symbol but the total number of symbols is unknown, the
/// returned value is 0 (see PDF417 if optional "Segment Count" not given).
int sequenceSize() const;
/// Returns the 0-based index of this symbol in a structured append sequence.
int sequenceIndex() const;
/// @brief Returns the sequenceId to check if a set of symbols belongs to the same structured append sequence.
///
/// If the symbology does not support this feature, the returned value is empty (see MaxiCode).
/// For QR Code, this is the parity integer converted to a string.
/// For PDF417 and DataMatrix, this is the "fileId".
std::string sequenceId() const;
/// Returns whether this symbol is the last in a structured append sequence.
bool isLastInSequence() const { return sequenceSize() == sequenceIndex() + 1; }
/// Returns whether this symbol is part of a structured append sequence.
bool isPartOfSequence() const { return sequenceSize() > -1 && sequenceIndex() > -1; }
/// Returns how many lines have been detected with this code (applies only to linear symbologies).
int lineCount() const;
/// Returns the bit matrix of the symbol as an ImageView.
ImageView symbol() const;
/// @brief Retrieve supplementary metadata associated with this barcode.
///
/// Returns a string containing additional and symbology specific information. In form of a JSON object
/// serialization. The optional parameter @p key can be used to retrieve a specific item only.
/// Key values are case insensitive. See BarcodeExtra namespace for valid keys.
/// If the key is not found or there is no info available, an empty string is returned.
std::string extra(std::string_view key = "") const;
/// @cond DEPRECATED
/// Returns the error correction level of the symbol (empty string if not applicable).
// [[deprecated ("use extra(BarcodeExtra::ECLevel) instead")]]
std::string ecLevel() const { return extra(BarcodeExtra::ECLevel); }
/// Returns whether the symbol is a Reader Initialisation/Programming symbol.
// [[deprecated ("use extra(BarcodeExtra::ReaderInit) instead")]]
bool readerInit() const { return !extra(BarcodeExtra::ReaderInit).empty(); }
/// Returns the version QRCode / DataMatrix / Aztec version or size.
// [[deprecated ("use extra(BarcodeExtra::Version) instead")]]
std::string version() const { return extra(BarcodeExtra::Version); }
/// @endcond
#if defined(ZXING_USE_ZINT) && defined(ZXING_EXPERIMENTAL_API)
zint_symbol* zint() const;
#endif
bool operator==(const Barcode& o) const;
};
/// Merge a list of Barcodes from one Structured Append sequence to a single barcode.
Barcode MergeStructuredAppendSequence(const Barcodes& barcodes);
/// Automatically merge all Structured Append sequences found in the given list of barcodes.
Barcodes MergeStructuredAppendSequences(const Barcodes& barcodes);
} // ZXing

100
tools/chatgui/zxing-cpp/core/src/BarcodeData.h

@ -0,0 +1,100 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Barcode.h"
#include "BitMatrix.h"
#include "Content.h"
#include "DecoderResult.h"
#include "DetectorResult.h"
#include "Error.h"
#include "Quadrilateral.h"
#include "ReaderOptions.h"
#include "StructuredAppend.h"
#include <memory>
#include <mutex>
#include <numbers>
#include <string>
#include <vector>
#ifdef ZXING_USE_ZINT
extern "C" struct zint_symbol;
struct zint_symbol_deleter
{
void operator()(zint_symbol* p) const noexcept;
};
using unique_zint_symbol = std::unique_ptr<zint_symbol, zint_symbol_deleter>;
#else
struct zint_symbol {};
using unique_zint_symbol = std::unique_ptr<zint_symbol>;
#endif
namespace ZXing {
struct BarcodeData
{
Content content;
Error error = {};
Position position = {};
BarcodeFormat format = BarcodeFormat::None;
std::string extra = {};
StructuredAppendInfo sai = {};
BitMatrix symbol = {};
#ifdef ZXING_USE_ZINT
unique_zint_symbol zint = {};
mutable std::unique_ptr<std::mutex> zintMutex = {};
#endif
TextMode defaultTextMode = TextMode::HRI;
int lineCount = 0;
bool isMirrored = false;
bool isInverted = false;
bool operator==(const BarcodeData& other) const;
inline bool isValid() const { return format != BarcodeFormat::None && !content.bytes.empty() && !error; }
inline int orientation() const { return narrow_cast<int>(std::lround(position.orientation() * 180 / std::numbers::pi)); }
};
using BarcodesData = std::vector<BarcodeData>;
inline BarcodeData LinearBarcode(BarcodeFormat format, const std::string& text, int y, int xStart, int xStop, SymbologyIdentifier si,
Error error = {}, std::string extra = {})
{
return {.content = Content(ByteArray(text), si, CharacterSet::ISO8859_1),
.error = std::move(error),
.position = Line(y, xStart, xStop),
.format = format,
.extra = std::move(extra)};
}
inline BarcodeData MatrixBarcode(DecoderResult&& decodeResult, DetectorResult&& detectorResult, BarcodeFormat format)
{
auto extra = std::move(decodeResult).json();
if (JsonFind(extra, BarcodeExtra::Version).empty() && decodeResult.versionNumber())
extra += JsonProp(BarcodeExtra::Version, std::to_string(decodeResult.versionNumber()));
if (JsonFind(extra, BarcodeExtra::ECLevel).empty() && !decodeResult.ecLevel().empty())
extra += JsonProp(BarcodeExtra::ECLevel, decodeResult.ecLevel());
extra += JsonProp(BarcodeExtra::ReaderInit, decodeResult.readerInit());
// the BitMatrix stores 'black'/foreground as 0xFF and 'white'/background as 0, but we
// want the ImageView returned by symbol() to be a standard luminance image (black == 0)
std::move(detectorResult).bits().flipAll();
return {.content = std::move(decodeResult).content(),
.error = std::move(decodeResult).error(),
.position = std::move(detectorResult).position(),
.format = format,
.extra = std::move(extra),
.sai = std::move(decodeResult).structuredAppend(),
.symbol = std::move(detectorResult).bits(),
.lineCount = decodeResult.lineCount(),
.isMirrored = decodeResult.isMirrored()};
}
} // namespace ZXing

286
tools/chatgui/zxing-cpp/core/src/BarcodeFormat.cpp

@ -0,0 +1,286 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "BarcodeFormat.h"
#include "Version.h"
#include "ZXAlgorithms.h"
#include <algorithm>
namespace ZXing {
// =================================== BarcodeFormat =================================
static_assert(ZX_BCF_ID('\0', '\0') == 0, "BarcodeFormat encoding error");
BarcodeFormat Symbology(BarcodeFormat format)
{
return BarcodeFormat(ZX_BCF_ID(SymbologyKey(format), ' ' * (VariantKey(format) != 0)));
}
std::string_view Name(BarcodeFormat format)
{
switch (format) {
#define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) \
case BarcodeFormat(ZX_BCF_ID(SYM, VAR)): return HRI;
ZX_BCF_LIST(X)
#undef X
default: return "Unknown";
};
}
BarcodeFormat BarcodeFormatFromString(std::string_view str)
{
if (str.size() < 3)
throw std::invalid_argument(StrCat("This is not a valid barcode format: '", str, "'"));
#define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) \
if ((str[0] == ']' && str[1] == SYM && str[2] == VAR) || IsEqualIgnoreCase(str, #NAME) || IsEqualIgnoreCaseAnd(str, HRI, " -_/")) \
return BarcodeFormat(ZX_BCF_ID(SYM, VAR));
ZX_BCF_LIST(X)
#undef X
throw std::invalid_argument(StrCat("This is not a valid barcode format: '", str, "'"));
}
std::string ToString(BarcodeFormat format)
{
return std::string(Name(format));
}
bool operator<=(BarcodeFormat e, BarcodeFormat s)
{
return e == s || s == BarcodeFormat::All
|| (SymbologyKey(s) == '*' ? SymbologyKey(e) != '*' && (e & s)
: SymbologyKey(s) == SymbologyKey(e) && VariantKey(s) == ' ');
}
bool operator&(BarcodeFormat a, BarcodeFormat b)
{
if (SymbologyKey(a) == '*' && SymbologyKey(b) == '*') {
switch (VariantKey(a)) {
case 'l': return ZXING_ENABLE_1D && !Contains("smp", VariantKey(b));
case 's': return ZXING_ENABLE_PDF417 && !Contains("lmp", VariantKey(b));
case 'p': return !Contains("lms", VariantKey(b)); // TODO: postal codes
case 'm':
return (ZXING_ENABLE_MAXICODE || ZXING_ENABLE_QRCODE || ZXING_ENABLE_DATAMATRIX || ZXING_ENABLE_AZTEC)
&& !Contains("lsp", VariantKey(b));
default: return true;
}
}
if (SymbologyKey(a) == '*')
std::swap(a, b);
if (SymbologyKey(b) == '*') {
char vkb = VariantKey(b);
if (vkb == '*')
return true;
switch (a) {
#ifdef ZXING_USE_ZINT
#define USING_ZINT 1
#else
#define USING_ZINT 0
#endif
#define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) \
case BarcodeFormat(ZX_BCF_ID(SYM, VAR)): \
return ENABLED \
&& ((vkb == 'w' && USING_ZINT) ? ZINT : (FLAGS[0] == vkb || FLAGS[1] == vkb || FLAGS[2] == vkb || FLAGS[3] == vkb || FLAGS[4] == vkb));
ZX_BCF_LIST(X)
#undef X
};
}
if (a == b || Symbology(a) == b || a == Symbology(b))
return true;
return false;
}
// ==================================== BarcodeFormats =================================
inline bool operator<(BarcodeFormat a, BarcodeFormat b)
{
return SymbologyKey(a) < SymbologyKey(b) || (SymbologyKey(a) == SymbologyKey(b) && VariantKey(a) < VariantKey(b));
}
void BarcodeFormats::normalize()
{
std::erase_if(formats_, [](BarcodeFormat t) { return t == BarcodeFormat::None; });
std::sort(formats_.begin(), formats_.end());
formats_.erase(std::unique(formats_.begin(), formats_.end()), formats_.end());
}
BarcodeFormats::BarcodeFormats(std::string_view str)
{
while (str.size() >= 3 && str[0] == ']') {
formats_.push_back(BarcodeFormat(ZX_BCF_ID(str[1], str[2])));
str.remove_prefix(3);
}
ForEachToken(TrimWS(str, " []"), ",|", [this](std::string_view token) {
if (!token.empty())
formats_.push_back(BarcodeFormatFromString(token));
});
normalize();
}
BarcodeFormats BarcodeFormats::list(const BarcodeFormats& filter)
{
std::vector<BarcodeFormat> res;
res.reserve(100);
if (filter.empty()) {
#define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) \
if (ENABLED) \
res.push_back(BarcodeFormat(ZX_BCF_ID(SYM, VAR)));
ZX_BCF_LIST(X)
#undef X
return res;
}
for (auto f : filter) {
// printf("Filter for: %s\n", IdStr(f).c_str());
#define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) \
if (ENABLED && SYM != '*' \
&& (SymbologyKey(f) == '*' ? BarcodeFormat(ZX_BCF_ID(SYM, VAR)) & f \
: SYM == SymbologyKey(f) && (VariantKey(f) == ' ' || VariantKey(f) == VAR))) \
res.push_back(BarcodeFormat(ZX_BCF_ID(SYM, VAR))); //, printf("adding: %c %c\n", SYM, VAR);
ZX_BCF_LIST(X)
#undef X
// printf("N: %d\n", (int)res.size());
}
return res;
}
// BarcodeFormats&& BarcodeFormats::operator|(BarcodeFormat bt) &&
// {
// types_.push_back(bt);
// normalize();
// return std::move(*this);
// }
// BarcodeFormats&& BarcodeFormats::operator|(const BarcodeFormats& other) &&
// {
// types_.insert(std::end(types_), other.begin(), other.end());
// normalize();
// return std::move(*this);
// }
BarcodeFormats BarcodeFormats::operator&(const BarcodeFormats& other)
{
std::vector<BarcodeFormat> res;
for (auto l : formats_)
for (auto r : other.formats_)
if (l <= r)
res.push_back(l);
else if (r <= l)
res.push_back(r);
return res;
}
BarcodeFormats BarcodeFormatsFromString(std::string_view str)
{
return BarcodeFormats(str);
}
std::string ToString(const BarcodeFormats& formats)
{
if (formats.empty())
return {};
std::string res;
for (auto f : formats)
res += std::string(Name(f)) + ", ";
return res.substr(0, res.size() - 2);
}
} // namespace ZXing
#if 0
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "BarcodeFormat.h"
#include "ZXAlgorithms.h"
#include <iterator>
#include <stdexcept>
namespace ZXing {
struct BarcodeFormatName
{
BarcodeFormat format;
std::string_view name;
};
static const BarcodeFormatName NAMES[] = {
{BarcodeFormat::None, "None"},
{BarcodeFormat::Aztec, "Aztec"},
{BarcodeFormat::Codabar, "Codabar"},
{BarcodeFormat::Code39, "Code39"},
{BarcodeFormat::Code93, "Code93"},
{BarcodeFormat::Code128, "Code128"},
{BarcodeFormat::DataBar, "DataBar"},
{BarcodeFormat::DataBarExpanded, "DataBarExpanded"},
{BarcodeFormat::DataBarLimited, "DataBarLimited"},
{BarcodeFormat::DataMatrix, "DataMatrix"},
{BarcodeFormat::DXFilmEdge, "DXFilmEdge"},
{BarcodeFormat::EAN8, "EAN-8"},
{BarcodeFormat::EAN13, "EAN-13"},
{BarcodeFormat::ITF, "ITF"},
{BarcodeFormat::MaxiCode, "MaxiCode"},
{BarcodeFormat::MicroPDF417, "MicroPDF417"},
{BarcodeFormat::MicroQRCode, "MicroQRCode"},
{BarcodeFormat::PDF417, "PDF417"},
{BarcodeFormat::QRCode, "QRCode"},
{BarcodeFormat::RMQRCode, "rMQRCode"},
{BarcodeFormat::UPCA, "UPC-A"},
{BarcodeFormat::UPCE, "UPC-E"},
{BarcodeFormat::LinearCodes, "Linear-Codes"},
{BarcodeFormat::MatrixCodes, "Matrix-Codes"},
};
std::string ToString(BarcodeFormat format)
{
auto i = FindIf(NAMES, [format](auto& v) { return v.format == format; });
return i == std::end(NAMES) ? std::string() : std::string(i->name);
}
std::string ToString(BarcodeFormats formats)
{
if (formats.empty())
return ToString(BarcodeFormat::None);
std::string res;
for (auto f : formats)
res += ToString(f) + "|";
return res.substr(0, res.size() - 1);
}
BarcodeFormat BarcodeFormatFromString(std::string_view str)
{
auto i = FindIf(NAMES, [str](auto& v) { return IsEqualIgnoreCaseAnd(v.name, str, "_-"); });
return i == std::end(NAMES) ? BarcodeFormat::None : i->format;
}
BarcodeFormats BarcodeFormatsFromString(std::string_view str)
{
BarcodeFormats res;
ForEachToken(TrimWS(str, " []"), " ,|", [&res](std::string_view token) {
if (!token.empty()) {
auto bc = BarcodeFormatFromString(token);
if (bc == BarcodeFormat::None)
throw std::invalid_argument("This is not a valid barcode format: '" + std::string(token) + "'");
res |= bc;
}
});
return res;
}
} // ZXing
#endif // 0

272
tools/chatgui/zxing-cpp/core/src/BarcodeFormat.h

@ -0,0 +1,272 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Version.h"
#include <stdint.h>
// FLAGS[0]: l == linear, m == matrix
// FLAGS[1]: r == readable
// FLAGS[2]: w == writable (MultiformatWriter))
// FLAGS[3]: G == GS1
// FLAGS[4]: R == Retail, I == Industrial, O == Other
// clang-format off
// NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI
#define ZX_BCF_LIST(X) \
X(None, 0 , 0, " ", 0, 1, "None") \
X(All, '*', '*', " ", 0, 1, "All") \
X(AllReadable, '*', 'r', " ", 0, 1, "All Readable") \
X(AllCreatable, '*', 'w', " ", 0, 1, "All Creatable") \
X(AllLinear, '*', 'l', " ", 0, 1, "All Linear") \
X(AllMatrix, '*', 'm', " ", 0, 1, "All Matrix") \
X(AllGS1, '*', 'G', " ", 0, 1, "All GS1") \
X(AllRetail, '*', 'R', " ", 0, 1, "All Retail") \
X(AllIndustrial, '*', 'I', " ", 0, 1, "All Industrial") \
X(Codabar, 'F', ' ', "lrw ", 18, ZXING_ENABLE_1D, "Codabar") \
X(Code39, 'A', ' ', "lrw I", 8, ZXING_ENABLE_1D, "Code 39") \
X(Code39Std, 'A', 's', "lrw I", 8, ZXING_ENABLE_1D, "Code 39 Standard") \
X(Code39Ext, 'A', 'e', "lr I", 9, ZXING_ENABLE_1D, "Code 39 Extended") \
X(Code32, 'A', '2', "lr I", 129, ZXING_ENABLE_1D, "Code 32") \
X(PZN, 'A', 'p', "lr I", 52, ZXING_ENABLE_1D, "Pharmazentralnummer") \
X(Code93, 'G', ' ', "lrw I", 25, ZXING_ENABLE_1D, "Code 93") \
X(Code128, 'C', ' ', "lrwGI", 20, ZXING_ENABLE_1D, "Code 128") \
X(ITF, 'I', ' ', "lrw I", 3, ZXING_ENABLE_1D, "ITF") \
X(ITF14, 'I', '4', "lr I", 89, ZXING_ENABLE_1D, "ITF-14") \
X(DataBar, 'e', ' ', "lr GR", 29, ZXING_ENABLE_1D, "DataBar") \
X(DataBarOmni, 'e', 'o', "lr GR", 29, ZXING_ENABLE_1D, "DataBar Omni") \
X(DataBarStk, 'e', 's', "lr GR", 79, ZXING_ENABLE_1D, "DataBar Stacked") \
X(DataBarStkOmni, 'e', 'O', "lr GR", 80, ZXING_ENABLE_1D, "DataBar Stacked Omni") \
X(DataBarLtd, 'e', 'l', "lr GR", 30, ZXING_ENABLE_1D, "DataBar Limited") \
X(DataBarExp, 'e', 'e', "lr GR", 31, ZXING_ENABLE_1D, "DataBar Expanded") \
X(DataBarExpStk, 'e', 'E', "lr GR", 81, ZXING_ENABLE_1D, "DataBar Expanded Stacked") \
X(EANUPC, 'E', ' ', "lr R", 15, ZXING_ENABLE_1D, "EAN/UPC") \
X(EAN13, 'E', '1', "lrw R", 15, ZXING_ENABLE_1D, "EAN-13") \
X(EAN8, 'E', '8', "lrw R", 10, ZXING_ENABLE_1D, "EAN-8") \
X(EAN5, 'E', '5', "l R", 12, ZXING_ENABLE_1D, "EAN-5") \
X(EAN2, 'E', '2', "l R", 11, ZXING_ENABLE_1D, "EAN-2") \
X(ISBN, 'E', 'i', "lr R", 69, ZXING_ENABLE_1D, "ISBN") \
X(UPCA, 'E', 'a', "lrw R", 34, ZXING_ENABLE_1D, "UPC-A") \
X(UPCE, 'E', 'e', "lrw R", 37, ZXING_ENABLE_1D, "UPC-E") \
X(Telepen, 'B', ' ', "lr I", 32, ZXING_ENABLE_1D, "Telepen") \
X(TelepenAlpha, 'B', '0', "lr I", 32, ZXING_ENABLE_1D, "Telepen Alpha") \
X(TelepenNumeric, 'B', '1', "lr I", 87, ZXING_ENABLE_1D, "Telepen Numeric") \
X(OtherBarcode, 'X', ' ', " r ", 0, ZXING_ENABLE_1D, "Other barcode") /* see ISO/IEC 15424:2025 */ \
X(DXFilmEdge, 'X', 'x', "lr ", 147, ZXING_ENABLE_1D, "DX Film Edge") \
X(PDF417, 'L', ' ', "mrw ", 55, ZXING_ENABLE_PDF417, "PDF417") \
X(CompactPDF417, 'L', 'c', "mr ", 56, ZXING_ENABLE_PDF417, "Compact PDF417") \
X(MicroPDF417, 'L', 'm', "mr ", 84, ZXING_ENABLE_PDF417, "MicroPDF417") \
X(Aztec, 'z', ' ', "mr G ", 92, ZXING_ENABLE_AZTEC, "Aztec") \
X(AztecCode, 'z', 'c', "mrwG ", 92, ZXING_ENABLE_AZTEC, "Aztec Code") \
X(AztecRune, 'z', 'r', "mr ", 128, ZXING_ENABLE_AZTEC, "Aztec Rune") \
X(QRCode, 'Q', ' ', "mrwG ", 58, ZXING_ENABLE_QRCODE, "QR Code") \
X(QRCodeModel1, 'Q', '1', "mr ", 0, ZXING_ENABLE_QRCODE, "QR Code Model 1") \
X(QRCodeModel2, 'Q', '2', "mr ", 58, ZXING_ENABLE_QRCODE, "QR Code Model 2") \
X(MicroQRCode, 'Q', 'm', "mr ", 97, ZXING_ENABLE_QRCODE, "Micro QR Code") \
X(RMQRCode, 'Q', 'r', "mr G ", 145, ZXING_ENABLE_QRCODE, "rMQR Code") \
X(DataMatrix, 'd', ' ', "mrwG ", 71, ZXING_ENABLE_DATAMATRIX, "Data Matrix") \
X(MaxiCode, 'U', ' ', "mr ", 57, ZXING_ENABLE_MAXICODE, "MaxiCode") \
/* Add new formats here */
// clang-format on
#define ZX_BCF_ID(SYM, VAR) (((uint8_t)(SYM) << 0) | ((uint8_t)(VAR) << 8))
#ifdef __cplusplus
#include <array>
#include <algorithm>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace ZXing {
/**
* @brief Enumerates barcode formats known to this package.
*
* Some formats represent a symbology (e.g. EANUPC) with multiple variants (EAN13, EAN8, etc.), while others represent a specific
* symbology variant (e.g. MicroQRCode). Both can be used with ReaderOptions::formats() to select which formats to read/scan for, but
* only specific variants are returned by the library when reading barcodes.
*
* If the format name starts with "All", it is a pseudo-format representing a set of formats (e.g. AllLinear, AllGS1, etc.). These can
* be used for filtering and selection purposes, but are not returned by the library when reading barcodes and can not be used when
* creating barcodes.
*/
enum class BarcodeFormat : unsigned int
{
#define ZX_(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) NAME = ZX_BCF_ID(SYM, VAR),
ZX_BCF_LIST(ZX_)
#undef ZX_
/// @cond DEPRECATED
DataBarExpanded [[deprecated("Use DataBarExp instead")]] = DataBarExp,
DataBarLimited [[deprecated("Use DataBarLtd instead")]] = DataBarLtd,
LinearCodes [[deprecated("Use AllLinear instead")]] = AllLinear,
MatrixCodes [[deprecated("Use AllMatrix instead")]] = AllMatrix,
Any [[deprecated("Use All instead")]] = All,
/// @endcond
};
/// @cond INTERNAL
inline char SymbologyKey(const BarcodeFormat& format)
{
return uint32_t(format) & 0xFF;
}
inline char VariantKey(const BarcodeFormat& format)
{
return (uint32_t(format) >> 8) & 0xFF;
}
inline std::string IdStr(const BarcodeFormat& format)
{
return {']', SymbologyKey(format), VariantKey(format)};
}
/// @endcond
/// Returns the symbology (base type) of the given barcode format (e.g. EAN/UPC for EAN13, EAN8, UPCA, etc.).
BarcodeFormat Symbology(BarcodeFormat format);
/// Returns the human-readable name of the given barcode format.
std::string_view Name(BarcodeFormat format);
/// Test if left hand side (e == element) is 'inside' right hand side (s == set) (e.g. MicroQRCode <= QRCode).
bool operator<=(BarcodeFormat e, BarcodeFormat s);
/// Test if the two BarcodeFormats have a non-empty intersection (e.g. AllMatrix & QRCode).
bool operator&(BarcodeFormat a, BarcodeFormat b);
template <std::size_t N>
using BarcodeFormatArray = std::array<BarcodeFormat, N>;
constexpr BarcodeFormatArray<2> operator|(BarcodeFormat a, BarcodeFormat b)
{
return {a, b};
}
template <std::size_t N>
constexpr BarcodeFormatArray<N + 1> operator|(BarcodeFormatArray<N> bts, BarcodeFormat bt)
{
BarcodeFormatArray<N + 1> ret{};
for (std::size_t i = 0; i < N; ++i)
ret[i] = bts[i];
ret[N] = bt;
return ret;
}
template <std::size_t N>
constexpr bool operator&(BarcodeFormat lhs, const BarcodeFormatArray<N>& rhs)
{
return std::any_of(rhs.begin(), rhs.end(), [lhs](BarcodeFormat bt) { return lhs & bt; });
}
/// @brief Parse a string into a BarcodeFormat. '-', '_', '/' and ' ' are optional.
/// @throws std::invalid_parameter if the string can not be fully parsed.
BarcodeFormat BarcodeFormatFromString(std::string_view str);
std::string ToString(BarcodeFormat format);
/**
* @brief A small container representing a collection of BarcodeFormat values.
*
* BarcodeFormats encapsulates an ordered collection of BarcodeFormat values and
* provides convenient construction, iteration, and set-like operations.
*
* General behavior and invariants:
* - The internal representation is kept in a canonical form (sorted, no duplicates)
* using normalize().
* - The API exposes read-only access and iteration.
*/
class BarcodeFormats
{
std::vector<BarcodeFormat> formats_;
void normalize();
public:
BarcodeFormats() = default;
BarcodeFormats(BarcodeFormat f) : formats_{f} {}
/// @brief Constructs a collection from a compile-time array of BarcodeFormat values.
/// ```c++
/// BarcodeFormats formats = BarcodeFormat::QRCode | BarcodeFormat::EAN13;
/// ```
template <std::size_t N>
BarcodeFormats(BarcodeFormatArray<N> formats) : formats_{formats.begin(), formats.end()}
{
normalize();
}
BarcodeFormats(std::vector<BarcodeFormat>&& formats) : formats_(std::move(formats)) { normalize(); }
/// Constructs a collection from a textual representation (e.g. a comma-separated list of format identifiers).
explicit BarcodeFormats(std::string_view str);
BarcodeFormats(const BarcodeFormats&) = default;
BarcodeFormats(BarcodeFormats&&) = default;
BarcodeFormats& operator=(const BarcodeFormats&) = default;
BarcodeFormats& operator=(BarcodeFormats&&) = default;
bool operator==(const BarcodeFormats& o) const noexcept { return formats_ == o.formats_; }
bool operator!=(const BarcodeFormats& o) const noexcept { return !(*this == o); }
// ~BarcodeFormats() = default;
auto begin() const noexcept { return formats_.cbegin(); }
auto end() const noexcept { return formats_.cend(); }
using value_type = typename std::vector<BarcodeFormat>::value_type;
using pointer = typename std::vector<BarcodeFormat>::pointer;
auto data() const noexcept { return formats_.data(); }
bool empty() const noexcept { return formats_.empty(); }
int size() const noexcept { return static_cast<int>(formats_.size()); }
// BarcodeFormats&& operator|(BarcodeFormat bt) &&;
// BarcodeFormats&& operator|(const BarcodeFormats& other) &&;
/// Returns a BarcodeFormats containing the intersection of this collection and other.
BarcodeFormats operator&(const BarcodeFormats& other);
/// Returns a list of available/supported barcode formats, optionally filtered by the provided formats.
/// e.g. `BarcodeFormats::list(BarcodeFormat::AllReadable);`
static BarcodeFormats list(const BarcodeFormats& filter = {});
/// @cond DEPRECATED
[[deprecated]] inline int count() const noexcept { return size(); }
[[deprecated]] inline bool testFlag(BarcodeFormat format) const noexcept
{
return std::any_of(begin(), end(), [format](BarcodeFormat f) { return f & format; });
}
[[deprecated]] inline bool testFlags(const BarcodeFormats& formats) const noexcept
{
return std::any_of(begin(), end(), [formats](BarcodeFormat fo) {
return std::any_of(formats.begin(), formats.end(), [fo](BarcodeFormat fi) { return fo & fi; });
});
}
/// @endcond
};
inline bool operator<=(BarcodeFormat lhs, const BarcodeFormats& rhs)
{
return std::any_of(rhs.begin(), rhs.end(), [lhs](BarcodeFormat bf) { return lhs <= bf; });
}
inline bool operator&(BarcodeFormat lhs, const BarcodeFormats& rhs)
{
return std::any_of(rhs.begin(), rhs.end(), [lhs](BarcodeFormat bf) { return lhs & bf; });
}
/// @brief Parses a string into a set of BarcodeFormats.
/// Separators can be (any combination of) '|' or ','. Input can be lower case and any of '-', '_', '/' or ' ' are optional,
/// e.g. "EAN-8 | qrcode, Itf" would be parsed into [EAN8, QRCode, ITF].
/// @throws std::invalid_parameter if the string can not be fully parsed.
BarcodeFormats BarcodeFormatsFromString(std::string_view str);
std::string ToString(const BarcodeFormats& formats);
} // namespace ZXing
#endif // __cplusplus

111
tools/chatgui/zxing-cpp/core/src/BinaryBitmap.cpp

@ -0,0 +1,111 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "BinaryBitmap.h"
#include "BitMatrix.h"
namespace ZXing {
struct BinaryBitmap::Cache
{
std::shared_ptr<const BitMatrix> matrix, transposed;
};
BitMatrix BinaryBitmap::binarize(const uint8_t threshold) const
{
BitMatrix res(width(), height());
if (_buffer.pixStride() == 1 && _buffer.rowStride() == _buffer.width()) {
// Specialize for a packed buffer with pixStride 1 to support auto vectorization (16x speedup on AVX2)
auto dst = res.row(0).begin();
for (auto src = _buffer.data(0, 0), end = _buffer.data(0, height()); src != end; ++src, ++dst)
*dst = (*src <= threshold) * BitMatrix::SET_V;
} else {
auto processLine = [&res, threshold](int y, const auto* src, const int stride) {
for (auto& dst : res.row(y)) {
dst = (*src <= threshold) * BitMatrix::SET_V;
src += stride;
}
};
for (int y = 0; y < res.height(); ++y) {
auto src = _buffer.data(0, y) + GreenIndex(_buffer.format());
// Specialize the inner loop for strides 1 and 4 to support auto vectorization
switch (_buffer.pixStride()) {
case 1: processLine(y, src, 1); break;
case 4: processLine(y, src, 4); break;
default: processLine(y, src, _buffer.pixStride()); break;
}
}
}
return res;
}
BinaryBitmap::BinaryBitmap(const ImageView& buffer) : _cache(new Cache), _buffer(buffer) {}
BinaryBitmap::~BinaryBitmap() = default;
const BitMatrix* BinaryBitmap::getBitMatrix(bool transposed) const
{
if (!_cache->matrix) {
_cache->matrix = getBlackMatrix();
}
if (transposed) {
if (!_cache->transposed) {
auto copy = std::make_shared<BitMatrix>(_cache->matrix->copy());
copy->rotate90();
_cache->transposed = std::move(copy);
}
return _cache->transposed.get();
}
return _cache->matrix.get();
}
void BinaryBitmap::invert()
{
if (_cache->matrix)
const_cast<BitMatrix*>(_cache->matrix.get())->flipAll();
if (_cache->transposed)
const_cast<BitMatrix*>(_cache->transposed.get())->flipAll();
_inverted = !_inverted;
}
template <typename F>
void SumFilter(const BitMatrix& in, BitMatrix& out, F func)
{
assert(in.height() >= 3);
const auto* in0 = in.row(0).begin();
const auto* in1 = in.row(1).begin();
const auto* in2 = in.row(2).begin();
for (auto *out1 = out.row(1).begin() + 1, *end = out.row(out.height() - 1).begin() - 1; out1 != end; ++in0, ++in1, ++in2, ++out1) {
int sum = 0;
for (int j = 0; j < 3; ++j)
sum += in0[j] + in1[j] + in2[j];
*out1 = func(sum);
}
}
void BinaryBitmap::close()
{
if (_cache->matrix) {
auto& matrix = *const_cast<BitMatrix*>(_cache->matrix.get());
BitMatrix tmp(matrix.width(), matrix.height());
// dilate
SumFilter(matrix, tmp, [](int sum) { return (sum > 0 * BitMatrix::SET_V) * BitMatrix::SET_V; });
// erode
SumFilter(tmp, matrix, [](int sum) { return (sum == 9 * BitMatrix::SET_V) * BitMatrix::SET_V; });
}
_cache->transposed.reset();
_closed = true;
}
} // ZXing

66
tools/chatgui/zxing-cpp/core/src/BinaryBitmap.h

@ -0,0 +1,66 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2021 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ImageView.h"
#include <cstdint>
#include <memory>
#include <vector>
namespace ZXing {
class BitMatrix;
using PatternRow = std::vector<uint16_t>;
/**
* This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
* accept a BinaryBitmap and attempt to decode it.
*/
class BinaryBitmap
{
struct Cache;
std::unique_ptr<Cache> _cache;
bool _inverted = false;
bool _closed = false;
protected:
const ImageView _buffer;
/**
* Converts a 2D array of luminance data to 1 bit (true means black).
*
* @return The 2D array of bits for the image, nullptr on error.
*/
virtual std::shared_ptr<const BitMatrix> getBlackMatrix() const = 0;
BitMatrix binarize(uint8_t threshold) const;
public:
BinaryBitmap(const ImageView& buffer);
virtual ~BinaryBitmap();
int width() const { return _buffer.width(); }
int height() const { return _buffer.height(); }
/**
* Converts one row of luminance data to a vector of ints denoting the widths of the bars and spaces.
*/
virtual bool getPatternRow(int row, int rotation, PatternRow& res) const = 0;
const BitMatrix* getBitMatrix(bool transposed = false) const;
void invert();
bool inverted() const { return _inverted; }
void close();
bool closed() const { return _closed; }
};
} // ZXing

38
tools/chatgui/zxing-cpp/core/src/BitArray.cpp

@ -0,0 +1,38 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "BitArray.h"
#include "ByteArray.h"
#include <cstddef>
#include <stdexcept>
namespace ZXing {
void
BitArray::bitwiseXOR(const BitArray& other)
{
if (size() != other.size()) {
throw std::invalid_argument("BitArray::xor(): Sizes don't match");
}
for (size_t i = 0; i < _bits.size(); i++) {
// The last int could be incomplete (i.e. not have 32 bits in
// it) but there is no problem since 0 XOR 0 == 0.
_bits[i] ^= other._bits[i];
}
}
ByteArray BitArray::toBytes(int bitOffset, int numBytes) const
{
ByteArray res(numBytes == -1 ? (size() - bitOffset + 7) / 8 : numBytes);
for (int i = 0; i < Size(res); i++)
for (int j = 0; j < 8; j++)
AppendBit(res[i], (numBytes != -1 || bitOffset < size()) ? get(bitOffset++) : false);
return res;
}
} // ZXing

201
tools/chatgui/zxing-cpp/core/src/BitArray.h

@ -0,0 +1,201 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2017 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Range.h"
#include "ZXAlgorithms.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include <vector>
namespace ZXing {
class ByteArray;
/**
* A simple, fast array of bits.
*/
class BitArray
{
std::vector<uint8_t> _bits;
friend class BitMatrix;
// Nothing wrong to support it, just to make it explicit, instead of by mistake.
// Use copy() below.
BitArray(const BitArray &) = default;
public:
using Iterator = std::vector<uint8_t>::const_iterator;
BitArray() = default;
explicit BitArray(int size) : _bits(size, 0) {}
BitArray(BitArray&& other) noexcept = default;
BitArray& operator=(BitArray&& other) noexcept = default;
BitArray& operator=(const BitArray &) = delete;
BitArray copy() const { return *this; }
int size() const noexcept { return Size(_bits); }
int sizeInBytes() const noexcept { return (size() + 7) / 8; }
bool get(int i) const { return _bits.at(i) != 0; }
void set(int i, bool val) { _bits.at(i) = val; }
// If you know exactly how may bits you are going to iterate
// and that you access bit in sequence, iterator is faster than get().
// However, be extremely careful since there is no check whatsoever.
// (Performance is the reason for the iterator to exist in the first place.)
Iterator iterAt(int i) const noexcept { return {_bits.cbegin() + i}; }
Iterator begin() const noexcept { return _bits.cbegin(); }
Iterator end() const noexcept { return _bits.cend(); }
/**
* Appends the least-significant bits, from value, in order from most-significant to
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
* 0, 1, 1, 1, 1, 0 in that order.
*
* @param value {@code int} containing bits to append
* @param numBits bits from value to append
*/
void appendBits(int value, int numBits)
{
assert(numBits >= 1 && numBits <= 32);
for (; numBits; --numBits)
_bits.push_back((value >> (numBits-1)) & 1);
}
void appendBits(uint32_t value, int numBits)
{
appendBits(static_cast<int>(value), numBits);
}
void appendBit(bool bit) { _bits.push_back(bit); }
void appendBitArray(const BitArray& other) { _bits.insert(_bits.end(), other.begin(), other.end()); }
/**
* Reverses all bits in the array.
*/
void reverse() { std::reverse(_bits.begin(), _bits.end()); }
void bitwiseXOR(const BitArray& other);
/**
* @param bitOffset first bit to extract
* @param numBytes how many bytes to extract (-1 == until the end, padded with '0')
* @return Bytes are written most-significant bit first.
*/
ByteArray toBytes(int bitOffset = 0, int numBytes = -1) const;
using Range = ZXing::Range<Iterator>;
Range range() const { return {begin(), end()}; }
friend bool operator==(const BitArray& a, const BitArray& b) { return a._bits == b._bits; }
};
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
constexpr T& AppendBit(T& val, bool bit)
{
return (val <<= 1) |= static_cast<T>(bit);
}
template <typename ARRAY, typename = std::enable_if_t<std::is_integral_v<typename ARRAY::value_type>>>
constexpr int ToInt(const ARRAY& a)
{
assert(Reduce(a) <= 32);
int pattern = 0;
for (int i = 0; i < Size(a); i++) {
if (a[i] < 0) // negative shift is undefined behavior
return -1;
pattern = (pattern << a[i]) | ~(0xffffffff << a[i]) * (~i & 1);
}
return pattern;
}
template <typename T = int, typename = std::enable_if_t<std::is_integral_v<T>>>
T ToInt(const BitArray& bits, int pos = 0, int count = 8 * sizeof(T))
{
assert(0 <= count && count <= 8 * (int)sizeof(T));
assert(0 <= pos && pos + count <= bits.size());
count = std::min(count, bits.size());
int res = 0;
auto it = bits.iterAt(pos);
for (int i = 0; i < count; ++i, ++it)
AppendBit(res, *it);
return res;
}
template <typename T = int, typename = std::enable_if_t<std::is_integral_v<T>>>
std::vector<T> ToInts(const BitArray& bits, int wordSize, int totalWords, int offset = 0)
{
assert(totalWords >= bits.size() / wordSize);
assert(wordSize <= 8 * (int)sizeof(T));
std::vector<T> res(totalWords, 0);
for (int i = offset; i < bits.size(); i += wordSize)
res[(i - offset) / wordSize] = ToInt(bits, i, wordSize);
return res;
}
class BitArrayView
{
const BitArray& bits;
BitArray::Iterator cur;
public:
BitArrayView(const BitArray& bits) : bits(bits), cur(bits.begin()) {}
BitArrayView& skipBits(int n)
{
if (cur + n > bits.end())
throw std::out_of_range("BitArrayView::skipBits() out of range.");
cur += n;
return *this;
}
int peekBits(int n) const
{
assert(n <= 32);
if (cur + n > bits.end())
throw std::out_of_range("BitArrayView::peekBits() out of range.");
int res = 0;
for (auto i = cur; n > 0; --n, i++)
AppendBit(res, *i);
return res;
}
int readBits(int n)
{
int res = peekBits(n);
cur += n;
return res;
}
int size() const
{
return narrow_cast<int>(bits.end() - cur);
}
explicit operator bool() const { return size(); }
};
} // ZXing

175
tools/chatgui/zxing-cpp/core/src/BitMatrix.cpp

@ -0,0 +1,175 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "BitMatrix.h"
#include "Pattern.h"
#include <algorithm>
#include <stdexcept>
#include <utility>
namespace ZXing {
void
BitMatrix::setRegion(int left, int top, int width, int height)
{
if (top < 0 || left < 0) {
throw std::invalid_argument("BitMatrix::setRegion(): Left and top must be nonnegative");
}
if (height < 1 || width < 1) {
throw std::invalid_argument("BitMatrix::setRegion(): Height and width must be at least 1");
}
int right = left + width;
int bottom = top + height;
if (bottom > _height || right > _width) {
throw std::invalid_argument("BitMatrix::setRegion(): The region must fit inside the matrix");
}
for (int y = top; y < bottom; y++) {
auto offset = y * _width;
for (int x = left; x < right; x++) {
_bits[offset + x] = SET_V;
}
}
}
void
BitMatrix::rotate90()
{
BitMatrix result(height(), width());
for (int x = 0; x < width(); ++x) {
for (int y = 0; y < height(); ++y) {
if (get(x, y)) {
result.set(y, width() - x - 1);
}
}
}
*this = std::move(result);
}
void
BitMatrix::rotate180()
{
std::reverse(_bits.begin(), _bits.end());
}
void
BitMatrix::mirror()
{
for (int x = 0; x < _width; x++) {
for (int y = x + 1; y < _height; y++) {
if (get(x, y) != get(y, x)) {
flip(y, x);
flip(x, y);
}
}
}
}
bool
BitMatrix::findBoundingBox(int &left, int& top, int& width, int& height, int minSize) const
{
int right, bottom;
if (!getTopLeftOnBit(left, top) || !getBottomRightOnBit(right, bottom) || bottom - top + 1 < minSize)
return false;
for (int y = top; y <= bottom; y++ ) {
for (int x = 0; x < left; ++x)
if (get(x, y)) {
left = x;
break;
}
for (int x = _width-1; x > right; x--)
if (get(x, y)) {
right = x;
break;
}
}
width = right - left + 1;
height = bottom - top + 1;
return width >= minSize && height >= minSize;
}
static auto isSet = [](auto v) { return bool(v); };
bool
BitMatrix::getTopLeftOnBit(int& left, int& top) const
{
int bitsOffset = (int)std::distance(_bits.begin(), std::find_if(_bits.begin(), _bits.end(), isSet));
if (bitsOffset == Size(_bits)) {
return false;
}
top = bitsOffset / _width;
left = (bitsOffset % _width);
return true;
}
bool
BitMatrix::getBottomRightOnBit(int& right, int& bottom) const
{
int bitsOffset = Size(_bits) - 1 - (int)std::distance(_bits.rbegin(), std::find_if(_bits.rbegin(), _bits.rend(), isSet));
if (bitsOffset < 0) {
return false;
}
bottom = bitsOffset / _width;
right = (bitsOffset % _width);
return true;
}
void GetPatternRow(const BitMatrix& matrix, int r, std::vector<uint16_t>& pr, bool transpose)
{
if (transpose)
GetPatternRow(matrix.col(r), pr);
else
GetPatternRow(matrix.row(r), pr);
}
BitMatrix Inflate(BitMatrix&& input, int width, int height, int quietZone)
{
const int codeWidth = input.width();
const int codeHeight = input.height();
const int outputWidth = std::max(width, codeWidth + 2 * quietZone);
const int outputHeight = std::max(height, codeHeight + 2 * quietZone);
if (input.width() == outputWidth && input.height() == outputHeight)
return std::move(input);
const int scale = std::min((outputWidth - 2*quietZone) / codeWidth, (outputHeight - 2*quietZone) / codeHeight);
// Padding includes both the quiet zone and the extra white pixels to
// accommodate the requested dimensions.
const int leftPadding = (outputWidth - (codeWidth * scale)) / 2;
const int topPadding = (outputHeight - (codeHeight * scale)) / 2;
BitMatrix result(outputWidth, outputHeight);
for (int inputY = 0, outputY = topPadding; inputY < input.height(); ++inputY, outputY += scale) {
for (int inputX = 0, outputX = leftPadding; inputX < input.width(); ++inputX, outputX += scale) {
if (input.get(inputX, inputY))
result.setRegion(outputX, outputY, scale, scale);
}
}
return result;
}
BitMatrix Deflate(const BitMatrix& input, int width, int height, float top, float left, float subSampling)
{
BitMatrix result(width, height);
for (int y = 0; y < result.height(); y++) {
auto yOffset = top + y * subSampling;
for (int x = 0; x < result.width(); x++) {
if (input.get(PointF(left + x * subSampling, yOffset)))
result.set(x, y);
}
}
return result;
}
} // ZXing

199
tools/chatgui/zxing-cpp/core/src/BitMatrix.h

@ -0,0 +1,199 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Matrix.h"
#include "Point.h"
#ifdef ZXING_INTERNAL
#include "Range.h"
#endif
#include <cstdint>
#include <stdexcept>
#include <vector>
namespace ZXing {
class BitArray;
/**
* @brief A simple, fast 2D array of bits.
*/
class BitMatrix
{
int _width = 0;
int _height = 0;
using data_t = uint8_t;
std::vector<data_t> _bits;
// There is nothing wrong to support this but disable to make it explicit since we may copy something very big here.
// Use copy() below.
BitMatrix(const BitMatrix&) = default;
const data_t& get(int i) const
{
#if 1
return _bits.at(i);
#else
return _bits[i];
#endif
}
data_t& get(int i) { return const_cast<data_t&>(static_cast<const BitMatrix*>(this)->get(i)); }
bool getTopLeftOnBit(int &left, int& top) const;
bool getBottomRightOnBit(int &right, int& bottom) const;
public:
static constexpr data_t SET_V = 0xff; // allows playing with SIMD binarization
static constexpr data_t UNSET_V = 0;
static_assert(bool(SET_V) && !bool(UNSET_V), "SET_V needs to evaluate to true, UNSET_V to false, see iterator usage");
BitMatrix() = default;
#if defined(__llvm__) || (defined(__GNUC__) && (__GNUC__ > 7))
__attribute__((no_sanitize("signed-integer-overflow")))
#endif
BitMatrix(int width, int height) : _width(width), _height(height), _bits(width * height, UNSET_V)
{
if (width != 0 && int(_bits.size()) / width != height)
throw std::invalid_argument("Invalid size: width * height is too big");
}
explicit BitMatrix(int dimension) : BitMatrix(dimension, dimension) {} // Construct a square matrix.
BitMatrix(BitMatrix&& other) noexcept = default;
BitMatrix& operator=(BitMatrix&& other) noexcept = default;
BitMatrix& operator=(const BitMatrix&) = delete;
BitMatrix copy() const { return *this; }
#ifdef ZXING_INTERNAL
Range<data_t*> row(int y) { return {_bits.data() + y * _width, _bits.data() + (y + 1) * _width}; }
Range<const data_t*> row(int y) const { return {_bits.data() + y * _width, _bits.data() + (y + 1) * _width}; }
Range<StrideIter<const data_t*>> col(int x) const
{
return {{_bits.data() + x + (_height - 1) * _width, -_width}, {_bits.data() + x - _width, -_width}};
}
#endif
bool get(int x, int y) const { return get(y * _width + x); }
void set(int x, int y, bool val = true) { get(y * _width + x) = val * SET_V; }
/**
* <p>Flips the given bit.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
*/
void flip(int x, int y)
{
auto& v = get(y * _width + x);
v = !v;
}
void flipAll()
{
for (auto& i : _bits)
i = !i * SET_V;
}
/**
* <p>Sets a square region of the bit matrix to true.</p>
*
* @param left The horizontal position to begin at (inclusive)
* @param top The vertical position to begin at (inclusive)
* @param width The width of the region
* @param height The height of the region
*/
void setRegion(int left, int top, int width, int height);
void rotate90();
void rotate180();
void mirror();
/**
* Find the rectangle that contains all non-white pixels. Useful for detection of 'pure' barcodes.
*
* @return True iff this rectangle is at least minWidth x minHeight pixels big
*/
bool findBoundingBox(int &left, int& top, int& width, int& height, int minSize = 1) const;
int width() const { return _width; }
int height() const { return _height; }
bool empty() const { return _bits.empty(); }
friend bool operator==(const BitMatrix& a, const BitMatrix& b)
{
return a._width == b._width && a._height == b._height && a._bits == b._bits;
}
template <typename T>
bool isIn(PointT<T> p, int b = 0) const noexcept
{
return b <= p.x && p.x < width() - b && b <= p.y && p.y < height() - b;
}
bool get(PointI p) const { return get(p.x, p.y); }
bool get(PointF p) const { return get(PointI(p)); }
void set(PointI p, bool v = true) { set(p.x, p.y, v); }
void set(PointF p, bool v = true) { set(PointI(p), v); }
};
void GetPatternRow(const BitMatrix& matrix, int r, std::vector<uint16_t>& pr, bool transpose);
/**
* @brief Inflate scales a BitMatrix up and adds a quiet Zone plus padding
* @param input matrix to be expanded
* @param width new width in bits (pixel)
* @param height new height in bits (pixel)
* @param quietZone size of quiet zone to add in modules
* @return expanded BitMatrix, maybe move(input) if size did not change
*/
BitMatrix Inflate(BitMatrix&& input, int width, int height, int quietZone);
/**
* @brief Deflate (crop + subsample) a bit matrix
* @param input matrix to be shrunk
* @param width new width
* @param height new height
* @param top cropping starts at top row
* @param left cropping starts at left col
* @param subSampling typically the module size
* @return deflated input
*/
BitMatrix Deflate(const BitMatrix& input, int width, int height, float top, float left, float subSampling);
template<typename T>
BitMatrix ToBitMatrix(const Matrix<T>& in, T trueValue = {true})
{
BitMatrix out(in.width(), in.height());
for (int y = 0; y < in.height(); ++y)
for (int x = 0; x < in.width(); ++x)
if (in.get(x, y) == trueValue)
out.set(x, y);
return out;
}
template<typename T>
Matrix<T> ToMatrix(const BitMatrix& in, T black = 0, T white = ~0)
{
Matrix<T> res(in.width(), in.height());
for (int y = 0; y < in.height(); ++y)
for (int x = 0; x < in.width(); ++x)
res.set(x, y, in.get(x, y) ? black : white);
return res;
}
} // ZXing

245
tools/chatgui/zxing-cpp/core/src/BitMatrixCursor.h

@ -0,0 +1,245 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BitMatrix.h"
#include "Point.h"
#include "StdGenerator.h"
#include <climits>
namespace ZXing {
enum class Direction { LEFT = -1, RIGHT = 1 };
inline Direction opposite(Direction dir) noexcept
{
return dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT;
}
/**
* @brief The BitMatrixCursor represents a current position inside an image and current direction it can advance towards.
*
* The current position and direction is a PointT<T>. So depending on the type it can be used to traverse the image
* in a Bresenham style (PointF) or in a discrete way (step only horizontal/vertical/diagonal (PointI)).
*/
template<typename POINT>
class BitMatrixCursor
{
using this_t = BitMatrixCursor<POINT>;
public:
const BitMatrix* img;
POINT p; // current position
POINT d; // current direction
BitMatrixCursor(const BitMatrix& image, POINT p, POINT d) : img(&image), p(p) { setDirection(d); }
class Value
{
enum { INVALID = -1, WHITE = 0, BLACK = 1 };
int v = INVALID;
public:
Value() = default;
Value(bool isBlack) : v(isBlack) {}
bool isValid() const noexcept { return v != INVALID; }
bool isWhite() const noexcept { return v == WHITE; }
bool isBlack() const noexcept { return v == BLACK; }
operator bool() const noexcept { return isValid(); }
bool operator==(Value o) const { return v == o.v; }
};
template <typename T>
Value testAt(PointT<T> p) const
{
return img->isIn(p) ? Value{img->get(p)} : Value{};
}
bool blackAt(POINT pos) const noexcept { return testAt(pos).isBlack(); }
bool whiteAt(POINT pos) const noexcept { return testAt(pos).isWhite(); }
bool isIn(POINT p) const noexcept { return img->isIn(p); }
bool isIn() const noexcept { return isIn(p); }
bool isBlack() const noexcept { return blackAt(p); }
bool isWhite() const noexcept { return whiteAt(p); }
POINT front() const noexcept { return d; }
POINT back() const noexcept { return {-d.x, -d.y}; }
POINT left() const noexcept { return {d.y, -d.x}; }
POINT right() const noexcept { return {-d.y, d.x}; }
POINT direction(Direction dir) const noexcept { return static_cast<int>(dir) * right(); }
void turnBack() noexcept { d = back(); }
void turnLeft() noexcept { d = left(); }
void turnRight() noexcept { d = right(); }
void turn(Direction dir) noexcept { d = direction(dir); }
Value edgeAt(POINT d) const noexcept
{
Value v = testAt(p);
return testAt(p + d) != v ? v : Value();
}
Value edgeAtFront() const noexcept { return edgeAt(front()); }
Value edgeAtBack() const noexcept { return edgeAt(back()); }
Value edgeAtLeft() const noexcept { return edgeAt(left()); }
Value edgeAtRight() const noexcept { return edgeAt(right()); }
Value edgeAt(Direction dir) const noexcept { return edgeAt(direction(dir)); }
this_t& setDirection(PointF dir) { return d = bresenhamDirection(dir), *this; }
this_t& setDirection(PointI dir) { return d = dir, *this; }
bool step(typename POINT::value_t s = 1)
{
p += s * d;
return isIn(p);
}
this_t movedBy(POINT o) const noexcept { return {*img, p + o, d}; }
this_t turnedBack() const noexcept { return {*img, p, back()}; }
/**
* @brief stepToEdge advances cursor to one step behind the next (or n-th) edge.
* @param nth number of edges to pass
* @param range max number of steps to take
* @param backup whether or not to backup one step so we land in front of the edge
* @return number of steps taken or 0 if moved outside of range/image
*/
int stepToEdge(int nth = 1, int range = 0, bool backup = false)
{
int steps = 0;
auto lv = testAt(p);
while (nth && (!range || steps < range) && lv.isValid()) {
++steps;
auto v = testAt(p + steps * d);
if (lv != v) {
lv = v;
--nth;
}
}
if (backup)
--steps;
p += steps * d;
return steps * (nth == 0);
}
bool stepAlongEdge(Direction dir, bool skipCorner = false)
{
if (!edgeAt(dir))
turn(dir);
else if (edgeAtFront()) {
turn(opposite(dir));
if (edgeAtFront()) {
turn(opposite(dir));
if (edgeAtFront())
return false;
}
}
bool ret = step();
if (ret && skipCorner && !edgeAt(dir)) {
turn(dir);
ret = step();
}
return ret;
}
int countEdges(int range)
{
int res = 0;
while (int steps = range ? stepToEdge(1, range) : 0) {
range -= steps;
++res;
}
return res;
}
template<typename ARRAY>
ARRAY readPattern(int range = 0)
{
ARRAY res = {};
for (auto& i : res) {
i = stepToEdge(1, range);
if (!i)
return res;
if (range)
range -= i;
}
return res;
}
template<typename ARRAY>
ARRAY readPatternFromBlack(int maxWhitePrefix, int range = 0)
{
if (maxWhitePrefix && isWhite() && !stepToEdge(1, maxWhitePrefix))
return {};
return readPattern<ARRAY>(range);
}
};
using BitMatrixCursorF = BitMatrixCursor<PointF>;
using BitMatrixCursorI = BitMatrixCursor<PointI>;
class FastEdgeToEdgeCounter
{
const uint8_t* p = nullptr;
int stride = 0;
int stepsToBorder = 0;
public:
FastEdgeToEdgeCounter(const BitMatrixCursorI& cur)
{
stride = cur.d.y * cur.img->width() + cur.d.x;
p = cur.img->row(cur.p.y).begin() + cur.p.x;
int maxStepsX = cur.d.x ? (cur.d.x > 0 ? cur.img->width() - 1 - cur.p.x : cur.p.x) : INT_MAX;
int maxStepsY = cur.d.y ? (cur.d.y > 0 ? cur.img->height() - 1 - cur.p.y : cur.p.y) : INT_MAX;
stepsToBorder = std::min(maxStepsX, maxStepsY);
}
int stepToNextEdge(int range)
{
int maxSteps = std::min(stepsToBorder, range);
int steps = 0;
do {
if (++steps > maxSteps) {
if (maxSteps == stepsToBorder)
break;
else
return 0;
}
} while (p[steps * stride] == p[0]);
p += steps * stride;
stepsToBorder -= steps;
return steps;
}
};
// Generate points in a spiral pattern around the center
inline std::generator<PointI> Spiral(int radius)
{
co_yield{0, 0};
for (int r = 1; r <= radius; ++r) {
// clang-format off
for (int k = 0; k < 2 * r; ++k) co_yield{ r, -(r - 1) + k}; // right -> down
for (int k = 0; k < 2 * r; ++k) co_yield{ (r - 1) - k, r}; // bottom -> left
for (int k = 0; k < 2 * r; ++k) co_yield{ -r, (r - 1) - k}; // left -> up
for (int k = 0; k < 2 * r; ++k) co_yield{-(r - 1) + k, -r}; // top -> right
// clang-format on
}
}
} // ZXing

101
tools/chatgui/zxing-cpp/core/src/BitMatrixIO.cpp

@ -0,0 +1,101 @@
/*
* Copyright 2017 Huy Cuong Nguyen
* Copyright 2017 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "BitMatrixIO.h"
#include <array>
#include <fstream>
#include <sstream>
namespace ZXing {
std::string ToString(const BitMatrix& matrix, char one, char zero, bool addSpace, bool printAsCString)
{
std::string result;
result.reserve((addSpace ? 2 : 1) * (matrix.width() * matrix.height()) + matrix.height());
for (int y = 0; y < matrix.height(); ++y) {
if (printAsCString)
result += '"';
for (auto bit : matrix.row(y)) {
result += bit ? one : zero;
if (addSpace)
result += ' ';
}
if (printAsCString)
result += "\\n\"";
result += '\n';
}
return result;
}
std::string ToString(const BitMatrix& matrix, bool inverted)
{
constexpr auto map = std::array{" ", "▀", "▄", "█"};
std::string res;
for (int y = 0; y < matrix.height(); y += 2) {
for (int x = 0; x < matrix.width(); ++x) {
int tp = matrix.get(x, y) ^ inverted;
int bt = (matrix.height() == 1 && tp) || (y + 1 < matrix.height() && (matrix.get(x, y + 1) ^ inverted));
res += map[tp | (bt << 1)];
}
res.push_back('\n');
}
return res;
}
std::string ToSVG(const BitMatrix& matrix)
{
// see https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350
const int width = matrix.width();
const int height = matrix.height();
std::ostringstream out;
out << R"(<?xml version="1.0" encoding="UTF-8"?>)" << "\n"
<< R"(<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 )" << width << ' ' << height << R"(" stroke="none">)"
<< "\n<path d=\"";
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
if (matrix.get(x, y))
out << "M" << x << "," << y << "h1v1h-1z";
out << "\"/>\n</svg>";
return out.str();
}
BitMatrix ParseBitMatrix(const std::string& str, char one, bool expectSpace)
{
auto lineLength = str.find('\n');
if (lineLength == std::string::npos)
return {};
int strStride = expectSpace ? 2 : 1;
int height = narrow_cast<int>(str.length() / (lineLength + 1));
int width = narrow_cast<int>(lineLength / strStride);
BitMatrix mat(width, height);
for (int y = 0; y < height; ++y) {
size_t offset = y * (lineLength + 1);
for (int x = 0; x < width; ++x, offset += strStride) {
if (str[offset] == one)
mat.set(x, y);
}
}
return mat;
}
void SaveAsPBM(const BitMatrix& matrix, const std::string& filename, int quietZone)
{
auto out = ToMatrix<uint8_t>(Inflate(matrix.copy(), 0, 0, quietZone));
std::ofstream file(filename);
file << "P5\n" << out.width() << ' ' << out.height() << "\n255\n";
file.write(reinterpret_cast<const char*>(out.data()), out.size());
}
} // ZXing

21
tools/chatgui/zxing-cpp/core/src/BitMatrixIO.h

@ -0,0 +1,21 @@
/*
* Copyright 2017 Huy Cuong Nguyen
* Copyright 2017 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string>
#include "BitMatrix.h"
namespace ZXing {
std::string ToString(const BitMatrix& matrix, bool inverted = false);
std::string ToString(const BitMatrix& matrix, char one, char zero = ' ', bool addSpace = true, bool printAsCString = false);
std::string ToSVG(const BitMatrix& matrix);
BitMatrix ParseBitMatrix(const std::string& str, char one = 'X', bool expectSpace = true);
void SaveAsPBM(const BitMatrix& matrix, const std::string& filename, int quietZone = 0);
} // ZXing

77
tools/chatgui/zxing-cpp/core/src/BitSource.cpp

@ -0,0 +1,77 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "BitSource.h"
#include "ByteArray.h"
#include "ZXAlgorithms.h"
#include <stdexcept>
namespace ZXing {
int
BitSource::available() const
{
return 8 * (Size(_bytes) - _byteOffset) - _bitOffset;
}
static int ReadBitsImpl(int numBits, const ByteArray& _bytes, int available, int& _byteOffset, int& _bitOffset)
{
if (numBits < 1 || numBits > 32 || numBits > available) {
throw std::out_of_range("BitSource::readBits: out of range");
}
int result = 0;
// First, read remainder from current byte
if (_bitOffset > 0) {
int bitsLeft = 8 - _bitOffset;
int toRead = numBits < bitsLeft ? numBits : bitsLeft;
int bitsToNotRead = bitsLeft - toRead;
int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
result = (_bytes[_byteOffset] & mask) >> bitsToNotRead;
numBits -= toRead;
_bitOffset += toRead;
if (_bitOffset == 8) {
_bitOffset = 0;
_byteOffset++;
}
}
// Next read whole bytes
if (numBits > 0) {
while (numBits >= 8) {
result = (result << 8) | _bytes[_byteOffset];
_byteOffset++;
numBits -= 8;
}
// Finally read a partial byte
if (numBits > 0) {
int bitsToNotRead = 8 - numBits;
int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
result = (result << numBits) | ((_bytes[_byteOffset] & mask) >> bitsToNotRead);
_bitOffset += numBits;
}
}
return result;
}
int BitSource::readBits(int numBits)
{
return ReadBitsImpl(numBits, _bytes, available(), _byteOffset, _bitOffset);
}
int BitSource::peekBits(int numBits) const
{
int bitOffset = _bitOffset;
int byteOffset = _byteOffset;
return ReadBitsImpl(numBits, _bytes, available(), byteOffset, bitOffset);
}
} // ZXing

72
tools/chatgui/zxing-cpp/core/src/BitSource.h

@ -0,0 +1,72 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
namespace ZXing {
class ByteArray;
/**
* <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
* number of bits read is not often a multiple of 8.</p>
*
* <p>This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
* it passed in, in which case all bets are off.</p>
*
* @author Sean Owen
*/
class BitSource
{
const ByteArray& _bytes;
int _byteOffset = 0;
int _bitOffset = 0;
public:
/**
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
* Bits are read within a byte from most-significant to least-significant bit.
* IMPORTANT: Bit source DOES NOT copy data byte, thus make sure that the bytes outlive the bit source object.
*/
explicit BitSource(const ByteArray& bytes) : _bytes(bytes) {}
BitSource(BitSource &) = delete;
BitSource& operator=(const BitSource &) = delete;
/**
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
*/
int bitOffset() const {
return _bitOffset;
}
/**
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
*/
int byteOffset() const {
return _byteOffset;
}
/**
* @param numBits number of bits to read
* @return int representing the bits read. The bits will appear as the least-significant bits of the int
*/
int readBits(int numBits);
/**
* @param numBits number of bits to peek at
* @return int representing the bits peeked at. The bits will appear as the least-significant
* bits of the int
*/
int peekBits(int numBits) const;
/**
* @return number of bits that can be read successfully
*/
int available() const;
};
} // ZXing

42
tools/chatgui/zxing-cpp/core/src/ByteArray.h

@ -0,0 +1,42 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Range.h"
#include <cstdint>
#include <string_view>
#include <vector>
namespace ZXing {
/**
ByteArray is an extension of std::vector<uint8_t>.
*/
class ByteArray : public std::vector<uint8_t>
{
public:
ByteArray() = default;
ByteArray(std::initializer_list<uint8_t> list) : std::vector<uint8_t>(list) {}
explicit ByteArray(int len) : std::vector<uint8_t>(len, 0) {}
explicit ByteArray(std::string_view str) : std::vector<uint8_t>(str.begin(), str.end()) {}
void append(ByteView other) { insert(end(), other.begin(), other.end()); }
void append(std::string_view other) { insert(end(), other.begin(), other.end()); }
std::string_view asString(size_t pos = 0, size_t len = std::string_view::npos) const
{
return std::string_view(reinterpret_cast<const char*>(data()), size()).substr(pos, len);
}
ByteView asView(size_t pos = 0, size_t len = size_t(-1)) const
{
return ByteView(*this).subview(pos, len);
}
};
} // ZXing

78
tools/chatgui/zxing-cpp/core/src/CharacterSet.cpp

@ -0,0 +1,78 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "CharacterSet.h"
#include "ZXAlgorithms.h"
#include <algorithm>
namespace ZXing {
struct CharacterSetName
{
std::string_view name;
CharacterSet cs;
};
static CharacterSetName NAME_TO_CHARSET[] = {
{"Cp437", CharacterSet::Cp437},
{"ISO-8859-1", CharacterSet::ISO8859_1},
{"ISO-8859-2", CharacterSet::ISO8859_2},
{"ISO-8859-3", CharacterSet::ISO8859_3},
{"ISO-8859-4", CharacterSet::ISO8859_4},
{"ISO-8859-5", CharacterSet::ISO8859_5},
{"ISO-8859-6", CharacterSet::ISO8859_6},
{"ISO-8859-7", CharacterSet::ISO8859_7},
{"ISO-8859-8", CharacterSet::ISO8859_8},
{"ISO-8859-9", CharacterSet::ISO8859_9},
{"ISO-8859-10", CharacterSet::ISO8859_10},
{"ISO-8859-11", CharacterSet::ISO8859_11},
{"ISO-8859-13", CharacterSet::ISO8859_13},
{"ISO-8859-14", CharacterSet::ISO8859_14},
{"ISO-8859-15", CharacterSet::ISO8859_15},
{"ISO-8859-16", CharacterSet::ISO8859_16},
{"SJIS", CharacterSet::Shift_JIS},
{"Shift_JIS", CharacterSet::Shift_JIS},
{"Cp1250", CharacterSet::Cp1250},
{"windows-1250",CharacterSet::Cp1250},
{"Cp1251", CharacterSet::Cp1251},
{"windows-1251",CharacterSet::Cp1251},
{"Cp1252", CharacterSet::Cp1252},
{"windows-1252",CharacterSet::Cp1252},
{"Cp1256", CharacterSet::Cp1256},
{"windows-1256",CharacterSet::Cp1256},
{"UTF-16BE", CharacterSet::UTF16BE},
{"UTF-16LE", CharacterSet::UTF16LE},
{"UTF-32BE", CharacterSet::UTF32BE},
{"UTF-32LE", CharacterSet::UTF32LE},
{"UnicodeBigUnmarked", CharacterSet::UTF16BE},
{"UnicodeBig", CharacterSet::UTF16BE},
{"UTF-8", CharacterSet::UTF8},
{"ASCII", CharacterSet::ASCII},
{"US-ASCII", CharacterSet::ASCII},
{"Big5", CharacterSet::Big5},
{"GB2312", CharacterSet::GB2312},
{"GB18030", CharacterSet::GB18030},
{"EUC-CN", CharacterSet::GB18030},
{"GBK", CharacterSet::GB18030},
{"EUC-KR", CharacterSet::EUC_KR},
{"BINARY", CharacterSet::BINARY},
};
CharacterSet CharacterSetFromString(std::string_view name)
{
auto i = FindIf(NAME_TO_CHARSET, [name](auto& v) { return IsEqualIgnoreCaseAnd(v.name, name, "_- "); });
return i == std::end(NAME_TO_CHARSET) ? CharacterSet::Unknown : i->cs;
}
std::string ToString(CharacterSet cs)
{
auto i = FindIf(NAME_TO_CHARSET, [cs](auto& v) { return v.cs == cs; });
return i == std::end(NAME_TO_CHARSET) ? "" : std::string(i->name);
}
} // namespace ZXing

58
tools/chatgui/zxing-cpp/core/src/CharacterSet.h

@ -0,0 +1,58 @@
/*
* Copyright 2016 Nu-book Inc.
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string>
#include <string_view>
namespace ZXing {
enum class CharacterSet : unsigned char
{
Unknown,
ASCII,
ISO8859_1,
ISO8859_2,
ISO8859_3,
ISO8859_4,
ISO8859_5,
ISO8859_6,
ISO8859_7,
ISO8859_8,
ISO8859_9,
ISO8859_10,
ISO8859_11,
ISO8859_13,
ISO8859_14,
ISO8859_15,
ISO8859_16,
Cp437,
Cp1250,
Cp1251,
Cp1252,
Cp1256,
Shift_JIS,
Big5,
GB2312,
GB18030,
EUC_JP,
EUC_KR,
UTF16BE,
UTF8,
UTF16LE,
UTF32BE,
UTF32LE,
BINARY,
CharsetCount
};
CharacterSet CharacterSetFromString(std::string_view name);
std::string ToString(CharacterSet cs);
} // ZXing

270
tools/chatgui/zxing-cpp/core/src/ConcentricFinder.cpp

@ -0,0 +1,270 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "ConcentricFinder.h"
#include "LogMatrix.h"
#include "RegressionLine.h"
#include "ZXAlgorithms.h"
namespace ZXing {
std::optional<PointF> AverageEdgePixels(BitMatrixCursorI cur, int range, int numOfEdges)
{
PointF sum = {};
for (int i = 0; i < numOfEdges; ++i) {
if (!cur.isIn())
return {};
cur.stepToEdge(1, range);
sum += centered(cur.p) + centered(cur.p + cur.back());
log(cur.p + cur.back(), 2);
}
return sum / (2 * numOfEdges);
}
std::optional<PointF> CenterOfDoubleCross(const BitMatrix& image, PointI center, int range, int numOfEdges)
{
PointF sum = {};
for (auto d : {PointI{0, 1}, {1, 0}, {1, 1}, {1, -1}}) {
auto avr1 = AverageEdgePixels({image, center, d}, range, numOfEdges);
auto avr2 = AverageEdgePixels({image, center, -d}, range, numOfEdges);
if (!avr1 || !avr2)
return {};
sum += *avr1 + *avr2;
}
return sum / 8;
}
std::optional<PointF> CenterOfRing(const BitMatrix& image, PointI center, int range, int nth, bool requireCircle)
{
#if 0
if (requireCircle) {
// alternative implementation with the aim of discarding closed loops that are not all circle like (M > 5*m)
auto points = CollectRingPoints(image, center, range, std::abs(nth), nth < 0);
if (points.empty())
return {};
auto res = Reduce(points, PointF{}, std::plus{}) / Size(points);
double m = range, M = 0;
for (auto p : points)
UpdateMinMax(m, M, maxAbsComponent(p - res));
if (M > 5 * m)
return {};
return res;
}
#endif
// range is the approximate width/height of the nth ring, if nth>1 then it would be plausible to limit the search radius
// to approximately range / 2 * sqrt(2) == range * 0.75 but it turned out to be too limiting with realworld/noisy data.
int radius = range;
bool inner = nth < 0;
nth = std::abs(nth);
log(center, 3);
BitMatrixCursorI cur(image, center, {1, 0});
if (!cur.stepToEdge(nth, radius, inner))
return {};
cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup
const auto edgeDir = inner ? Direction::LEFT : Direction::RIGHT;
uint32_t neighbourMask = 0;
auto start = cur.p;
PointF sum = {};
int n = 0;
do {
log(cur.p, 4);
sum += centered(cur.p);
++n;
// find out if we come full circle around the center. 8 bits have to be set in the end.
neighbourMask |= (1 << (4 + dot(bresenhamDirection(cur.p - center), PointI(1, 3))));
if (!cur.stepAlongEdge(edgeDir))
return {};
// use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate
if (maxAbsComponent(cur.p - center) > radius || center == cur.p || n > 4 * 2 * range)
return {};
} while (cur.p != start);
if (requireCircle && neighbourMask != 0b111101111)
return {};
return sum / n;
}
std::optional<PointF> CenterOfRings(const BitMatrix& image, PointF center, int range, int numOfRings)
{
int n = 1;
PointF sum = center;
for (int i = 2; i < numOfRings + 1; ++i) {
auto c = CenterOfRing(image, PointI(center), range, i);
if (!c) {
if (n == 1)
return {};
else
return sum / n;
} else if (distance(*c, center) > range / numOfRings / 2) {
return {};
}
sum += *c;
n++;
}
return sum / n;
}
static std::vector<PointF> CollectRingPoints(const BitMatrix& image, PointF center, int range, int edgeIndex, bool backup)
{
PointI centerI(center);
int radius = range;
BitMatrixCursorI cur(image, centerI, {1, 0});
if (!cur.stepToEdge(edgeIndex, radius, backup))
return {};
cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup
const auto edgeDir = backup ? Direction::LEFT : Direction::RIGHT;
uint32_t neighbourMask = 0;
auto start = cur.p;
std::vector<PointF> points;
points.reserve(4 * range);
do {
log(cur.p, 4);
points.push_back(centered(cur.p));
// find out if we come full circle around the center. 8 bits have to be set in the end.
neighbourMask |= (1 << (4 + dot(bresenhamDirection(cur.p - centerI), PointI(1, 3))));
if (!cur.stepAlongEdge(edgeDir))
return {};
// use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate
if (maxAbsComponent(cur.p - centerI) > radius || centerI == cur.p || Size(points) > 4 * 2 * range)
return {};
} while (cur.p != start);
if (neighbourMask != 0b111101111)
return {};
return points;
}
static std::optional<QuadrilateralF> FitQadrilateralToPoints(PointF center, std::vector<PointF>& points)
{
auto dist2Center = [c = center](auto a, auto b) { return distance(a, c) < distance(b, c); };
auto [minDistElem, maxDistElem] = std::minmax_element(points.begin(), points.end(), dist2Center);
// check if points are on a circle: for a square the min/max ratio is 0.7, for a circle it is 1
if (distance(center, *minDistElem) / distance(center, *maxDistElem) > 0.85)
return {};
// rotate points such that the first one is the furthest away from the center (hence, a corner)
std::rotate(points.begin(), maxDistElem, points.end());
std::array<const PointF*, 4> corners;
corners[0] = &points[0];
// find the opposite corner by looking for the farthest point near the opposite point
corners[2] = std::max_element(&points[Size(points) * 3 / 8], &points[Size(points) * 5 / 8], dist2Center);
// find the two in between corners by looking for the points farthest from the long diagonal
auto dist2Diagonal = [l = RegressionLine(*corners[0], *corners[2])](auto a, auto b) { return l.distance(a) < l.distance(b); };
corners[1] = std::max_element(&points[Size(points) * 1 / 8], &points[Size(points) * 3 / 8], dist2Diagonal);
corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal);
std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]},
RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &points.back() + 1}};
if (std::any_of(lines.begin(), lines.end(), [](const auto& line) { return !line.isValid(); }))
return {};
std::array<const PointF*, 4> beg = {corners[0] + 1, corners[1] + 1, corners[2] + 1, corners[3] + 1};
std::array<const PointF*, 4> end = {corners[1], corners[2], corners[3], &points.back() + 1};
// check if all points belonging to each line segment are sufficiently close to that line
for (int i = 0; i < 4; ++i)
for (const PointF* p = beg[i]; p != end[i]; ++p) {
auto len = std::distance(beg[i], end[i]);
if (len > 3 && lines[i].distance(*p) > std::max(1., std::min(8., len / 8.))) {
#ifdef PRINT_DEBUG
printf("%d: %.2f > %.2f @ %.fx%.f\n", i, lines[i].distance(*p), std::distance(beg[i], end[i]) / 1., p->x, p->y);
#endif
return {};
}
}
QuadrilateralF res;
for (int i = 0; i < 4; ++i)
res[i] = intersect(lines[i], lines[(i + 1) % 4]);
return res;
}
static bool QuadrilateralIsPlausibleSquare(const QuadrilateralF q, int lineIndex)
{
double m, M;
m = M = distance(q[0], q[3]);
for (int i = 1; i < 4; ++i)
UpdateMinMax(m, M, distance(q[i - 1], q[i]));
return m >= lineIndex * 2 && m > M / 3;
}
std::optional<QuadrilateralF> FitSquareToPoints(const BitMatrix& image, PointF center, int range, int lineIndex, bool backup)
{
auto points = CollectRingPoints(image, center, range, lineIndex, backup);
if (points.empty())
return {};
auto res = FitQadrilateralToPoints(center, points);
if (!res || !QuadrilateralIsPlausibleSquare(*res, lineIndex - backup))
return {};
return res;
}
std::optional<QuadrilateralF> FindConcentricPatternCorners(const BitMatrix& image, PointF center, int range, int ringIndex)
{
auto innerCorners = FitSquareToPoints(image, center, range, ringIndex, false);
if (!innerCorners)
return {};
auto outerCorners = FitSquareToPoints(image, center, range, ringIndex + 1, true);
if (!outerCorners)
return {};
auto res = Blend(*innerCorners, *outerCorners);
for (auto p : *innerCorners)
log(p, 3);
for (auto p : *outerCorners)
log(p, 3);
for (auto p : res)
log(p, 3);
return res;
}
std::optional<PointF> FinetuneConcentricPatternCenter(const BitMatrix& image, PointF center, int range, int finderPatternSize)
{
// make sure we have at least one path of white around the center
if (auto res1 = CenterOfRing(image, PointI(center), range, 1); res1 && image.get(*res1)) {
// and then either at least one more ring around that
if (auto res2 = CenterOfRings(image, *res1, range, finderPatternSize / 2); res2 && image.get(*res2))
return res2;
// or the center can be approximated by a square
if (FitSquareToPoints(image, *res1, range, 1, false))
return res1;
// TODO: this is currently only keeping #258 alive, evaluate if still worth it
if (auto res2 = CenterOfDoubleCross(image, PointI(*res1), range, finderPatternSize / 2 + 1); res2 && image.get(*res2))
return res2;
}
return {};
}
} // ZXing

150
tools/chatgui/zxing-cpp/core/src/ConcentricFinder.h

@ -0,0 +1,150 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BitMatrixCursor.h"
#include "Pattern.h"
#include "Quadrilateral.h"
#include "ZXAlgorithms.h"
#include <optional>
namespace ZXing {
template <typename T, size_t N>
static float CenterFromEnd(const std::array<T, N>& pattern, float end)
{
if (N == 5) {
float a = pattern[4] + pattern[3] + pattern[2] / 2.f;
float b = pattern[4] + (pattern[3] + pattern[2] + pattern[1]) / 2.f;
float c = (pattern[4] + pattern[3] + pattern[2] + pattern[1] + pattern[0]) / 2.f;
return end - (2 * a + b + c) / 4;
} else if (N == 3) {
float a = pattern[2] + pattern[1] / 2.f;
float b = (pattern[2] + pattern[1] + pattern[0]) / 2.f;
return end - (2 * a + b) / 3;
} else { // aztec
auto a = Reduce(pattern.begin() + (N/2 + 1), pattern.end(), pattern[N/2] / 2.f);
return end - a;
}
}
template<int N, typename Cursor>
std::optional<Pattern<N>> ReadSymmetricPattern(Cursor& cur, int range)
{
static_assert(N % 2 == 1);
assert(range > 0);
Pattern<N> res = {};
auto constexpr s_2 = Size(res)/2;
auto cuo = cur.turnedBack();
auto next = [&](auto& cur, int i) {
auto v = cur.stepToEdge(1, range);
res[s_2 + i] += v;
if (range)
range -= v;
return v;
};
for (int i = 0; i <= s_2; ++i) {
if (!next(cur, i) || !next(cuo, -i))
return {};
}
res[s_2]--; // the starting pixel has been counted twice, fix this
return res;
}
template<bool RELAXED_THRESHOLD = false, typename PATTERN>
int CheckSymmetricPattern(BitMatrixCursorI& cur, PATTERN pattern, int range, bool updatePosition)
{
FastEdgeToEdgeCounter curFwd(cur), curBwd(cur.turnedBack());
int centerFwd = curFwd.stepToNextEdge(range);
if (!centerFwd)
return 0;
int centerBwd = curBwd.stepToNextEdge(range);
if (!centerBwd)
return 0;
assert(range > 0);
Pattern<pattern.size()> res = {};
auto constexpr s_2 = Size(res)/2;
res[s_2] = centerFwd + centerBwd - 1; // -1 because the starting pixel is counted twice
range -= res[s_2];
auto next = [&](auto& cur, int i) {
auto v = cur.stepToNextEdge(range);
res[s_2 + i] = v;
range -= v;
return v;
};
for (int i = 1; i <= s_2; ++i) {
if (!next(curFwd, i) || !next(curBwd, -i))
return 0;
}
if (!IsPattern<RELAXED_THRESHOLD>(res, pattern))
return 0;
if (updatePosition)
cur.step(res[s_2] / 2 - (centerBwd - 1));
return Reduce(res);
}
std::optional<PointF> CenterOfRing(const BitMatrix& image, PointI center, int range, int nth, bool requireCircle = true);
std::optional<PointF> FinetuneConcentricPatternCenter(const BitMatrix& image, PointF center, int range, int finderPatternSize);
std::optional<QuadrilateralF> FitSquareToPoints(const BitMatrix& image, PointF center, int range, int lineIndex, bool backup);
std::optional<QuadrilateralF> FindConcentricPatternCorners(const BitMatrix& image, PointF center, int range, int ringIndex);
struct ConcentricPattern : public PointF
{
int size = 0;
};
template <bool E2E = false, typename PATTERN>
std::optional<ConcentricPattern> LocateConcentricPattern(const BitMatrix& image, PATTERN pattern, PointF center, int range)
{
auto cur = BitMatrixCursor(image, PointI(center), {});
int minSpread = image.width(), maxSpread = 0;
// TODO: setting maxError to 1 can subtantially help with detecting symbols with low print quality resulting in damaged
// finder patterns, but it sutantially increases the runtime (approx. 20% slower for the falsepositive images).
int maxError = 0;
for (auto d : {PointI{0, 1}, {1, 0}}) {
int spread = CheckSymmetricPattern<E2E>(cur.setDirection(d), pattern, range, true);
if (spread)
UpdateMinMax(minSpread, maxSpread, spread);
else if (--maxError < 0)
return {};
}
#if 1
for (auto d : {PointI{1, 1}, {1, -1}}) {
int spread = CheckSymmetricPattern<true>(cur.setDirection(d), pattern, range * 2, false);
if (spread)
UpdateMinMax(minSpread, maxSpread, spread);
else if (--maxError < 0)
return {};
}
#endif
if (maxSpread > 5 * minSpread)
return {};
auto newCenter = FinetuneConcentricPatternCenter(image, PointF(cur.p), range, pattern.size());
if (!newCenter)
return {};
return ConcentricPattern{*newCenter, (maxSpread + minSpread) / 2};
}
} // ZXing

471
tools/chatgui/zxing-cpp/core/src/Content.cpp

@ -0,0 +1,471 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "Content.h"
#include "ByteArray.h"
#include "CharacterSet.h"
#include "ECI.h"
#include "HRI.h"
#include "TextDecoder.h"
#include "Utf.h"
#include "ZXAlgorithms.h"
#include "Version.h"
#include <cassert>
namespace ZXing {
std::string ToString(ContentType type)
{
const char* t2s[] = {"Text", "Binary", "Mixed", "GS1", "ISO15434", "UnknownECI"};
int idx = static_cast<int>(type);
if (idx < 0 || idx >= Size(t2s))
return "InvalidContentType";
return t2s[idx];
}
template <typename FUNC>
void Content::ForEachECIBlock(FUNC func) const
{
ECI defaultECI = hasECI ? ECI::ISO8859_1 : ECI::Unknown;
if (encodings.empty())
func(defaultECI, 0, Size(bytes));
else if (encodings.front().pos != 0)
func(defaultECI, 0, encodings.front().pos);
for (int i = 0; i < Size(encodings); ++i) {
auto [eci, start] = encodings[i];
int end = i + 1 == Size(encodings) ? Size(bytes) : encodings[i + 1].pos;
if (start != end)
func(eci, start, end);
}
}
void Content::switchEncoding(ECI eci, bool isECI)
{
// remove all non-ECI entries on first ECI entry
if (isECI && !hasECI)
encodings.clear();
if (isECI || !hasECI)
encodings.push_back({eci, Size(bytes)});
hasECI |= isECI;
}
Content::Content(ByteArray&& bytes, SymbologyIdentifier si, CharacterSet defaultCharset)
: bytes(std::move(bytes)), symbology(si), defaultCharset(defaultCharset) {}
void Content::switchEncoding(CharacterSet cs)
{
switchEncoding(ToECI(cs), false);
}
void Content::append(const Content& other)
{
if (!hasECI && other.hasECI)
encodings.clear();
if (other.hasECI || !hasECI)
for (auto& e : other.encodings)
encodings.push_back({e.eci, Size(bytes) + e.pos});
append(other.bytes);
hasECI |= other.hasECI;
}
void Content::erase(int pos, int n)
{
bytes.erase(bytes.begin() + pos, bytes.begin() + pos + n);
for (auto& e : encodings)
if (e.pos > pos)
e.pos -= n;
}
void Content::insert(int pos, std::string_view str)
{
bytes.insert(bytes.begin() + pos, str.begin(), str.end());
for (auto& e : encodings)
if (e.pos > pos)
e.pos += Size(str);
}
bool Content::canProcess() const
{
return std::all_of(encodings.begin(), encodings.end(), [](Encoding e) { return CanProcess(e.eci); });
}
std::string Content::render(bool withECI) const
{
if (empty() || !canProcess())
return {};
#ifdef ZXING_READERS
std::string res;
res.reserve(bytes.size() * 2);
if (withECI)
res += symbology.toString(true);
ECI lastECI = ECI::Unknown;
auto fallbackCS = defaultCharset;
if (!hasECI && fallbackCS == CharacterSet::Unknown)
fallbackCS = guessEncoding();
ForEachECIBlock([&](ECI eci, int begin, int end) {
// basic idea: if IsText(eci), we transcode it to UTF8, otherwise we treat it as binary but
// transcoded it to valid UTF8 bytes sequences representing the code points 0-255. The eci we report
// back to the caller by inserting their "\XXXXXX" ECI designator is UTF8 for text and
// the original ECI for everything else.
// first determine how to decode the content (use fallback if unknown)
auto inEci = IsText(eci) ? eci : eci == ECI::Unknown ? ToECI(fallbackCS) : ECI::Binary;
if (withECI) {
// then find the eci to report back in the ECI designator
auto outEci = IsText(inEci) ? ECI::UTF8 : eci;
if (lastECI != outEci)
res += ToString(outEci);
lastECI = outEci;
for (auto c : BytesToUtf8(bytes.asView(begin, end - begin), inEci)) {
res += c;
if (c == '\\') // in the ECI protocol a '\' (0x5c) has to be doubled, works only because 0x5c can only mean `\`
res += c;
}
} else {
res += BytesToUtf8(bytes.asView(begin, end - begin), inEci);
}
});
return res;
#elif defined(ZXING_USE_ZINT)
assert(!utf8Cache.empty());
if (!withECI)
return std::accumulate(utf8Cache.begin(), utf8Cache.end(), std::string());
std::string res;
res.reserve(3 + TransformReduce(utf8Cache, 0, std::size<std::string>) * 2 + encodings.size() * 7);
res += symbology.toString(true);
ECI lastECI = ECI::Unknown;
auto fallbackCS = defaultCharset;
if (!hasECI && fallbackCS == CharacterSet::Unknown)
fallbackCS = guessEncoding();
assert(utf8Cache.size() == encodings.size());
for (int i = 0; i < Size(encodings); ++i) {
const auto eci = encodings[i].eci;
const auto inEci = IsText(eci) ? eci : eci == ECI::Unknown ? ToECI(fallbackCS) : ECI::Binary;
const auto outEci = IsText(inEci) ? ECI::UTF8 : eci;
if (lastECI != outEci)
res += ToString(outEci);
lastECI = outEci;
for (auto c : utf8Cache[i]) {
res += c;
if (c == '\\') // in the ECI protocol a '\' (0x5c) has to be doubled, works only because 0x5c can only mean `\`
res += c;
}
}
return res;
#else
(void)withECI;
return std::string(bytes.asString());
#endif
}
std::string Content::text(TextMode mode) const
{
switch (mode) {
case TextMode::Plain: return render(false);
case TextMode::ECI: return render(true);
case TextMode::HRI:
switch (type()) {
#if defined(ZXING_READERS) || defined(ZXING_USE_ZINT)
case ContentType::GS1: {
auto plain = render(false);
auto hri = HRIFromGS1(plain);
return hri.empty() ? EscapeNonGraphical(plain) : hri;
}
case ContentType::ISO15434: return HRIFromISO15434(render(false));
case ContentType::Text: return render(false);
#endif
default: return text(TextMode::Escaped);
}
case TextMode::Escaped: return EscapeNonGraphical(render(false));
case TextMode::Hex: return ToHex(bytes);
case TextMode::HexECI: return ToHex(bytesECI());
}
return {}; // silence compiler warning
}
std::wstring Content::utfW() const
{
return FromUtf8(render(false));
}
ByteArray Content::bytesECI() const
{
if (empty())
return {};
ByteArray res;
res.reserve(3 + bytes.size() + hasECI * encodings.size() * 7);
// report ECI protocol only if actually found ECI data in the barode bit stream
// see also https://github.com/zxing-cpp/zxing-cpp/issues/936
res.append(symbology.toString(hasECI));
if (hasECI)
ForEachECIBlock([&](ECI eci, int begin, int end) {
if (hasECI)
res.append(ToString(eci));
for (auto b : bytes.asView(begin, end - begin)) {
res.push_back(b);
if (b == '\\') // in the ECI protocol a '\' has to be doubled
res.push_back(b);
}
});
else
res.append(bytes);
return res;
}
#if defined(ZXING_READERS) || defined(ZXING_USE_ZINT)
/**
* @param bytes bytes encoding a string, whose encoding should be guessed
* @return name of guessed encoding; at the moment will only guess one of:
* {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform
* default encoding if none of these can possibly be correct
*/
CharacterSet GuessTextEncoding(ByteView bytes, CharacterSet fallback = CharacterSet::ISO8859_1)
{
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
// which should be by far the most common encodings.
bool canBeISO88591 = true;
bool canBeShiftJIS = true;
bool canBeUTF8 = IsValidUtf8(bytes);
int utf8BytesLeft = 0;
//int utf8LowChars = 0;
int utf2BytesChars = 0;
int utf3BytesChars = 0;
int utf4BytesChars = 0;
int sjisBytesLeft = 0;
//int sjisLowChars = 0;
int sjisKatakanaChars = 0;
//int sjisDoubleBytesChars = 0;
int sjisCurKatakanaWordLength = 0;
int sjisCurDoubleBytesWordLength = 0;
int sjisMaxKatakanaWordLength = 0;
int sjisMaxDoubleBytesWordLength = 0;
//int isoLowChars = 0;
//int isoHighChars = 0;
int isoHighOther = 0;
bool utf8bom = bytes.size() > 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF;
for (int value : bytes)
{
if(!(canBeISO88591 || canBeShiftJIS || canBeUTF8))
break;
// UTF-8 stuff
if (canBeUTF8) {
if (utf8BytesLeft > 0) {
if ((value & 0x80) == 0) {
canBeUTF8 = false;
}
else {
utf8BytesLeft--;
}
}
else if ((value & 0x80) != 0) {
if ((value & 0x40) == 0) {
canBeUTF8 = false;
}
else {
utf8BytesLeft++;
if ((value & 0x20) == 0) {
utf2BytesChars++;
}
else {
utf8BytesLeft++;
if ((value & 0x10) == 0) {
utf3BytesChars++;
}
else {
utf8BytesLeft++;
if ((value & 0x08) == 0) {
utf4BytesChars++;
}
else {
canBeUTF8 = false;
}
}
}
}
} //else {
//utf8LowChars++;
//}
}
// ISO-8859-1 stuff
if (canBeISO88591) {
if (value > 0x7F && value < 0xA0) {
canBeISO88591 = false;
}
else if (value > 0x9F) {
if (value < 0xC0 || value == 0xD7 || value == 0xF7) {
isoHighOther++;
} //else {
//isoHighChars++;
//}
} //else {
//isoLowChars++;
//}
}
// Shift_JIS stuff
if (canBeShiftJIS) {
if (sjisBytesLeft > 0) {
if (value < 0x40 || value == 0x7F || value > 0xFC) {
canBeShiftJIS = false;
}
else {
sjisBytesLeft--;
}
}
else if (value == 0x80 || value == 0xA0 || value > 0xEF || (value < 0x20 && value != 0xa && value != 0xd)) {
canBeShiftJIS = false; // use non-printable ASCII as indication for binary content
}
else if (value > 0xA0 && value < 0xE0) {
sjisKatakanaChars++;
sjisCurDoubleBytesWordLength = 0;
sjisCurKatakanaWordLength++;
if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) {
sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength;
}
}
else if (value > 0x7F) {
sjisBytesLeft++;
//sjisDoubleBytesChars++;
sjisCurKatakanaWordLength = 0;
sjisCurDoubleBytesWordLength++;
if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) {
sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength;
}
}
else {
//sjisLowChars++;
sjisCurKatakanaWordLength = 0;
sjisCurDoubleBytesWordLength = 0;
}
}
}
if (canBeUTF8 && utf8BytesLeft > 0) {
canBeUTF8 = false;
}
if (canBeShiftJIS && sjisBytesLeft > 0) {
canBeShiftJIS = false;
}
// Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done
if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) {
return CharacterSet::UTF8;
}
bool assumeShiftJIS = fallback == CharacterSet::Shift_JIS || fallback == CharacterSet::EUC_JP;
// Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii characters (and no evidence it can't be), done
if (canBeShiftJIS && (assumeShiftJIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) {
return CharacterSet::Shift_JIS;
}
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is:
// - If we saw
// - only two consecutive katakana chars in the whole text, or
// - at least 10% of bytes that could be "upper" not-alphanumeric Latin1,
// - then we conclude Shift_JIS, else ISO-8859-1
if (canBeISO88591 && canBeShiftJIS) {
return (sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) || isoHighOther * 10 >= Size(bytes)
? CharacterSet::Shift_JIS : CharacterSet::ISO8859_1;
}
// Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding
if (canBeISO88591) {
return CharacterSet::ISO8859_1;
}
if (canBeShiftJIS) {
return CharacterSet::Shift_JIS;
}
if (canBeUTF8) {
return CharacterSet::UTF8;
}
// Otherwise, we take a wild guess with platform encoding
return fallback;
}
#endif
CharacterSet Content::guessEncoding() const
{
#if defined(ZXING_READERS) || defined(ZXING_USE_ZINT)
// assemble all blocks with unknown encoding
ByteArray input;
ForEachECIBlock([&](ECI eci, int begin, int end) {
if (eci == ECI::Unknown)
input.append(bytes.asView(begin, end - begin));
});
if (input.empty())
return CharacterSet::Unknown;
return GuessTextEncoding(input);
#else
return CharacterSet::ISO8859_1;
#endif
}
ContentType Content::type() const
{
#if 1 //def ZXING_READERS
if (empty())
return ContentType::Text;
if (!canProcess())
return ContentType::UnknownECI;
if (symbology.aiFlag == AIFlag::GS1)
return ContentType::GS1;
// check for the absolute minimum of a ISO 15434 conforming message ("[)>" + RS + digit + digit)
if (bytes.size() > 6 && bytes.asString(0, 4) == "[)>\x1E" && IsDigit(bytes[4]) && IsDigit(bytes[5]))
return ContentType::ISO15434;
ECI fallback = ToECI(guessEncoding());
std::vector<bool> binaryECIs;
ForEachECIBlock([&](ECI eci, int begin, int end) {
if (eci == ECI::Unknown)
eci = fallback;
binaryECIs.push_back((!IsText(eci)
|| (ToInt(eci) > 0 && ToInt(eci) < 28 && ToInt(eci) != 25
&& std::any_of(bytes.begin() + begin, bytes.begin() + end,
[](auto c) { return c < 0x20 && c != 0x9 && c != 0xa && c != 0xd; }))));
});
if (!Contains(binaryECIs, true))
return ContentType::Text;
if (!Contains(binaryECIs, false))
return ContentType::Binary;
return ContentType::Mixed;
#else
//TODO: replace by proper construction from encoded data from within zint
return ContentType::Text;
#endif
}
} // namespace ZXing

82
tools/chatgui/zxing-cpp/core/src/Content.h

@ -0,0 +1,82 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ByteArray.h"
#include "CharacterSet.h"
#include "ContentType.h"
#include "ECI.h"
#include "ReaderOptions.h"
#include "SymbologyIdentifier.h"
#include "Version.h"
#include "ZXAlgorithms.h"
#include <string>
#include <string_view>
#include <vector>
namespace ZXing {
class Content
{
template <typename FUNC>
void ForEachECIBlock(FUNC f) const;
void switchEncoding(ECI eci, bool isECI);
std::string render(bool withECI) const;
public:
struct Encoding
{
ECI eci;
int pos;
};
ByteArray bytes;
std::vector<Encoding> encodings;
#if !defined(ZXING_READERS) && defined(ZXING_USE_ZINT)
std::vector<std::string> utf8Cache;
#endif
SymbologyIdentifier symbology;
CharacterSet defaultCharset = CharacterSet::Unknown;
bool hasECI = false;
Content() = default;
Content(ByteArray&& bytes, SymbologyIdentifier si, CharacterSet defaultCharset = CharacterSet::Unknown);
// make movable but not copyable
Content(const Content& other) = delete;
Content& operator=(const Content& other) = delete;
Content(Content&& other) = default;
Content& operator=(Content&& other) = default;
void switchEncoding(ECI eci) { switchEncoding(eci, true); }
void switchEncoding(CharacterSet cs);
void reserve(int count) { bytes.reserve(bytes.size() + count); }
void push_back(uint8_t val) { bytes.push_back(val); }
void push_back(int val) { bytes.push_back(narrow_cast<uint8_t>(val)); }
void append(std::string_view str) { bytes.insert(bytes.end(), str.begin(), str.end()); }
void append(ByteView bv) { bytes.insert(bytes.end(), bv.begin(), bv.end()); }
void append(const Content& other);
void erase(int pos, int n);
void insert(int pos, std::string_view str);
bool empty() const { return bytes.empty(); }
bool canProcess() const;
std::string text(TextMode mode) const;
std::wstring utfW() const; // utf16 or utf32 depending on the platform, i.e. on size_of(wchar_t)
std::string utf8() const { return render(false); }
ByteArray bytesECI() const;
CharacterSet guessEncoding() const;
ContentType type() const;
};
} // ZXing

19
tools/chatgui/zxing-cpp/core/src/ContentType.h

@ -0,0 +1,19 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string>
namespace ZXing {
/**
* @brief Provides a hint to the type of content encoded in a barcode, such as text, binary data, etc.
*/
enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI };
std::string ToString(ContentType type);
} // ZXing

490
tools/chatgui/zxing-cpp/core/src/CreateBarcode.cpp

@ -0,0 +1,490 @@
/*
* Copyright 2024 Axel Waggershauser
* Copyright 2025 gitlost
*/
// SPDX-License-Identifier: Apache-2.0
#include "CreateBarcode.h"
#include "BarcodeData.h"
#include "BitMatrix.h"
#include "ByteArray.h"
#include "DecoderResult.h"
#include "DetectorResult.h"
#include "JSON.h"
#include "Version.h"
#include "ZXAlgorithms.h"
#ifdef ZXING_READERS
#include "ReadBarcode.h"
#endif
#ifdef ZXING_USE_ZINT
#include "ECI.h"
#include "TextEncoder.h"
#include <zint.h>
#else
#include "MultiFormatWriter.h"
#endif // ZXING_USE_ZINT
#include <charconv>
#include <optional>
using namespace std::literals;
namespace ZXing {
struct CreatorOptions::Data
{
BarcodeFormat format;
std::string options;
// symbol size (qrcode, datamatrix, etc), map from I, 'WxH'
// structured_append (idx, cnt, ID)
mutable unique_zint_symbol zint;
#ifndef __cpp_aggregate_paren_init // make XCode 14.x happy
Data(BarcodeFormat format, std::string options) : format(format), options(std::move(options)) {}
#endif
};
// TODO: check return type
#define ZX_PROPERTY(TYPE, NAME) \
const TYPE& CreatorOptions::NAME() const noexcept { return d->NAME; } \
CreatorOptions& CreatorOptions::NAME(TYPE v)& { return d->NAME = std::move(v), *this; } \
CreatorOptions&& CreatorOptions::NAME(TYPE v)&& { return d->NAME = std::move(v), std::move(*this); }
ZX_PROPERTY(BarcodeFormat, format)
ZX_PROPERTY(std::string, options)
#undef ZX_PROPERTY
#define ZX_RO_PROPERTY(TYPE, NAME) \
std::optional<TYPE> CreatorOptions::NAME() const noexcept { return JsonGet<TYPE>(d->options, #NAME); }
ZX_RO_PROPERTY(std::string, ecLevel);
ZX_RO_PROPERTY(std::string, eci);
ZX_RO_PROPERTY(bool, gs1);
ZX_RO_PROPERTY(bool, readerInit);
ZX_RO_PROPERTY(bool, forceSquare);
ZX_RO_PROPERTY(int, columns);
ZX_RO_PROPERTY(int, rows);
ZX_RO_PROPERTY(int, version);
ZX_RO_PROPERTY(int, dataMask);
#undef ZX_RO_PROPERTY
CreatorOptions::CreatorOptions(BarcodeFormat format, std::string options) : d(std::make_unique<Data>(format, std::move(options))) {}
CreatorOptions::~CreatorOptions() = default;
CreatorOptions::CreatorOptions(CreatorOptions&&) noexcept = default;
CreatorOptions& CreatorOptions::operator=(CreatorOptions&&) noexcept = default;
inline bool IsAscii(ByteView bv)
{
return std::all_of(bv.begin(), bv.end(), [](uint8_t c) { return c <= 127; });
}
#ifdef ZXING_WRITERS
#ifdef ZXING_USE_ZINT
struct String2Int
{
const char* str;
int val;
};
static int ParseECLevel(int symbology, std::string_view s)
{
constexpr std::string_view EC_LABELS_QR[4] = {"L", "M", "Q", "H"};
int res = 0;
// Convert L/M/Q/H to Zint 1-4
if (Contains({BARCODE_QRCODE, BARCODE_MICROQR, BARCODE_RMQR}, symbology))
if ((res = IndexOf(EC_LABELS_QR, s)) != -1)
return res + 1;
if (std::from_chars(s.data(), s.data() + s.size() - (s.back() == '%'), res).ec != std::errc{})
throw std::invalid_argument(StrCat("Invalid ecLevel: '", s, "'"));
auto findClosestECLevel = [](const std::vector<int>& list, int val) {
int mIdx = -2, mAbs = 100;
for (int i = 0; i < Size(list); ++i)
if (int abs = std::abs(val - list[i]); abs < mAbs) {
mIdx = i;
mAbs = abs;
}
return mIdx + 1;
};
// Convert percentage to Zint
if (s.back() == '%') {
switch (symbology) {
case BARCODE_QRCODE: return findClosestECLevel({20, 37, 55, 65}, res);
case BARCODE_MICROQR: return findClosestECLevel({20, 37, 55}, res);
case BARCODE_RMQR: return res <= 46 ? 2 : 4;
case BARCODE_AZTEC: return findClosestECLevel({10, 23, 36, 50}, res);
case BARCODE_PDF417:
// TODO: do something sensible with PDF417?
default:
return -1;
}
}
return res;
};
static constexpr struct { BarcodeFormat format; SymbologyIdentifier si; } barcodeFormat2SymbologyIdentifier[] = {
{BarcodeFormat::Aztec, {'z', '0', 3}}, // '1' GS1, '2' AIM
{BarcodeFormat::Codabar, {'F', '0'}}, // if checksum processing were implemented and checksum present and stripped then modifier would be 4
// {BarcodeFormat::CodablockF, {'O', '4'}}, // '5' GS1
{BarcodeFormat::Code128, {'C', '0'}}, // '1' GS1, '2' AIM
// {BarcodeFormat::Code16K, {'K', '0'}}, // '1' GS1, '2' AIM, '4' D1 PAD
// {BarcodeFormat::Code39, {'A', '0'}}, // '3' checksum, '4' extended, '7' checksum,extended
{BarcodeFormat::Code39Ext, {'A', '4'}}, // '3' checksum, '4' extended, '7' checksum,extended
{BarcodeFormat::DataBar, {'e', '0', 0, AIFlag::GS1}},
{BarcodeFormat::DataBarOmni, {'e', '0', 0, AIFlag::GS1}},
{BarcodeFormat::DataBarStk, {'e', '0', 0, AIFlag::GS1}},
{BarcodeFormat::DataBarStkOmni, {'e', '0', 0, AIFlag::GS1}},
{BarcodeFormat::DataBarExp, {'e', '0', 0, AIFlag::GS1}},
{BarcodeFormat::DataBarExpStk, {'e', '0', 0, AIFlag::GS1}},
{BarcodeFormat::DataBarLtd, {'e', '0', 0, AIFlag::GS1}},
{BarcodeFormat::DataMatrix, {'d', '1', 3}}, // '2' GS1, '3' AIM
// {BarcodeFormat::DotCode, {'J', '0', 3}}, // '1' GS1, '2' AIM
{BarcodeFormat::DXFilmEdge, {'X', 'F'}},
{BarcodeFormat::EAN8, {'E', '4'}},
// {BarcodeFormat::HanXin, {'h', '0', 1}}, // '2' GS1
// {BarcodeFormat::ITF, {'I', '0'}}, // '1' check digit
{BarcodeFormat::MaxiCode, {'U', '0', 2}}, // '1' mode 2 or 3
{BarcodeFormat::MicroPDF417, {'L', '2', -1}},
{BarcodeFormat::MicroQRCode, {'Q', '1', 1}},
{BarcodeFormat::PDF417, {'L', '2', -1}},
{BarcodeFormat::QRCode, {'Q', '1', 1}}, // '3' GS1, '5' AIM
{BarcodeFormat::RMQRCode, {'Q', '1', 1}}, // '3' GS1, '5' AIM
};
static SymbologyIdentifier SymbologyIdentifierZint2ZXing(const CreatorOptions& opts, const ByteArray& ba)
{
using enum BarcodeFormat;
const BarcodeFormat format = opts.format();
auto i = FindIf(barcodeFormat2SymbologyIdentifier, [format](auto& v) { return v.format == format; });
SymbologyIdentifier ret = i != std::end(barcodeFormat2SymbologyIdentifier) ? i->si : SymbologyIdentifier{SymbologyKey(format), '0'};
if (format & (EAN13 | UPCA | UPCE)) {
if (ba.size() > 13) // Have EAN-2/5 add-on?
ret.modifier = '3'; // Combined packet, EAN-13, UPC-A, UPC-E, with add-on
} else if (opts.gs1() && format & AllGS1) {
if (format & (Aztec | Code128))
ret.modifier = '1';
else if (format == DataMatrix)
ret.modifier = '2';
else if (format & (QRCode | RMQRCode))
ret.modifier = '3';
ret.aiFlag = AIFlag::GS1;
}
return ret;
}
static std::string ECLevelZint2ZXing(const zint_symbol* zint)
{
constexpr char EC_LABELS_QR[4] = {'L', 'M', 'Q', 'H'};
const int symbology = zint->symbology;
const int option_1 = zint->option_1;
switch (symbology) {
case BARCODE_AZTEC:
if ((option_1 >> 8) >= 0 && (option_1 >> 8) <= 99)
return std::to_string(option_1 >> 8) + "%";
break;
case BARCODE_MAXICODE:
// Mode
if (option_1 >= 2 && option_1 <= 6)
return std::to_string(option_1);
break;
case BARCODE_PDF417:
case BARCODE_PDF417COMP:
// Convert to percentage
if (option_1 >= 0 && option_1 <= 8) {
int overhead = symbology == BARCODE_PDF417COMP ? 35 : 69;
int cols = (zint->width - overhead) / 17;
int tot_cws = zint->rows * cols;
assert(tot_cws);
return std::to_string((2 << option_1) * 100 / tot_cws) + "%";
}
break;
// case BARCODE_MICROPDF417:
// if ((option_1 >> 8) >= 0 && (option_1 >> 8) <= 99)
// return std::to_string(option_1 >> 8) + "%";
// break;
case BARCODE_QRCODE:
case BARCODE_MICROQR:
case BARCODE_RMQR:
// Convert to L/M/Q/H
if (option_1 >= 1 && option_1 <= 4)
return {EC_LABELS_QR[option_1 - 1]};
break;
// case BARCODE_HANXIN:
// if (option_1 >= 1 && option_1 <= 4)
// return "L" + std::to_string(option_1);
// break;
default:
break;
}
return {};
}
#ifndef ZXING_READERS
// Convert bytes to UTF-8 code points 0-255
static std::string BinaryToUtf8(ByteView ba)
{
std::string utf8;
utf8.reserve(ba.size() * 2);
for (auto c : ba)
if (c < 0x80) {
utf8.push_back(c);
} else if (c < 0xC0) {
utf8.push_back(0xC2);
utf8.push_back(c);
} else {
utf8.push_back(0xC3);
utf8.push_back(c - 0x40);
}
return utf8;
}
#endif
zint_symbol* CreatorOptions::zint() const
{
using enum BarcodeFormat;
auto& zint = d->zint;
if (!zint) {
#ifdef PRINT_DEBUG
// printf("zint version: %d, sizeof(zint_symbol): %ld, options: %s\n", ZBarcode_Version(), sizeof(zint_symbol), options().c_str());
#endif
zint.reset(ZBarcode_Create());
switch (format()) {
#define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) \
case BarcodeFormat(ZX_BCF_ID(SYM, VAR)): zint->symbology = ZINT; break;
ZX_BCF_LIST(X)
#undef X
};
if (zint->symbology == 0)
throw std::invalid_argument(StrCat("Unsupported barcode format for creation: ", ToString(format())));
if (format() == Code128 && gs1())
zint->symbology = BARCODE_GS1_128;
zint->scale = 0.5f;
if (auto val = ecLevel(); val)
zint->option_1 = ParseECLevel(zint->symbology, *val);
if (auto val = version(); val && !(format() & AllLinear))
zint->option_2 = *val;
if (auto val = columns(); val && format() & (DataBarExpStk | PDF417 | MicroPDF417 | CompactPDF417))
zint->option_2 = *val;
if (auto val = rows(); val && format() & (DataBarExpStk | PDF417))
zint->option_3 = *val;
if (auto val = dataMask(); val && format() & (QRCode | MicroQRCode))
zint->option_3 = (zint->option_3 & 0xFF) | (*val + 1) << 8;
if (format() == DataMatrix)
zint->option_3 = (forceSquare() ? DM_SQUARE : DM_DMRE) | DM_ISO_144;
}
return zint.get();
}
#define CHECK_WARN(ZINT_CALL, WARN) \
if (WARN = (ZINT_CALL); WARN >= ZINT_ERROR) \
throw std::invalid_argument(StrCat(zint->errtxt, " (retval: ", std::to_string(WARN), ")"));
Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& opts)
{
if (!data || size < 1)
throw std::invalid_argument("Can not create a barcode from NULL or empty data");
auto zint = opts.zint();
zint->input_mode = mode == UNICODE_MODE && opts.gs1() && (opts.format() & BarcodeFormat::AllGS1) ? GS1_MODE : mode;
if (mode == UNICODE_MODE && static_cast<const char*>(data)[0] != '[')
zint->input_mode |= GS1PARENS_MODE;
zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_NO_QUIET_ZONES | BARCODE_CONTENT_SEGS;
if (opts.readerInit())
zint->output_options |= READER_INIT;
if (ZBarcode_Cap(zint->symbology, ZINT_CAP_ECI)) {
if (auto eci = opts.eci(); eci) {
if (auto cs = CharacterSetFromString(*eci); cs != CharacterSet::Unknown) {
zint->eci = static_cast<int>(ToECI(cs));
} else if (std::all_of(eci->begin(), eci->end(), IsDigit<char>)) {
zint->eci = std::stoi(*eci);
}
} else if (mode == DATA_MODE) {
zint->eci = static_cast<int>(ECI::Binary);
}
// the following would make sure that non-ASCII text is encoded as UTF-8, hence the GuessTextEncoding for
// non-ECI data can't produce a wrong result.
// else if (mode == UNICODE_MODE && !IsAscii({data, narrow_cast<size_t>(size)}))
// zint->eci = static_cast<int>(ECI::UTF8);
}
if (opts.format() == BarcodeFormat::Telepen && (std::all_of((const char*)data, (const char*)data + size, IsDigit<char>)))
zint->symbology = BARCODE_TELEPEN_NUM;
int warning;
CHECK_WARN(ZBarcode_Encode_and_Buffer(zint, (uint8_t*)data, size, 0), warning);
#ifdef PRINT_DEBUG
printf("create symbol with size: %dx%d\n", zint->width, zint->rows);
#endif
#if 0 // use ReadBarcode to create Barcode object
auto buffer = std::vector<uint8_t>(zint->bitmap_width * zint->bitmap_height);
std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, buffer.data(),
[](unsigned char v) { return (v == '0') * 0xff; });
auto res = ReadBarcode({buffer.data(), zint->bitmap_width, zint->bitmap_height, ImageFormat::Lum},
ReaderOptions().formats(opts.format()).isPure(true).binarizer(Binarizer::BoolCast));
#else
assert(zint->content_seg_count == 1);
const auto& content_seg = zint->content_segs[0];
const size_t content_seg_len =
static_cast<size_t>(content_seg.length - (opts.format() == BarcodeFormat::Code93 && content_seg.length >= 2 ? 2 : 0));
Content content;
if (zint->eci || warning == ZINT_WARN_USES_ECI)
content.switchEncoding(ECI(content_seg.eci));
else
content.switchEncoding(ToCharacterSet(ECI(content_seg.eci)));
if ((zint->input_mode & 0x07) == UNICODE_MODE) {
// `content_segs` returned as UTF-8
std::string utf8(reinterpret_cast<const char *>(content_seg.source), content_seg_len);
content.append(TextEncoder::FromUnicode(utf8, ToCharacterSet(ECI(content_seg.eci))));
#ifndef ZXING_READERS
content.utf8Cache.push_back(std::move(utf8));
#endif
} else {
content.append({content_seg.source, content_seg_len});
#ifndef ZXING_READERS
content.utf8Cache.push_back(BinaryToUtf8(content.bytes));
#endif
}
content.symbology = SymbologyIdentifierZint2ZXing(opts, content.bytes);
DecoderResult decRes(std::move(content));
decRes.setEcLevel(ECLevelZint2ZXing(zint));
decRes.setReaderInit(zint->output_options & READER_INIT);
auto bits = BitMatrix(zint->bitmap_width, zint->bitmap_height);
std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, bits.row(0).begin(),
[](unsigned char v) { return (v == '1') * BitMatrix::SET_V; });
int left, top, width, height;
bits.findBoundingBox(left, top, width, height);
auto res = MatrixBarcode(std::move(decRes), {std::move(bits), Rectangle<PointI>(left, top, width, height)}, opts.format());
#endif
res.zint = std::move(opts.d->zint);
res.zintMutex = std::make_unique<std::mutex>();
return res;
}
Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& options)
{
return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, options);
}
Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& options)
{
return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, options);
}
Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& options)
{
return CreateBarcode(data, size, DATA_MODE, options);
}
#else // ZXING_USE_ZINT
zint_symbol* CreatorOptions::zint() const { return nullptr; }
static Barcode CreateBarcode(BitMatrix&& bits, std::string_view contents, const CreatorOptions& opts)
{
Content content;
content.append(contents);
DecoderResult decRes(std::move(content));
DetectorResult detRes(std::move(bits), Rectangle<PointI>(0, 0, bits.width(), bits.height()));
return MatrixBarcode(std::move(decRes), std::move(detRes), opts.format());
}
Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& opts)
{
auto writer = MultiFormatWriter(opts.format()).setMargin(0);
if (auto ecLevel = opts.ecLevel(); ecLevel && ecLevel->size() == 1 && strchr("012345678", (*ecLevel)[0]))
writer.setEccLevel(std::stoi(*ecLevel));
if (!IsAscii({(const uint8_t*)contents.data(), contents.size()}))
writer.setEncoding(CharacterSet::UTF8); // write UTF8 (ECI value 26) for maximum compatibility
return CreateBarcode(writer.encode(std::string(contents), 0, opts.format() & BarcodeFormat::AllLinear ? 50 : 0), contents, opts);
}
Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& opts)
{
return CreateBarcodeFromText({reinterpret_cast<const char*>(contents.data()), contents.size()}, opts);
}
Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& opts)
{
std::wstring bytes;
for (uint8_t c : ByteView(data, size))
bytes.push_back(c);
auto writer = MultiFormatWriter(opts.format()).setMargin(0);
if (auto ecLevel = opts.ecLevel(); ecLevel && ecLevel->size() == 1 && strchr("012345678", (*ecLevel)[0]))
writer.setEccLevel(std::stoi(*ecLevel));
writer.setEncoding(CharacterSet::BINARY);
return CreateBarcode(writer.encode(bytes, 0, opts.format() & BarcodeFormat::AllLinear ? 50 : 0), {(const char*)data, (size_t)size}, opts);
}
#endif // ZXING_USE_ZINT
#else // ZXING_WRITERS
zint_symbol* CreatorOptions::zint() const { return nullptr; }
Barcode CreateBarcodeFromText(std::string_view, const CreatorOptions&)
{
throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
}
Barcode CreateBarcodeFromText(std::u8string_view, const CreatorOptions&)
{
throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
}
Barcode CreateBarcodeFromBytes(const void*, int, const CreatorOptions&)
{
throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
}
#endif // ZXING_WRITERS
} // namespace ZXing

132
tools/chatgui/zxing-cpp/core/src/CreateBarcode.h

@ -0,0 +1,132 @@
/*
* Copyright 2024 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Barcode.h"
#include <memory>
#include <optional>
#include <string_view>
extern "C" struct zint_symbol;
namespace ZXing {
/**
* @brief Configuration options for barcode creation.
*
* This class encapsulates all the parameters needed to create a barcode with
* specific format and settings.
*
* The options property is a string that can contain multiple key-value pairs
* separated by commas. Each key-value pair configures a specific aspect of the
* barcode generation process and are dependent on the used BarcodeFormat.
* Boolean properties are interpreted as true if only the property name is present.
* Keys are case-insensitive. Passing a serialized JSON object is also supported.
*
* For a list of all supported options, see the list of read-only properties below.
*
* ```c++
* // example of creating a QR code with 30% error correction level and GS1 mode enabled
* auto opts = CreatorOptions(BarcodeFormat::QRCode, "ecLevel=30%, gs1");
* ```
*/
class CreatorOptions
{
struct Data;
std::unique_ptr<Data> d;
friend Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& options);
public:
CreatorOptions(BarcodeFormat format, std::string options = {});
~CreatorOptions();
CreatorOptions(CreatorOptions&&) noexcept;
CreatorOptions& operator=(CreatorOptions&&) noexcept;
zint_symbol* zint() const;
#define ZX_PROPERTY(TYPE, NAME) \
const TYPE& NAME() const noexcept; \
CreatorOptions& NAME(TYPE v)&; \
CreatorOptions&& NAME(TYPE v)&&;
ZX_PROPERTY(BarcodeFormat, format)
ZX_PROPERTY(std::string, options)
#undef ZX_PROPERTY
#define ZX_RO_PROPERTY(TYPE, NAME) \
std::optional<TYPE> NAME() const noexcept;
/// EcLevel, e.g. "30%", see also libzint docs for supported values per symbology
ZX_RO_PROPERTY(std::string, ecLevel);
/// Specify ECI designator to use (e.g. "UTF-8" or "26" for UTF-8), see also libzint docs (most 2D symbologies)
ZX_RO_PROPERTY(std::string, eci);
/// GS1 mode (most 2D symbologies + Code128)
ZX_RO_PROPERTY(bool, gs1);
/// Set the "reader init" flag (most 2D symbologies)
ZX_RO_PROPERTY(bool, readerInit);
/// DataMatrix: only consider square symbol versions
ZX_RO_PROPERTY(bool, forceSquare);
/// Specify number of columns (e.g. for DataBarExpStk, PDF417)
ZX_RO_PROPERTY(int, columns);
/// Specify number of rows (e.g. for DataBarExpStk, PDF417)
ZX_RO_PROPERTY(int, rows);
/// Specify the version/size of the symbol (most 2D symbologies)
ZX_RO_PROPERTY(int, version);
/// Specify dataMask to use (QRCode/MicroQRCode)
ZX_RO_PROPERTY(int, dataMask);
#undef ZX_RO_PROPERTY
};
/**
* @brief Generate Barcode from unicode text
*
* @param contents UTF-8 string to encode into a barcode
* @param options CreatorOptions (including BarcodeFormat)
*/
Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& options);
/**
* @brief Generate Barcode from raw binary data
*
* @param data array of bytes to encode into a barcode
* @param size size of byte array
* @param options CreatorOptions (including BarcodeFormat)
*/
Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& options);
#if defined(__cpp_lib_char8_t)
Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& options);
#endif
#if defined(__cpp_lib_ranges)
template <typename R>
requires std::ranges::contiguous_range<R> && std::ranges::sized_range<R> && (sizeof(std::ranges::range_value_t<R>) == 1)
Barcode CreateBarcodeFromBytes(const R& contents, const CreatorOptions& options)
{
return CreateBarcodeFromBytes(std::ranges::data(contents), std::ranges::size(contents), options);
}
#else
template <typename R>
Barcode CreateBarcodeFromBytes(const R& contents, const CreatorOptions& options)
{
return CreateBarcodeFromBytes(std::data(contents), std::size(contents), options);
}
#endif
} // ZXing

92
tools/chatgui/zxing-cpp/core/src/DecoderResult.h

@ -0,0 +1,92 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Content.h"
#include "Error.h"
#include "JSON.h"
#include "StructuredAppend.h"
#include <memory>
#include <string>
#include <utility>
namespace ZXing {
struct CustomData
{
virtual ~CustomData() = default;
};
class DecoderResult
{
Content _content;
std::string _ecLevel;
int _lineCount = 0;
int _versionNumber = 0;
StructuredAppendInfo _structuredAppend;
bool _isMirrored = false;
bool _readerInit = false;
Error _error;
std::string _json;
std::shared_ptr<CustomData> _customData;
public:
DecoderResult() = default;
DecoderResult(Error error) : _error(std::move(error)) {}
DecoderResult(Content&& bytes) : _content(std::move(bytes)) {}
DecoderResult(DecoderResult&&) noexcept = default;
DecoderResult& operator=(DecoderResult&&) noexcept = default;
DecoderResult(const DecoderResult&) = delete;
DecoderResult& operator=(const DecoderResult&) = delete;
bool isValid(bool includeErrors = false) const
{
return (!_content.bytes.empty() && !_error) || (includeErrors && !!_error);
}
const Content& content() const & { return _content; }
Content&& content() && { return std::move(_content); }
// to keep the unit tests happy for now:
std::wstring text() const { return _content.utfW(); }
std::string symbologyIdentifier() const { return _content.symbology.toString(false); }
// Simple macro to set up getter/setter methods that save lots of boilerplate.
// It sets up a standard 'const & () const', 2 setters for setting lvalues via
// copy and 2 for setting rvalues via move. They are provided each to work
// either on lvalues (normal 'void (...)') or on rvalues (returning '*this' as
// rvalue). The latter can be used to optionally initialize a temporary in a
// return statement, e.g.
// return DecoderResult(bytes, text).setEcLevel(level);
#define ZX_PROPERTY(TYPE, GETTER, SETTER) \
const TYPE& GETTER() const & { return _##GETTER; } \
TYPE&& GETTER() && { return std::move(_##GETTER); } \
void SETTER(const TYPE& v) & { _##GETTER = v; } \
void SETTER(TYPE&& v) & { _##GETTER = std::move(v); } \
DecoderResult&& SETTER(const TYPE& v) && { _##GETTER = v; return std::move(*this); } \
DecoderResult&& SETTER(TYPE&& v) && { _##GETTER = std::move(v); return std::move(*this); }
ZX_PROPERTY(std::string, ecLevel, setEcLevel)
ZX_PROPERTY(int, lineCount, setLineCount)
ZX_PROPERTY(int, versionNumber, setVersionNumber)
ZX_PROPERTY(StructuredAppendInfo, structuredAppend, setStructuredAppend)
ZX_PROPERTY(Error, error, setError)
ZX_PROPERTY(bool, isMirrored, setIsMirrored)
ZX_PROPERTY(bool, readerInit, setReaderInit)
ZX_PROPERTY(std::string, json, setJson)
ZX_PROPERTY(std::shared_ptr<CustomData>, customData, setCustomData)
#undef ZX_PROPERTY
template<typename T>
DecoderResult&& addExtra(std::string_view key, T val, T ignore = {}) { _json += JsonProp(key, val, ignore); return std::move(*this); }
};
} // ZXing

44
tools/chatgui/zxing-cpp/core/src/DetectorResult.h

@ -0,0 +1,44 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BitMatrix.h"
#include "Quadrilateral.h"
#include <utility>
namespace ZXing {
/**
* Encapsulates the result of detecting a barcode in an image. This includes the raw
* matrix of black/white pixels corresponding to the barcode and the position of the code
* in the input image.
*/
class DetectorResult
{
BitMatrix _bits;
QuadrilateralI _position;
public:
DetectorResult() = default;
DetectorResult(DetectorResult&&) noexcept = default;
DetectorResult& operator=(DetectorResult&&) noexcept = default;
DetectorResult(const DetectorResult&) = delete;
DetectorResult& operator=(const DetectorResult&) = delete;
DetectorResult(BitMatrix&& bits, QuadrilateralI&& position) : _bits(std::move(bits)), _position(std::move(position)) {}
const BitMatrix& bits() const & { return _bits; }
BitMatrix&& bits() && { return std::move(_bits); }
const QuadrilateralI& position() const & { return _position; }
QuadrilateralI&& position() && { return std::move(_position); }
bool isValid() const { return !_bits.empty(); }
};
} // ZXing

81
tools/chatgui/zxing-cpp/core/src/ECI.cpp

@ -0,0 +1,81 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "ECI.h"
#include "ZXAlgorithms.h"
#include <map>
namespace ZXing {
static const std::map<ECI, CharacterSet> ECI_TO_CHARSET = {
{ECI(0), CharacterSet::Cp437}, // Obsolete
{ECI(1), CharacterSet::ISO8859_1}, // Obsolete
{ECI::Cp437, CharacterSet::Cp437}, // Obsolete but still used by PDF417 Macro fields (ISO/IEC 15438:2015 Annex H.2.3)
{ECI::ISO8859_1, CharacterSet::ISO8859_1},
{ECI::ISO8859_2, CharacterSet::ISO8859_2},
{ECI::ISO8859_3, CharacterSet::ISO8859_3},
{ECI::ISO8859_4, CharacterSet::ISO8859_4},
{ECI::ISO8859_5, CharacterSet::ISO8859_5},
{ECI::ISO8859_6, CharacterSet::ISO8859_6},
{ECI::ISO8859_7, CharacterSet::ISO8859_7},
{ECI::ISO8859_8, CharacterSet::ISO8859_8},
{ECI::ISO8859_9, CharacterSet::ISO8859_9},
{ECI::ISO8859_10, CharacterSet::ISO8859_10},
{ECI::ISO8859_11, CharacterSet::ISO8859_11},
{ECI::ISO8859_13, CharacterSet::ISO8859_13},
{ECI::ISO8859_14, CharacterSet::ISO8859_14},
{ECI::ISO8859_15, CharacterSet::ISO8859_15},
{ECI::ISO8859_16, CharacterSet::ISO8859_16},
{ECI::Shift_JIS, CharacterSet::Shift_JIS},
{ECI::Cp1250, CharacterSet::Cp1250},
{ECI::Cp1251, CharacterSet::Cp1251},
{ECI::Cp1252, CharacterSet::Cp1252},
{ECI::Cp1256, CharacterSet::Cp1256},
{ECI::UTF8, CharacterSet::UTF8},
{ECI::UTF16BE, CharacterSet::UTF16BE},
{ECI::UTF16LE, CharacterSet::UTF16LE},
{ECI::UTF32BE, CharacterSet::UTF32BE},
{ECI::UTF32LE, CharacterSet::UTF32LE},
{ECI::ASCII, CharacterSet::ASCII},
{ECI::Big5, CharacterSet::Big5},
{ECI::GB18030, CharacterSet::GB18030},
{ECI::GB2312, CharacterSet::GB2312},
{ECI::EUC_KR, CharacterSet::EUC_KR},
{ECI::ISO646_Inv, CharacterSet::ASCII},
{ECI::Binary, CharacterSet::BINARY},
};
std::string ToString(ECI eci)
{
return '\\' + ToString(ToInt(eci), 6);
}
CharacterSet ToCharacterSet(ECI eci)
{
if (auto it = ECI_TO_CHARSET.find(eci); it != ECI_TO_CHARSET.end())
return it->second;
return CharacterSet::Unknown;
}
ECI ToECI(CharacterSet cs)
{
// Special case ISO8859_1 to avoid obsolete ECI 1
if (cs == CharacterSet::ISO8859_1)
return ECI::ISO8859_1;
// Special case Cp437 to avoid obsolete ECI 0 for slightly less obsolete ECI 2
if (cs == CharacterSet::Cp437)
return ECI::Cp437;
for (auto& [key, value] : ECI_TO_CHARSET)
if (value == cs)
return key;
return ECI::Unknown;
}
} // namespace ZXing

78
tools/chatgui/zxing-cpp/core/src/ECI.h

@ -0,0 +1,78 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "CharacterSet.h"
#include <string>
namespace ZXing {
enum class ECI : int
{
Unknown = -1,
Cp437 = 2, // obsolete
ISO8859_1 = 3,
ISO8859_2 = 4,
ISO8859_3 = 5,
ISO8859_4 = 6,
ISO8859_5 = 7,
ISO8859_6 = 8,
ISO8859_7 = 9,
ISO8859_8 = 10,
ISO8859_9 = 11,
ISO8859_10 = 12,
ISO8859_11 = 13,
ISO8859_13 = 15,
ISO8859_14 = 16,
ISO8859_15 = 17,
ISO8859_16 = 18,
Shift_JIS = 20,
Cp1250 = 21,
Cp1251 = 22,
Cp1252 = 23,
Cp1256 = 24,
UTF16BE = 25,
UTF8 = 26,
ASCII = 27,
Big5 = 28,
GB2312 = 29,
EUC_KR = 30,
GB18030 = 32,
UTF16LE = 33,
UTF32BE = 34,
UTF32LE = 35,
ISO646_Inv = 170,
Binary = 899
};
inline constexpr int ToInt(ECI eci)
{
return static_cast<int>(eci);
}
inline constexpr bool IsText(ECI eci)
{
return ToInt(eci) >= 0 && ToInt(eci) <= 170;
}
inline constexpr bool CanProcess(ECI eci)
{
// see https://github.com/zxing-cpp/zxing-cpp/commit/d8587545434d533c4e568181e1c12ef04a8e42d9#r74864359
return ToInt(eci) <= 899;
}
/**
* @brief ToString converts the numerical ECI value to a 7 character string as used in the ECI protocol
* @return e.g. "\000020"
*/
std::string ToString(ECI eci);
CharacterSet ToCharacterSet(ECI eci);
ECI ToECI(CharacterSet cs);
} // namespace ZXing

29
tools/chatgui/zxing-cpp/core/src/Error.cpp

@ -0,0 +1,29 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "Error.h"
namespace ZXing {
std::string Error::location() const
{
if (!_file)
return {};
std::string file(_file);
return file.substr(file.find_last_of("/\\") + 1) + ":" + std::to_string(_line);
}
std::string ToString(const Error& e)
{
const char* name[] = {"", "FormatError", "ChecksumError", "Unsupported"};
std::string ret = name[static_cast<int>(e.type())];
if (!e.msg().empty())
ret += " (" + e.msg() + ")";
if (auto location = e.location(); !location.empty())
ret += " @ " + e.location();
return ret;
}
}

79
tools/chatgui/zxing-cpp/core/src/Error.h

@ -0,0 +1,79 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string>
#include <cstdint>
namespace ZXing {
/**
* @brief The Error class is a value type for the error() member of Barcode
*
* The use-case of this class is to communicate whether or not a particular Barcode
* symbol is in error. It is (primarily) not meant to be thrown as an exception and
* therefore not derived from std::exception. The library code may throw (and catch!)
* objects of this class as a convenient means of flow control (c++23's std::expected
* will allow to replace those use-cases with something similarly convenient). In
* those situations, the author is advised to make sure any thrown Error object is
* caught before leaking into user/wrapper code, i.e. the functions of the public
* API should be considered `noexcept` with respect to this class.
*/
class Error
{
public:
enum class Type : uint8_t
{
None, ///< No error
Format, ///< Format error (i.e. the decoded data is not valid for the detected symbology)
Checksum, ///< Checksum error (i.e. the decoded data does not pass the checksum validation)
Unsupported ///< Unsupported error (i.e. a particular feature detected is not supported by zxing-cpp)
};
Type type() const noexcept { return _type; }
const std::string& msg() const noexcept { return _msg; }
explicit operator bool() const noexcept { return _type != Type::None; }
/// The source code location where the error was detected (if available)
std::string location() const;
Error() = default;
Error(Type type, std::string msg = {}) : _msg(std::move(msg)), _type(type) {}
Error(const char* file, short line, Type type, std::string msg = {}) : _msg(std::move(msg)), _file(file), _line(line), _type(type) {}
static constexpr auto Format = Type::Format;
static constexpr auto Checksum = Type::Checksum;
static constexpr auto Unsupported = Type::Unsupported;
inline bool operator==(const Error& o) const noexcept
{
return _type == o._type && _msg == o._msg && _file == o._file && _line == o._line;
}
inline bool operator!=(const Error& o) const noexcept { return !(*this == o); }
protected:
/// @cond INTERNAL
std::string _msg;
const char* _file = nullptr;
short _line = -1;
Type _type = Type::None;
/// @endcond
};
/// @cond INTERNAL
inline bool operator==(const Error& e, Error::Type t) noexcept { return e.type() == t; }
inline bool operator!=(const Error& e, Error::Type t) noexcept { return !(e == t); }
inline bool operator==(Error::Type t, const Error& e) noexcept { return e.type() == t; }
inline bool operator!=(Error::Type t, const Error& e) noexcept { return !(t == e); }
#define FormatError(...) Error(__FILE__, __LINE__, Error::Format, std::string(__VA_ARGS__))
#define ChecksumError(...) Error(__FILE__, __LINE__, Error::Checksum, std::string(__VA_ARGS__))
#define UnsupportedError(...) Error(__FILE__, __LINE__, Error::Unsupported, std::string(__VA_ARGS__))
/// @endcond
std::string ToString(const Error& e);
}

165
tools/chatgui/zxing-cpp/core/src/Flags.h

@ -0,0 +1,165 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <cstddef>
#include <iterator>
#include <limits>
#include <type_traits>
#include <version>
#ifdef __cpp_lib_bitops
#include <bit>
#else
namespace std {
template <class T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
constexpr int popcount(T x) noexcept
{
int n = 0;
for (; x; ++n)
x &= x - 1; // clear lowest bit
return n;
}
template <class T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
constexpr int countr_zero(T x) noexcept
{
if (x == 0)
return std::numeric_limits<T>::digits;
int n = 0;
while ((x & T(1)) == 0) {
x >>= 1;
++n;
}
return n;
}
template <class T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
constexpr int countl_zero(T x) noexcept
{
if (x == 0)
return std::numeric_limits<T>::digits;
int n = 0;
for (int i = std::numeric_limits<T>::digits; i-- && !(x & (T(1) << i)); ++n)
;
return n;
}
} // namespace std
#endif
namespace ZXing {
template<typename Enum>
class Flags
{
static_assert(std::is_enum<Enum>::value, "Flags is only usable on enumeration types.");
using Int = std::make_unsigned_t<typename std::underlying_type<Enum>::type>;
Int i = 0;
constexpr inline Flags(Int other) : i(other) {}
constexpr static inline auto highestBitSet(Int x) noexcept
{
return std::numeric_limits<Int>::digits - 1 - std::countl_zero(x);
}
public:
using enum_type = Enum;
constexpr inline Flags() noexcept = default;
constexpr inline Flags(Enum flag) noexcept : i(Int(flag)) {}
// constexpr inline Flags(std::initializer_list<Enum> flags) noexcept
// : i(initializer_list_helper(flags.begin(), flags.end()))
// {}
class iterator
{
friend class Flags;
const Int _flags = 0;
int _pos = 0;
iterator(Int i, int p) : _flags(i), _pos(p) {}
public:
using iterator_category = std::input_iterator_tag;
using value_type = Enum;
using difference_type = std::ptrdiff_t;
using pointer = Enum*;
using reference = Enum&;
Enum operator*() const noexcept { return Enum(Int(1) << _pos); }
iterator& operator++() noexcept
{
while (++_pos < highestBitSet(_flags) && !((Int(1) << _pos) & _flags))
;
return *this;
}
bool operator==(const iterator& rhs) const noexcept { return _pos == rhs._pos; }
bool operator!=(const iterator& rhs) const noexcept { return !(*this == rhs); }
};
iterator begin() const noexcept { return {i, std::countr_zero(i)}; }
iterator end() const noexcept { return {i, highestBitSet(i) + 1}; }
bool empty() const noexcept { return i == 0; }
int count() const noexcept { return std::popcount(i); }
constexpr inline bool operator==(Flags other) const noexcept { return i == other.i; }
inline Flags& operator&=(Flags mask) noexcept { return i &= mask.i, *this; }
inline Flags& operator&=(Enum mask) noexcept { return i &= Int(mask), *this; }
inline Flags& operator|=(Flags other) noexcept { return i |= other.i, *this; }
inline Flags& operator|=(Enum other) noexcept { return i |= Int(other), *this; }
// inline Flags &operator^=(Flags other) noexcept { return i ^= other.i, *this; }
// inline Flags &operator^=(Enum other) noexcept { return i ^= Int(other), *this; }
constexpr inline Flags operator&(Flags other) const noexcept { return i & other.i; }
constexpr inline Flags operator&(Enum other) const noexcept { return i & Int(other); }
constexpr inline Flags operator|(Flags other) const noexcept { return i | other.i; }
constexpr inline Flags operator|(Enum other) const noexcept { return i | Int(other); }
// constexpr inline Flags operator^(Flags other) const noexcept { return i ^ other.i; }
// constexpr inline Flags operator^(Enum other) const noexcept { return i ^ Int(other); }
// constexpr inline Flags operator~() const noexcept { return ~i; }
// constexpr inline operator Int() const noexcept { return i; }
// constexpr inline bool operator!() const noexcept { return !i; }
// constexpr inline Int asInt() const noexcept { return i; }
constexpr inline bool testFlag(Enum flag) const noexcept
{
return (i & Int(flag)) == Int(flag) && (Int(flag) != 0 || i == Int(flag));
}
constexpr inline bool testFlags(Flags mask) const noexcept { return i & mask.i; }
inline Flags& setFlag(Enum flag, bool on = true) noexcept
{
return on ? (*this |= flag) : (*this &= ~Int(flag));
}
inline void clear() noexcept { i = 0; }
constexpr static Flags all() noexcept { return ~(Int(~0) << highestBitSet(Int(Enum::_max))); }
private:
// constexpr static inline Int
// initializer_list_helper(typename std::initializer_list<Enum>::const_iterator it,
// typename std::initializer_list<Enum>::const_iterator end) noexcept
// {
// return (it == end ? Int(0) : (Int(*it) | initializer_list_helper(it + 1, end)));
// }
};
#define ZX_DECLARE_FLAGS(FLAGS, ENUM) \
using FLAGS = Flags<ENUM>; \
constexpr inline FLAGS operator|(FLAGS::enum_type e1, FLAGS::enum_type e2) noexcept { return FLAGS(e1) | e2; } \
constexpr inline FLAGS operator|(FLAGS::enum_type e, FLAGS f) noexcept { return f | e; } \
constexpr inline bool operator==(FLAGS::enum_type e, FLAGS f) noexcept { return FLAGS(e) == f; } \
constexpr inline bool operator==(FLAGS f, FLAGS::enum_type e) noexcept { return FLAGS(e) == f; }
} // ZXing

253
tools/chatgui/zxing-cpp/core/src/GTIN.cpp

@ -0,0 +1,253 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "GTIN.h"
#include <algorithm>
#ifdef __cpp_lib_format // not available on gcc 12
#include <format>
#endif
#include <iomanip>
#include <iterator>
#include <string>
namespace ZXing::GTIN {
struct CountryId
{
uint16_t first;
uint16_t last;
const char id[3];
};
bool operator<(const CountryId& lhs, const CountryId& rhs)
{
return lhs.last < rhs.last;
}
// https://www.gs1.org/standards/id-keys/company-prefix (as of 7 Feb 2022)
// and https://en.wikipedia.org/wiki/List_of_GS1_country_codes
static const CountryId COUNTRIES[] = {
// clang-format off
{1, 19, "US"},
{30, 39, "US"},
{60, 99, "US"}, // Note 99 coupon identification
{100, 139, "US"},
{300, 379, "FR"}, // France (and Monaco according to Wikipedia)
{380, 380, "BG"}, // Bulgaria
{383, 383, "SI"}, // Slovenia
{385, 385, "HR"}, // Croatia
{387, 387, "BA"}, // Bosnia and Herzegovina
{389, 389, "ME"}, // Montenegro
//{390, 390, "XK"}, // (Kosovo according to Wikipedia, unsourced)
{400, 440, "DE"}, // Germany
{450, 459, "JP"}, // Japan
{460, 469, "RU"}, // Russia
{470, 470, "KG"}, // Kyrgyzstan
{471, 471, "TW"}, // Taiwan
{474, 474, "EE"}, // Estonia
{475, 475, "LV"}, // Latvia
{476, 476, "AZ"}, // Azerbaijan
{477, 477, "LT"}, // Lithuania
{478, 478, "UZ"}, // Uzbekistan
{479, 479, "LK"}, // Sri Lanka
{480, 480, "PH"}, // Philippines
{481, 481, "BY"}, // Belarus
{482, 482, "UA"}, // Ukraine
{483, 483, "TM"}, // Turkmenistan
{484, 484, "MD"}, // Moldova
{485, 485, "AM"}, // Armenia
{486, 486, "GE"}, // Georgia
{487, 487, "KZ"}, // Kazakhstan
{488, 488, "TJ"}, // Tajikistan
{489, 489, "HK"}, // Hong Kong
{490, 499, "JP"}, // Japan
{500, 509, "GB"}, // UK
{520, 521, "GR"}, // Greece
{528, 528, "LB"}, // Lebanon
{529, 529, "CY"}, // Cyprus
{530, 530, "AL"}, // Albania
{531, 531, "MK"}, // North Macedonia
{535, 535, "MT"}, // Malta
{539, 539, "IE"}, // Ireland
{540, 549, "BE"}, // Belgium & Luxembourg
{560, 560, "PT"}, // Portugal
{569, 569, "IS"}, // Iceland
{570, 579, "DK"}, // Denmark (and Faroe Islands and Greenland according to Wikipedia)
{590, 590, "PL"}, // Poland
{594, 594, "RO"}, // Romania
{599, 599, "HU"}, // Hungary
{600, 601, "ZA"}, // South Africa
{603, 603, "GH"}, // Ghana
{604, 604, "SN"}, // Senegal
{608, 608, "BH"}, // Bahrain
{609, 609, "MU"}, // Mauritius
{611, 611, "MA"}, // Morocco
{613, 613, "DZ"}, // Algeria
{615, 615, "NG"}, // Nigeria
{616, 616, "KE"}, // Kenya
{617, 617, "CM"}, // Cameroon
{618, 618, "CI"}, // Côte d'Ivoire
{619, 619, "TN"}, // Tunisia
{620, 620, "TZ"}, // Tanzania
{621, 621, "SY"}, // Syria
{622, 622, "EG"}, // Egypt
{623, 623, "BN"}, // Brunei
{624, 624, "LY"}, // Libya
{625, 625, "JO"}, // Jordan
{626, 626, "IR"}, // Iran
{627, 627, "KW"}, // Kuwait
{628, 628, "SA"}, // Saudi Arabia
{629, 629, "AE"}, // United Arab Emirates
{630, 630, "QA"}, // Qatar
{631, 631, "NA"}, // Namibia
{640, 649, "FI"}, // Finland
{690, 699, "CN"}, // China
{700, 709, "NO"}, // Norway
{729, 729, "IL"}, // Israel
{730, 739, "SE"}, // Sweden
{740, 740, "GT"}, // Guatemala
{741, 741, "SV"}, // El Salvador
{742, 742, "HN"}, // Honduras
{743, 743, "NI"}, // Nicaragua
{744, 744, "CR"}, // Costa Rica
{745, 745, "PA"}, // Panama
{746, 746, "DO"}, // Dominican Republic
{750, 750, "MX"}, // Mexico
{754, 755, "CA"}, // Canada
{759, 759, "VE"}, // Venezuela
{760, 769, "CH"}, // Switzerland (and Liechtenstein according to Wikipedia)
{770, 771, "CO"}, // Colombia
{773, 773, "UY"}, // Uruguay
{775, 775, "PE"}, // Peru
{777, 777, "BO"}, // Bolivia
{778, 779, "AR"}, // Argentina
{780, 780, "CL"}, // Chile
{784, 784, "PY"}, // Paraguay
{786, 786, "EC"}, // Ecuador
{789, 790, "BR"}, // Brazil
{800, 839, "IT"}, // Italy (and San Marino and Vatican City according to Wikipedia)
{840, 849, "ES"}, // Spain (and Andorra according to Wikipedia)
{850, 850, "CU"}, // Cuba
{858, 858, "SK"}, // Slovakia
{859, 859, "CZ"}, // Czechia
{860, 860, "RS"}, // Serbia
{865, 865, "MN"}, // Mongolia
{867, 867, "KP"}, // North Korea
{868, 869, "TR"}, // Turkey
{870, 879, "NL"}, // Netherlands
{880, 880, "KR"}, // South Korea
{883, 883, "MM"}, // Myanmar
{884, 884, "KH"}, // Cambodia
{885, 885, "TH"}, // Thailand
{888, 888, "SG"}, // Singapore
{890, 890, "IN"}, // India
{893, 893, "VN"}, // Vietnam
{896, 896, "PK"}, // Pakistan
{899, 899, "ID"}, // Indonesia
{900, 919, "AT"}, // Austria
{930, 939, "AU"}, // Australia
{940, 949, "NZ"}, // New Zealand
{955, 955, "MY"}, // Malaysia
{958, 958, "MO"}, // Macao
//{960, 961, "GB"}, // Global Office - assigned to GS1 UK for GTIN-8 allocations (also 9620-9624999)
// clang-format on
};
std::string LookupCountryIdentifier(std::string_view GTIN, const BarcodeFormat format)
{
// Ignore add-on if any
const auto space = GTIN.find(' ');
const std::string::size_type size = space != std::string::npos ? space : GTIN.size();
if (size != 14 && size != 13 && size != 12 && size != 8)
return {};
// GTIN-14 leading packaging level indicator
const int first = size == 14 ? 1 : 0;
// UPC-A/E implicit leading 0
const int implicitZero = size == 12 || (size == 8 && format != BarcodeFormat::EAN8) ? 1 : 0;
if (size != 8 || format != BarcodeFormat::EAN8) { // Assuming following doesn't apply to EAN-8
// 0000000 Restricted Circulation Numbers; 0000001-0000099 unused to avoid collision with GTIN-8
int prefix = FromString<int>(GTIN.substr(first, 7 - implicitZero));
if (prefix >= 0 && prefix <= 99)
return {};
// 00001-00009 US
prefix = FromString<int>(GTIN.substr(first, 5 - implicitZero));
if (prefix >= 1 && prefix <= 9)
return "US";
// 0001-0009 US
prefix = FromString<int>(GTIN.substr(first, 4 - implicitZero));
if (prefix >= 1 && prefix <= 9)
return "US";
}
const int prefix = FromString<int>(GTIN.substr(first, 3 - implicitZero));
// Special case EAN-8 for prefix < 100 (GS1 General Specifications Figure 1.4.3-1)
if (size == 8 && format == BarcodeFormat::EAN8 && prefix <= 99) // Restricted Circulation Numbers
return {};
const auto it = std::lower_bound(std::begin(COUNTRIES), std::end(COUNTRIES), CountryId{0, narrow_cast<uint16_t>(prefix), ""});
return it != std::end(COUNTRIES) && prefix >= it->first && prefix <= it->last ? it->id : std::string();
}
std::string EanAddOn(const Barcode& barcode)
{
if (barcode.symbologyIdentifier() != "]E3")
return {};
return barcode.text().substr(barcode.format() == BarcodeFormat::EAN8 ? 8 : 13);
}
std::string IssueNr(const std::string& ean2AddOn)
{
if (ean2AddOn.size() != 2)
return {};
return std::to_string(std::stoi(ean2AddOn));
}
std::string Price(const std::string& ean5AddOn)
{
if (ean5AddOn.size() != 5)
return {};
std::string currency;
switch (ean5AddOn.front()) {
case '0': [[fallthrough]];
case '1': currency = "GBP £"; break; // UK
case '3': currency = "AUD $"; break; // AUS
case '4': currency = "NZD $"; break; // NZ
case '5': currency = "USD $"; break; // US
case '6': currency = "CAD $"; break; // CA
case '9':
// Reference: http://www.jollytech.com
if (ean5AddOn == "90000") // No suggested retail price
return {};
if (ean5AddOn == "99991") // Complementary
return "0.00";
if (ean5AddOn == "99990")
return "Used";
// Otherwise... unknown currency?
currency = "";
break;
default: currency = ""; break;
}
int rawAmount = std::stoi(ean5AddOn.substr(1));
#if !defined(__cpp_lib_to_chars) || !defined(__cpp_lib_format) // not available on older macOS / gcc 12
return currency + std::to_string(rawAmount / 100) + '.' + std::to_string(rawAmount % 100);
#else
return std::format("{}{:.2f}", currency, float(rawAmount) / 100);
#endif
}
} // namespace ZXing::GTIN

58
tools/chatgui/zxing-cpp/core/src/GTIN.h

@ -0,0 +1,58 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Barcode.h"
#include "BarcodeFormat.h"
#ifdef ZXING_INTERNAL
#include "ZXAlgorithms.h"
#endif
#include <string>
#include <string_view>
namespace ZXing::GTIN {
#ifdef ZXING_INTERNAL
template <typename T>
T ComputeCheckDigit(const std::basic_string<T>& digits, bool skipTail = false)
{
int sum = 0, N = Size(digits) - skipTail;
for (int i = N - 1; i >= 0; i -= 2)
sum += digits[i] - '0';
sum *= 3;
for (int i = N - 2; i >= 0; i -= 2)
sum += digits[i] - '0';
return ToDigit<T>((10 - (sum % 10)) % 10);
}
template <typename T>
bool IsCheckDigitValid(const std::basic_string<T>& s)
{
return ComputeCheckDigit(s, true) == s.back();
}
#endif
/**
* Evaluate the prefix of the GTIN to estimate the country of origin. See
* <a href="https://www.gs1.org/standards/id-keys/company-prefix">
* https://www.gs1.org/standards/id-keys/company-prefix</a> and
* <a href="https://en.wikipedia.org/wiki/List_of_GS1_country_codes">
* https://en.wikipedia.org/wiki/List_of_GS1_country_codes</a>.
*
* `format` required for EAN-8 (UPC-E assumed if not given)
*/
std::string LookupCountryIdentifier(std::string_view GTIN, BarcodeFormat format = BarcodeFormat::None);
std::string EanAddOn(const Barcode& barcode);
std::string IssueNr(const std::string& ean2AddOn);
std::string Price(const std::string& ean5AddOn);
} // namespace ZXing::GTIN

197
tools/chatgui/zxing-cpp/core/src/GlobalHistogramBinarizer.cpp

@ -0,0 +1,197 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "GlobalHistogramBinarizer.h"
#include "BitMatrix.h"
#include "Pattern.h"
#include "ZXConfig.h"
#include <algorithm>
#include <array>
#include <utility>
namespace ZXing {
static constexpr int LUMINANCE_BITS = 5;
static constexpr int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
static constexpr int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
using Histogram = std::array<uint16_t, LUMINANCE_BUCKETS>;
GlobalHistogramBinarizer::GlobalHistogramBinarizer(const ImageView& buffer) : BinaryBitmap(buffer) {}
GlobalHistogramBinarizer::~GlobalHistogramBinarizer() = default;
using ImageLineView = Range<StrideIter<const uint8_t*>>;
inline ImageLineView RowView(const ImageView& iv, int row)
{
return {{iv.data(0, row), iv.pixStride()}, {iv.data(iv.width(), row), iv.pixStride()}};
}
template <typename I>
static void ThresholdSharpened(Range<I> in, int threshold, std::vector<uint8_t>& out)
{
out.resize(in.size());
auto i = in.begin();
auto o = out.begin();
*o++ = (*i++ <= threshold) * BitMatrix::SET_V;
for (auto end = in.end() - 1; i != end; ++i)
*o++ = ((-i[-1] + (int(i[0]) * 4) - i[1]) / 2 <= threshold) * BitMatrix::SET_V;
*o++ = (*i++ <= threshold) * BitMatrix::SET_V;
}
template <typename I>
static auto GenHistogram(Range<I> line)
{
// This code causes about 20% of the total runtime on an AVX2 system for a EAN13 search on Lum input data.
// Trying to increase the performance by performing 2 or 4 "parallel" histograms helped nothing on the Intel
// system but was able to make the total runtime for that use case go down about 10% on an ARM M4.
#if 1
Histogram res0 = {}, res1 = {}, res2 = {}, res3 = {};
auto src = line.begin();
const auto end = line.end();
while (end - src >= 4) {
res0[*src++ >> LUMINANCE_SHIFT]++;
res1[*src++ >> LUMINANCE_SHIFT]++;
res2[*src++ >> LUMINANCE_SHIFT]++;
res3[*src++ >> LUMINANCE_SHIFT]++;
}
Histogram res = {};
for (int i = 0; i < LUMINANCE_BUCKETS; ++i)
res[i] = res0[i] + res1[i] + res2[i] + res3[i];
while (src != end)
res[*src++ >> LUMINANCE_SHIFT]++;
return res;
#else
Histogram res = {};
for (auto pix : line)
res[pix >> LUMINANCE_SHIFT]++;
return res;
#endif
}
// Return -1 on error
static int EstimateBlackPoint(const Histogram& buckets)
{
// Find the tallest peak in the histogram.
auto firstPeakPos = std::max_element(buckets.begin(), buckets.end());
int firstPeak = narrow_cast<int>(firstPeakPos - buckets.begin());
int firstPeakSize = *firstPeakPos;
int maxBucketCount = firstPeakSize;
// Find the second-tallest peak which is somewhat far from the tallest peak.
int secondPeak = 0;
int secondPeakScore = 0;
for (int x = 0; x < Size(buckets); x++) {
int distanceToBiggest = x - firstPeak;
// Encourage more distant second peaks by multiplying by square of distance.
int score = buckets[x] * distanceToBiggest * distanceToBiggest;
if (score > secondPeakScore) {
secondPeak = x;
secondPeakScore = score;
}
}
// Make sure firstPeak corresponds to the black peak.
if (firstPeak > secondPeak) {
std::swap(firstPeak, secondPeak);
}
// If there is too little contrast in the image to pick a meaningful black point, throw rather
// than waste time trying to decode the image, and risk false positives.
if (secondPeak - firstPeak <= LUMINANCE_BUCKETS / 16) {
return -1;
}
// Find a valley between them that is low and closer to the white peak.
int bestValley = secondPeak - 1;
int bestValleyScore = -1;
for (int x = secondPeak - 1; x > firstPeak; x--) {
int fromFirst = x - firstPeak;
int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]);
if (score > bestValleyScore) {
bestValley = x;
bestValleyScore = score;
}
}
return bestValley << LUMINANCE_SHIFT;
}
bool GlobalHistogramBinarizer::getPatternRow(int row, int rotation, PatternRow& res) const
{
auto buffer = _buffer.rotated(rotation);
auto lineView = RowView(buffer, row);
if (buffer.width() < 3)
return false; // special casing the code below for a width < 3 makes no sense
#if defined(__AVX__) || defined(__ARM_NEON)
// If we are extracting a column (instead of a row), we run into cache misses on every pixel access both
// during the histogram calculation and during the sharpen+threshold operation. Additionally, if we
// perform the ThresholdSharpened function on pixStride==1 data, the auto-vectorizer makes that part
// 8x faster on an AVX2 cpu which easily recovers the extra cost that we pay for the copying.
ZX_THREAD_LOCAL std::vector<uint8_t> line;
if (std::abs(buffer.pixStride()) > 4) {
line.resize(lineView.size());
std::copy(lineView.begin(), lineView.end(), line.begin());
lineView = {{line.data(), 1}, {line.data() + line.size(), 1}};
}
#endif
auto packedLineView =
lineView.begin().stride == 1 ? Range(lineView.begin().pos, lineView.end().pos) : Range<const uint8_t*>(nullptr, nullptr);
auto histogram = packedLineView ? GenHistogram(packedLineView) : GenHistogram(lineView);
auto threshold = EstimateBlackPoint(histogram) - 1;
if (threshold <= 0)
return false;
ZX_THREAD_LOCAL std::vector<uint8_t> binarized;
// the optimizer can generate a specialized version for pixStride==1 (non-rotated input) that is about 8x faster on AVX2 hardware
if (packedLineView)
ThresholdSharpened(packedLineView, threshold, binarized);
else
ThresholdSharpened(lineView, threshold, binarized);
GetPatternRow(Range(binarized), res);
return true;
}
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
std::shared_ptr<const BitMatrix>
GlobalHistogramBinarizer::getBlackMatrix() const
{
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
// more robust on the blackbox tests than sampling a diagonal as we used to do.
Histogram localBuckets = {};
{
for (int y = 1; y < 5; y++) {
int row = height() * y / 5;
const uint8_t* luminances = _buffer.data(0, row);
int right = (width() * 4) / 5;
for (int x = width() / 5; x < right; x++)
localBuckets[luminances[x] >> LUMINANCE_SHIFT]++;
}
}
int blackPoint = EstimateBlackPoint(localBuckets);
if (blackPoint <= 0)
return {};
return std::make_shared<const BitMatrix>(binarize(blackPoint));
}
} // ZXing

34
tools/chatgui/zxing-cpp/core/src/GlobalHistogramBinarizer.h

@ -0,0 +1,34 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BinaryBitmap.h"
namespace ZXing {
/**
* This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
* for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
* algorithm. However, because it picks a global black point, it cannot handle difficult shadows
* and gradients.
*
* Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
*
* @author dswitkin@google.com (Daniel Switkin)
* @author Sean Owen
*/
class GlobalHistogramBinarizer : public BinaryBitmap
{
public:
explicit GlobalHistogramBinarizer(const ImageView& buffer);
~GlobalHistogramBinarizer() override;
bool getPatternRow(int row, int rotation, PatternRow &res) const override;
std::shared_ptr<const BitMatrix> getBlackMatrix() const override;
};
} // ZXing

124
tools/chatgui/zxing-cpp/core/src/GridSampler.cpp

@ -0,0 +1,124 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "GridSampler.h"
#ifdef PRINT_DEBUG
#include "LogMatrix.h"
#include "BitMatrixIO.h"
#endif
namespace ZXing {
#ifdef PRINT_DEBUG
LogMatrix log;
#endif
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const PerspectiveTransform& mod2Pix)
{
return SampleGrid(image, width, height, {ROI{0, width, 0, height, mod2Pix}});
}
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const ROIs& rois)
{
#ifdef PRINT_DEBUG
LogMatrix log;
static int i = 0;
LogMatrixWriter lmw(log, image, 5, "grid" + std::to_string(i++) + ".pnm");
#endif
if (width <= 0 || height <= 0)
return {};
for (auto&& [x0, x1, y0, y1, mod2Pix] : rois) {
// Precheck the corners of every roi to bail out early if the grid is "obviously" not completely inside the image
auto isInside = [&mod2Pix = mod2Pix, &image](int x, int y) { return image.isIn(mod2Pix(centered(PointI(x, y)))); };
if (!mod2Pix.isValid() || !isInside(x0, y0) || !isInside(x1 - 1, y0) || !isInside(x1 - 1, y1 - 1) || !isInside(x0, y1 - 1))
return {};
}
BitMatrix res(width, height);
for (auto&& [x0, x1, y0, y1, mod2Pix] : rois) {
for (int y = y0; y < y1; ++y)
for (int x = x0; x < x1; ++x) {
auto p = mod2Pix(centered(PointI{x, y}));
// Due to a "numerical instability" in the PerspectiveTransform generation/application it has been observed
// that even though all boundary grid points get projected inside the image, it can still happen that an
// inner grid points is not. See #563. A true perspective transformation cannot have this property.
// The following check takes 100% care of the issue and turned out to be less of a performance impact than feared.
// TODO: Check some mathematical/numercial property of mod2Pix to determine if it is a perspective transformation.
if (!image.isIn(p))
return {};
#ifdef PRINT_DEBUG
log(p, 3);
#endif
#if 0
int sum = 0;
for (int dy = -1; dy <= 1; ++dy)
for (int dx = -1; dx <= 1; ++dx)
sum += image.get(p + PointF(dx, dy));
if (sum >= 5)
#else
if (image.get(p))
#endif
res.set(x, y);
}
}
#ifdef PRINT_DEBUG
printf("width: %d, height: %d\n", width, height);
// printf("%s", ToString(res).c_str());
#endif
auto projectCorner = [&](PointI p) {
for (auto&& [x0, x1, y0, y1, mod2Pix] : rois)
if (x0 <= p.x && p.x <= x1 && y0 <= p.y && p.y <= y1)
return PointI(mod2Pix(PointF(p)) + PointF(0.5, 0.5));
return PointI();
};
return {std::move(res),
{projectCorner({0, 0}), projectCorner({width, 0}), projectCorner({width, height}), projectCorner({0, height})}};
}
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const PerspectiveTransform& mod2Pix,
Matrix<std::optional<PointF>>&& apP, const std::vector<int>& apMX, const std::vector<int>& apMY)
{
const int W = Size(apMX) - 1, H = Size(apMY) - 1;
// go over the set of alignment patters and fill any remaining gaps by a projection based on the fallback
// mod2Pix projection. This works if the symbol is flat, which is a reasonable fall-back assumption.
for (int y = 0; y <= H; ++y)
for (int x = 0; x <= W; ++x) {
if (apP(x, y)) {
#ifdef PRINT_DEBUG
log(*apP(x, y), 2);
#endif
} else {
#ifdef PRINT_DEBUG
printf("locate failed at %dx%d\n", x, y);
#endif
// project the alignment pattern at module coordinates x/y to pixel coordinate based on current mod2Pix
apP.set(x, y, mod2Pix(centered(PointI(apMX[x], apMY[y]))));
}
}
// assemble a list of region-of-interests based on the found alignment pattern pixel positions
ROIs rois;
for (int y = 0; y < H; ++y)
for (int x = 0; x < W; ++x) {
int x0 = apMX[x], x1 = apMX[x + 1], y0 = apMY[y], y1 = apMY[y + 1];
rois.push_back({x == 0 ? 0 : x0, (x == W - 1) ? width : x1, (y == 0) ? 0 : y0, (y == H - 1) ? height : y1,
PerspectiveTransform{Rectangle(x0, x1, y0, y1, 0.5),
{*apP(x, y), *apP(x + 1, y), *apP(x + 1, y + 1), *apP(x, y + 1)}}});
}
return SampleGrid(image, width, height, rois);
}
} // ZXing

59
tools/chatgui/zxing-cpp/core/src/GridSampler.h

@ -0,0 +1,59 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "DetectorResult.h"
#include "Matrix.h"
#include "PerspectiveTransform.h"
#include <optional>
#include <vector>
namespace ZXing {
/**
* Samples an image for a rectangular matrix of bits of the given dimension. The sampling
* transformation is determined by the coordinates of 4 points, in the original and transformed
* image space.
*
* The following figure is showing the layout a 'pixel'. The point (0,0) is the upper left corner
* of the first pixel. (1,1) is its lower right corner.
*
* 0 1 ... w
* 0 #----#-- ... --#
* | | ... |
* | | ... |
* 1 #----# ... --#
* | | ... |
*
* | | ... |
* h #----#-- ... --#
*
* @param image image to sample
* @param width width of {@link BitMatrix} to sample from image
* @param height height of {@link BitMatrix} to sample from image
* @param mod2Pix transforming a module (grid) coordinate into an image (pixel) coordinate
* @return {@link DetectorResult} representing a grid of points sampled from the image within a region
* defined by the "src" parameters. Result is empty if transformation is invalid (out of bound access).
*/
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const PerspectiveTransform& mod2Pix);
class ROI
{
public:
int x0, x1, y0, y1;
PerspectiveTransform mod2Pix;
};
using ROIs = std::vector<ROI>;
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const ROIs& rois);
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const PerspectiveTransform& mod2Pix,
Matrix<std::optional<PointF>>&& apP, const std::vector<int>& apMX, const std::vector<int>& apMY);
} // ZXing

343
tools/chatgui/zxing-cpp/core/src/HRI.cpp

@ -0,0 +1,343 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "HRI.h"
#include "ZXAlgorithms.h"
#include <cmath>
#include <cstring>
#include <string_view>
namespace ZXing {
struct AiInfo
{
const char aiPrefix[5];
int8_t _fieldSize; // if negative, the length is variable and abs(length) give the max size
bool isVariableLength() const noexcept { return _fieldSize < 0; }
int fieldSize() const noexcept { return std::abs(_fieldSize); }
int aiSize() const
{
using namespace std::literals;
if ((aiPrefix[0] == '3' && Contains("1234569", aiPrefix[1])) || aiPrefix == "703"sv || aiPrefix == "723"sv)
return 4;
else
return narrow_cast<int>(strlen(aiPrefix));
}
};
// https://github.com/gs1/gs1-syntax-dictionary 2025-12-08 HEAD [c7e0c5b]
static const AiInfo aiInfos[] = {
//TWO_DIGIT_DATA_LENGTH
{ "00", 18 },
{ "01", 14 },
{ "02", 14 },
{ "03", 14 },
{ "10", -20 },
{ "11", 6 },
{ "12", 6 },
{ "13", 6 },
{ "15", 6 },
{ "16", 6 },
{ "17", 6 },
{ "20", 2 },
{ "21", -20 },
{ "22", -20 },
{ "30", -8 },
{ "37", -8 },
{ "90", -30 },
{ "91", -90 },
{ "92", -90 },
{ "93", -90 },
{ "94", -90 },
{ "95", -90 },
{ "96", -90 },
{ "97", -90 },
{ "98", -90 },
{ "99", -90 },
//THREE_DIGIT_DATA_LENGTH
{ "235", -28 },
{ "240", -30 },
{ "241", -30 },
{ "242", -6 },
{ "243", -20 },
{ "250", -30 },
{ "251", -30 },
{ "253", -30 },
{ "254", -20 },
{ "255", -25 },
{ "400", -30 },
{ "401", -30 },
{ "402", 17 },
{ "403", -30 },
{ "410", 13 },
{ "411", 13 },
{ "412", 13 },
{ "413", 13 },
{ "414", 13 },
{ "415", 13 },
{ "416", 13 },
{ "417", 13 },
{ "420", -20 },
{ "421", -12 },
{ "422", 3 },
{ "423", -15 },
{ "424", 3 },
{ "425", -15 },
{ "426", 3 },
{ "427", -3 },
{ "710", -20 },
{ "711", -20 },
{ "712", -20 },
{ "713", -20 },
{ "714", -20 },
{ "715", -20 },
{ "716", -20 },
{ "717", -20 },
//THREE_DIGIT_PLUS_DIGIT_DATA_LENGTH
{ "310", 6 },
{ "311", 6 },
{ "312", 6 },
{ "313", 6 },
{ "314", 6 },
{ "315", 6 },
{ "316", 6 },
{ "320", 6 },
{ "321", 6 },
{ "322", 6 },
{ "323", 6 },
{ "324", 6 },
{ "325", 6 },
{ "326", 6 },
{ "327", 6 },
{ "328", 6 },
{ "329", 6 },
{ "330", 6 },
{ "331", 6 },
{ "332", 6 },
{ "333", 6 },
{ "334", 6 },
{ "335", 6 },
{ "336", 6 },
{ "337", 6 },
{ "340", 6 },
{ "341", 6 },
{ "342", 6 },
{ "343", 6 },
{ "344", 6 },
{ "345", 6 },
{ "346", 6 },
{ "347", 6 },
{ "348", 6 },
{ "349", 6 },
{ "350", 6 },
{ "351", 6 },
{ "352", 6 },
{ "353", 6 },
{ "354", 6 },
{ "355", 6 },
{ "356", 6 },
{ "357", 6 },
{ "360", 6 },
{ "361", 6 },
{ "362", 6 },
{ "363", 6 },
{ "364", 6 },
{ "365", 6 },
{ "366", 6 },
{ "367", 6 },
{ "368", 6 },
{ "369", 6 },
{ "390", -15 },
{ "391", -18 },
{ "392", -15 },
{ "393", -18 },
{ "394", 4 },
{ "395", 6 },
{ "703", -30 },
{ "723", -30 },
//FOUR_DIGIT_DATA_LENGTH
{ "4300", -35 },
{ "4301", -35 },
{ "4302", -70 },
{ "4303", -70 },
{ "4304", -70 },
{ "4305", -70 },
{ "4306", -70 },
{ "4307", 2 },
{ "4308", -30 },
{ "4309", 20 },
{ "4310", -35 },
{ "4311", -35 },
{ "4312", -70 },
{ "4313", -70 },
{ "4314", -70 },
{ "4315", -70 },
{ "4316", -70 },
{ "4317", 2 },
{ "4318", -20 },
{ "4319", -30 },
{ "4320", -35 },
{ "4321", 1 },
{ "4322", 1 },
{ "4323", 1 },
{ "4324", 10 },
{ "4325", 10 },
{ "4326", 6 },
{ "4330", -7 },
{ "4331", -7 },
{ "4332", -7 },
{ "4333", -7 },
{ "7001", 13 },
{ "7002", -30 },
{ "7003", 10 },
{ "7004", -4 },
{ "7005", -12 },
{ "7006", 6 },
{ "7007", -12 },
{ "7008", -3 },
{ "7009", -10 },
{ "7010", -2 },
{ "7011", -10 },
{ "7020", -20 },
{ "7021", -20 },
{ "7022", -20 },
{ "7023", -30 },
{ "7040", 4 },
{ "7041", -4 },
{ "7240", -20 },
{ "7241", 2 },
{ "7242", -25 },
{ "7250", 8 },
{ "7251", 12 },
{ "7252", 1 },
{ "7253", -40 },
{ "7254", -40 },
{ "7255", -10 },
{ "7256", -90 },
{ "7257", -70 },
{ "7258", 3 },
{ "7259", -40 },
{ "8001", 14 },
{ "8002", -20 },
{ "8003", -30 },
{ "8004", -30 },
{ "8005", 6 },
{ "8006", 18 },
{ "8007", -34 },
{ "8008", -12 },
{ "8009", -50 },
{ "8010", -30 },
{ "8011", -12 },
{ "8012", -20 },
{ "8013", -25 },
{ "8014", -25 },
{ "8017", 18 },
{ "8018", 18 },
{ "8019", -10 },
{ "8020", -25 },
{ "8026", 18 },
{ "8030", -90 },
{ "8040", 15 },
{ "8041", 15 },
{ "8042", 32 },
{ "8043", -20 },
{ "8110", -70 },
{ "8111", 4 },
{ "8112", -70 },
{ "8200", -70 },
};
std::string HRIFromGS1(std::string_view gs1)
{
constexpr char GS = 29; // GS character (29 / 0x1D)
std::string_view rem = gs1;
std::string res;
while (rem.size()) {
const AiInfo* i = FindIf(aiInfos, [&](const AiInfo& i) { return rem.starts_with(i.aiPrefix); });
if (i == std::end(aiInfos))
return {};
int aiSize = i->aiSize();
if (Size(rem) < aiSize)
return {};
res += '(';
res += rem.substr(0, aiSize);
res += ')';
rem.remove_prefix(aiSize);
int fieldSize = i->fieldSize();
if (i->isVariableLength()) {
auto gsPos = rem.find(GS);
#if 1
fieldSize = std::min(gsPos == std::string_view::npos ? Size(rem) : narrow_cast<int>(gsPos), fieldSize);
#else
// TODO: ignore the 'max field size' part for now as it breaks rssexpanded-3/13.png?
fieldSize = gsPos == std::string_view::npos ? Size(rem) : narrow_cast<int>(gsPos);
#endif
}
if (fieldSize == 0 || Size(rem) < fieldSize)
return {};
res += rem.substr(0, fieldSize);
rem.remove_prefix(fieldSize);
// See General Specification v22.0 Section 7.8.6.3: "...the processing routine SHALL tolerate a single separator character
// immediately following any element string, whether necessary or not..."
if (Size(rem) && rem.front() == GS)
rem.remove_prefix(1);
}
return res;
}
std::string HRIFromISO15434(std::string_view str)
{
// Use available unicode symbols to simulate sub- and superscript letters as specified in
// ISO/IEC 15434:2019(E) 6. Human readable representation
std::string res;
res.reserve(str.size());
for (char c : str) {
#if 1
if (!((c) & ~0x1f) || c == 0x20)
(res += "\xe2\x90") += char(0x80 + c); // Unicode Block “Control Pictures”: 0x2400
else
res += c;
#else
switch (c) {
case 4: oss << u8"\u1d31\u1d52\u209c"; break; // EOT
case 28: oss << u8"\ua7f3\u209b"; break; // FS
case 29: oss << u8"\u1d33\u209b"; break; // GS
case 30: oss << u8"\u1d3f\u209b"; break; // RS
case 31: oss << u8"\u1d41\u209b"; break; // US
default: oss << c;
}
#endif
}
return res;
}
} // namespace ZXing

16
tools/chatgui/zxing-cpp/core/src/HRI.h

@ -0,0 +1,16 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string>
#include <string_view>
namespace ZXing {
std::string HRIFromGS1(std::string_view gs1);
std::string HRIFromISO15434(std::string_view str);
} // namespace ZXing

315
tools/chatgui/zxing-cpp/core/src/HybridBinarizer.cpp

@ -0,0 +1,315 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "HybridBinarizer.h"
#include "BitMatrix.h"
#include "Matrix.h"
#include <algorithm>
#include <cstdint>
#include <fstream>
#include <memory>
#define USE_NEW_ALGORITHM
namespace ZXing {
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
// So this is the smallest dimension in each axis we can accept.
static constexpr int BLOCK_SIZE = 8;
static constexpr int WINDOW_SIZE = BLOCK_SIZE * (1 + 2 * 2);
static constexpr int MIN_DYNAMIC_RANGE = 24;
HybridBinarizer::HybridBinarizer(const ImageView& iv) : GlobalHistogramBinarizer(iv) {}
HybridBinarizer::~HybridBinarizer() = default;
bool HybridBinarizer::getPatternRow(int row, int rotation, PatternRow& res) const
{
#if 1
// This is the original "hybrid" behavior: use GlobalHistogram for the 1D case
return GlobalHistogramBinarizer::getPatternRow(row, rotation, res);
#else
// This is an alternative that can be faster in general and perform better in unevenly lit situations like
// https://github.com/zxing-cpp/zxing-cpp/blob/master/test/samples/ean13-2/21.png. That said, it fairs
// worse in borderline low resolution situations. With the current black box sample set we'd loose 94
// test cases while gaining 53 others.
auto bits = getBitMatrix();
if (bits)
GetPatternRow(*bits, row, res, rotation % 180 != 0);
return bits != nullptr;
#endif
}
using T_t = uint8_t;
/**
* Applies a single threshold to a block of pixels.
*/
static void ThresholdBlock(const uint8_t* __restrict luminances, int xoffset, int yoffset, T_t threshold, int rowStride,
BitMatrix& matrix)
{
for (int y = yoffset; y < yoffset + BLOCK_SIZE; ++y) {
auto* src = luminances + y * rowStride + xoffset;
auto* const dstBegin = matrix.row(y).begin() + xoffset;
// TODO: fix pixelStride > 1 case
for (auto* dst = dstBegin; dst < dstBegin + BLOCK_SIZE; ++dst, ++src)
*dst = (*src <= threshold) * BitMatrix::SET_V;
}
}
#ifndef USE_NEW_ALGORITHM
/**
* Calculates a single black point for each block of pixels and saves it away.
* See the following thread for a discussion of this algorithm:
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
*/
static Matrix<T_t> CalculateBlackPoints(const uint8_t* __restrict luminances, int subWidth, int subHeight, int width, int height,
int rowStride)
{
Matrix<T_t> blackPoints(subWidth, subHeight);
for (int y = 0; y < subHeight; y++) {
int yoffset = std::min(y * BLOCK_SIZE, height - BLOCK_SIZE);
for (int x = 0; x < subWidth; x++) {
int xoffset = std::min(x * BLOCK_SIZE, width - BLOCK_SIZE);
int sum = 0;
uint8_t min = luminances[yoffset * rowStride + xoffset];
uint8_t max = min;
for (int yy = 0, offset = yoffset * rowStride + xoffset; yy < BLOCK_SIZE; yy++, offset += rowStride) {
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
auto pixel = luminances[offset + xx];
sum += pixel;
if (pixel < min)
min = pixel;
if (pixel > max)
max = pixel;
}
// short-circuit min/max tests once dynamic range is met
if (max - min > MIN_DYNAMIC_RANGE) {
// finish the rest of the rows quickly
for (yy++, offset += rowStride; yy < BLOCK_SIZE; yy++, offset += rowStride) {
for (int xx = 0; xx < BLOCK_SIZE; xx++) {
sum += luminances[offset + xx];
}
}
}
}
// The default estimate is the average of the values in the block.
int average = sum / (BLOCK_SIZE * BLOCK_SIZE);
if (max - min <= MIN_DYNAMIC_RANGE) {
// If variation within the block is low, assume this is a block with only light or only
// dark pixels. In that case we do not want to use the average, as it would divide this
// low contrast area into black and white pixels, essentially creating data out of noise.
//
// The default assumption is that the block is light/background. Since no estimate for
// the level of dark pixels exists locally, use half the min for the block.
average = min / 2;
if (y > 0 && x > 0) {
// Correct the "white background" assumption for blocks that have neighbors by comparing
// the pixels in this block to the previously calculated black points. This is based on
// the fact that dark barcode symbology is always surrounded by some amount of light
// background for which reasonable black point estimates were made. The bp estimated at
// the boundaries is used for the interior.
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
int averageNeighborBlackPoint =
(blackPoints(x, y - 1) + (2 * blackPoints(x - 1, y)) + blackPoints(x - 1, y - 1)) / 4;
if (min < averageNeighborBlackPoint) {
average = averageNeighborBlackPoint;
}
}
}
blackPoints(x, y) = average;
}
}
return blackPoints;
}
/**
* For each block in the image, calculate the average black point using a 5x5 grid
* of the blocks around it. Also handles the corner cases (fractional blocks are computed based
* on the last pixels in the row/column which are also used in the previous block).
*/
static std::shared_ptr<BitMatrix> CalculateMatrix(const uint8_t* __restrict luminances, int subWidth, int subHeight, int width,
int height, int rowStride, const Matrix<T_t>& blackPoints)
{
auto matrix = std::make_shared<BitMatrix>(width, height);
#ifdef PRINT_DEBUG
Matrix<uint8_t> out(width, height);
Matrix<uint8_t> out2(width, height);
#endif
for (int y = 0; y < subHeight; y++) {
int yoffset = std::min(y * BLOCK_SIZE, height - BLOCK_SIZE);
for (int x = 0; x < subWidth; x++) {
int xoffset = std::min(x * BLOCK_SIZE, width - BLOCK_SIZE);
int left = std::clamp(x, 2, subWidth - 3);
int top = std::clamp(y, 2, subHeight - 3);
int sum = 0;
for (int dy = -2; dy <= 2; ++dy) {
for (int dx = -2; dx <= 2; ++dx) {
sum += blackPoints(left + dx, top + dy);
}
}
int average = sum / 25;
ThresholdBlock(luminances, xoffset, yoffset, average, rowStride, *matrix);
#ifdef PRINT_DEBUG
for (int yy = 0; yy < 8; ++yy)
for (int xx = 0; xx < 8; ++xx) {
out.set(xoffset + xx, yoffset + yy, blackPoints(x, y));
out2.set(xoffset + xx, yoffset + yy, average);
}
#endif
}
}
#ifdef PRINT_DEBUG
std::ofstream file("thresholds.pnm");
file << "P5\n" << out.width() << ' ' << out.height() << "\n255\n";
file.write(reinterpret_cast<const char*>(out.data()), out.size());
std::ofstream file2("thresholds_avg.pnm");
file2 << "P5\n" << out.width() << ' ' << out.height() << "\n255\n";
file2.write(reinterpret_cast<const char*>(out2.data()), out2.size());
#endif
return matrix;
}
#else
// Subdivide the image in blocks of BLOCK_SIZE and calculate one threshold value per block as
// (max - min > MIN_DYNAMIC_RANGE) ? (max + min) / 2 : 0
static Matrix<T_t> BlockThresholds(const ImageView iv)
{
int subWidth = (iv.width() + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(width/BS)
int subHeight = (iv.height() + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(height/BS)
Matrix<T_t> thresholds(subWidth, subHeight);
for (int y = 0; y < subHeight; y++) {
int y0 = std::min(y * BLOCK_SIZE, iv.height() - BLOCK_SIZE);
for (int x = 0; x < subWidth; x++) {
int x0 = std::min(x * BLOCK_SIZE, iv.width() - BLOCK_SIZE);
uint8_t min = 255;
uint8_t max = 0;
for (int yy = 0; yy < BLOCK_SIZE; yy++) {
auto line = iv.data(x0, y0 + yy);
for (int xx = 0; xx < BLOCK_SIZE; xx++)
UpdateMinMax(min, max, line[xx]);
}
thresholds(x, y) = (max - min > MIN_DYNAMIC_RANGE) ? (int(max) + min) / 2 : 0;
}
}
return thresholds;
}
// Apply gaussian-like smoothing filter over all non-zero thresholds and fill any remaining gaps with nearest neighbor
static Matrix<T_t> SmoothThresholds(Matrix<T_t>&& in)
{
Matrix<T_t> out(in.width(), in.height());
constexpr int R = WINDOW_SIZE / BLOCK_SIZE / 2;
for (int y = 0; y < in.height(); y++) {
for (int x = 0; x < in.width(); x++) {
int left = std::clamp(x, R, in.width() - R - 1);
int top = std::clamp(y, R, in.height() - R - 1);
int sum = in(x, y) * 2;
int n = (sum > 0) * 2;
auto add = [&](int x, int y) {
int t = in(x, y);
sum += t;
n += t > 0;
};
for (int dy = -R; dy <= R; ++dy)
for (int dx = -R; dx <= R; ++dx)
add(left + dx, top + dy);
out(x, y) = n > 0 ? sum / n : 0;
}
}
// flood fill any remaining gaps of (very large) no-contrast regions
auto last = out.begin() - 1;
for (auto* i = out.begin(); i != out.end(); ++i) {
if (*i) {
if (last != i - 1)
std::fill(last + 1, i, *i);
last = i;
}
}
std::fill(last + 1, out.end(), *(std::max(last, out.begin())));
return out;
}
static std::shared_ptr<BitMatrix> ThresholdImage(const ImageView iv, const Matrix<T_t>& thresholds)
{
auto matrix = std::make_shared<BitMatrix>(iv.width(), iv.height());
#ifdef PRINT_DEBUG
Matrix<uint8_t> out(iv.width(), iv.height());
#endif
for (int y = 0; y < thresholds.height(); y++) {
int yoffset = std::min(y * BLOCK_SIZE, iv.height() - BLOCK_SIZE);
for (int x = 0; x < thresholds.width(); x++) {
int xoffset = std::min(x * BLOCK_SIZE, iv.width() - BLOCK_SIZE);
ThresholdBlock(iv.data(), xoffset, yoffset, thresholds(x, y), iv.rowStride(), *matrix);
#ifdef PRINT_DEBUG
for (int yy = 0; yy < 8; ++yy)
for (int xx = 0; xx < 8; ++xx)
out.set(xoffset + xx, yoffset + yy, thresholds(x, y));
#endif
}
}
#ifdef PRINT_DEBUG
std::ofstream file("thresholds_new.pnm");
file << "P5\n" << out.width() << ' ' << out.height() << "\n255\n";
file.write(reinterpret_cast<const char*>(out.data()), out.size());
#endif
return matrix;
}
#endif
std::shared_ptr<const BitMatrix> HybridBinarizer::getBlackMatrix() const
{
if (width() >= WINDOW_SIZE && height() >= WINDOW_SIZE) {
#ifdef USE_NEW_ALGORITHM
auto thrs = SmoothThresholds(BlockThresholds(_buffer));
if (std::ranges::max(thrs) == 0)
return GlobalHistogramBinarizer::getBlackMatrix();
return ThresholdImage(_buffer, thrs);
#else
const uint8_t* luminances = _buffer.data();
int subWidth = (width() + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(width/BS)
int subHeight = (height() + BLOCK_SIZE - 1) / BLOCK_SIZE; // ceil(height/BS)
auto blackPoints =
CalculateBlackPoints(luminances, subWidth, subHeight, width(), height(), _buffer.rowStride());
return CalculateMatrix(luminances, subWidth, subHeight, width(), height(), _buffer.rowStride(), blackPoints);
#endif
} else {
// If the image is too small, fall back to the global histogram approach.
return GlobalHistogramBinarizer::getBlackMatrix();
}
}
} // ZXing

40
tools/chatgui/zxing-cpp/core/src/HybridBinarizer.h

@ -0,0 +1,40 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "GlobalHistogramBinarizer.h"
namespace ZXing {
/**
* This class implements a local thresholding algorithm, which while slower than the
* GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
* high frequency images of barcodes with black data on white backgrounds. For this application,
* it does a much better job than a global blackpoint with severe shadows and gradients.
* However it tends to produce artifacts on lower frequency images and is therefore not
* a good general purpose binarizer for uses outside ZXing.
*
* This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
* and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
* inherently local, and only fails for horizontal gradients. We can revisit that problem later,
* but for now it was not a win to use local blocks for 1D.
*
* This Binarizer is the default for the unit tests and the recommended class for library users.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
class HybridBinarizer : public GlobalHistogramBinarizer
{
public:
explicit HybridBinarizer(const ImageView& iv);
~HybridBinarizer() override;
bool getPatternRow(int row, int rotation, PatternRow &res) const override;
std::shared_ptr<const BitMatrix> getBlackMatrix() const override;
};
} // ZXing

168
tools/chatgui/zxing-cpp/core/src/ImageView.h

@ -0,0 +1,168 @@
/*
* Copyright 2019 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <stdexcept>
namespace ZXing {
/// @brief Supported image formats for ImageView. The format encodes the pixel format and layout information.
enum class ImageFormat : uint32_t
{
None = 0,
Lum = 0x01000000,
LumA = 0x02000000,
RGB = 0x03000102,
BGR = 0x03020100,
RGBA = 0x04000102,
ARGB = 0x04010203,
BGRA = 0x04020100,
ABGR = 0x04030201,
RGBX [[deprecated("use RGBA")]] = RGBA,
XRGB [[deprecated("use ARGB")]] = ARGB,
BGRX [[deprecated("use BGRA")]] = BGRA,
XBGR [[deprecated("use ABGR")]] = ABGR,
};
/// @cond INTERNAL
constexpr inline int PixStride(ImageFormat format) { return (static_cast<uint32_t>(format) >> 3*8) & 0xFF; }
constexpr inline int RedIndex(ImageFormat format) { return (static_cast<uint32_t>(format) >> 2*8) & 0xFF; }
constexpr inline int GreenIndex(ImageFormat format) { return (static_cast<uint32_t>(format) >> 1*8) & 0xFF; }
constexpr inline int BlueIndex(ImageFormat format) { return (static_cast<uint32_t>(format) >> 0*8) & 0xFF; }
constexpr inline uint8_t RGBToLum(unsigned r, unsigned g, unsigned b)
{
// .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),
// (306*R) >> 10 is approximately equal to R*0.299, and so on.
// 0x200 >> 10 is 0.5, it implements rounding.
return static_cast<uint8_t>((306 * r + 601 * g + 117 * b + 0x200) >> 10);
}
/// @endcond
/**
* @brief Simple class that stores a non-owning const pointer to image data plus layout and format information.
*/
class ImageView
{
/// @cond INTERNAL
protected:
const uint8_t* _data = nullptr;
ImageFormat _format = ImageFormat::None;
int _width = 0, _height = 0, _pixStride = 0, _rowStride = 0;
/// @endcond
public:
/**
* ImageView default constructor creates a 'null' image view
*/
ImageView() = default;
/**
* ImageView constructor
*
* @param data pointer to image buffer
* @param width image width in pixels
* @param height image height in pixels
* @param format image/pixel format
* @param rowStride optional row stride in bytes, default is width * pixStride
* @param pixStride optional pixel stride in bytes, default is calculated from format
*/
ImageView(const uint8_t* data, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0)
: _data(data),
_format(format),
_width(width),
_height(height),
_pixStride(pixStride ? pixStride : PixStride(format)),
_rowStride(rowStride ? rowStride : width * _pixStride)
{
if (_data == nullptr)
throw std::invalid_argument("Can not construct an ImageView from a NULL pointer");
if (_width <= 0 || _height <= 0)
throw std::invalid_argument("Neither width nor height of ImageView can be less or equal to 0");
}
/**
* ImageView constructor with bounds checking.
*
* This constructor checks if the provided size is consistent with the width, height, row stride and pixel stride
* parameters to prevent out of bounds access when using the ImageView.
*
* @param data pointer to image buffer
* @param size size of the image buffer in bytes
* @param width image width in pixels
* @param height image height in pixels
* @param format image/pixel format
* @param rowStride optional row stride in bytes, default is width * pixStride
* @param pixStride optional pixel stride in bytes, default is calculated from format
*/
ImageView(const uint8_t* data, int size, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0)
: ImageView(data, width, height, format, rowStride, pixStride)
{
if (_rowStride < 0 || _pixStride < 0 || size < _height * _rowStride)
throw std::invalid_argument("ImageView parameters are inconsistent (out of bounds)");
}
int width() const { return _width; }
int height() const { return _height; }
int pixStride() const { return _pixStride; }
int rowStride() const { return _rowStride; }
ImageFormat format() const { return _format; }
const uint8_t* data() const { return _data; }
const uint8_t* data(int x, int y) const { return _data + y * _rowStride + x * _pixStride; }
/// Create a new ImageView that is a cropped version of this one.
/// Negative width/height will be interpreted as "until the end of the line/image".
ImageView cropped(int left, int top, int width, int height) const
{
left = std::clamp(left, 0, _width - 1);
top = std::clamp(top, 0, _height - 1);
width = width <= 0 ? (_width - left) : std::min(_width - left, width);
height = height <= 0 ? (_height - top) : std::min(_height - top, height);
return {data(left, top), width, height, _format, _rowStride, _pixStride};
}
/// Create a new ImageView that is a rotated version of this one.
/// Rotation is clockwise and only supports 90 degree steps.
ImageView rotated(int degree) const
{
switch ((degree + 360) % 360) {
case 90: return {data(0, _height - 1), _height, _width, _format, _pixStride, -_rowStride};
case 180: return {data(_width - 1, _height - 1), _width, _height, _format, -_rowStride, -_pixStride};
case 270: return {data(_width - 1, 0), _height, _width, _format, -_pixStride, _rowStride};
default: return *this;
}
}
/// Create a new ImageView that is a subsampled version of this one.
/// The subsampled image will have width/height of original divided by scale.
ImageView subsampled(int scale) const
{
return {_data, _width / scale, _height / scale, _format, _rowStride * scale, _pixStride * scale};
}
};
/**
* @brief Simple class that inherits ImageView but owns the image data and frees it on destruction.
*/
class Image : public ImageView
{
std::unique_ptr<uint8_t[]> _memory;
Image(std::unique_ptr<uint8_t[]>&& data, int w, int h, ImageFormat f) : ImageView(data.get(), w, h, f), _memory(std::move(data)) {}
public:
Image() = default;
Image(int w, int h, ImageFormat f = ImageFormat::Lum) : Image(std::make_unique<uint8_t[]>(w * h * PixStride(f)), w, h, f) {}
};
} // ZXing

154
tools/chatgui/zxing-cpp/core/src/JSON.cpp

@ -0,0 +1,154 @@
/*
* Copyright 2025 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "JSON.h"
#include "ZXAlgorithms.h"
#include <string_view>
// This code is trying to find the value of a key-value pair in a string of those.
// The input could be valid JSON, like '{"key": "val"}' or a stripped down version like
// 'key:val'. This is also compatible with the string serialization of a python dictionary.
// For convenience, also 'key=val' is supported, key is checked case insensitive and '_' is ignored.
// This could easily be done with the following regex (see below).
// But using std::regex adds 140k+ to the binary size. ctre is _very_ nice to use and has
// the same binary footprint as the hand-roled code below! But I don't want to add
// 5k lines of c++ code for that.
// #define ZXING_USE_CTRE
#ifdef ZXING_USE_CTRE
#pragma GCC diagnostic ignored "-Wundef" // see https://github.com/hanickadot/compile-time-regular-expressions/issues/340
#include "ctre.hpp"
#pragma GCC diagnostic error "-Wundef"
static constexpr auto PATTERN =
ctll::fixed_string{R"(["']?([[:alpha:]][[:alnum:]]*)["']?\s*(?:[:]\s*["']?([[:alnum:]]+)["']?)?(?:,|\}|$))"};
#endif
namespace ZXing {
// Trim whitespace and quotes/braces from both ends
inline std::string_view Trim(std::string_view sv)
{
return TrimWS(sv, " \t\n\r\"'{}");
}
inline bool IsEqualIgnoreCaseAndUnderscore(std::string_view a, std::string_view b)
{
return IsEqualIgnoreCaseAnd(a, b, "_");
}
std::string_view JsonFind(std::string_view json, std::string_view key)
{
#ifdef ZXING_USE_CTRE
for (auto [ma, mk, mv] : ctre::search_all<PATTERN>(json))
if (IsEqualCaseInsensitive(key, mk))
return mv.size() ? mv.to_view() : std::string_view(mk.data(), 0);
return {};
#else
json = Trim(json);
// TODO: use something like ForEachToken here?
while (!json.empty()) {
auto posComma = json.find(',');
auto pair = Trim(json.substr(0, posComma));
if (IsEqualIgnoreCaseAndUnderscore(pair, key))
return {pair.data(), 0}; // return non-null data to signal that we found the key
auto posSep = pair.find_first_of(":=");
if (posSep != std::string_view::npos && IsEqualIgnoreCaseAndUnderscore(Trim(pair.substr(0, posSep)), key))
return Trim(pair.substr(posSep + 1));
json = (posComma == std::string_view::npos) ? std::string_view{} : json.substr(posComma + 1);
}
return {};
#endif
}
std::string JsonEscapeStr(std::string_view str)
{
std::string res;
res.reserve(str.size() + 10);
for (unsigned char c : str) {
switch (c) {
case '\"': res += "\\\""; break;
case '\\': res += "\\\\"; break;
case '\b': res += "\\b"; break;
case '\f': res += "\\f"; break;
case '\n': res += "\\n"; break;
case '\r': res += "\\r"; break;
case '\t': res += "\\t"; break;
default:
if (c <= 0x1F) {
char buf[7];
std::snprintf(buf, sizeof(buf), "\\u%04X", c);
res += buf;
} else {
res += c;
}
break;
}
}
return res;
}
std::string JsonUnEscapeStr(std::string_view str)
{
std::string res;
res.reserve(str.size());
for (size_t i = 0; i < str.size(); ++i) {
char c = str[i];
if (c == '\\') {
if (++i >= str.size())
throw std::runtime_error("Invalid escape sequence");
char esc = str[i];
switch (esc) {
case '"': res += '\"'; break;
case '\\': res += '\\'; break;
case '/': res += '/'; break;
case 'b': res += '\b'; break;
case 'f': res += '\f'; break;
case 'n': res += '\n'; break;
case 'r': res += '\r'; break;
case 't': res += '\t'; break;
case 'u': {
if (i + 4 >= str.size())
throw std::runtime_error("Incomplete \\u escape");
uint32_t code = 0;
auto first = str.data() + i + 1;
auto last = first + 4;
auto [ptr, ec] = std::from_chars(first, last, code, 16);
if (ec != std::errc() || ptr != last)
throw std::runtime_error("Failed to parse hex code");
if (code > 0x1F)
throw std::runtime_error("Unexpected code point in \\u escape");
res += static_cast<char>(code);
i += 4;
break;
}
default: throw std::runtime_error("Unknown escape sequence");
}
} else {
res += c;
}
}
return res;
}
} // ZXing

66
tools/chatgui/zxing-cpp/core/src/JSON.h

@ -0,0 +1,66 @@
/*
* Copyright 2025 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ZXAlgorithms.h"
#include <cstring>
#ifdef __cpp_lib_format // not available on gcc 12
#include <format>
#endif
#include <optional>
#include <string>
#include <string_view>
namespace ZXing {
std::string JsonEscapeStr(std::string_view str);
std::string JsonUnEscapeStr(std::string_view str);
template<typename T>
inline std::string JsonProp(std::string_view key, const T& val, const T& ignore = {})
{
#define ZX_JSON_KEY_VAL(...) StrCat("\"", key, "\":", __VA_ARGS__, ',')
#if !defined(__cpp_lib_to_chars) || !defined(__cpp_lib_format) // not available on older macOS / gcc 12
#define ZX_JSON_FMT_FLOAT(val) (std::to_string(int(val)) + '.' + std::to_string(int(val * 100) % 100))
#else
#define ZX_JSON_FMT_FLOAT(val) std::format("{:.2f}", val)
#endif
if constexpr (std::is_same_v<T, bool>)
return val != ignore ? ZX_JSON_KEY_VAL(val ? "true" : "false") : "";
else if constexpr (std::is_floating_point_v<T>)
return val != ignore ? ZX_JSON_KEY_VAL(ZX_JSON_FMT_FLOAT(val)) : "";
else if constexpr (std::is_integral_v<T>)
return val != ignore ? ZX_JSON_KEY_VAL(std::to_string(val)) : "";
else if constexpr (std::is_convertible_v<T, std::string_view>)
return std::string_view(val) != std::string_view(ignore) ? ZX_JSON_KEY_VAL("\"", JsonEscapeStr(val), "\"") : "";
else
static_assert("unsupported JSON value type");
#undef ZX_JSON_KEY_VAL
}
// Finds the (potentially escaped / raw) value of the provided key
std::string_view JsonFind(std::string_view json, std::string_view key);
template <typename T>
inline std::optional<T> JsonGet(std::string_view json, std::string_view key)
{
auto str = JsonFind(json, key);
if (!str.data())
return std::nullopt;
if constexpr (std::is_same_v<bool, T>)
return str.empty() || Contains("1tT", str.front()) ? std::optional(true) : std::nullopt;
else if constexpr (std::is_arithmetic_v<T>)
return str.empty() ? std::nullopt : std::optional(FromString<T>(str));
else if constexpr (std::is_same_v<std::string, T>)
return JsonUnEscapeStr(str);
else
static_assert("unsupported JSON value type");
}
} // ZXing

242
tools/chatgui/zxing-cpp/core/src/LocalGrid.cpp

@ -0,0 +1,242 @@
// Copyright 2026 Axel Waggershauser
// SPDX-License-Identifier: Apache-2.0
#include "LocalGrid.h"
#include "LogMatrix.h"
#include "StdGenerator.h"
#include "ZXAlgorithms.h"
#include <algorithm>
#include <optional>
#include <ranges>
#include <span>
#include <vector>
#ifndef PRINT_DEBUG
#define printf(...){}
#endif
namespace ZXing {
// returns the average of the largest cluster of values where cluster is defined as values that are within threshold of each other
double clusterAvg(std::ranges::range auto& v, double threshold)
{
std::ranges::sort(v);
size_t bestStart = 0, bestLen = 0, start = 0;
for (size_t end = 0; end < v.size(); ++end) {
while (v[end] - v[start] >= threshold)
++start;
if (end - start + 1 > bestLen) {
bestLen = end - start + 1;
bestStart = start;
}
}
double sum = 0;
for (size_t i = bestStart; i < bestStart + bestLen; ++i)
sum += v[i];
sum /= bestLen;
#ifdef PRINT_DEBUG
printf("ds: ");
for (auto d : v)
printf("%5.2f ", d);
printf(" -> len: %zu, avg: %5.2f\n", bestLen, sum);
#endif
return sum;
};
void LocalGrid::adjustOriginAndStep(PointF& step, int radius, const std::span<const PointF> offsets)
{
auto modSize = length(step);
int limit = int(6 * modSize);
auto dir = bresenhamDirection(step);
modSize /= length(dir); // mod size in terms of steps in the given direction
printf("step: (%.2f, %.2f) %.2f: ", step.x, step.y, modSize);
auto nearestHalfResidual = [](double n, double s) { return n - (std::round((n - s / 2) / s) * s + s / 2); };
struct DistMod {
double dist, modSize;
};
thread_local std::vector<DistMod> distMod;
distMod.clear();
distMod.reserve(offsets.size() * radius + 1);
for (int r = 0; r <= radius; ++r)
for (auto offset : offsets) {
auto start = origin + r * offset;
auto startC = centered(start);
int stepsPos = BitMatrixCursorF(*img, startC, dir).stepToEdge(1, limit);
int stepsNeg = BitMatrixCursorF(*img, startC, -dir).stepToEdge(1, limit);
auto distPos = stepsPos - dot(start - startC, dir) - 0.5; // +0.5 because the center of the pixel is at .5, .5
auto distNeg = stepsNeg - dot(start - startC, -dir) - 0.5;
if (stepsPos && stepsNeg) {
int blockSize = stepsPos + stepsNeg - 1;
auto localModSize = blockSize / std::max(1.0, std::round(blockSize / modSize));
distMod.emplace_back(distPos, blockSize / modSize < 5 ? localModSize : 0.0);
// if (r == 0 && (std::min(distNeg, distPos) < modSize / 4)) {
// origin += (distNeg < distPos ? 1 : -1) * modSize / 4 * dir;
// continue;
// }
}
else if (stepsPos)
distMod.emplace_back(distPos, 0.0);
else if (stepsNeg)
distMod.emplace_back(-distNeg, 0.0);
printf("+%.2f -%.2f (%.1f) | ", distPos, distNeg, distMod.empty() ? 0.0 : distMod.back().modSize);
if (r == 0)
break; // only do the center point once
}
// calcuate the average local module size from the points where we found one...
double localModSize = 0.0;
int n = 0;
for (const auto& t : distMod | std::views::filter([](const DistMod& t) { return t.modSize > 0; }))
localModSize += t.modSize, ++n;
if (n == 0)
return;
localModSize /= n;
printf("\nlocal mod size: %.2f\n", localModSize);
// ... and use it for the points where we didn't find one
thread_local std::vector<double> d;
d.clear();
d.reserve(distMod.size());
for (auto& t : distMod) {
if (t.modSize == 0.0)
t.modSize = localModSize;
d.push_back(t.dist < 0 ? -nearestHalfResidual(-t.dist, t.modSize) : nearestHalfResidual(t.dist, t.modSize));
printf("%.2f (%.1f) -> %.2f | ", t.dist, t.modSize, d.back());
}
printf("\n");
origin += clusterAvg(d, modSize / 2) * dir;
// if we found local mod sizes for each point (hopefully at the timing pattern crosses), we update the step size
if (n == Size(distMod) && std::abs(localModSize - modSize) > modSize * 0.1) {
step = localModSize / modSize * step;
printf(" adjusted mod size from %.2f to %.2f\n", modSize, localModSize);
}
}
LocalGrid::LocalGrid(const BitMatrix& image, const PerspectiveTransform& mod2Pix, PointI p, PointI dim, PointI offset)
: img(&image), dim(dim), center(p)
{
origin = mod2Pix(centered(p));
stepX = mod2Pix(centered(p) + PointF{1, 0}) - origin;
stepY = mod2Pix(centered(p) + PointF{0, 1}) - origin;
printf("LocalGrid @ (%d, %d), initial origin: (%.2f, %.2f), offset: (%d, %d), stepX: (%.2f, %.2f), stepY: (%.2f, %.2f)\n",
center.x, center.y, origin.x, origin.y, offset.x, offset.y, stepX.x, stepX.y, stepY.x, stepY.y);
log(origin, 3);
auto offsets = std::array{-stepX, -stepY, stepX, stepY}; // works better for DataMatrix (especially near the symbol edges)
// auto offsets = std::array{-stepX - stepY, stepX - stepY, stepX + stepY, -stepX + stepY};
// auto offsets = std::array{-stepX, -stepY, stepX, stepY, -stepX - stepY, stepX - stepY, stepX + stepY, -stepX + stepY};
// evaluate the image at origin + offset (allows to effectively work near the border of the symbol)
origin = getPos(PointF(offset));
for (int i = 0; i < 2; ++i) {
adjustOriginAndStep(stepX, 2, offsets);
adjustOriginAndStep(stepY, 2, offsets);
printf("\n");
}
origin = getPos(PointF(-offset));
}
bool LocalGrid::isTimingPatternCross(PointI p, bool isBlack, int radius, int errorThreshold)
{
// TODO: look into replacing this with something along the lines of CheckSymmetricAztecCenterPattern
auto wrapOffset = [&](int center, int offset, int dim) {
int pos = center + offset;
return pos < 0 ? radius - pos : (pos >= dim ? -(radius + (pos - dim) + 1) : offset);
};
int errors = 0;
for (int r = 0; r <= radius; ++r) {
auto check = [&](int x, int y) {
x = wrapOffset(center.x, x, dim.x);
y = wrapOffset(center.y, y, dim.y);
auto d = PointI(x, y);
errors += !findValue(p + d, d, Value((x + y) % 2 == (isBlack ? 0 : 1)));
};
check(-r, 0);
check(r, 0);
check(0, -r);
check(0, r);
if (errors > errorThreshold)
return false;
}
return errors <= errorThreshold;
};
std::optional<PointF> LocalGrid::findTimingPatternCross(bool isBlack, int radius)
{
for (auto p : Spiral(3)) {
// check if there is a timing pattern cross candidate centered at p with half the radius
if (isTimingPatternCross(p, isBlack, radius / 2)) {
auto original = origin;
origin = getPos(p);
// adjust origin and step with full radius and only in the direction of the timing pattern
printf("timing pattern:\n");
adjustOriginAndStep(stepX, radius, std::array{-stepX, stepX});
adjustOriginAndStep(stepY, radius, std::array{-stepY, stepY});
// check again, now with the full radius, to make sure we are correctly aligned to the timing pattern
if (isTimingPatternCross(PointI{0, 0}, isBlack, radius))
return origin;
origin = original;
}
}
return {};
}
bool LocalGrid::findPattern(int radius, PointI timingStart, Directions timingDirs, PointI blackStart, Directions blackDirs,
PointI whiteStart, Directions whiteDirs)
{
auto isPatternAt = [&](PointI p) {
for (int r = 0; r <= radius; ++r) {
for (auto d : timingDirs)
if (!findValue(p + timingStart + r * d, d, Value(r % 2 == 1)))
return false;
for (auto d : blackDirs)
if (!findValue(p + blackStart + r * d, d, Value(true)))
return false;
for (auto d : whiteDirs)
if (!findValue(p + whiteStart + r * d, d, Value(false)))
return false;
}
return true;
};
for (auto p : Spiral(3)) {
if (isPatternAt(p)) {
auto original = origin;
origin = getPos(PointF(p));
// adjust origin and step with full radius and only in the direction of the timing pattern
printf("found pattern at (%.2f, %.2f)\n", origin.x, origin.y);
std::vector<PointF> stepsX, stepsY;
for (auto d : timingDirs)
d.y == 0 ? stepsX.push_back(d.x * stepX) : stepsY.push_back(d.y * stepY);
if (!stepsX.empty())
adjustOriginAndStep(stepX, radius, stepsX);
if (!stepsY.empty())
adjustOriginAndStep(stepY, radius, stepsY);
if ((!stepsX.empty() || !stepsY.empty()) && !isPatternAt(PointI{0, 0})) {
origin = original;
printf("pattern lost after adjusting for timing pattern, reverting origin\n");
}
printf("\n");
return true;
}
}
printf("\n");
return false;
}
} // namespace ZXing

91
tools/chatgui/zxing-cpp/core/src/LocalGrid.h

@ -0,0 +1,91 @@
// Copyright 2026 Axel Waggershauser
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BitMatrixCursor.h"
#include "BitMatrix.h"
#include "LogMatrix.h"
#include "Point.h"
#include "PerspectiveTransform.h"
#include <optional>
#include <span>
namespace ZXing {
/**
* @brief Represents a local grid for sampling and analyzing regions within a BitMatrix image.
*
* The LocalGrid class tries to find the center of a module and step vectors that point to the next module in x and y direction.
* It provides methods to get the position of a point in the grid and to sample values from the image at those positions.
* It supports searching for timing pattern crosses and specific (alignment) patterns within the grid, which updates the origin and
* step vectors accordingly.
*/
class LocalGrid
{
const BitMatrix* img;
PointF origin, stepX, stepY;
PointI dim, center;
using Value = BitMatrixCursorF::Value;
void adjustOriginAndStep(PointF& dir, int radius, std::span<const PointF> offsets);
bool isTimingPatternCross(PointI p, bool isBlack, int radius, int errorThreshold = 0);
bool findValue(PointI p, PointI dir, Value v)
{
// check if there is a value v at p or a quarter module before or after p or p is outside the image
auto d = PointF(dir.x ? 0.25 : 0.0, dir.y ? 0.25 : 0.0);
return !img->isIn(getPos(p)) || get(p) == v || get(p + d) == v || get(p - d) == v;
};
public:
/**
* @brief Constructs a LocalGrid object for mapping a region of a BitMatrix using a perspective transform.
*
* @param image The source BitMatrix representing the image data.
* @param mod2Pix The (global) PerspectiveTransform used to map module coordinates to pixel coordinates.
* @param p The logical position of the grid origin in module coordinates.
* @param dim The dimensions (width and height) of the (global) grid in modules.
* @param offset Optional offset in modules to specify the starting position of the search for the module center.
*/
LocalGrid(const BitMatrix& image, const PerspectiveTransform& mod2Pix, PointI p, PointI dim, PointI offset = {});
inline PointF getPos(PointF p = {}) const { return origin + p.x * stepX + p.y * stepY; }
inline PointF getPos(PointI p) const { return getPos(PointF(p.x, p.y)); }
inline Value get(PointF p) const
{
auto q = getPos(p);
log(q, 4);
return img->isIn(q) ? Value{img->get(q)} : Value{};
}
inline Value get(PointI p) const { return get(PointF(p.x, p.y)); }
/**
* @brief Finds the center of a timing pattern cross in the local grid.
*
* This function searches for the center point of a timing pattern cross, which is a key feature in Aztec code detection, within a
* specified radius.
*
* @param isBlack Indicates whether the central module of the timing pattern cross is expected to be black or white.
* @param radius The length of the timing pattern cross arms.
* @return An optional PointF representing the center of the timing pattern cross if found; std::nullopt otherwise.
*/
std::optional<PointF> findTimingPatternCross(bool isBlack, int radius);
using Directions = std::span<const PointI>;
/**
* @brief Searches for a specific pattern within a grid based on provided parameters.
*
* This function attempts to locate a pattern in the grid by starting from given points
* and following specified directions for timing, black, and white modules.
*/
bool findPattern(int radius, PointI timingStart, Directions timingDirs, PointI blackStart, Directions blackDirs, PointI whiteStart,
Directions whiteDirs);
};
} // namespace ZXing

104
tools/chatgui/zxing-cpp/core/src/LogMatrix.h

@ -0,0 +1,104 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BitMatrix.h"
#include "Matrix.h"
#include "Point.h"
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <string>
namespace ZXing {
#ifdef PRINT_DEBUG
class LogMatrix
{
using LogBuffer = Matrix<uint8_t>;
LogBuffer _log;
const BitMatrix* _image = nullptr;
int _scale = 1;
public:
void init(const BitMatrix* image, int scale = 1)
{
_image = image;
_scale = scale;
_log = LogBuffer(_image->width() * _scale, _image->height() * _scale);
}
void write(const char* fn)
{
assert(_image);
FILE* f = fopen(fn, "wb");
// Write PPM header, P5 == grey, P6 == rgb
fprintf(f, "P6\n%d %d\n255\n", _log.width(), _log.height());
// Write pixels
for (int y = 0; y < _log.height(); ++y)
for (int x = 0; x < _log.width(); ++x) {
unsigned char pix[3];
unsigned char &r = pix[0], &g = pix[1], &b = pix[2];
r = g = b = _image->get(x / _scale, y / _scale) ? 0 : 255;
if (_scale > 1 && x % _scale == _scale / 2 && y % _scale == _scale / 2)
r = g = b = r ? 230 : 50;
switch (_log.get(x, y)) {
case 1: r = g = b = _scale > 1 ? 128 : (r ? 230 : 50); break;
case 2: r = b = 50, g = 220; break;
case 3: g = r = 100, b = 250; break;
case 4: g = b = 100, r = 250; break;
}
fwrite(&pix, 3, 1, f);
}
fclose(f);
}
template <typename T>
void operator()(const PointT<T>& p, int color = 1)
{
if (_image && _image->isIn(p))
_log.set(static_cast<int>(p.x * _scale), static_cast<int>(p.y * _scale), color);
}
void operator()(const PointT<int>& p, int color)
{
operator()(centered(p), color);
}
template <typename T>
void operator()(const std::vector<PointT<T>>& points, int color = 2)
{
for (auto p : points)
operator()(p, color);
}
};
extern LogMatrix log;
class LogMatrixWriter
{
LogMatrix &log;
std::string fn;
public:
LogMatrixWriter(LogMatrix& log, const BitMatrix& image, int scale, std::string fn) : log(log), fn(fn)
{
log.init(&image, scale);
}
~LogMatrixWriter() { log.write(fn.c_str()); }
};
#else
template<typename T> void log(PointT<T>, int = 0) {}
#endif
} // namespace ZXing

125
tools/chatgui/zxing-cpp/core/src/Matrix.h

@ -0,0 +1,125 @@
/*
* Copyright 2016 Huy Cuong Nguyen
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Point.h"
#include <stdexcept>
#include <algorithm>
#include <cassert>
#include <vector>
namespace ZXing {
template <class T>
class Matrix
{
public:
using value_t = T;
private:
int _width = 0;
int _height = 0;
std::vector<value_t> _data;
// Nothing wrong to support it, just to make it explicit, instead of by mistake.
// Use copy() below.
Matrix(const Matrix &) = default;
public:
Matrix() = default;
#if defined(__llvm__) || (defined(__GNUC__) && (__GNUC__ > 7))
__attribute__((no_sanitize("signed-integer-overflow")))
#endif
Matrix(int width, int height, value_t val = {}) : _width(width), _height(height), _data(_width * _height, val) {
if (width != 0 && int(_data.size()) / width != height)
throw std::invalid_argument("Invalid size: width * height is too big");
}
Matrix(Matrix&&) noexcept = default;
Matrix& operator=(Matrix&&) noexcept = default;
Matrix& operator=(const Matrix &) = delete;
Matrix copy() const {
return *this;
}
int height() const {
return _height;
}
int width() const {
return _width;
}
int size() const {
return int(_data.size());
}
value_t& operator()(int x, int y)
{
assert(x >= 0 && x < _width && y >= 0 && y < _height);
return _data[y * _width + x];
}
const T& operator()(int x, int y) const
{
assert(x >= 0 && x < _width && y >= 0 && y < _height);
return _data[y * _width + x];
}
value_t& operator()(PointI p) {
return operator()(p.x, p.y);
}
const T& operator()(PointI p) const {
return operator()(p.x, p.y);
}
const value_t& get(int x, int y) const {
return operator()(x, y);
}
value_t& set(int x, int y, value_t value) {
return operator()(x, y) = value;
}
const value_t& get(PointI p) const {
return operator()(p.x, p.y);
}
value_t& set(PointI p, value_t value) {
return operator()(p.x, p.y) = value;
}
const value_t* data() const {
return _data.data();
}
const value_t* begin() const {
return _data.data();
}
const value_t* end() const {
return _data.data() + _width * _height;
}
value_t* begin() {
return _data.data();
}
value_t* end() {
return _data.data() + _width * _height;
}
void clear(value_t value = {}) {
std::fill(_data.begin(), _data.end(), value);
}
};
} // ZXing

107
tools/chatgui/zxing-cpp/core/src/MultiFormatReader.cpp

@ -0,0 +1,107 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "MultiFormatReader.h"
#include "BarcodeFormat.h"
#include "BinaryBitmap.h"
#include "Reader.h"
#include "ReaderOptions.h"
#include "Version.h"
#if ZXING_ENABLE_AZTEC
#include "aztec/AZReader.h"
#endif
#if ZXING_ENABLE_DATAMATRIX
#include "datamatrix/DMReader.h"
#endif
#if ZXING_ENABLE_MAXICODE
#include "maxicode/MCReader.h"
#endif
#if ZXING_ENABLE_1D
#include "oned/ODReader.h"
#endif
#if ZXING_ENABLE_PDF417
#include "pdf417/PDFReader.h"
#include "pdf417/MicroPDFReader.h"
#endif
#if ZXING_ENABLE_QRCODE
#include "qrcode/QRReader.h"
#endif
#include <memory>
namespace ZXing {
MultiFormatReader::MultiFormatReader(const ReaderOptions& opts) : _opts(opts)
{
using enum BarcodeFormat;
// Put linear readers upfront in "normal" mode
#if ZXING_ENABLE_1D
if (!opts.tryHarder() && opts.hasAnyFormat(AllLinear))
_readers.emplace_back(new OneD::Reader(opts));
#endif
#if ZXING_ENABLE_QRCODE
if (opts.hasAnyFormat(QRCode))
_readers.emplace_back(new QRCode::Reader(opts, true));
#endif
#if ZXING_ENABLE_DATAMATRIX
if (opts.hasAnyFormat(DataMatrix))
_readers.emplace_back(new DataMatrix::Reader(opts, true));
#endif
#if ZXING_ENABLE_AZTEC
if (opts.hasAnyFormat(Aztec))
_readers.emplace_back(new Aztec::Reader(opts, true));
#endif
#if ZXING_ENABLE_PDF417
if (opts.hasFormat(PDF417 | CompactPDF417))
_readers.emplace_back(new Pdf417::Reader(opts));
if (opts.hasFormat(MicroPDF417))
_readers.emplace_back(new MicroPdf417::Reader(opts));
#endif
#if ZXING_ENABLE_MAXICODE
if (opts.hasAnyFormat(MaxiCode))
_readers.emplace_back(new MaxiCode::Reader(opts));
#endif
// At end in "try harder" mode
#if ZXING_ENABLE_1D
if (opts.tryHarder() && opts.hasAnyFormat(AllLinear))
_readers.emplace_back(new OneD::Reader(opts));
#endif
}
MultiFormatReader::~MultiFormatReader() = default;
Barcodes MultiFormatReader::read(const BinaryBitmap& image, int maxSymbols) const
{
Barcodes res;
for (const auto& reader : _readers) {
if (image.inverted() && !reader->supportsInversion)
continue;
auto r = reader->read(image, maxSymbols);
if (!_opts.returnErrors())
std::erase_if(r, [](auto&& s) { return !s.isValid(); });
maxSymbols -= Size(r);
res.insert(res.end(), std::move_iterator(r.begin()), std::move_iterator(r.end()));
if (maxSymbols <= 0)
break;
}
// sort barcodes based on their position on the image
std::sort(res.begin(), res.end(), [](const Barcode& l, const Barcode& r) {
auto lp = l.position().topLeft();
auto rp = r.position().topLeft();
return lp.y < rp.y || (lp.y == rp.y && lp.x < rp.x);
});
return res;
}
} // ZXing

34
tools/chatgui/zxing-cpp/core/src/MultiFormatReader.h

@ -0,0 +1,34 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Barcode.h"
#include <vector>
#include <memory>
namespace ZXing {
class Reader;
class BinaryBitmap;
class ReaderOptions;
class MultiFormatReader
{
public:
explicit MultiFormatReader(const ReaderOptions& opts);
explicit MultiFormatReader(ReaderOptions&& opts) = delete;
~MultiFormatReader();
Barcodes read(const BinaryBitmap& image, int maxSymbols = 0xFF) const;
private:
std::vector<std::unique_ptr<Reader>> _readers;
const ReaderOptions& _opts;
};
} // ZXing

109
tools/chatgui/zxing-cpp/core/src/MultiFormatWriter.cpp

@ -0,0 +1,109 @@
/*
* Copyright 2017 Huy Cuong Nguyen
*/
// SPDX-License-Identifier: Apache-2.0
#include "MultiFormatWriter.h"
#include "BitMatrix.h"
#include "Utf.h"
#include "Version.h"
#if ZXING_ENABLE_AZTEC
#include "aztec/AZWriter.h"
#endif
#if ZXING_ENABLE_DATAMATRIX
#include "datamatrix/DMWriter.h"
#endif
#if ZXING_ENABLE_1D
#include "oned/ODCodabarWriter.h"
#include "oned/ODCode128Writer.h"
#include "oned/ODCode39Writer.h"
#include "oned/ODCode93Writer.h"
#include "oned/ODEAN13Writer.h"
#include "oned/ODEAN8Writer.h"
#include "oned/ODITFWriter.h"
#include "oned/ODUPCAWriter.h"
#include "oned/ODUPCEWriter.h"
#endif
#if ZXING_ENABLE_PDF417
#include "pdf417/PDFWriter.h"
#endif
#if ZXING_ENABLE_QRCODE
#include "qrcode/QRErrorCorrectionLevel.h"
#include "qrcode/QRWriter.h"
#endif
#include <stdexcept>
namespace ZXing {
BitMatrix
MultiFormatWriter::encode(const std::wstring& contents, int width, int height) const
{
[[maybe_unused]] auto exec0 = [&](auto&& writer) {
if (_margin >=0)
writer.setMargin(_margin);
return writer.encode(contents, width, height);
};
#if ZXING_ENABLE_AZTEC
auto AztecEccLevel = [&](Aztec::Writer& writer, int eccLevel) { writer.setEccPercent(eccLevel * 100 / 8); };
#endif
#if ZXING_ENABLE_PDF417
auto Pdf417EccLevel = [&](Pdf417::Writer& writer, int eccLevel) { writer.setErrorCorrectionLevel(eccLevel); };
#endif
#if ZXING_ENABLE_QRCODE
auto QRCodeEccLevel = [&](QRCode::Writer& writer, int eccLevel) {
writer.setErrorCorrectionLevel(static_cast<QRCode::ErrorCorrectionLevel>(--eccLevel / 2));
};
#endif
[[maybe_unused]] auto exec1 = [&](auto&& writer, auto setEccLevel) {
if (_encoding != CharacterSet::Unknown)
writer.setEncoding(_encoding);
if (_eccLevel >= 0 && _eccLevel <= 8)
setEccLevel(writer, _eccLevel);
return exec0(std::forward<decltype(writer)>(writer));
};
[[maybe_unused]] auto exec2 = [&](auto&& writer) {
if (_encoding != CharacterSet::Unknown)
writer.setEncoding(_encoding);
return exec0(std::forward<decltype(writer)>(writer));
};
switch (_format) {
#if ZXING_ENABLE_AZTEC
case BarcodeFormat::AztecCode: return exec1(Aztec::Writer(), AztecEccLevel);
#endif
#if ZXING_ENABLE_DATAMATRIX
case BarcodeFormat::DataMatrix: return exec2(DataMatrix::Writer());
#endif
#if ZXING_ENABLE_PDF417
case BarcodeFormat::PDF417: return exec1(Pdf417::Writer(), Pdf417EccLevel);
#endif
#if ZXING_ENABLE_QRCODE
case BarcodeFormat::QRCode: return exec1(QRCode::Writer(), QRCodeEccLevel);
#endif
#if ZXING_ENABLE_1D
case BarcodeFormat::Codabar: return exec0(OneD::CodabarWriter());
case BarcodeFormat::Code39: return exec0(OneD::Code39Writer());
case BarcodeFormat::Code93: return exec0(OneD::Code93Writer());
case BarcodeFormat::Code128: return exec0(OneD::Code128Writer());
case BarcodeFormat::EAN8: return exec0(OneD::EAN8Writer());
case BarcodeFormat::EAN13: return exec0(OneD::EAN13Writer());
case BarcodeFormat::ITF: return exec0(OneD::ITFWriter());
case BarcodeFormat::UPCA: return exec0(OneD::UPCAWriter());
case BarcodeFormat::UPCE: return exec0(OneD::UPCEWriter());
#endif
default: throw std::invalid_argument("Unsupported format: " + ToString(_format));
}
}
BitMatrix MultiFormatWriter::encode(const std::string& contents, int width, int height) const
{
return encode(FromUtf8(contents), width, height);
}
} // ZXing

62
tools/chatgui/zxing-cpp/core/src/MultiFormatWriter.h

@ -0,0 +1,62 @@
/*
* Copyright 2017 Huy Cuong Nguyen
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BarcodeFormat.h"
#include "CharacterSet.h"
#include <string>
namespace ZXing {
class BitMatrix;
/**
* This class is here just for convenience as it offers single-point service
* to generate barcodes for all supported formats. As a result, this class
* offer very limited customization compared to what are available in each
* individual encoder.
*/
class MultiFormatWriter
{
public:
[[deprecated("use CreateBarcode()")]] explicit MultiFormatWriter(BarcodeFormat format) : _format(format) {}
/**
* Used for Aztec, PDF417, and QRCode only.
*/
MultiFormatWriter& setEncoding(CharacterSet encoding) {
_encoding = encoding;
return *this;
}
/**
* Used for Aztec, PDF417, and QRCode only, [0-8].
*/
MultiFormatWriter& setEccLevel(int level) {
_eccLevel = level;
return *this;
}
/**
* Used for all formats, sets the minimum number of quiet zone pixels.
*/
MultiFormatWriter& setMargin(int margin) {
_margin = margin;
return *this;
}
BitMatrix encode(const std::wstring& contents, int width, int height) const;
BitMatrix encode(const std::string& contents, int width, int height) const;
private:
BarcodeFormat _format;
CharacterSet _encoding = CharacterSet::Unknown;
int _margin = -1;
int _eccLevel = -1;
};
} // ZXing

427
tools/chatgui/zxing-cpp/core/src/Pattern.h

@ -0,0 +1,427 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Range.h"
#include "ZXAlgorithms.h"
#include <algorithm>
#include <array>
#include <bit>
#include <cmath>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits>
#include <type_traits>
#include <vector>
namespace ZXing {
using PatternType = uint16_t;
template<int N> using Pattern = std::array<PatternType, N>;
using PatternRow = std::vector<PatternType>;
class PatternView
{
using Iterator = PatternRow::const_pointer;
Iterator _data = nullptr;
int _size = 0;
Iterator _base = nullptr;
Iterator _end = nullptr;
public:
using value_type = PatternRow::value_type;
PatternView() = default;
// A PatternRow always starts with the width of whitespace in front of the first black bar.
// The first element of the PatternView is the first bar.
PatternView(const PatternRow& bars)
: _data(bars.data() + 1), _size(Size(bars) - 1), _base(bars.data()), _end(bars.data() + bars.size())
{}
PatternView(Iterator data, int size, Iterator base, Iterator end) : _data(data), _size(size), _base(base), _end(end) {}
template <size_t N>
PatternView(const Pattern<N>& row) : _data(row.data()), _size(N)
{}
Iterator data() const { return _data; }
Iterator begin() const { return _data; }
Iterator end() const { return _data + _size; }
value_type operator[](int i) const
{
// assert(i < _count);
return _data[i];
}
int sum(int n = 0) const { return Reduce(_data, _data + (n == 0 ? _size : n)); }
int size() const { return _size; }
// index is the number of bars and spaces from the first bar to the current position
int index() const { return narrow_cast<int>(_data - _base) - 1; }
int pixelsInFront() const { return Reduce(_base, _data); }
int pixelsTillEnd() const { return Reduce(_base, _data + _size) - 1; }
bool isAtFirstBar() const { return _data == _base + 1; }
bool isAtLastBar() const { return _data + _size == _end - 1; }
bool isValid(int n) const { return _data && _data >= _base && _data + n <= _end; }
bool isValid() const { return isValid(size()); }
int spaceInFront() const { return isAtFirstBar() ? INT_MAX : _data[-1]; }
template<bool acceptIfAtFirstBar = false>
bool hasQuietZoneBefore(float scale) const
{
return (acceptIfAtFirstBar && isAtFirstBar()) || _data[-1] >= sum() * scale;
}
template<bool acceptIfAtLastBar = true>
bool hasQuietZoneAfter(float scale) const
{
return (acceptIfAtLastBar && isAtLastBar()) || _data[_size] >= sum() * scale;
}
PatternView subView(int offset, int size = 0) const
{
// if(std::abs(size) > count())
// printf("%d > %d\n", std::abs(size), _count);
// assert(std::abs(size) <= count());
if (size == 0)
size = _size - offset;
else if (size < 0)
size = _size - offset + size;
return {begin() + offset, std::max(size, 0), _base, _end};
}
bool shift(int n)
{
return _data && ((_data += n) + _size <= _end);
}
bool skipPair()
{
return shift(2);
}
bool skipSymbol()
{
return shift(_size);
}
bool skipSingle(int maxWidth)
{
return shift(1) && _data[-1] <= maxWidth;
}
void extend()
{
_size = std::max(0, narrow_cast<int>(_end - _data));
}
};
/**
* @brief The BarAndSpace struct is a simple 2 element data structure to hold information about bar(s) and space(s).
*
* The operator[](int) can be used in combination with a PatternView
*/
template <typename T>
struct BarAndSpace
{
using value_type = T;
T bar = {}, space = {};
// even index -> bar, odd index -> space
constexpr T& operator[](int i) noexcept { return reinterpret_cast<T*>(this)[i & 1]; }
constexpr T operator[](int i) const noexcept { return reinterpret_cast<const T*>(this)[i & 1]; }
bool isValid() const { return bar != T{} && space != T{}; }
};
using BarAndSpaceI = BarAndSpace<PatternType>;
template <int LEN, typename RT, typename T>
constexpr auto BarAndSpaceSum(const T* view) noexcept
{
BarAndSpace<RT> res;
for (int i = 0; i < LEN; ++i)
res[i] += view[i];
return res;
}
/**
* @brief FixedPattern describes a compile-time constant (start/stop) pattern.
*
* N = number of bars/spaces
* SUM = sum over all N elements (size of pattern in modules)
* IS_SPARSE = whether or not the pattern contains '0's denoting 'wide' bars/spaces
*/
template <int N, int SUM, bool IS_SPARSE = false>
struct FixedPattern
{
using value_type = PatternRow::value_type;
value_type _data[N];
constexpr value_type operator[](int i) const noexcept { return _data[i]; }
constexpr const value_type* data() const noexcept { return _data; }
constexpr int size() const noexcept { return N; }
constexpr const value_type* begin() const noexcept { return _data; }
constexpr const value_type* end() const noexcept { return _data + N; }
constexpr BarAndSpace<value_type> sums() const noexcept { return BarAndSpaceSum<N, value_type>(_data); }
};
template <int N, int SUM>
using FixedSparsePattern = FixedPattern<N, SUM, true>;
template <bool E2E = false, int LEN, int SUM>
double IsPattern(const PatternView& view, const FixedPattern<LEN, SUM, false>& pattern, int spaceInPixel = 0,
double minQuietZone = 0, double moduleSizeRef = 0)
{
if constexpr (E2E) {
auto widths = BarAndSpaceSum<LEN, double>(view.data());
auto sums = pattern.sums();
BarAndSpace<double> modSize = {widths[0] / sums[0], widths[1] / sums[1]};
auto [m, M] = std::minmax(modSize[0], modSize[1]);
if (M > 4 * m) // make sure module sizes of bars and spaces are not too far away from each other
return 0;
if (minQuietZone && spaceInPixel < minQuietZone * modSize.space)
return 0;
const BarAndSpace<double> thr = {modSize[0] * .75 + .5, modSize[1] * .6 + .5};
for (int x = 0; x < LEN; ++x)
if (std::abs(view[x] - pattern[x] * modSize[x]) > thr[x])
return 0;
return (modSize[0] + modSize[1]) / 2;
}
double width = view.sum(LEN);
if (SUM > LEN && width < SUM)
return 0;
const auto moduleSize = width / SUM;
if (minQuietZone && spaceInPixel < minQuietZone * moduleSize - 1)
return 0;
if (!moduleSizeRef)
moduleSizeRef = moduleSize;
// the offset of 0.5 is to make the code less sensitive to quantization errors for small (near 1) module sizes.
// TODO: review once we have upsampling in the binarizer in place.
const auto threshold = moduleSizeRef * (0.5 + E2E * 0.25) + 0.5;
for (int x = 0; x < LEN; ++x)
if (std::abs(view[x] - pattern[x] * moduleSizeRef) > threshold)
return 0;
return moduleSize;
}
template <bool RELAXED_THRESHOLD = false, int N, int SUM>
double IsPattern(const PatternView& view, const FixedPattern<N, SUM, true>& pattern, int spaceInPixel = 0,
double minQuietZone = 0, double moduleSizeRef = 0)
{
// pattern contains the indices with the bars/spaces that need to be equally wide
double width = 0;
for (int x = 0; x < SUM; ++x)
width += view[pattern[x]];
const auto moduleSize = width / SUM;
if (minQuietZone && spaceInPixel < minQuietZone * moduleSize - 1)
return 0;
if (!moduleSizeRef)
moduleSizeRef = moduleSize;
// the offset of 0.5 is to make the code less sensitive to quantization errors for small (near 1) module sizes.
// TODO: review once we have upsampling in the binarizer in place.
const auto threshold = moduleSizeRef * (0.5 + RELAXED_THRESHOLD * 0.25) + 0.5;
for (int x = 0; x < SUM; ++x)
if (std::abs(view[pattern[x]] - moduleSizeRef) > threshold)
return 0;
return moduleSize;
}
template <bool E2E = false, int N, int SUM, bool IS_SPARSE>
bool IsRightGuard(const PatternView& view, const FixedPattern<N, SUM, IS_SPARSE>& pattern, double minQuietZone,
double moduleSizeRef = 0)
{
assert(view.size() == pattern.size());
if (!view.isValid())
return false;
int spaceInPixel = view.isAtLastBar() ? std::numeric_limits<int>::max() : *view.end();
return IsPattern<E2E>(view, pattern, spaceInPixel, minQuietZone, moduleSizeRef) != 0;
}
template<int LEN, typename Pred>
PatternView FindLeftGuard(const PatternView& view, int minSize, Pred isGuard)
{
if (view.size() < minSize)
return {};
auto window = view.subView(0, LEN);
if (window.isAtFirstBar() && isGuard(window, std::numeric_limits<int>::max()))
return window;
for (auto end = view.end() - minSize; window.data() < end; window.skipPair())
if (isGuard(window, window[-1]))
return window;
return {};
}
template <bool E2E = false, int LEN, int SUM, bool IS_SPARSE>
PatternView FindLeftGuard(const PatternView& view, int minSize, const FixedPattern<LEN, SUM, IS_SPARSE>& pattern,
double minQuietZone)
{
return FindLeftGuard<LEN>(view, std::max(minSize, LEN),
[&pattern, minQuietZone](const PatternView& window, int spaceInPixel) {
return IsPattern<E2E>(window, pattern, spaceInPixel, minQuietZone);
});
}
template <int LEN>
std::array<int, LEN - 2> NormalizedE2EPattern(const PatternView& view, int mods, bool reverse = false)
{
double moduleSize = static_cast<double>(view.sum(LEN)) / mods;
std::array<int, LEN - 2> e2e;
for (int i = 0; i < LEN - 2; i++) {
int i_v = reverse ? LEN - 2 - i : i;
double v = (view[i_v] + view[i_v + 1]) / moduleSize;
e2e[i] = int(v + .5);
}
return e2e;
}
template <int LEN, int SUM>
std::array<int, LEN> NormalizedPattern(const PatternView& view)
{
double moduleSize = static_cast<double>(view.sum(LEN)) / SUM;
#if 1
int err = SUM;
std::array<int, LEN> is;
std::array<double, LEN> rs;
for (int i = 0; i < LEN; i++) {
double v = view[i] / moduleSize;
is[i] = int(v + .5);
rs[i] = v - is[i];
err -= is[i];
}
if (std::abs(err) > 1)
return {};
if (err) {
auto mi = err > 0 ? std::ranges::max_element(rs) - rs.begin()
: std::ranges::min_element(rs) - rs.begin();
is[mi] += err;
rs[mi] -= err;
}
#else
std::array<int, LEN> is, e2e;
int min_v = view[0], min_i = 0;
for (int i = 1; i < LEN; i++) {
double v = (view[i - 1] + view[i]) / moduleSize;
e2e[i] = int(v + .5);
if (view[i] < min_v) {
min_v = view[i];
min_i = i;
}
}
is[min_i] = 1;
for (int i = min_i + 1; i < LEN; ++i)
is[i] = e2e[i] - is[i - 1];
for (int i = min_i - 1; i >= 0; --i)
is[i] = e2e[i + 1] - is[i + 1];
#endif
return is;
}
template<typename I>
void GetPatternRow(Range<I> b_row, PatternRow& p_row)
{
// TODO: if reactivating the bit-packed array (!ZX_FAST_BIT_STORAGE) should be of interest then the following code could be
// considerably speed up by using a specialized variant along the lines of the old BitArray::getNextSetTo() function that
// was removed between 1.4 and 2.0.
#if 0
p_row.reserve(64);
p_row.clear();
auto lastPos = b_row.begin();
if (*lastPos)
p_row.push_back(0); // first value is number of white pixels, here 0
for (auto p = b_row.begin() + 1; p != b_row.end(); ++p)
if (bool(*p) != bool(*lastPos))
p_row.push_back(p - std::exchange(lastPos, p));
p_row.push_back(b_row.end() - lastPos);
if (*lastPos)
p_row.push_back(0); // last value is number of white pixels, here 0
#else
p_row.resize(b_row.size() + 2);
std::fill(p_row.begin(), p_row.end(), 0);
auto bitPos = b_row.begin();
const auto bitPosEnd = b_row.end();
auto intPos = p_row.data();
if (*bitPos)
intPos++; // first value is number of white pixels, here 0
// The following code has been observed to cause a speedup of up to 30% on large images on an AVX cpu
// and on an a Google Pixel 3 Android phone. Your mileage may vary.
if constexpr (std::contiguous_iterator<I> && sizeof(std::remove_cv_t<std::iter_value_t<I>>) == 1) {
using simd_t = uint64_t;
const auto* const bitPtrBegin = std::to_address(bitPos);
const auto* const bitPtrEnd = std::to_address(bitPosEnd);
auto* bitPtr = bitPtrBegin;
while (bitPtr < bitPtrEnd - sizeof(simd_t)) {
auto asSimd0 = LoadU<simd_t>(bitPtr);
auto asSimd1 = LoadU<simd_t>(bitPtr + 1);
auto z = asSimd0 ^ asSimd1;
if (z) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
int step = std::countr_zero(z) / 8 + 1;
#else
int step = std::countl_zero(z) / 8 + 1;
#endif
(*intPos++) += step;
bitPtr += step;
} else {
(*intPos) += sizeof(simd_t);
bitPtr += sizeof(simd_t);
}
}
bitPos += bitPtr - bitPtrBegin;
}
while (++bitPos != bitPosEnd) {
++(*intPos);
intPos += bitPos[0] != bitPos[-1];
}
++(*intPos);
if (bitPos[-1])
intPos++;
p_row.resize(intPos - p_row.data() + 1);
#endif
}
} // ZXing

81
tools/chatgui/zxing-cpp/core/src/PerspectiveTransform.cpp

@ -0,0 +1,81 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "PerspectiveTransform.h"
#include <array>
namespace ZXing {
PerspectiveTransform PerspectiveTransform::inverse() const
{
// Here, the adjoint serves as the inverse:
// Adjoint is the transpose of the cofactor matrix:
return {
a22 * a33 - a23 * a32,
a23 * a31 - a21 * a33,
a21 * a32 - a22 * a31,
a13 * a32 - a12 * a33,
a11 * a33 - a13 * a31,
a12 * a31 - a11 * a32,
a12 * a23 - a13 * a22,
a13 * a21 - a11 * a23,
a11 * a22 - a12 * a21
};
}
PerspectiveTransform PerspectiveTransform::times(const PerspectiveTransform& other) const
{
return {
a11 * other.a11 + a21 * other.a12 + a31 * other.a13,
a11 * other.a21 + a21 * other.a22 + a31 * other.a23,
a11 * other.a31 + a21 * other.a32 + a31 * other.a33,
a12 * other.a11 + a22 * other.a12 + a32 * other.a13,
a12 * other.a21 + a22 * other.a22 + a32 * other.a23,
a12 * other.a31 + a22 * other.a32 + a32 * other.a33,
a13 * other.a11 + a23 * other.a12 + a33 * other.a13,
a13 * other.a21 + a23 * other.a22 + a33 * other.a23,
a13 * other.a31 + a23 * other.a32 + a33 * other.a33
};
}
PerspectiveTransform PerspectiveTransform::UnitSquareTo(const QuadrilateralF& q)
{
auto [x0, y0, x1, y1, x2, y2, x3, y3] = reinterpret_cast<const std::array<PointF::value_t, 8>&>(q);
auto d3 = q[0] - q[1] + q[2] - q[3];
if (d3 == PointF(0, 0)) {
// Affine
return {x1 - x0, x2 - x1, x0,
y1 - y0, y2 - y1, y0,
0.0f, 0.0f, 1.0f};
} else {
auto d1 = q[1] - q[2];
auto d2 = q[3] - q[2];
auto denominator = cross(d1, d2);
auto a13 = cross(d3, d2) / denominator;
auto a23 = cross(d1, d3) / denominator;
return {x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0,
y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0,
a13, a23, 1.0f};
}
}
PerspectiveTransform::PerspectiveTransform(const QuadrilateralF& src, const QuadrilateralF& dst)
{
if (!IsConvex(src) || !IsConvex(dst))
return;
*this = UnitSquareTo(dst).times(UnitSquareTo(src).inverse());
}
PointF PerspectiveTransform::operator()(PointF p) const
{
auto denominator = a13 * p.x + a23 * p.y + a33;
return {(a11 * p.x + a21 * p.y + a31) / denominator, (a12 * p.x + a22 * p.y + a32) / denominator};
}
} // ZXing

47
tools/chatgui/zxing-cpp/core/src/PerspectiveTransform.h

@ -0,0 +1,47 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Point.h"
#include "Quadrilateral.h"
namespace ZXing {
/**
* This class implements a perspective transform in two dimensions. Given four source and four
* destination points, it will compute the transformation implied between them. The code is based
* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
*
* See also e.g. https://math.stackexchange.com/a/339033
*/
class PerspectiveTransform
{
using value_t = PointF::value_t;
value_t a11, a12, a13, a21, a22, a23, a31, a32, a33 = NAN;
PerspectiveTransform(value_t a11, value_t a21, value_t a31, value_t a12, value_t a22, value_t a32, value_t a13,
value_t a23, value_t a33)
: a11(a11), a12(a12), a13(a13), a21(a21), a22(a22), a23(a23), a31(a31), a32(a32), a33(a33)
{}
PerspectiveTransform inverse() const;
PerspectiveTransform times(const PerspectiveTransform& other) const;
static PerspectiveTransform UnitSquareTo(const QuadrilateralF& q);
public:
PerspectiveTransform() = default;
PerspectiveTransform(const QuadrilateralF& src, const QuadrilateralF& dst);
/// Project from the destination space (grid of modules) into the image space (bit matrix)
PointF operator()(PointF p) const;
bool isValid() const { return !std::isnan(a33); }
};
} // ZXing

173
tools/chatgui/zxing-cpp/core/src/Point.h

@ -0,0 +1,173 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <algorithm>
#include <cmath>
#include <string>
namespace ZXing {
/**
* @brief A simple 2D point class template.
*
* PointT is a simple 2D point class template with direct access to its x and y coordinate members.
*/
template <typename T>
struct PointT
{
using value_t = T;
T x = 0, y = 0;
constexpr PointT() = default;
constexpr PointT(T xx, T yy) : x(xx), y(yy) {}
template <typename U>
constexpr explicit PointT(const PointT<U>& p) : x(static_cast<T>(p.x)), y(static_cast<T>(p.y))
{}
template <typename U>
PointT& operator+=(const PointT<U>& b)
{
x += b.x;
y += b.y;
return *this;
}
};
using PointI = PointT<int>;
using PointF = PointT<double>;
/// @cond INTERNAL
template <typename T>
bool operator==(const PointT<T>& a, const PointT<T>& b)
{
return a.x == b.x && a.y == b.y;
}
template <typename T>
bool operator!=(const PointT<T>& a, const PointT<T>& b)
{
return !(a == b);
}
template <typename T>
auto operator-(const PointT<T>& a) -> PointT<T>
{
return {-a.x, -a.y};
}
template <typename T, typename U>
auto operator+(const PointT<T>& a, const PointT<U>& b) -> PointT<decltype(a.x + b.x)>
{
return {a.x + b.x, a.y + b.y};
}
template <typename T, typename U>
auto operator-(const PointT<T>& a, const PointT<U>& b) -> PointT<decltype(a.x - b.x)>
{
return {a.x - b.x, a.y - b.y};
}
template <typename T, typename U>
auto operator*(const PointT<T>& a, const PointT<U>& b) -> PointT<decltype(a.x * b.x)>
{
return {a.x * b.x, a.y * b.y};
}
template <typename T, typename U>
PointT<T> operator*(U s, const PointT<T>& a)
{
return {s * a.x, s * a.y};
}
template <typename T, typename U>
PointT<T> operator/(const PointT<T>& a, U d)
{
return {a.x / d, a.y / d};
}
template <typename T, typename U>
auto dot(const PointT<T>& a, const PointT<U>& b) -> decltype (a.x * b.x)
{
return a.x * b.x + a.y * b.y;
}
template <typename T>
auto cross(PointT<T> a, PointT<T> b) -> decltype(a.x * b.x)
{
return a.x * b.y - b.x * a.y;
}
/// L1 norm
template <typename T>
T sumAbsComponent(PointT<T> p)
{
return std::abs(p.x) + std::abs(p.y);
}
/// L2 norm
template <typename T>
auto length(PointT<T> p) -> decltype(std::sqrt(dot(p, p)))
{
return std::sqrt(dot(p, p));
}
/// L-inf norm
template <typename T>
T maxAbsComponent(PointT<T> p)
{
return std::max(std::abs(p.x), std::abs(p.y));
}
template <typename T>
auto distance(PointT<T> a, PointT<T> b) -> decltype(length(a - b))
{
return length(a - b);
}
template <typename T>
PointF normalized(PointT<T> d)
{
return PointF(d) / length(PointF(d));
}
template <typename T>
PointT<T> bresenhamDirection(PointT<T> d)
{
return d / maxAbsComponent(d);
}
template <typename T>
PointT<T> mainDirection(PointT<T> d)
{
return std::abs(d.x) > std::abs(d.y) ? PointT<T>(d.x, 0) : PointT<T>(0, d.y);
}
/// Calculate a floating point pixel coordinate representing the 'center' of the pixel.
/// This is sort of the inverse operation of the PointI(PointF) conversion constructor.
/// See also the documentation of the GridSampler API.
inline PointF centered(PointI p)
{
return p + PointF(0.5f, 0.5f);
}
inline PointF centered(PointF p)
{
return {std::floor(p.x) + 0.5f, std::floor(p.y) + 0.5f};
}
/// @endcond
template <typename T>
std::string ToString(const PointT<T>& p, bool swap = false, char delim = 'x')
{
return std::to_string(swap ? p.y : p.x) + delim + std::to_string(swap ? p.x : p.y);
}
} // ZXing

215
tools/chatgui/zxing-cpp/core/src/Quadrilateral.h

@ -0,0 +1,215 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Point.h"
#ifdef ZXING_INTERNAL
#include "ZXAlgorithms.h"
#endif
#include <array>
#include <cmath>
#include <string>
namespace ZXing {
/**
* @brief A simple class representing a quadrilateral defined by its four corner points.
*
* It represents a quadrilateral defined by its four corner points:
* top-left, top-right, bottom-right, and bottom-left. Those points are relative to the detected symbol
* i.e. topLeft() will return the coordinates of the top-left corner of the symbol. If e.g. the symbol
* is rotated 180 degrees, the top-left corner will be the one that is at the bottom-right position in the image.
*/
template <typename T>
class Quadrilateral : public std::array<T, 4>
{
using Base = std::array<T, 4>;
using Base::at;
public:
using Point = T;
Quadrilateral() = default;
Quadrilateral(T tl, T tr, T br, T bl) : Base{tl, tr, br, bl} {}
template <typename U>
Quadrilateral(PointT<U> tl, PointT<U> tr, PointT<U> br, PointT<U> bl)
: Quadrilateral(Point(tl), Point(tr), Point(br), Point(bl))
{}
constexpr Point topLeft() const noexcept { return at(0); }
constexpr Point topRight() const noexcept { return at(1); }
constexpr Point bottomRight() const noexcept { return at(2); }
constexpr Point bottomLeft() const noexcept { return at(3); }
/// Return the orientation of the quadrilateral in radians, where 0 means the horizontal center line
/// is parallel to the x-axis and positive values mean a clockwise rotation.
double orientation() const
{
auto centerLine = (topRight() + bottomRight()) - (topLeft() + bottomLeft());
if (centerLine == Point{})
return 0.;
auto centerLineF = normalized(centerLine);
return std::atan2(centerLineF.y, centerLineF.x);
}
};
using QuadrilateralF = Quadrilateral<PointF>;
using QuadrilateralI = Quadrilateral<PointI>;
template <typename T>
std::string ToString(const Quadrilateral<PointT<T>>& points)
{
std::string res;
for (const auto& p : points)
res += std::to_string(p.x) + "x" + std::to_string(p.y) + (&p == &points.back() ? "" : " ");
return res;
}
#ifdef ZXING_INTERNAL
template <typename PointT = PointF>
Quadrilateral<PointT> Rectangle(int width, int height, typename PointT::value_t margin = 0)
{
return {
PointT{margin, margin}, {width - margin, margin}, {width - margin, height - margin}, {margin, height - margin}};
}
template <typename PointT = PointF>
Quadrilateral<PointT> Rectangle(int x0, int x1, int y0, int y1, typename PointT::value_t o)
{
return {PointT{x0 + o, y0 + o}, {x1 + o, y0 + o}, {x1 + o, y1 + o}, {x0 + o, y1 + o}};
}
template <typename PointT = PointF>
Quadrilateral<PointT> Rectangle(int left, int top, int width, int height)
{
int right = left + width - 1;
int bottom = top + height - 1;
return {PointT{left, top}, {right, top}, {right, bottom}, {left, bottom}};
}
template <typename PointT = PointF>
Quadrilateral<PointT> CenteredSquare(int size)
{
return Scale(Quadrilateral(PointT{-1, -1}, {1, -1}, {1, 1}, {-1, 1}), size / 2);
}
template <typename PointT = PointI>
Quadrilateral<PointT> Line(int y, int xStart, int xStop)
{
return {PointT{xStart, y}, {xStop, y}, {xStop, y}, {xStart, y}};
}
template <typename PointT>
bool IsConvex(const Quadrilateral<PointT>& poly)
{
const int N = Size(poly);
bool sign = false;
typename PointT::value_t m = INFINITY, M = 0;
for(int i = 0; i < N; i++)
{
auto d1 = poly[(i + 2) % N] - poly[(i + 1) % N];
auto d2 = poly[i] - poly[(i + 1) % N];
auto cp = cross(d1, d2);
// TODO: see if the isInside check for all boundary points in GridSampler is still required after fixing the wrong fabs()
// application in the following line
UpdateMinMax(m, M, std::fabs(cp));
if (i == 0)
sign = cp > 0;
else if (sign != (cp > 0))
return false;
}
// It turns out being convex is not enough to prevent a "numerical instability"
// that can cause the corners being projected inside the image boundaries but
// some points near the corners being projected outside. This has been observed
// where one corner is almost in line with two others. The M/m ratio is below 2
// for the complete existing sample set. For very "skewed" QRCodes a value of
// around 3 is realistic. A value of 14 has been observed to trigger the
// instability.
return M / m < 4.0;
}
template <typename PointT>
Quadrilateral<PointT> Scale(const Quadrilateral<PointT>& q, int factor)
{
return {factor * q[0], factor * q[1], factor * q[2], factor * q[3]};
}
template <typename PointT>
Quadrilateral<PointT> Move(const Quadrilateral<PointT>& q, PointT offset)
{
return {q[0] + offset, q[1] + offset, q[2] + offset, q[3] + offset};
}
template <typename PointT>
PointT Center(const Quadrilateral<PointT>& q)
{
return Reduce(q) / Size(q);
}
template <typename PointT>
Quadrilateral<PointT> RotatedCorners(const Quadrilateral<PointT>& q, int n = 1, bool mirror = false)
{
Quadrilateral<PointT> res;
std::rotate_copy(q.begin(), q.begin() + ((n + 4) % 4), q.end(), res.begin());
if (mirror)
std::swap(res[1], res[3]);
return res;
}
template <typename PointT>
bool IsInside(const PointT& p, const Quadrilateral<PointT>& q)
{
// Test if p is on the same side (right or left) of all polygon segments
int pos = 0, neg = 0;
for (int i = 0; i < Size(q); ++i)
(cross(p - q[i], q[(i + 1) % Size(q)] - q[i]) < 0 ? neg : pos)++;
return pos == 0 || neg == 0;
}
template <typename PointT>
Quadrilateral<PointT> BoundingBox(const Quadrilateral<PointT>& q)
{
auto [minX, maxX] = std::minmax({q[0].x, q[1].x, q[2].x, q[3].x});
auto [minY, maxY] = std::minmax({q[0].y, q[1].y, q[2].y, q[3].y});
return {PointT{minX, minY}, {maxX, minY}, {maxX, maxY}, {minX, maxY}};
}
template <typename PointT>
bool HaveIntersectingBoundingBoxes(const Quadrilateral<PointT>& a, const Quadrilateral<PointT>& b)
{
auto bba = BoundingBox(a), bbb = BoundingBox(b);
bool x = bbb.topRight().x < bba.topLeft().x || bbb.topLeft().x > bba.topRight().x;
bool y = bbb.bottomLeft().y < bba.topLeft().y || bbb.topLeft().y > bba.bottomLeft().y;
return !(x || y);
}
template <typename PointT>
Quadrilateral<PointT> Blend(const Quadrilateral<PointT>& a, const Quadrilateral<PointT>& b)
{
auto dist2First = [r = a[0]](auto s, auto t) { return distance(s, r) < distance(t, r); };
// rotate points such that the the two topLeft points are closest to each other
auto offset = std::min_element(b.begin(), b.end(), dist2First) - b.begin();
Quadrilateral<PointT> res;
for (int i = 0; i < 4; ++i)
res[i] = (a[i] + b[(i + offset) % 4]) / 2;
return res;
}
#endif
} // ZXing

112
tools/chatgui/zxing-cpp/core/src/Range.h

@ -0,0 +1,112 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ZXAlgorithms.h"
#include <cstdint>
#include <iterator>
namespace ZXing {
template <typename Iterator>
struct StrideIter
{
Iterator pos;
int stride;
using iterator_category = std::random_access_iterator_tag;
using difference_type = typename std::iterator_traits<Iterator>::difference_type;
using value_type = typename std::iterator_traits<Iterator>::value_type;
using pointer = Iterator;
using reference = typename std::iterator_traits<Iterator>::reference;
auto operator*() const { return *pos; }
auto operator[](int i) const { return *(pos + i * stride); }
StrideIter<Iterator>& operator++() { return pos += stride, *this; }
StrideIter<Iterator> operator++(int) { auto temp = *this; ++*this; return temp; }
bool operator==(const StrideIter<Iterator>& rhs) const { return pos == rhs.pos; }
StrideIter<Iterator> operator+(int i) const { return {pos + i * stride, stride}; }
StrideIter<Iterator> operator-(int i) const { return {pos - i * stride, stride}; }
int operator-(const StrideIter<Iterator>& rhs) const { return narrow_cast<int>((pos - rhs.pos) / stride); }
};
template <typename Iterator>
StrideIter(const Iterator&, int) -> StrideIter<Iterator>;
template <typename Iterator>
struct Range
{
Iterator _begin, _end;
Range(Iterator b, Iterator e) : _begin(b), _end(e) {}
template <typename C>
Range(const C& c) : _begin(std::begin(c)), _end(std::end(c)) {}
Iterator begin() const noexcept { return _begin; }
Iterator end() const noexcept { return _end; }
explicit operator bool() const { return begin() < end(); }
int size() const { return narrow_cast<int>(end() - begin()); }
};
template <typename C>
Range(const C&) -> Range<typename C::const_iterator>;
/**
* ArrayView is a lightweight, non-owning, non-mutable view over a contiguous sequence of elements.
* Similar to std::span<const T>. See also Range template for general iterator use case.
*/
template <typename T>
class ArrayView
{
const T* _data = nullptr;
std::size_t _size = 0;
public:
using value_type = T;
using pointer = const value_type*;
using const_pointer = const value_type*;
using reference = const value_type&;
using const_reference = const value_type&;
using size_type = std::size_t;
constexpr ArrayView() noexcept = default;
constexpr ArrayView(pointer data, size_type size) noexcept : _data(data), _size(size) {}
template <typename P, typename U = T,
typename = std::enable_if_t<
sizeof(U) == 1 && std::is_same_v<void, std::remove_cv_t<std::remove_reference_t<std::remove_pointer_t<P>>>>>>
constexpr ArrayView(P data, size_type size) noexcept : _data(reinterpret_cast<pointer>(data)), _size(size)
{}
template <typename Container,
typename = std::enable_if_t<std::is_convertible_v<decltype(std::data(std::declval<Container&>())), const_pointer>>>
constexpr ArrayView(const Container& c) noexcept : _data(std::data(c)), _size(std::size(c))
{}
constexpr pointer data() const noexcept { return _data; }
constexpr size_type size() const noexcept { return _size; }
constexpr bool empty() const noexcept { return _size == 0; }
constexpr const_reference operator[](size_type index) const noexcept { return _data[index]; }
constexpr pointer begin() const noexcept { return _data; }
constexpr pointer end() const noexcept { return _data + _size; }
constexpr ArrayView<T> subview(size_type pos, size_type len = size_type(-1)) const noexcept
{
if (pos > _size)
return {};
return {_data + pos, std::min(len, _size - pos)};
}
};
using ByteView = ArrayView<uint8_t>;
} // namespace ZXing

335
tools/chatgui/zxing-cpp/core/src/ReadBarcode.cpp

@ -0,0 +1,335 @@
/*
* Copyright 2019 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "ReadBarcode.h"
#include "ReaderOptions.h"
#include "BarcodeData.h"
#include "Version.h"
#include <utility>
#ifdef ZXING_READERS
#include "GlobalHistogramBinarizer.h"
#include "HybridBinarizer.h"
#include "MultiFormatReader.h"
#include "Pattern.h"
#include "ThresholdBinarizer.h"
#endif
#include <climits>
#include <memory>
#include <stdexcept>
namespace ZXing {
// ==============================================================================
// ReaderOptions implementation
// ==============================================================================
struct ReaderOptions::Data
{
bool tryHarder : 1 = true;
bool tryRotate : 1 = true;
bool tryInvert : 1 = true;
bool tryDownscale : 1 = true;
#ifdef ZXING_EXPERIMENTAL_API
bool tryDenoise : 1 = false;
#endif
bool isPure : 1 = false;
bool validateOptionalChecksum : 1 = false;
bool returnErrors : 1 = false;
uint8_t downscaleFactor : 3 = 3; // values 2, 3, 4
EanAddOnSymbol eanAddOnSymbol : 2 = EanAddOnSymbol::Ignore;
Binarizer binarizer : 2 = Binarizer::LocalAverage;
TextMode textMode : 3 = TextMode::HRI;
CharacterSet characterSet : 6 = CharacterSet::Unknown;
uint8_t minLineCount = 2;
uint8_t maxNumberOfSymbols = 0xff;
uint16_t downscaleThreshold = 500;
BarcodeFormats formats = {};
};
ReaderOptions::ReaderOptions() : d(std::make_unique<Data>()) {}
ReaderOptions::~ReaderOptions() = default;
// copy
ReaderOptions::ReaderOptions(const ReaderOptions& other) : d(std::make_unique<Data>(*other.d)) {}
ReaderOptions& ReaderOptions::operator=(const ReaderOptions& other)
{
if (this != &other)
d = std::make_unique<Data>(*other.d);
return *this;
}
// move
ReaderOptions::ReaderOptions(ReaderOptions&&) noexcept = default;
ReaderOptions& ReaderOptions::operator=(ReaderOptions&&) noexcept = default;
const BarcodeFormats& ReaderOptions::formats() const noexcept { return d->formats; }
ReaderOptions& ReaderOptions::formats(BarcodeFormats&& v) & { return (void)(d->formats = std::move(v)), *this; }
ReaderOptions&& ReaderOptions::formats(BarcodeFormats&& v) && { return (void)(d->formats = std::move(v)), std::move(*this); }
#define ZX_PROPERTY(TYPE, NAME, SETTER) \
TYPE ReaderOptions::NAME() const noexcept { return d->NAME; } \
ReaderOptions& ReaderOptions::NAME(TYPE v) & { return (void)(d->NAME = std::move(v)), *this; } \
ReaderOptions&& ReaderOptions::NAME(TYPE v) && { return (void)(d->NAME = std::move(v)), std::move(*this); }
ZX_PROPERTY(bool, tryHarder, setTryHarder)
ZX_PROPERTY(bool, tryRotate, setTryRotate)
ZX_PROPERTY(bool, tryInvert, setTryInvert)
ZX_PROPERTY(bool, tryDownscale, setTryDownscale)
#ifdef ZXING_EXPERIMENTAL_API
ZX_PROPERTY(bool, tryDenoise, setTryDenoise)
#endif
ZX_PROPERTY(Binarizer, binarizer, setBinarizer)
ZX_PROPERTY(bool, isPure, setIsPure)
ZX_PROPERTY(uint16_t, downscaleThreshold, setDownscaleThreshold)
ZX_PROPERTY(uint8_t, downscaleFactor, setDownscaleFactor)
ZX_PROPERTY(uint8_t, minLineCount, setMinLineCount)
ZX_PROPERTY(uint8_t, maxNumberOfSymbols, setMaxNumberOfSymbols)
ZX_PROPERTY(bool, validateOptionalChecksum, setValidateOptionalChecksum)
ZX_PROPERTY(bool, returnErrors, setReturnErrors)
ZX_PROPERTY(EanAddOnSymbol, eanAddOnSymbol, setEanAddOnSymbol)
ZX_PROPERTY(TextMode, textMode, setTextMode)
ZX_PROPERTY(CharacterSet, characterSet, setCharacterSet)
#undef ZX_PROPERTY
ReaderOptions& ReaderOptions::characterSet(std::string_view v) &
{
d->characterSet = CharacterSetFromString(v);
return *this;
}
ReaderOptions&& ReaderOptions::characterSet(std::string_view v) &&
{
d->characterSet = CharacterSetFromString(v);
return std::move(*this);
}
bool ReaderOptions::hasFormat(const BarcodeFormats& formats) const noexcept
{
return d->formats.empty() || std::any_of(formats.begin(), formats.end(), [this](BarcodeFormat bt) { return bt <= d->formats; });
}
bool ReaderOptions::hasAnyFormat(const BarcodeFormats& formats) const noexcept
{
return d->formats.empty() || std::any_of(formats.begin(), formats.end(), [this](BarcodeFormat bt) { return bt & d->formats; });
}
// ==============================================================================
// ReadBarcode implementation
// ==============================================================================
#ifdef ZXING_READERS
class LumImage : public Image
{
public:
using Image::Image;
uint8_t* data() { return const_cast<uint8_t*>(Image::data()); }
};
template<typename P>
static LumImage ExtractLum(const ImageView& iv, P projection)
{
LumImage res(iv.width(), iv.height());
auto* dst = res.data();
for(int y = 0; y < iv.height(); ++y)
for(int x = 0, w = iv.width(); x < w; ++x)
*dst++ = projection(iv.data(x, y));
return res;
}
class LumImagePyramid
{
std::vector<LumImage> buffers;
template<int N>
void addLayer()
{
auto siv = layers.back();
buffers.emplace_back(siv.width() / N, siv.height() / N);
layers.push_back(buffers.back());
auto& div = buffers.back();
auto* d = div.data();
for (int dy = 0; dy < div.height(); ++dy)
for (int dx = 0; dx < div.width(); ++dx) {
int sum = (N * N) / 2;
for (int ty = 0; ty < N; ++ty)
for (int tx = 0; tx < N; ++tx)
sum += *siv.data(dx * N + tx, dy * N + ty);
*d++ = sum / (N * N);
}
}
void addLayer(int factor)
{
// help the compiler's auto-vectorizer by hard-coding the scale factor
switch (factor) {
case 2: addLayer<2>(); break;
case 3: addLayer<3>(); break;
case 4: addLayer<4>(); break;
default: throw std::invalid_argument("Invalid ReaderOptions::downscaleFactor"); break;
}
}
public:
std::vector<ImageView> layers;
LumImagePyramid(const ImageView& iv, int threshold, int factor)
{
if (factor < 2)
throw std::invalid_argument("Invalid ReaderOptions::downscaleFactor");
layers.push_back(iv);
// TODO: if only matrix codes were considered, then using std::min would be sufficient (see #425)
while (threshold > 0 && std::max(layers.back().width(), layers.back().height()) > threshold &&
std::min(layers.back().width(), layers.back().height()) >= factor)
addLayer(factor);
#if 0
// Reversing the layers means we'd start with the smallest. that can make sense if we are only looking for a
// single symbol. If we start with the higher resolution, we get better (high res) position information.
// TODO: see if masking out higher res layers based on found symbols in lower res helps overall performance.
std::reverse(layers.begin(), layers.end());
#endif
}
};
ImageView SetupLumImageView(ImageView iv, LumImage& lum, const ReaderOptions& opts)
{
if (iv.format() == ImageFormat::None)
throw std::invalid_argument("Invalid image format");
if (opts.binarizer() == Binarizer::GlobalHistogram || opts.binarizer() == Binarizer::LocalAverage) {
// manually spell out the 3 most common pixel formats to get at least gcc to vectorize the code
if (iv.format() == ImageFormat::RGB && iv.pixStride() == 3) {
lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[0], src[1], src[2]); });
} else if (iv.format() == ImageFormat::RGBA && iv.pixStride() == 4) {
lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[0], src[1], src[2]); });
} else if (iv.format() == ImageFormat::BGR && iv.pixStride() == 3) {
lum = ExtractLum(iv, [](const uint8_t* src) { return RGBToLum(src[2], src[1], src[0]); });
} else if (iv.format() != ImageFormat::Lum) {
lum = ExtractLum(iv, [r = RedIndex(iv.format()), g = GreenIndex(iv.format()), b = BlueIndex(iv.format())](
const uint8_t* src) { return RGBToLum(src[r], src[g], src[b]); });
} else if (iv.pixStride() != 1) {
// GlobalHistogram and LocalAverage need dense line memory layout
lum = ExtractLum(iv, [](const uint8_t* src) { return *src; });
}
if (lum.data())
return lum;
}
return iv;
}
std::unique_ptr<BinaryBitmap> CreateBitmap(ZXing::Binarizer binarizer, const ImageView& iv)
{
switch (binarizer) {
case Binarizer::BoolCast: return std::make_unique<ThresholdBinarizer>(iv, 0);
case Binarizer::FixedThreshold: return std::make_unique<ThresholdBinarizer>(iv, 127);
case Binarizer::GlobalHistogram: return std::make_unique<GlobalHistogramBinarizer>(iv);
case Binarizer::LocalAverage: return std::make_unique<HybridBinarizer>(iv);
}
return {}; // silence gcc warning
}
Barcode ReadBarcode(const ImageView& _iv, const ReaderOptions& opts)
{
return FirstOrDefault(ReadBarcodes(_iv, ReaderOptions(opts).maxNumberOfSymbols(1)));
}
Barcodes ReadBarcodes(const ImageView& _iv, const ReaderOptions& opts)
{
if (sizeof(PatternType) < 4 && (_iv.width() > 0xffff || _iv.height() > 0xffff))
throw std::invalid_argument("Maximum image width/height is 65535");
if (!_iv.data() || _iv.width() * _iv.height() == 0)
throw std::invalid_argument("ImageView is null/empty");
LumImage lum;
ImageView iv = SetupLumImageView(_iv, lum, opts);
MultiFormatReader reader(opts);
if (opts.isPure())
return {FirstOrDefault(reader.read(*CreateBitmap(opts.binarizer(), iv), 1)).setReaderOptions(opts)};
std::unique_ptr<MultiFormatReader> closedReader;
#ifdef ZXING_EXPERIMENTAL_API
using enum BarcodeFormat;
BarcodeFormats formatsBenefittingFromClosing = Aztec | DataMatrix | QRCode;
ReaderOptions closedOptions = opts;
if (opts.tryDenoise() && opts.hasAnyFormat(formatsBenefittingFromClosing) && _iv.height() >= 3) {
closedOptions.formats(opts.formats().empty() ? formatsBenefittingFromClosing : formatsBenefittingFromClosing & opts.formats());
closedReader = std::make_unique<MultiFormatReader>(closedOptions);
}
#endif
LumImagePyramid pyramid(iv, opts.downscaleThreshold() * opts.tryDownscale(), opts.downscaleFactor());
Barcodes res;
int maxSymbols = opts.maxNumberOfSymbols() ? opts.maxNumberOfSymbols() : INT_MAX;
for (auto&& iv : pyramid.layers) {
auto bitmap = CreateBitmap(opts.binarizer(), iv);
for (int close = 0; close <= (closedReader ? 1 : 0); ++close) {
if (close) {
// if we already inverted the image in the first round, we need to undo that first
if (bitmap->inverted())
bitmap->invert();
bitmap->close();
}
// TODO: check if closing after invert would be beneficial
for (int invert = 0; invert <= static_cast<int>(opts.tryInvert() && !close); ++invert) {
if (invert)
bitmap->invert();
auto rs = (close ? *closedReader : reader).read(*bitmap, maxSymbols);
for (auto& r : rs) {
if (iv.width() != _iv.width())
r.d->position = Scale(r.position(), _iv.width() / iv.width());
if (!Contains(res, r)) {
r.setReaderOptions(opts);
r.d->isInverted = bitmap->inverted();
res.push_back(std::move(r));
--maxSymbols;
}
}
if (maxSymbols <= 0)
return res;
}
}
}
if (opts.returnErrors()) {
// if symbols overlap and one is in error remove it
for (auto a = res.begin(); a != res.end(); ++a)
for (auto b = std::next(a); b != res.end(); ++b)
if (HaveIntersectingBoundingBoxes(a->position(), b->position()) && (a->error() != b->error()))
*(a->error() ? a : b) = BarcodeData();
std::erase_if(res, [](auto&& r) { return r.format() == BarcodeFormat::None; });
}
return res;
}
#else // ZXING_READERS
Barcode ReadBarcode(const ImageView&, const ReaderOptions&)
{
throw std::runtime_error("This build of zxing-cpp does not support reading barcodes.");
}
Barcodes ReadBarcodes(const ImageView&, const ReaderOptions&)
{
throw std::runtime_error("This build of zxing-cpp does not support reading barcodes.");
}
#endif // ZXING_READERS
} // ZXing

33
tools/chatgui/zxing-cpp/core/src/ReadBarcode.h

@ -0,0 +1,33 @@
/*
* Copyright 2019 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ReaderOptions.h"
#include "ImageView.h"
#include "Barcode.h"
namespace ZXing {
/**
* Read barcode from an ImageView
*
* @param image view of the image data including layout and format
* @param options optional ReaderOptions to parameterize / speed up detection
* @return Barcode found, if any, otherwise a Barcode with empty content and format ZXing::BarcodeFormat::None
*/
Barcode ReadBarcode(const ImageView& image, const ReaderOptions& options = {});
/**
* Read barcodes from an ImageView
*
* @param image view of the image data including layout and format
* @param options optional ReaderOptions to parameterize / speed up detection
* @return List of Barcode found, may be empty
*/
Barcodes ReadBarcodes(const ImageView& image, const ReaderOptions& options = {});
} // ZXing

32
tools/chatgui/zxing-cpp/core/src/Reader.h

@ -0,0 +1,32 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ReaderOptions.h"
#include "BarcodeData.h"
namespace ZXing {
class BinaryBitmap;
class ReaderOptions;
class Reader
{
protected:
const ReaderOptions& _opts;
public:
const bool supportsInversion;
explicit Reader(const ReaderOptions& opts, bool supportsInversion = false) : _opts(opts), supportsInversion(supportsInversion) {}
explicit Reader(ReaderOptions&& opts) = delete;
virtual ~Reader() = default;
virtual BarcodesData read(const BinaryBitmap& image, int maxSymbols) const = 0;
};
} // ZXing

210
tools/chatgui/zxing-cpp/core/src/ReaderOptions.h

@ -0,0 +1,210 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BarcodeFormat.h"
#include "CharacterSet.h"
#include "Version.h"
#include <string_view>
#include <utility>
#include <memory>
namespace ZXing {
/**
* @brief Specify which algorithm to use for the grayscale to binary transformation.
*
* The difference is how to get to a threshold value T which results in a bit value R(esult) = L(uminance) <= T(hreshold).
*/
enum class Binarizer : unsigned char // needs to be unsigned for the bitfield below to work, uint8_t fails as well
{
LocalAverage, ///< T = average of neighboring pixels for matrix and GlobalHistogram for linear (HybridBinarizer)
GlobalHistogram, ///< T = valley between the 2 largest peaks in the histogram (per line in linear case)
FixedThreshold, ///< T = 127
BoolCast, ///< T = 0, fastest possible
};
/**
* @brief Specify whether to ignore, read or require EAN-2/5 add-on symbols while scanning EAN/UPC codes.
*/
enum class EanAddOnSymbol : unsigned char // see above
{
Ignore, ///< Ignore any Add-On symbol during read/scan
Read, ///< Read EAN-2/EAN-5 Add-On symbol if found
Require, ///< Require EAN-2/EAN-5 Add-On symbol to be present
};
/**
* @brief Specify how the decoded byte content of a barcode should be transcoded to text.
*
* @see Barcode::text(), ReaderOptions::textMode().
*/
enum class TextMode : unsigned char // see above
{
Plain, ///< bytes() transcoded to unicode based on ECI info or guessed charset (the default mode prior to 2.0)
ECI, ///< standard content following the ECI protocol with every character set ECI segment transcoded to unicode
HRI, ///< Human Readable Interpretation (dependent on the ContentType)
Escaped, ///< Use the EscapeNonGraphical() function (e.g. ASCII 29 will be transcoded to "<GS>")
Hex, ///< bytes() transcoded to ASCII string of HEX values
HexECI, ///< bytesECI() transcoded to ASCII string of HEX values
};
/**
* @brief Configuration options for barcode reading and decoding behavior.
*
* ReaderOptions encapsulates a set of flags and parameters that control
* how barcode detection and decoding is performed. It provides
* fluent setters that support chaining. Both `name(val)` and `setName(val)`
* forms are available for convenience and compatibility.
*
* The class is intended to be passed to the ReadBarcodes function to
* influence scanning heuristics, performance vs. accuracy trade-offs, output
* formatting, and symbol filtering. Instances can be reused across multiple
* read operations.
*
* The default settings are optimized for detection rate and can be tuned
* for speed or specific use-cases.
*
* @see BarcodeFormats, Binarizer, TextMode, CharacterSet, ReadBarcodes
*/
class ReaderOptions
{
struct Data;
std::unique_ptr<Data> d;
public:
ReaderOptions();
~ReaderOptions();
ReaderOptions(const ReaderOptions&);
ReaderOptions& operator=(const ReaderOptions&);
ReaderOptions(ReaderOptions&&) noexcept;
ReaderOptions& operator=(ReaderOptions&&) noexcept;
#define ZX_PROPERTY(TYPE, NAME, SETTER, ...) \
TYPE NAME() const noexcept; \
__VA_ARGS__ ReaderOptions& NAME(TYPE v) &; \
__VA_ARGS__ ReaderOptions&& NAME(TYPE v) &&; \
__VA_ARGS__ inline ReaderOptions& SETTER(TYPE v) & { return NAME(v); } \
__VA_ARGS__ inline ReaderOptions&& SETTER(TYPE v) && { return std::move(*this).NAME(v); }
/// Specify a set of BarcodeFormats that should be searched for, the default is all supported formats.
const BarcodeFormats& formats() const noexcept;
ReaderOptions& formats(BarcodeFormats&& v) &;
ReaderOptions&& formats(BarcodeFormats&& v) &&;
ReaderOptions& formats(const BarcodeFormats& v) & { return formats(BarcodeFormats(v)); }
ReaderOptions&& formats(const BarcodeFormats& v) && { return std::move(*this).formats(BarcodeFormats(v)); }
inline ReaderOptions& setFormats(BarcodeFormats&& v) & { return formats(std::move(v)); }
inline ReaderOptions&& setFormats(BarcodeFormats&& v) && { return std::move(*this).formats(std::move(v)); }
inline ReaderOptions& setFormats(const BarcodeFormats& v) & { return formats(BarcodeFormats(v)); }
inline ReaderOptions&& setFormats(const BarcodeFormats& v) && { return std::move(*this).formats(BarcodeFormats(v)); }
/// Spend more time to try to find a barcode; optimize for accuracy instead of not speed (default: true).
ZX_PROPERTY(bool, tryHarder, setTryHarder)
/// Try detecting codes in 90, 180 and 270 degree rotated images (default: true).
ZX_PROPERTY(bool, tryRotate, setTryRotate)
/// Try detecting inverted ("reversed reflectance") codes if the format allows for those (default: true).
ZX_PROPERTY(bool, tryInvert, setTryInvert)
/// Try detecting code in downscaled images (depending on image size) (default: true).
ZX_PROPERTY(bool, tryDownscale, setTryDownscale)
#ifdef ZXING_EXPERIMENTAL_API
/// Also try detecting code after denoising (currently morphological closing filter for 2D formats only).
ZX_PROPERTY(bool, tryDenoise, setTryDenoise)
#endif
/// Binarizer to use for grayscale to binary transformation (default: Binarizer::LocalAverage).
ZX_PROPERTY(Binarizer, binarizer, setBinarizer)
/// Set to true if the input contains nothing but a single perfectly aligned barcode (generated image).
ZX_PROPERTY(bool, isPure, setIsPure)
/// Image size ( min(width, height) ) threshold at which to start downscaled scanning.
ZX_PROPERTY(uint16_t, downscaleThreshold, setDownscaleThreshold)
/// Scale factor used during downscaling, meaningful values are 2, 3 and 4.
ZX_PROPERTY(uint8_t, downscaleFactor, setDownscaleFactor)
/// The number of scan lines in a linear barcode that have to be equal to accept the result (default: 2).
ZX_PROPERTY(uint8_t, minLineCount, setMinLineCount)
/// The maximum number of symbols (barcodes) to detect / look for with ReadBarcodes().
ZX_PROPERTY(uint8_t, maxNumberOfSymbols, setMaxNumberOfSymbols)
/// Validate optional checksums where applicable (e.g. Code39, ITF) (default: false).
ZX_PROPERTY(bool, validateOptionalChecksum, setValidateOptionalChecksum)
/// If true, return the barcodes with errors as well (e.g. checksum errors, see Barcode::error()) (default: false).
ZX_PROPERTY(bool, returnErrors, setReturnErrors)
/// Specify whether to ignore, read or require EAN-2/5 add-on symbols while scanning EAN/UPC codes.
ZX_PROPERTY(EanAddOnSymbol, eanAddOnSymbol, setEanAddOnSymbol)
/// Specifies the TextMode that controls the return of the Barcode::text() function (default: TextMode::HRI).
ZX_PROPERTY(TextMode, textMode, setTextMode)
/// Specifies fallback character set to use instead of auto-detecting it (when applicable).
ZX_PROPERTY(CharacterSet, characterSet, setCharacterSet)
ReaderOptions& characterSet(std::string_view v) &;
ReaderOptions&& characterSet(std::string_view v) &&;
inline ReaderOptions& setCharacterSet(std::string_view v) & { return characterSet(v); }
inline ReaderOptions&& setCharacterSet(std::string_view v) && { return std::move(*this).characterSet(v); }
#undef ZX_PROPERTY
/// @cond DEPRECATED
// Silence deprecated-declarations warnings, only happening here for deprecated inline functions
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
#define ZX_DEPRECATED_PROPERTY(TYPE, NAME, SETTER, GET_IMPL, SET_IMPL) \
[[deprecated]] inline TYPE NAME() const noexcept { return GET_IMPL; } \
[[deprecated]] ReaderOptions& NAME(TYPE v) & { SET_IMPL; return *this; } \
[[deprecated]] ReaderOptions&& NAME(TYPE v) && { SET_IMPL; return std::move(*this); } \
[[deprecated]] inline ReaderOptions& SETTER(TYPE v) & { return NAME(v); } \
[[deprecated]] inline ReaderOptions&& SETTER(TYPE v) && { return std::move(*this).NAME(v); }
/// @deprecated (does nothing). See BarcodeFormat::Code39Ext and ::Code39Std to select full ASCII or standard Code39 mode.
ZX_DEPRECATED_PROPERTY(bool, tryCode39ExtendedMode, setTryCode39ExtendedMode, true, (void)v)
/// @deprecated (use validateOptionalChecksum). The Code39 symbol has a valid checksum iff symbologyIdentifier()[2] is an odd digit
ZX_DEPRECATED_PROPERTY(bool, validateCode39CheckSum, setValidateCode39CheckSum, validateOptionalChecksum(),
validateOptionalChecksum(v))
/// @deprecated (use validateOptionalChecksum). The ITF symbol has a valid checksum iff symbologyIdentifier()[2] == '1'.
ZX_DEPRECATED_PROPERTY(bool, validateITFCheckSum, setValidateITFCheckSum, validateOptionalChecksum(), validateOptionalChecksum(v))
#undef ZX_DEPRECATED_PROPERTY
#ifdef __GNUC__
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
/// @endcond
#ifdef ZXING_INTERNAL
/// Check if a specific format is explicitly enabled in the formats set
bool hasFormat(const BarcodeFormats& formats) const noexcept;
/// Check if any format is explicitly or implicitly enabled in the formats set
bool hasAnyFormat(const BarcodeFormats& formats) const noexcept;
#endif
};
} // ZXing

101
tools/chatgui/zxing-cpp/core/src/ReedSolomon.cpp

@ -0,0 +1,101 @@
// Copyright 2026 Axel Waggershauser
// SPDX-License-Identifier: Apache-2.0
#include "ReedSolomon.h"
#include "Version.h"
#include "ZXAlgorithms.h"
#include "librscpp/encode.h"
#include "librscpp/decode.h"
#include <algorithm>
#include <type_traits>
#include <stdexcept>
#include <utility>
namespace ZXing {
namespace rs = librscpp;
using GF2nI = rs::GF2n<>;
const GF2nI& GetGF2n(RSField field)
{
static GF2nI gf2n_4_1(0x0013, 1); // x^4 + x + 1
static GF2nI gf2n_6_1(0x0043, 1); // x^6 + x + 1
static GF2nI gf2n_8_5_3_2(0x012D, 1); // x^8 + x^5 + x^3 + x^2 + 1 : DataMatrix
static GF2nI gf2n_8_4_3_2(0x011D, 0); // x^8 + x^4 + x^3 + x^2 + 1 : QRCode
static GF2nI gf2n_10_3(0x0409, 1); // x^10 + x^3 + 1
static GF2nI gf2n_12_6_5_3(0x1069, 1); // x^12 + x^6 + x^5 + x^3 + 1
switch (field) {
case RSField::Aztec4: return gf2n_4_1;
case RSField::Aztec6: return gf2n_6_1;
case RSField::Aztec8: return gf2n_8_5_3_2;
case RSField::Aztec10: return gf2n_10_3;
case RSField::Aztec12: return gf2n_12_6_5_3;
case RSField::DataMatrix: return gf2n_8_5_3_2;
case RSField::MaxiCode: return gf2n_6_1;
case RSField::QRCode: return gf2n_8_4_3_2;
default: throw std::invalid_argument("Unsupported RSField");
}
}
const rs::GFp<>& GetGFPDF417()
{
static rs::GFp<> gf929_3(929, 3, 1);
return gf929_3;
}
// See ISO/IEC 15415:2024 7.5.9
static std::optional<double> UEC(std::optional<int> usedECC, int numECC)
{
return usedECC ? std::optional(std::clamp(1.0 - static_cast<double>(*usedECC) / numECC, 0.0, 1.0)) : std::nullopt;
}
#ifdef ZXING_READERS
std::optional<double> ReedSolomonDecode(RSField field, std::span<uint8_t> codeword, int numECC, std::span<const int> erasures)
{
return UEC(rs::decode(GetGF2n(field), codeword, numECC, erasures), numECC);
}
std::optional<double> ReedSolomonDecode(RSField field, std::span<int> codeword, int numECC, std::span<const int> erasures)
{
// The number of erasures may at most be numECC.
// Note that leaves 0 parity symbols to error correction (or even detection) but the final syndrome verification in rs::decode will
// still catch any errors in that case.
if (Size(erasures) > numECC)
return {};
#if ZXING_ENABLE_PDF417
if (field == RSField::PDF417)
return UEC(rs::decode(GetGFPDF417(), codeword, numECC, erasures), numECC);
else
#endif
return UEC(rs::decode(GetGF2n(field), codeword, numECC, erasures), numECC);
}
#endif
#ifdef ZXING_WRITERS
void ReedSolomonEncode(RSField field, std::span<const uint8_t> data, std::span<uint8_t> parity)
{
rs::encode(GetGF2n(field), data, parity);
}
void ReedSolomonEncode(RSField field, std::span<uint8_t> codeword, int numECC)
{
rs::encode_inplace(GetGF2n(field), codeword, numECC);
}
void ReedSolomonEncode(RSField field, std::span<int> codeword, int numECC)
{
#if ZXING_ENABLE_PDF417
if (field == RSField::PDF417)
return rs::encode_inplace(GetGFPDF417(), codeword, numECC);
else
#endif
rs::encode_inplace(GetGF2n(field), codeword, numECC);
}
#endif
} // namespace ZXing

65
tools/chatgui/zxing-cpp/core/src/ReedSolomon.h

@ -0,0 +1,65 @@
// Copyright 2026 Axel Waggershauser
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
namespace ZXing {
enum class RSField
{
Aztec4,
Aztec6,
Aztec8,
Aztec10,
Aztec12,
DataMatrix,
MaxiCode,
PDF417,
QRCode
};
inline RSField GF2nAztec(int wordSize)
{
switch (wordSize) {
case 4: return RSField::Aztec4;
case 6: return RSField::Aztec6;
case 8: return RSField::Aztec8;
case 10: return RSField::Aztec10;
case 12: return RSField::Aztec12;
default: throw std::invalid_argument("Unsupported word size " + std::to_string(wordSize));
}
}
/**
* @brief ReedSolomonDecode fixes errors in a codeword containing both data and parity symbols.
*
* @param codeword data and error-correction/parity symbols; corrected in place on success
* @param numECC number of error-correction/parity symbols
* @param erasures positions of known erasures in the codeword
* @return optional unused error correction in the range [0, 1] if codeword errors could successfully be fixed (or there have not been
* any), std::nullopt otherwise
*/
std::optional<double> ReedSolomonDecode(RSField field, std::span<int> codeword, int numECC, std::span<const int> erasures = {});
std::optional<double> ReedSolomonDecode(RSField field, std::span<uint8_t> codeword, int numECC,
std::span<const int> erasures = {});
/**
* @brief ReedSolomonEncode generates error correction symbols for the given data symbols.
*
* @param field The Galois field to use for encoding.
* @param data The input data symbols.
* @param parity The output buffer for the generated error correction symbols.
*/
void ReedSolomonEncode(RSField field, std::span<const uint8_t> data, std::span<uint8_t> parity);
/// @brief ReedSolomonEncode replaces the last numECC symbols in codeword with parity symbols
void ReedSolomonEncode(RSField field, std::span<uint8_t> codeword, int numECC);
void ReedSolomonEncode(RSField field, std::span<int> codeword, int numECC);
} // ZXing

161
tools/chatgui/zxing-cpp/core/src/RegressionLine.h

@ -0,0 +1,161 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Point.h"
#include "ZXAlgorithms.h"
#include <algorithm>
#include <cmath>
#include <vector>
#ifdef PRINT_DEBUG
#include <cstdio>
#endif
namespace ZXing {
class RegressionLine
{
protected:
std::vector<PointF> _points;
PointF _directionInward;
PointF::value_t a = NAN, b = NAN, c = NAN;
friend PointF intersect(const RegressionLine& l1, const RegressionLine& l2);
template<typename T> bool evaluate(const PointT<T>* begin, const PointT<T>* end)
{
auto mean = Reduce(begin, end, PointF()) / std::distance(begin, end);
PointF::value_t sumXX = 0, sumYY = 0, sumXY = 0;
for (auto p = begin; p != end; ++p) {
auto d = *p - mean;
sumXX += d.x * d.x;
sumYY += d.y * d.y;
sumXY += d.x * d.y;
}
if (sumYY >= sumXX) {
auto l = std::sqrt(sumYY * sumYY + sumXY * sumXY);
a = +sumYY / l;
b = -sumXY / l;
} else {
auto l = std::sqrt(sumXX * sumXX + sumXY * sumXY);
a = +sumXY / l;
b = -sumXX / l;
}
if (dot(_directionInward, normal()) < 0) {
a = -a;
b = -b;
}
c = dot(normal(), mean); // (a*mean.x + b*mean.y);
return dot(_directionInward, normal()) > 0.5f; // angle between original and new direction is at most 60 degree
}
template <typename T> bool evaluate(const std::vector<PointT<T>>& points) { return evaluate(&points.front(), &points.back() + 1); }
template <typename T> static auto distance(PointT<T> a, PointT<T> b) { return ZXing::distance(a, b); }
public:
RegressionLine() { _points.reserve(16); } // arbitrary but plausible start size (tiny performance improvement)
template<typename T> RegressionLine(PointT<T> a, PointT<T> b)
{
evaluate(std::vector{a, b});
}
template<typename T> RegressionLine(const PointT<T>* b, const PointT<T>* e)
{
evaluate(b, e);
}
const auto& points() const { return _points; }
int length() const { return _points.size() >= 2 ? int(distance(_points.front(), _points.back())) : 0; }
bool isValid() const { return !std::isnan(a); }
PointF normal() const { return isValid() ? PointF(a, b) : _directionInward; }
auto signedDistance(PointF p) const { return dot(normal(), p) - c; }
template <typename T> auto distance(PointT<T> p) const { return std::abs(signedDistance(PointF(p))); }
PointF project(PointF p) const { return p - signedDistance(p) * normal(); }
PointF centroid() const { return Reduce(_points) / _points.size(); }
void reset()
{
_points.clear();
_directionInward = {};
a = b = c = NAN;
}
void add(PointF p) {
assert(_directionInward != PointF());
_points.push_back(p);
if (_points.size() == 1)
c = dot(normal(), p);
}
void pop_back() { _points.pop_back(); }
void pop_front()
{
std::rotate(_points.begin(), _points.begin() + 1, _points.end());
_points.pop_back();
}
void setDirectionInward(PointF d) { _directionInward = normalized(d); }
bool evaluate(double maxSignedDist = -1, bool updatePoints = false)
{
bool ret = evaluate(_points);
if (maxSignedDist > 0) {
auto points = _points;
while (true) {
auto old_points_size = points.size();
// remove points that are further 'inside' than maxSignedDist or further 'outside' than 2 x maxSignedDist
std::erase_if(points, [this, maxSignedDist](auto p) {
auto sd = this->signedDistance(p);
return sd > maxSignedDist || sd < -2 * maxSignedDist;
});
// if we threw away too many points, something is off with the line to begin with
if (points.size() < old_points_size / 2 || points.size() < 2)
return false;
if (old_points_size == points.size())
break;
#ifdef PRINT_DEBUG
printf("removed %zu points -> %zu remaining\n", old_points_size - points.size(), points.size());
fflush(stdout);
#endif
ret = evaluate(points);
}
if (updatePoints)
_points = std::move(points);
}
return ret;
}
bool isHighRes() const
{
PointF min = _points.front(), max = _points.front();
for (auto p : _points) {
UpdateMinMax(min.x, max.x, p.x);
UpdateMinMax(min.y, max.y, p.y);
}
auto diff = max - min;
auto len = maxAbsComponent(diff);
auto steps = std::min(std::abs(diff.x), std::abs(diff.y));
// due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal
return steps > 2 || len > 50;
}
};
inline PointF intersect(const RegressionLine& l1, const RegressionLine& l2)
{
assert(l1.isValid() && l2.isValid());
auto d = l1.a * l2.b - l1.b * l2.a;
auto x = (l1.c * l2.b - l1.b * l2.c) / d;
auto y = (l1.a * l2.c - l1.c * l2.a) / d;
return {x, y};
}
} // ZXing

20
tools/chatgui/zxing-cpp/core/src/ResultPoint.cpp

@ -0,0 +1,20 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "ResultPoint.h"
#include <cmath>
namespace ZXing {
float ResultPoint::Distance(int aX, int aY, int bX, int bY)
{
auto dx = static_cast<float>(aX - bX);
auto dy = static_cast<float>(aY - bY);
return std::sqrt(dx * dx + dy * dy);
}
} // ZXing

35
tools/chatgui/zxing-cpp/core/src/ResultPoint.h

@ -0,0 +1,35 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "Point.h"
namespace ZXing {
/**
* <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
* would be the location of a finder pattern or the corner of the barcode, for example.</p>
*
* @author Sean Owen
*/
class ResultPoint : public PointF
{
public:
ResultPoint() = default;
ResultPoint(float x, float y) : PointF(x, y) {}
ResultPoint(int x, int y) : PointF(x, y) {}
template <typename T> ResultPoint(PointT<T> p) : PointF(p) {}
float x() const { return static_cast<float>(PointF::x); }
float y() const { return static_cast<float>(PointF::y); }
void set(float x, float y) { *this = PointF(x, y); }
static float Distance(int aX, int aY, int bX, int bY);
};
} // ZXing

121
tools/chatgui/zxing-cpp/core/src/StdGenerator.h

@ -0,0 +1,121 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <version>
#if defined(__cpp_lib_generator)
#include <generator>
#else
#include <concepts>
#include <coroutine>
#include <optional>
#include <utility>
#include <iterator>
namespace std {
template <std::movable T>
class generator
{
public:
struct promise_type
{
std::optional<T> value_;
generator get_return_object() noexcept { return generator{handle_type::from_promise(*this)}; }
static std::suspend_always initial_suspend() noexcept { return {}; }
static std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T&& value) noexcept
{
value_ = std::move(value);
return {};
}
static void return_void() noexcept {}
// Disallow co_await in generator coroutines.
void await_transform() = delete;
[[noreturn]] static void unhandled_exception() { throw; }
};
using handle_type = std::coroutine_handle<promise_type>;
generator() noexcept = default;
explicit generator(handle_type h) noexcept : h_(h) {}
~generator()
{
if (h_)
h_.destroy();
}
generator(generator&& other) noexcept : h_(other.h_) { other.h_ = nullptr; }
generator& operator=(generator&& other) noexcept
{
if (this != &other) {
if (h_)
h_.destroy();
h_ = other.h_;
other.h_ = nullptr;
}
return *this;
}
generator(const generator&) = delete;
generator& operator=(const generator&) = delete;
class iterator
{
public:
void operator++() { h_.resume(); }
T&& operator*() const noexcept { return std::move(*h_.promise().value_); }
bool operator==(std::default_sentinel_t) const noexcept { return !h_ || h_.done(); }
explicit iterator(handle_type h) : h_(h) {}
private:
handle_type h_ = nullptr;
};
iterator begin()
{
if (h_)
h_.resume();
return iterator{h_};
}
std::default_sentinel_t end() noexcept { return {}; }
private:
handle_type h_;
};
} // namespace std
#endif // __cpp_lib_generator
/*
usage example:
template <std::integral T>
std::generator<T> range(T first, const T last)
{
while (first < last)
co_yield first++;
}
int main()
{
for (const char i : range(65, 91))
std::print("{} ", i);
std::println("");
}
*/

38
tools/chatgui/zxing-cpp/core/src/StdPrint.h

@ -0,0 +1,38 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <version>
#if defined(__cpp_lib_print) && __cpp_lib_print >= 202207L
#include <print>
#else
#include <format>
#include <cstdio>
#include <utility>
namespace std {
template <class... Args>
inline void print(std::format_string<Args...> fmt, Args&&... args)
{
auto s = std::format(fmt, std::forward<Args>(args)...);
std::fwrite(s.data(), 1, s.size(), stdout);
}
template <class... Args>
inline void println(std::format_string<Args...> fmt, Args&&... args)
{
std::print(fmt, std::forward<Args>(args)...);
std::fputc('\n', stdout);
}
} // namespace std
#endif

55
tools/chatgui/zxing-cpp/core/src/StdScope.h

@ -0,0 +1,55 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
// idea from <experimental/scope> (library fundamentals TS v3)
#include <type_traits>
#include <utility>
namespace std {
template <class F>
class scope_exit
{
public:
explicit scope_exit(F&& f) noexcept(std::is_nothrow_move_constructible_v<F>) : func_(std::forward<F>(f)), active_(true) {}
scope_exit(scope_exit&& other) noexcept(std::is_nothrow_move_constructible_v<F>)
: func_(std::move(other.func_)), active_(std::exchange(other.active_, false))
{}
scope_exit(const scope_exit&) = delete;
scope_exit& operator=(const scope_exit&) = delete;
scope_exit& operator=(scope_exit&&) = delete;
~scope_exit() noexcept(noexcept(std::declval<F&>()()))
{
if (active_)
func_();
}
void release() noexcept { active_ = false; }
private:
F func_;
bool active_;
};
template <class F>
scope_exit(F) -> scope_exit<F>;
} // namespace std
/**
* The SCOPE_EXIT macro is eliminating the need to give the object a name.
* Example usage:
* SCOPE_EXIT([]{ printf("exiting scope"); });
*/
#define SCOPE_EXIT_CAT2(x, y) x##y
#define SCOPE_EXIT_CAT(x, y) SCOPE_EXIT_CAT2(x, y)
#define SCOPE_EXIT const auto SCOPE_EXIT_CAT(scopeExit_, __COUNTER__) = std::scope_exit

19
tools/chatgui/zxing-cpp/core/src/StructuredAppend.h

@ -0,0 +1,19 @@
/*
* Copyright 2021 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string>
namespace ZXing {
struct StructuredAppendInfo
{
int index = -1;
int count = -1;
std::string id;
};
} // ZXing

29
tools/chatgui/zxing-cpp/core/src/SymbologyIdentifier.h

@ -0,0 +1,29 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ZXAlgorithms.h"
#include <string>
namespace ZXing {
enum class AIFlag : char { None, GS1, AIM };
struct SymbologyIdentifier
{
char code = 0, modifier = 0;
signed char eciModifierOffset = 0;
AIFlag aiFlag = AIFlag::None;
std::string toString(bool hasECI = false) const
{
int modVal = (modifier >= 'A' ? modifier - 'A' + 10 : modifier - '0') + eciModifierOffset * hasECI;
return code ? StrCat(']', code, static_cast<char>((modVal >= 10 ? 'A' - 10 : '0') + modVal)) : std::string();
}
};
} // ZXing

54
tools/chatgui/zxing-cpp/core/src/TextDecoder.cpp

@ -0,0 +1,54 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2022 gitlost
* Copyright 2025 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "TextDecoder.h"
#include "ZXAlgorithms.h"
#include "libzueci/zueci.h"
#include <cassert>
#include <stdexcept>
#include <cstdio>
namespace ZXing {
std::string BytesToUtf8(ByteView bytes, ECI eci)
{
constexpr unsigned int replacement = 0xFFFD;
constexpr unsigned int flags = ZUECI_FLAG_SB_STRAIGHT_THRU | ZUECI_FLAG_SJIS_STRAIGHT_THRU;
int utf8_len;
if (eci == ECI::Unknown)
eci = ECI::Binary;
int error_number = zueci_dest_len_utf8(ToInt(eci), bytes.data(), Size(bytes), replacement, flags, &utf8_len);
if (error_number >= ZUECI_ERROR) {
#ifdef PRINT_DEBUG
fprintf(stderr, "zueci_dest_len_utf8 error: %d\n", error_number);
#endif
// throw FormatError("Invalid UTF-8 data");
return {};
}
std::string utf8(utf8_len, 0);
error_number = zueci_eci_to_utf8(ToInt(eci), bytes.data(), Size(bytes), replacement, flags,
reinterpret_cast<uint8_t*>(utf8.data()), &utf8_len);
if (error_number >= ZUECI_ERROR) {
#ifdef PRINT_DEBUG
fprintf(stderr, "zueci_eci_to_utf8 error: %d\n", error_number);
#endif
// throw FormatError("Invalid UTF-8 data");
return {};
}
assert(Size(utf8) == utf8_len);
return utf8;
}
} // ZXing

23
tools/chatgui/zxing-cpp/core/src/TextDecoder.h

@ -0,0 +1,23 @@
/*
* Copyright 2025 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "CharacterSet.h"
#include "ECI.h"
#include "Range.h"
#include <string>
namespace ZXing {
std::string BytesToUtf8(ByteView bytes, ECI eci);
inline std::string BytesToUtf8(ByteView bytes, CharacterSet cs)
{
return BytesToUtf8(bytes, ToECI(cs));
}
} // ZXing

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save