chatgui: invite link dialogs, zxing-cpp QR code, crypto transport, channel improvements; msg_transport: chat protocol support

This commit is contained in:
Evgeny committed 2026-06-25 09:29:44 +03:00
1 parent d8c8c1c5b0
commit 22c24d135e
2206 files changed
+33191 -257

No files matched your search

+43
View File
@@ -0,0 +1,43 @@
---
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
View File
@@ -0,0 +1,58 @@
---
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
View File
@@ -0,0 +1,19 @@
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
View File
@@ -0,0 +1,3 @@
test/samples export-ignore
zint export-ignore
.git* export-ignore
+2
View File
@@ -0,0 +1,2 @@
github: axxel
custom: 'www.paypal.com/donate/?hosted_button_id=8LDF4NNQF8Z3L'
@@ -0,0 +1,102 @@
#!/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
View File
@@ -0,0 +1,435 @@
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 }}
@@ -0,0 +1,59 @@
# 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
@@ -0,0 +1,71 @@
# 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 }}
@@ -0,0 +1,41 @@
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.
@@ -0,0 +1,130 @@
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
@@ -0,0 +1,131 @@
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
@@ -0,0 +1,97 @@
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.
@@ -0,0 +1,100 @@
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/
@@ -0,0 +1,39 @@
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 }}
@@ -0,0 +1,82 @@
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
View File
@@ -0,0 +1,27 @@
*.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
View File
@@ -0,0 +1,4 @@
[submodule "zint"]
path = zint
url = https://github.com/zint/zint.git
shallow = true
+121
View File
@@ -0,0 +1,121 @@
cmake_minimum_required(VERSION 3.16)
project(ZXing)
option (ZXING_READERS "Build with reader support (decoders)" ON)
set (ZXING_WRITERS "ON" CACHE STRING "Build with old and/or new writer (encoder) backend (OFF/ON/OLD/NEW/BOTH)")
option (ZXING_C_API "Build the C-API" ON)
option (ZXING_EXPERIMENTAL_API "Build with experimental API" OFF)
option (ZXING_EXAMPLES "Build the example barcode reader/writer applications" ON)
option (ZXING_EXAMPLES_QT "Build the Qt based example barcode reader/writer applications" OFF)
option (ZXING_BLACKBOX_TESTS "Build the black box reader/writer tests" OFF)
option (ZXING_UNIT_TESTS "Build the unit tests (don't enable for production builds)" OFF)
option (ZXING_TEST_DOTNET "Test if the .NET wrapper works" OFF)
option (ZXING_TEST_GO "Test if the Go wrapper works" OFF)
option (ZXING_TEST_INSTALL "Test if the installed/public API works" OFF)
option (ZXING_PYTHON_MODULE "Build the python module" OFF)
set (ZXING_DEPENDENCIES "AUTO" CACHE STRING "Fetch from github or use locally installed (AUTO/GITHUB/LOCAL)")
option (ZXING_CLANG_TIDY "Enable clang-tidy analysis" OFF)
if (WIN32)
option (BUILD_SHARED_LIBS "Build and link as shared library" OFF)
else()
option (BUILD_SHARED_LIBS "Build and link as shared library" ON)
endif()
if (MSVC)
add_definitions (-DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS)
option (ZXING_LINK_CPP_STATICALLY "MSVC only, link standard library statically (/MT and /MTd)" OFF)
if (LINK_CPP_STATICALLY OR ZXING_LINK_CPP_STATICALLY)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endif()
if (NOT CMAKE_BUILD_TYPE)
set (DEFAULT_BUILD_TYPE "Release")
message (STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
set (CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)
set_property (CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
if (NOT (ZXING_READERS OR ZXING_WRITERS))
message(FATAL_ERROR "At least one of ZXING_READERS/ZXING_WRITERS must be enabled.")
endif()
set(ZXING_WRITERS_LIST OFF ON OLD NEW BOTH)
set_property(CACHE ZXING_WRITERS PROPERTY STRINGS ${ZXING_WRITERS_LIST})
if(NOT ZXING_WRITERS IN_LIST ZXING_WRITERS_LIST)
message(FATAL_ERROR "ZXING_WRITERS must be one of ${ZXING_WRITERS_LIST}")
endif()
set(ZXING_DEPENDENCIES_LIST AUTO GITHUB LOCAL)
set_property(CACHE ZXING_DEPENDENCIES PROPERTY STRINGS ${ZXING_DEPENDENCIES_LIST})
if(NOT ZXING_DEPENDENCIES IN_LIST ZXING_DEPENDENCIES_LIST)
message(FATAL_ERROR "ZXING_DEPENDENCIES must be one of ${ZXING_DEPENDENCIES_LIST}")
endif()
if (NOT DEFINED CMAKE_CXX_STANDARD)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
endif()
set(CMAKE_COLOR_DIAGNOSTICS ON)
if (ZXING_CLANG_TIDY)
find_program(CMAKE_CXX_CLANG_TIDY NAMES "clang-tidy" DOC "Path to clang-tidy executable")
if (NOT CMAKE_CXX_CLANG_TIDY)
message(WARNING "clang-tidy not found.")
else()
message(STATUS "clang-tidy found: ${CMAKE_CXX_CLANG_TIDY}")
set(ZXING_DISABLE_PCH ON)
endif()
endif()
# make installed binaries relocatable on macOS and Linux
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) # improve build reproducibility
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
set(CMAKE_INSTALL_RPATH "$<$<PLATFORM_ID:Darwin>:@loader_path/../lib>$<$<PLATFORM_ID:Linux>:$ORIGIN/../lib>")
add_subdirectory (core)
add_subdirectory (docs)
enable_testing()
include(zxing.cmake)
if (ZXING_EXAMPLES)
add_subdirectory (example)
endif()
if (ZXING_EXAMPLES_QT)
add_subdirectory (wrappers/qt)
endif()
if (ZXING_BLACKBOX_TESTS)
add_subdirectory (test/blackbox)
endif()
if (ZXING_UNIT_TESTS)
add_subdirectory (test/unit)
endif()
if (ZXING_TEST_DOTNET)
add_subdirectory (wrappers/dotnet)
endif()
if (ZXING_TEST_GO)
add_subdirectory (wrappers/go)
endif()
if (ZXING_TEST_INSTALL)
add_test(
NAME InstallTest
COMMAND
${CMAKE_COMMAND}
-DPROJECT_BINARY_DIR=${CMAKE_BINARY_DIR}
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}/test/install
-DZXING_WRITERS=${ZXING_WRITERS}
-P ${CMAKE_SOURCE_DIR}/test/install/run.cmake
)
endif()
if (ZXING_PYTHON_MODULE)
add_subdirectory (wrappers/python)
endif()
if (ZXING_C_API)
add_subdirectory (wrappers/c)
endif()
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+38
View File
@@ -0,0 +1,38 @@
// 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
View File
@@ -0,0 +1,154 @@
[![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).]
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.16)
project(ZXingDocs VERSION 3.1.0)
find_package(Doxygen)
if(DOXYGEN_FOUND)
include(FetchContent)
FetchContent_Declare(
doxygen-awesome-css
URL https://github.com/jothepro/doxygen-awesome-css/archive/refs/heads/main.zip
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_MakeAvailable(doxygen-awesome-css)
# Save the location the files were cloned into
# This allows us to get the path to doxygen-awesome.css
FetchContent_GetProperties(doxygen-awesome-css SOURCE_DIR AWESOME_CSS_DIR)
# Generate the Doxyfile
set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
add_custom_target(docs
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM)
endif()
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,58 @@
zxing_add_package_stb()
option (ZXING_EXAMPLES_USE_WEBP "Add support for WebP image decoding" OFF)
if (ZXING_EXAMPLES_USE_WEBP)
zxing_add_package(WebP WebP https://github.com/webmproject/libwebp.git v1.6.0)
endif()
include (GNUInstallDirs)
if (ZXING_WRITERS AND ZXING_ENABLE_QRCODE)
add_executable (ZXingWriter ZXingWriter.cpp)
target_link_libraries (ZXingWriter ZXing::ZXing stb::stb)
add_test(NAME ZXingWriterTest COMMAND ZXingWriter qrcode "I have the best words." test.png)
install(TARGETS ZXingWriter DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
if (ZXING_READERS)
add_executable (ZXingReader ZXingReader.cpp)
target_link_libraries (ZXingReader ZXing::ZXing stb::stb)
if (ZXING_EXAMPLES_USE_WEBP)
target_link_libraries (ZXingReader ZXing::ZXing stb::stb WebP::webp)
target_compile_definitions(ZXingReader PRIVATE ZXING_USE_WEBP)
endif()
add_test(NAME ZXingReaderTest COMMAND ZXingReader -fast -format qrcode test.png) # see above
set_tests_properties(ZXingReaderTest PROPERTIES DEPENDS ZXingWriterTest)
if (ZXING_EXAMPLES_USE_WEBP AND WIN32)
add_custom_command(TARGET ZXingReader POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_RUNTIME_DLLS:ZXingReader>
$<TARGET_FILE_DIR:ZXingReader>
COMMAND_EXPAND_LISTS
VERBATIM
)
endif()
install(TARGETS ZXingReader DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
if (ZXING_READERS)
find_package(OpenCV QUIET)
if (OpenCV_FOUND)
add_executable (ZXingOpenCV ZXingOpenCV.cpp)
target_include_directories (ZXingOpenCV PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries (ZXingOpenCV ZXing::ZXing ${OpenCV_LIBS})
else ()
message("INFO: OpenCV not found, skipping ZXingOpenCV example")
endif()
endif()
add_executable (ZXingBarcodeFormat ZXingBarcodeFormat.cpp)
target_link_libraries (ZXingBarcodeFormat ZXing::ZXing)
@@ -0,0 +1,106 @@
/*
* Copyright 2026 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "BarcodeFormat.h"
#include <version>
#ifdef __cpp_lib_format
#include "StdPrint.h"
#include <cctype>
#include <cstring>
#include <format>
#include <functional>
#include <string>
static void PrintUsage(const char* exePath)
{
std::print("ZXingBarcodeFormat - A command line tool to generate wrapper source code for the BarcodeFormat enum\n\n");
std::println("Usage: {} <Android|C#|Go|K/N|Rust|Swift>", exePath);
}
static void PrintBFs(
std::format_string<std::string, int> fmt,
std::function<std::string(const char*)> name = [](const char* name) { return std::string(name); })
{
std::println(fmt, name("Invalid"), 0xffff);
#define X(NAME, SYM, VAR, FLAGS, ZINT, ENABLED, HRI) std::println(fmt, name(#NAME), ZX_BCF_ID(SYM, VAR));
ZX_BCF_LIST(X)
#undef X
}
int main(int argc, char* argv[])
{
auto is = [&, i=1](const char* str) { return strncmp(argv[i], str, strlen(argv[i])) == 0; };
if (argc != 2) {
PrintUsage(argv[0]);
return -1;
} else if (is("Go")) {
std::println("package zxingcpp\n");
std::println("const (");
PrintBFs(" BarcodeFormat{:14} BarcodeFormat = 0x{:04X}");
std::println(")");
} else if (is("C#")) {
PrintBFs(" public static readonly BarcodeFormat {:15} = new BarcodeFormat(0x{:04X});");
} else if (is("K/N")) {
PrintBFs(" {0:15}(ZXing_BarcodeFormat.ZXing_BarcodeFormat_{0}),");
} else if (is("Rust")) {
PrintBFs("pub const ZXing_BarcodeFormat_{}: ZXing_BarcodeFormat = 0x{:04X};");
} else if (is("Swift")) {
auto swiftName = [](const char* name) {
std::string sv(name);
std::string ret = (char)std::tolower(sv[0]) + sv.substr(1);
// Convert to camelCase, but keep uppercase letters if they are followed by a lowercase letter
// (e.g. QRCode -> qrCode). This is a bit hacky, but it works for the current set of names and avoids the
// need for a manual mapping. This ensures that the generated Swift code follows typical naming patterns.
for (int i = 2; i < ret.size() && (std::isupper(ret[i]) || std::isdigit(ret[i])); ++i)
ret[i - 1] = std::tolower(ret[i - 1]);
ret.back() = std::tolower(ret.back());
return ret == "eanupc" ? "eanUPC" : ret == "upca" ? "upcA" : ret == "upce" ? "upcE" : ret;
};
PrintBFs(" public static let {:15} = BarcodeFormat(rawValue: 0x{:04X})", swiftName);
} else if (is("Android")) {
auto androidName = [](const char* name) {
std::string sv(name);
std::string ret = std::string(1, (char)std::toupper(sv[0]));
for (size_t i = 1; i < sv.size(); ++i) {
const int c = sv[i];
const int prev = sv[i - 1];
const int next = i + 1 < sv.size() ? sv[i + 1] : 0;
if ((std::isdigit(c) != std::isdigit(prev)) || (std::isupper(c) && std::islower(prev))
|| (std::isupper(c) && std::isupper(prev) && next != 0 && std::islower(next)))
ret.push_back('_');
ret.push_back(static_cast<char>(std::toupper(c)));
}
return ret == "EANUPC" ? "EAN_UPC"
: ret == "UPCA" ? "UPC_A"
: ret == "UPCE" ? "UPC_E"
: ret == "ALL_GS_1" ? "ALL_GS1"
: ret;
};
PrintBFs("\t\t{:20}(0x{:04X}),", androidName);
} else {
PrintUsage(argv[0]);
return -1;
}
return 0;
}
#else
#include <cstdio>
int main()
{
printf("This tool requires C++20 support with std::format. Please use a compatible compiler.\n");
return -1;
}
#endif
@@ -0,0 +1,31 @@
/*
* Copyright 2021 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "ZXingOpenCV.h"
#include <iostream>
using namespace cv;
int main()
{
namedWindow("Display window");
Mat image;
VideoCapture cap(0);
if (!cap.isOpened())
std::cout << "cannot open camera";
else
while (waitKey(25) != 27) {
cap >> image;
auto barcodes = ReadBarcodes(image);
for (auto& barcode : barcodes)
DrawBarcode(image, barcode);
imshow("Display window", image);
}
return 0;
}
@@ -0,0 +1,45 @@
/*
* Copyright 2020 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ReadBarcode.h"
#include <opencv2/opencv.hpp>
inline ZXing::ImageView ImageViewFromMat(const cv::Mat& image)
{
using ZXing::ImageFormat;
auto fmt = ImageFormat::None;
switch (image.channels()) {
case 1: fmt = ImageFormat::Lum; break;
case 2: fmt = ImageFormat::LumA; break;
case 3: fmt = ImageFormat::BGR; break;
case 4: fmt = ImageFormat::BGRA; break;
}
if (image.depth() != CV_8U || fmt == ImageFormat::None)
return {nullptr, 0, 0, ImageFormat::None};
return {image.data, image.cols, image.rows, fmt};
}
inline ZXing::Barcodes ReadBarcodes(const cv::Mat& image, const ZXing::ReaderOptions& options = {})
{
return ZXing::ReadBarcodes(ImageViewFromMat(image), options);
}
inline void DrawBarcode(cv::Mat& img, ZXing::Barcode barcode)
{
auto pos = barcode.position();
auto zx2cv = [](ZXing::PointI p) { return cv::Point(p.x, p.y); };
auto contour = std::vector<cv::Point>{zx2cv(pos[0]), zx2cv(pos[1]), zx2cv(pos[2]), zx2cv(pos[3])};
const auto* pts = contour.data();
int npts = contour.size();
cv::polylines(img, &pts, &npts, 1, true, CV_RGB(0, 255, 0));
cv::putText(img, barcode.text(), zx2cv(pos[3]) + cv::Point(0, 20), cv::FONT_HERSHEY_DUPLEX, 0.5, CV_RGB(0, 255, 0));
}
@@ -0,0 +1,392 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2019 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "GTIN.h"
#include "ZXingCpp.h"
#include <chrono>
#include <cctype>
#include <cstring>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <memory>
#include <string>
#include <vector>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#ifdef ZXING_USE_WEBP
#include <webp/decode.h>
#endif
using namespace ZXing;
struct CLI
{
std::vector<std::string> filePaths;
std::string outPath;
int forceChannels = 0;
int rotate = 0;
bool oneLine = false;
bool bytesOnly = false;
bool showSymbol = false;
bool json = false;
};
static void PrintUsage(const char* exePath)
{
std::cout << "Usage: " << exePath << " [options] <image file>...\n"
<< " -fast Skip some lines/pixels during detection (faster)\n"
<< " -norotate Don't try rotated image during detection (faster)\n"
<< " -noinvert Don't search for inverted codes during detection (faster)\n"
<< " -noscale Don't try downscaled images during detection (faster)\n"
<< " -formats <FORMAT[,...]>\n"
<< " Only detect given format(s) (faster)\n"
<< " -single Stop after the first barcode is detected (faster)\n"
<< " -ispure Assume the image contains only a 'pure'/perfect code (faster)\n"
<< " -errors Include barcodes with errors (like checksum error)\n"
<< " -binarizer <local|global|fixed>\n"
<< " Binarizer to be used for gray to binary conversion\n"
<< " -mode <plain|eci|hri|escaped>\n"
<< " Text mode used to render the raw byte content into text\n"
<< " -1 Print only file name, content/error on one line per file/barcode (implies '-mode Escaped')\n"
<< " -symbol Print the detected symbol (if available)\n"
<< " -json Print a complete JSON formatted serialization\n"
#ifdef ZXING_EXPERIMENTAL_API
<< " -denoise Use extra denoiseing (closing operation)\n"
#endif
<< " -bytes Write (only) the bytes content of the symbol(s) to stdout\n"
<< " -pngout <file name>\n"
<< " Write a copy of the input image with barcodes outlined by a green line\n"
<< " -help Print usage information\n"
<< " -version Print version information\n"
<< "\n"
<< "Supported formats are (Symbology : Variants):";
for (auto f : BarcodeFormats::list(BarcodeFormat::AllReadable)) {
if (Symbology(f) == f)
std::cout << "\n " << std::setw(13) << ToString(f) << " : ";
else
std::cout << ToString(f) << ", ";
}
std::cout << "\n\n";
std::cout << "BarcodeFormats can be lowercase, with or without any of ' -_/', separated by ',' or '|'\n";
}
static bool ParseOptions(int argc, char* argv[], ReaderOptions& options, CLI& cli)
{
for (int i = 1; i < argc; ++i) {
auto is = [&](const char* str) { return strlen(argv[i]) > 1 && strncmp(argv[i], str, strlen(argv[i])) == 0; };
if (is("-fast")) {
options.tryHarder(false);
} else if (is("-norotate")) {
options.tryRotate(false);
} else if (is("-noinvert")) {
options.tryInvert(false);
} else if (is("-noscale")) {
options.tryDownscale(false);
#ifdef ZXING_EXPERIMENTAL_API
} else if (is("-denoise")) {
options.tryDenoise(true);
#endif
} else if (is("-single")) {
options.maxNumberOfSymbols(1);
} else if (is("-ispure")) {
options.isPure(true);
options.binarizer(Binarizer::FixedThreshold);
} else if (is("-errors")) {
options.returnErrors(true);
} else if (is("-formats")) {
if (++i == argc)
return false;
try {
options.formats(BarcodeFormatsFromString(argv[i]));
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n\n";
return false;
}
} else if (is("-binarizer")) {
if (++i == argc)
return false;
else if (is("local"))
options.binarizer(Binarizer::LocalAverage);
else if (is("global"))
options.binarizer(Binarizer::GlobalHistogram);
else if (is("fixed"))
options.binarizer(Binarizer::FixedThreshold);
else
return false;
} else if (is("-mode")) {
if (++i == argc)
return false;
else if (is("plain"))
options.textMode(TextMode::Plain);
else if (is("eci"))
options.textMode(TextMode::ECI);
else if (is("hri"))
options.textMode(TextMode::HRI);
else if (is("escaped"))
options.textMode(TextMode::Escaped);
else
return false;
} else if (is("-1")) {
cli.oneLine = true;
} else if (is("-bytes")) {
cli.bytesOnly = true;
} else if (is("-symbol")) {
cli.showSymbol = true;
} else if (is("-json")) {
cli.json = true;
} else if (is("-pngout")) {
if (++i == argc)
return false;
cli.outPath = argv[i];
} else if (is("-channels")) {
if (++i == argc)
return false;
cli.forceChannels = std::stoi(argv[i]);
} else if (is("-rotate")) {
if (++i == argc)
return false;
cli.rotate = std::stoi(argv[i]);
} else if (is("-help") || is("--help")) {
PrintUsage(argv[0]);
exit(0);
} else if (is("-version") || is("--version")) {
std::cout << "ZXingReader version " << Version() << "\n";
exit(0);
} else {
cli.filePaths.push_back(argv[i]);
}
}
return !cli.filePaths.empty();
}
void drawLine(const ImageView& iv, PointI a, PointI b, bool error)
{
int steps = maxAbsComponent(b - a);
PointF dir = bresenhamDirection(PointF(b - a));
int R = RedIndex(iv.format()), G = GreenIndex(iv.format()), B = BlueIndex(iv.format());
for (int i = 0; i < steps; ++i) {
auto p = PointI(centered(a + i * dir));
auto* dst = const_cast<uint8_t*>(iv.data(p.x, p.y));
if (dst < iv.data(0, 0) || dst > iv.data(iv.width() - 1, iv.height() - 1))
continue;
dst[R] = error ? 0xff : 0;
dst[G] = error ? 0 : 0xff;
dst[B] = 0;
}
}
void drawRect(const ImageView& image, const Position& pos, bool error)
{
for (int i = 0; i < 4; ++i)
drawLine(image, pos[i], pos[(i + 1) % 4], error);
}
std::unique_ptr<uint8_t, void (*)(void*)> LoadWebP(const std::string& filePath, int* width, int* height, int* channels)
{
#ifdef ZXING_USE_WEBP
std::ifstream file(filePath, std::ios::binary);
if (!file)
throw std::runtime_error("Failed to read image: " + filePath + " (failed to open file)");
std::vector<uint8_t> compressedData(std::istreambuf_iterator<char>(file), {});
if (compressedData.empty())
throw std::runtime_error("Failed to read image: " + filePath + " (empty file)");
*channels = 3;
uint8_t* img = WebPDecodeRGB(compressedData.data(), compressedData.size(), width, height);
if (img == nullptr)
throw std::runtime_error("Failed to read image: " + filePath + " (WebP decode failed)");
return std::unique_ptr<uint8_t, void (*)(void*)>(img, WebPFree);
#else
std::cerr << "Failed to read image: " << filePath << " (WebP support is not enabled in this build)" << "\n";
return std::unique_ptr<uint8_t, void (*)(void*)>(nullptr, nullptr);
#endif
}
std::unique_ptr<uint8_t, void (*)(void*)> LoadStbi(const std::string& filePath, int* width, int* height, int* channels, int forceChannels)
{
auto buffer = std::unique_ptr<uint8_t, void (*)(void*)>(
filePath == "-" ? stbi_load_from_file(stdin, width, height, channels, forceChannels)
: stbi_load(filePath.c_str(), width, height, channels, forceChannels),
stbi_image_free);
if (buffer == nullptr)
std::cerr << "Failed to read image: " << filePath << " (" << stbi_failure_reason() << ")" << "\n";
return buffer;
}
bool IEndsWith(std::string_view str, std::string_view suffix)
{
return str.size() > suffix.size() && std::equal(str.end() - suffix.size(), str.end(), suffix.begin(),
[](char a, char b) { return std::tolower(static_cast<unsigned char>(a)) == b; });
}
int main(int argc, char* argv[])
{
ReaderOptions options;
CLI cli;
Barcodes allBarcodes;
int ret = 0;
options.textMode(TextMode::HRI);
options.eanAddOnSymbol(EanAddOnSymbol::Read);
if (!ParseOptions(argc, argv, options, cli)) {
PrintUsage(argv[0]);
return -1;
}
std::cout.setf(std::ios::boolalpha);
if (!cli.outPath.empty())
cli.forceChannels = 3; // the drawing code only works for RGB data
for (const auto& filePath : cli.filePaths) {
int width, height, channels;
auto buffer = IEndsWith(filePath, ".webp")
? LoadWebP(filePath, &width, &height, &channels)
: LoadStbi(filePath, &width, &height, &channels, cli.forceChannels);
if (buffer == nullptr)
continue;
channels = cli.forceChannels ? cli.forceChannels : channels;
auto ImageFormatFromChannels = std::array{ImageFormat::None, ImageFormat::Lum, ImageFormat::LumA, ImageFormat::RGB, ImageFormat::RGBA};
ImageView image{buffer.get(), width, height, ImageFormatFromChannels.at(channels)};
auto barcodes = ReadBarcodes(image.rotated(cli.rotate), options);
// if we did not find anything, insert a dummy to produce some output for each file
if (barcodes.empty())
barcodes.emplace_back();
allBarcodes.insert(allBarcodes.end(), barcodes.begin(), barcodes.end());
if (filePath == cli.filePaths.back()) {
auto merged = MergeStructuredAppendSequences(allBarcodes);
// report all merged sequences as part of the last file to make the logic not overly complicated here
barcodes.insert(barcodes.end(), std::make_move_iterator(merged.begin()), std::make_move_iterator(merged.end()));
}
for (auto&& barcode : barcodes) {
if (!cli.outPath.empty())
drawRect(image, barcode.position(), bool(barcode.error()));
ret |= static_cast<int>(barcode.error().type());
if (cli.bytesOnly) {
std::cout.write(reinterpret_cast<const char*>(barcode.bytes().data()), barcode.bytes().size());
continue;
}
if (cli.json) {
if (barcode.format() != BarcodeFormat::None)
std::cout << "{\"FilePath\":\"" << filePath << "\"," << barcode.extra("ALL").substr(1) << "\n";
continue;
}
if (cli.oneLine) {
std::cout << filePath << " " << ToString(barcode.format());
if (barcode.isValid())
std::cout << " \"" << barcode.text(TextMode::Escaped) << "\"";
else if (barcode.error())
std::cout << " " << ToString(barcode.error());
std::cout << "\n";
continue;
}
if (cli.filePaths.size() > 1 || barcodes.size() > 1) {
static bool firstFile = true;
if (!firstFile)
std::cout << "\n";
if (cli.filePaths.size() > 1)
std::cout << "File: " << filePath << "\n";
firstFile = false;
}
if (barcode.format() == BarcodeFormat::None) {
std::cout << "No barcode found\n";
continue;
}
std::cout << "Text: \"" << barcode.text() << "\"\n"
<< "Bytes: " << barcode.text(options.textMode() == TextMode::ECI ? TextMode::HexECI : TextMode::Hex) << "\n"
<< "Format: " << ToString(barcode.format()) << "\n"
<< "Symbology: " << ToString(barcode.symbology()) << "\n"
<< "Identifier: " << barcode.symbologyIdentifier() << "\n"
<< "Content: " << ToString(barcode.contentType()) << "\n"
<< "HasECI: " << barcode.hasECI() << "\n"
<< "Position: " << ToString(barcode.position()) << "\n"
<< "Rotation: " << barcode.orientation() << " deg\n"
<< "IsMirrored: " << barcode.isMirrored() << "\n"
<< "IsInverted: " << barcode.isInverted() << "\n";
auto printOptional = [](const char* key, const std::string& v) {
if (!v.empty())
std::cout << key << v << "\n";
};
printOptional("ECLevel: ", barcode.ecLevel());
printOptional("Version: ", barcode.version());
printOptional("Error: ", ToString(barcode.error()));
if (barcode.lineCount())
std::cout << "Lines: " << barcode.lineCount() << "\n";
if (barcode.symbology() == BarcodeFormat::EANUPC) {
printOptional("Country: ", GTIN::LookupCountryIdentifier(barcode.text(), barcode.format()));
printOptional("Add-On: ", GTIN::EanAddOn(barcode));
printOptional("Price: ", GTIN::Price(GTIN::EanAddOn(barcode)));
printOptional("Issue #: ", GTIN::IssueNr(GTIN::EanAddOn(barcode)));
} else if (barcode.format() == BarcodeFormat::ITF && barcode.bytes().size() == 14) {
printOptional("Country: ", GTIN::LookupCountryIdentifier(barcode.text(), barcode.format()));
}
if (barcode.isPartOfSequence())
std::cout << "Structured Append: symbol " << barcode.sequenceIndex() + 1 << " of "
<< barcode.sequenceSize() << " (parity/id: '" << barcode.sequenceId() << "')\n";
else if (barcode.sequenceSize() > 0)
std::cout << "Structured Append: merged result from " << barcode.sequenceSize() << " symbols (parity/id: '"
<< barcode.sequenceId() << "')\n";
printOptional("Extra: ", barcode.extra());
if (cli.showSymbol && barcode.symbol().data())
std::cout << "Symbol:\n" << WriteBarcodeToUtf8(barcode);
}
if (cli.filePaths.size() == 1 && !cli.outPath.empty())
stbi_write_png(cli.outPath.c_str(), image.width(), image.height(), 3, image.data(), image.rowStride());
#ifdef NDEBUG
if (getenv("MEASURE_PERF")) {
auto startTime = std::chrono::high_resolution_clock::now();
auto duration = startTime - startTime;
int N = 0;
int blockSize = 1;
do {
for (int i = 0; i < blockSize; ++i)
ReadBarcodes(image, options);
N += blockSize;
duration = std::chrono::high_resolution_clock::now() - startTime;
if (blockSize < 1000 && duration < std::chrono::milliseconds(100))
blockSize *= 10;
} while (duration < std::chrono::seconds(1));
fprintf(stderr, "time: %5.2f ms per frame\n", double(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()) / N);
}
#endif
}
return ret;
}
@@ -0,0 +1,235 @@
/*
* Copyright 2016 Nu-book Inc.
*/
// SPDX-License-Identifier: Apache-2.0
// #define USE_OLD_WRITER_API
#ifndef USE_OLD_WRITER_API
#include "CreateBarcode.h"
#include "WriteBarcode.h"
#else
#include "BitMatrix.h"
#include "BitMatrixIO.h"
#include "CharacterSet.h"
#include "MultiFormatWriter.h"
#endif
#include "Version.h"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <string>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
using namespace ZXing;
static void PrintUsage(const char* exePath)
{
std::cout << "Usage: " << exePath
<< " [-options <creator-options>] [-scale <factor>] [-binary] [-noqz] [-hrt] [-invert] <format> <text> <output>\n"
<< " -options Comma separated list of format specific options and flags\n"
<< " -scale module size of generated image / negative numbers mean 'target size in pixels'\n"
<< " -rotate Rotate image by given angle (90, 180 or 270)\n"
// << " -encoding Encoding used to encode input text\n"
<< " -binary Interpret <text> as a file name containing binary data\n"
<< " -noqz Print barcode without quiet zone\n"
<< " -hrt Print human readable text below the barcode (if supported)\n"
<< " -invert Invert colors (switch black and white)\n"
<< " -help Print usage information\n"
<< " -version Print version information\n"
<< "\n"
<< "Supported formats are (Symbology : Variants):";
for (auto f : BarcodeFormats::list(BarcodeFormat::AllCreatable)) {
if (Symbology(f) == f || f == BarcodeFormat::DXFilmEdge)
std::cout << "\n " << std::setw(13) << ToString(f) << " : ";
else
std::cout << ToString(f) << ", ";
}
std::cout << "\n\n";
std::cout << "Format can be lowercase letters, with or without any of ' -_/'.\n"
<< "Output format is determined by file name, supported are png, jpg and svg.\n";
}
struct CLI
{
BarcodeFormat format = BarcodeFormat::None;
int scale = 0;
int rotate = 0;
std::string input;
std::string outPath;
std::string options;
bool inputIsFile = false;
bool invert = false;
bool addHRT = false;
bool addQZs = true;
bool verbose = false;
// CharacterSet encoding = CharacterSet::Unknown;
};
static bool ParseOptions(int argc, char* argv[], CLI& cli)
{
int nonOptArgCount = 0;
for (int i = 1; i < argc; ++i) {
auto is = [&](const char* str) { return strncmp(argv[i], str, strlen(argv[i])) == 0; };
if (is("-scale")) {
if (++i == argc)
return false;
cli.scale = std::stoi(argv[i]);
} else if (is("-rotate")) {
if (++i == argc)
return false;
cli.rotate = std::stoi(argv[i]);
// } else if (is("-encoding")) {
// if (++i == argc)
// return false;
// cli.encoding = CharacterSetFromString(argv[i]);
} else if (is("-binary")) {
cli.inputIsFile = true;
} else if (is("-hrt")) {
cli.addHRT = true;
} else if (is("-noqz")) {
cli.addQZs = false;
} else if (is("-invert")) {
cli.invert = true;
} else if (is("-options")) {
if (++i == argc)
return false;
cli.options = argv[i];
} else if (is("-verbose")) {
cli.verbose = true;
} else if (is("-help") || is("--help")) {
PrintUsage(argv[0]);
exit(0);
} else if (is("-version") || is("--version")) {
std::cout << "ZXingWriter version " << ZXING_VERSION_STR << " (with "
#ifdef ZXING_USE_ZINT
<< "zint"
#else
<< "zxing-cpp"
#endif
<< " backend) \n";
exit(0);
} else if (nonOptArgCount == 0) {
try {
cli.format = BarcodeFormatFromString(argv[i]);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n\n";
return false;
}
++nonOptArgCount;
} else if (nonOptArgCount == 1) {
cli.input = argv[i];
++nonOptArgCount;
} else if (nonOptArgCount == 2) {
cli.outPath = argv[i];
++nonOptArgCount;
} else {
return false;
}
}
return nonOptArgCount == 3;
}
static std::string GetExtension(const std::string& path)
{
auto fileNameStart = path.find_last_of("/\\");
auto fileName = fileNameStart == std::string::npos ? path : path.substr(fileNameStart + 1);
auto extStart = fileName.find_last_of('.');
auto ext = extStart == std::string::npos ? "" : fileName.substr(extStart + 1);
std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { return std::tolower(c); });
return ext;
}
template <typename T = char>
std::vector<T> ReadFile(const std::string& fn)
{
std::basic_ifstream<T> ifs(fn, std::ios::binary);
if (!ifs.good())
throw std::runtime_error("failed to open/read file " + fn);
return ifs ? std::vector(std::istreambuf_iterator<T>(ifs), std::istreambuf_iterator<T>()) : std::vector<T>();
};
int main(int argc, char* argv[])
{
CLI cli;
if (!ParseOptions(argc, argv, cli)) {
PrintUsage(argv[0]);
return -1;
}
try {
#if 1
auto cOpts = CreatorOptions(cli.format, cli.options);
auto barcode = cli.inputIsFile ? CreateBarcodeFromBytes(ReadFile(cli.input), cOpts) : CreateBarcodeFromText(cli.input, cOpts);
auto wOpts = WriterOptions().scale(cli.scale).addQuietZones(cli.addQZs).addHRT(cli.addHRT).invert(cli.invert).rotate(cli.rotate);
auto bitmap = WriteBarcodeToImage(barcode, wOpts);
if (cli.verbose) {
std::cout.setf(std::ios::boolalpha);
std::cout << "Text: \"" << barcode.text() << "\"\n"
<< "Bytes: " << barcode.text(TextMode::Hex) << "\n"
<< "Format: " << ToString(barcode.format()) << "\n"
<< "Symbology: " << ToString(barcode.symbology()) << "\n"
<< "Identifier: " << barcode.symbologyIdentifier() << "\n"
<< "Content: " << ToString(barcode.contentType()) << "\n"
<< "HasECI: " << barcode.hasECI() << "\n"
<< "Position: " << ToString(barcode.position()) << "\n"
<< "Rotation: " << barcode.orientation() << " deg\n"
<< "IsMirrored: " << barcode.isMirrored() << "\n"
<< "IsInverted: " << barcode.isInverted() << "\n"
<< "ECLevel: " << barcode.ecLevel() << "\n";
std::cout << WriteBarcodeToUtf8(barcode, wOpts);
}
#else // 'old' writer API (non zint based)
auto writer = MultiFormatWriter(cli.format).setMargin(cli.addQZs ? 10 : 0);
BitMatrix matrix;
if (cli.inputIsFile) {
auto file = ReadFile(cli.input);
std::wstring bytes;
for (uint8_t c : file)
bytes.push_back(c);
writer.setEncoding(CharacterSet::BINARY);
matrix = writer.encode(bytes, cli.scale, std::clamp(cli.scale / 2, 50, 300));
} else {
writer.setEncoding(CharacterSet::UTF8);
matrix = writer.encode(cli.input, cli.scale, std::clamp(cli.scale / 2, 50, 300));
}
auto bitmap = ToMatrix<uint8_t>(matrix);
#endif
auto ext = GetExtension(cli.outPath);
int success = 0;
if (ext == "" || ext == "png") {
success = stbi_write_png(cli.outPath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0);
} else if (ext == "jpg" || ext == "jpeg") {
success = stbi_write_jpg(cli.outPath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0);
} else if (ext == "svg") {
#ifndef USE_OLD_WRITER_API
success = (std::ofstream(cli.outPath) << WriteBarcodeToSVG(barcode, wOpts)).good();
#else
success = (std::ofstream(cli.outPath) << ToSVG(matrix)).good();
#endif
}
if (!success) {
std::cerr << "Failed to write image: " << cli.outPath << "\n";
return -1;
}
} catch (const std::exception& e) {
std::cerr << e.what() << "\n";
return -1;
}
return 0;
}
@@ -0,0 +1,752 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2019 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "BlackboxTestRunner.h"
#include "ByteArray.h"
#include "ImageLoader.h"
#include "ReadBarcode.h"
#include "Utf.h"
#include "ZXAlgorithms.h"
#include "StdPrint.h"
#include "Version.h"
#include <chrono>
#include <exception>
#include <format>
#include <fstream>
#include <map>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#ifdef __GNUC__
// turns out that libstdc++ is depending on TBB without even linking against it, so this fails to link :-/
#undef __cpp_lib_execution
#endif
#ifdef __cpp_lib_execution
#include <execution>
#else
#include <future>
#endif
namespace ZXing::Test {
namespace {
struct PureTag {} pure;
struct TestCase
{
struct TC
{
std::string name = {};
int minPassCount = 0; // The number of images which must decode for the test to pass.
int maxMisreads = 0; // Maximum number of successfully read images with the wrong contents.
std::set<fs::path> notDetectedFiles = {};
std::map<fs::path, std::string> misReadFiles = {};
};
TC tc[2] = {};
int rotation = 0; // The rotation in degrees clockwise to use for this test.
TestCase(int mntf, int mnts, int mmf, int mms, int r) : tc{{"fast", mntf, mmf}, {"slow", mnts, mms}}, rotation(r) {}
TestCase(int mntf, int mnts, int r) : TestCase(mntf, mnts, 0, 0, r) {}
TestCase(int mntp, int mmp, PureTag) : tc{{"pure", mntp, mmp}} {}
};
struct FalsePositiveTestCase
{
int maxAllowed; // Maximum number of images which can fail due to successfully reading the wrong contents
int rotation; // The rotation in degrees clockwise to use for this test.
};
}
// Helper for `compareResult()` - map `key` to Barcode property, converting value to std::string
static std::string getBarcodeValue(const Barcode& barcode, const std::string& key)
{
if (key == "format")
return ToString(barcode.format());
if (key == "contentType")
return ToString(barcode.contentType());
if (key == "ecLevel")
return barcode.ecLevel();
if (key == "orientation")
return std::to_string(barcode.orientation());
if (key == "symbologyIdentifier")
return barcode.symbologyIdentifier();
if (key == "sequenceSize")
return std::to_string(barcode.sequenceSize());
if (key == "sequenceIndex")
return std::to_string(barcode.sequenceIndex());
if (key == "sequenceId")
return barcode.sequenceId();
if (key == "isLastInSequence")
return barcode.isLastInSequence() ? "true" : "false";
if (key == "isPartOfSequence")
return barcode.isPartOfSequence() ? "true" : "false";
if (key == "isMirrored")
return barcode.isMirrored() ? "true" : "false";
if (key == "isInverted")
return barcode.isInverted() ? "true" : "false";
if (key == "readerInit")
return barcode.readerInit() ? "true" : "false";
return std::format("***Unknown key '{}'***", key);
}
// Read ".result.txt" file contents `expected` with lines "key=value" and compare to `actual`
static bool compareResult(const Barcode& barcode, const std::string& expected, std::string& actual)
{
bool ret = true;
actual.clear();
actual.reserve(expected.size());
std::stringstream expectedLines(expected);
std::string expectedLine;
while (std::getline(expectedLines, expectedLine)) {
if (expectedLine.empty() || expectedLine[0] == '#')
continue;
auto equals = expectedLine.find('=');
if (equals == std::string::npos) {
actual += "***Bad format, missing equals***\n";
return false;
}
std::string key = expectedLine.substr(0, equals);
std::string expectedValue = expectedLine.substr(equals + 1);
std::string actualValue = getBarcodeValue(barcode, key);
if (actualValue != expectedValue) {
ret = false;
actualValue += " ***Mismatch***";
}
actual += StrCat(key, '=', actualValue, '\n');
}
return ret;
}
static std::string checkResult(const fs::path& imgPath, BarcodeFormat expectedFormat, const Barcode& barcode)
{
if (expectedFormat != barcode.format() && expectedFormat != barcode.symbology()) // allow matching either format or symbology
return std::format("Format mismatch: expected '{}' but got '{}'", ToString(expectedFormat), ToString(barcode.format()));
auto readFile = [imgPath](const char* ending) {
std::ifstream ifs(fs::path(imgPath).replace_extension(ending), std::ios::binary);
return ifs ? std::optional(std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>())) : std::nullopt;
};
if (auto expected = readFile(".result.txt")) {
std::string actual;
if (!compareResult(barcode, *expected, actual))
return std::format("Result mismatch: expected\n{} but got\n{}", *expected, actual);
}
if (auto expected = readFile(".txt")) {
expected = EscapeNonGraphical(*expected);
auto utf8Result = barcode.text(TextMode::Escaped);
return utf8Result != *expected ? std::format("Content mismatch: expected '{}' but got '{}'", *expected, utf8Result) : "";
}
if (auto expected = readFile(".bin")) {
ByteArray binaryExpected(*expected);
return barcode.bytes() != binaryExpected
? std::format("Content mismatch: expected '{}' but got '{}'", ToHex(binaryExpected), ToHex(barcode.bytes()))
: "";
}
return "Error reading file";
}
static int failed = 0;
static int extra = 0;
static int totalImageLoadTime = 0;
int timeSince(std::chrono::steady_clock::time_point startTime)
{
auto duration = std::chrono::steady_clock::now() - startTime;
return narrow_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
}
// pre-load images into cache, so the disc io time does not end up in the timing measurement
void preloadImageCache(const std::vector<fs::path>& imgPaths)
{
auto startTime = std::chrono::steady_clock::now();
ImageLoader::clearCache();
for (const auto& imgPath : imgPaths)
ImageLoader::load(imgPath);
totalImageLoadTime += timeSince(startTime);
}
static std::string printPositiveTestStats(int imageCount, const TestCase::TC& tc)
{
int passCount = imageCount - Size(tc.misReadFiles) - Size(tc.notDetectedFiles);
std::print(" | {}: {:3} of {:3}, misread {} of {}", tc.name, passCount, tc.minPassCount, Size(tc.misReadFiles), tc.maxMisreads);
std::string failures;
if (passCount < tc.minPassCount && !tc.notDetectedFiles.empty()) {
failures += std::format(" Not detected ({}):", tc.name);
for (const auto& f : tc.notDetectedFiles)
failures += std::format(" {}", f.filename().string());
failures += "\n";
failed += tc.minPassCount - passCount;
}
extra += std::max(0, passCount - tc.minPassCount);
if (passCount > tc.minPassCount)
failures += std::format(" Unexpected detections ({}): {}\n", tc.name, passCount - tc.minPassCount);
if (Size(tc.misReadFiles) > tc.maxMisreads) {
failures += std::format(" Read error ({}):", tc.name);
for (const auto& [path, error] : tc.misReadFiles)
failures += std::format(" {}: {}\n", path.filename().string(), error);
failed += Size(tc.misReadFiles) - tc.maxMisreads;
}
return failures;
}
static std::vector<fs::path> getImagesInDirectory(const fs::path& directory)
{
std::vector<fs::path> result;
for (const auto& entry : fs::directory_iterator(directory))
if (fs::is_regular_file(entry.status()) && Contains({".webp", ".png", ".jpg", ".pgm", ".gif"}, entry.path().extension()))
result.push_back(entry.path());
preloadImageCache(result);
return result;
}
static void doRunTests(const fs::path& directory, BarcodeFormat format, int totalTests, const std::vector<TestCase>& tests,
ReaderOptions opts)
{
auto imgPaths = getImagesInDirectory(directory);
auto folderName = directory.stem();
if (Size(imgPaths) != totalTests)
std::println("TEST {} => Expected number of tests: {}, got: {} => FAILED", folderName.string(), totalTests, imgPaths.size());
for (auto& test : tests) {
std::print("{:20} @ {:3}, {:3}", folderName.string(), test.rotation, Size(imgPaths));
std::vector<int> times;
std::string failures;
for (auto tc : test.tc) {
if (tc.name.empty())
break;
auto startTime = std::chrono::steady_clock::now();
opts.tryDownscale(tc.name == "slow_");
opts.downscaleFactor(2);
opts.downscaleThreshold(180);
opts.tryHarder(tc.name == "slow");
opts.tryRotate(tc.name == "slow");
opts.tryInvert(tc.name == "slow");
opts.isPure(tc.name == "pure");
if (opts.isPure())
opts.binarizer(Binarizer::FixedThreshold);
#ifndef PRINT_DEBUG
#ifdef __cpp_lib_execution
std::vector<Barcode> barcodes(imgPaths.size());
std::transform(std::execution::par, imgPaths.begin(), imgPaths.end(), barcodes.begin(), [&](const fs::path& imgPath) {
return ReadBarcode(ImageLoader::load(imgPath).rotated(test.rotation), opts);
});
for (size_t i = 0; i < imgPaths.size(); ++i) {
const auto& imgPath = imgPaths[i];
const auto& barcode = barcodes[i];
#else
auto futures = std::vector<std::pair<fs::path, std::future<Barcode>>>{};
for (const auto& imgPath : imgPaths) {
futures.push_back(std::make_pair(imgPath, std::async(std::launch::async, [&](const fs::path& path) {
return ReadBarcode(ImageLoader::load(path).rotated(test.rotation), opts);
}, imgPath)));
}
for (auto& [imgPath, fut] : futures) {
auto barcode = fut.get();
#endif // __cpp_lib_execution
#else
for (const auto& imgPath : imgPaths) {
auto barcode = ReadBarcode(ImageLoader::load(imgPath).rotated(test.rotation), opts);
#endif
if (barcode.isValid()) {
auto error = checkResult(imgPath, format, barcode);
if (!error.empty())
tc.misReadFiles[imgPath] = error;
} else {
tc.notDetectedFiles.insert(imgPath);
}
}
times.push_back(timeSince(startTime));
failures += printPositiveTestStats(Size(imgPaths), tc);
}
std::println(" | time: {:3} vs {:3} ms", times.front(), times.back());
if (!failures.empty())
std::println("\n{}", failures);
}
}
static Barcode readMultiple(const std::vector<fs::path>& imgPaths, BarcodeFormat format)
{
Barcodes allBarcodes;
for (const auto& imgPath : imgPaths) {
auto barcodes = ReadBarcodes(ImageLoader::load(imgPath),
ReaderOptions().formats(format).tryDownscale(false));
allBarcodes.insert(allBarcodes.end(), barcodes.begin(), barcodes.end());
}
return MergeStructuredAppendSequence(allBarcodes);
}
static void doRunStructuredAppendTest(const fs::path& directory, BarcodeFormat format, int totalTests,
const std::vector<TestCase>& tests)
{
auto imgPaths = getImagesInDirectory(directory);
auto folderName = directory.stem();
std::map<fs::path, std::vector<fs::path>> imageGroups;
for (const auto& imgPath : imgPaths) {
std::string fn = imgPath.filename().string();
auto p = fn.find_last_of('-');
imageGroups[imgPath.parent_path() / fn.substr(0, p)].push_back(imgPath);
}
if (Size(imageGroups) != totalTests)
std::println("TEST {} => Expected number of tests: {}, got: {} => FAILED", folderName.string(), totalTests,
imageGroups.size());
for (auto& test : tests) {
std::print("{:20} @ {:3}, {:3}", folderName.string(), test.rotation, Size(imgPaths));
auto tc = test.tc[0];
auto startTime = std::chrono::steady_clock::now();
for (const auto& [testPath, testImgPaths] : imageGroups) {
auto barcode = readMultiple(testImgPaths, format);
if (barcode.isValid()) {
auto error = checkResult(testPath, format, barcode);
if (!error.empty())
tc.misReadFiles[testPath] = error;
} else {
tc.notDetectedFiles.insert(testPath);
}
}
auto failures = printPositiveTestStats(Size(imageGroups), tc);
std::println(" | time: {:3} ms", timeSince(startTime));
if (!failures.empty())
std::println("\n{}", failures);
}
}
int runBlackBoxTests(const fs::path& testPathPrefix, const std::set<std::string>& includedTests)
{
auto hasTest = [&includedTests](const fs::path& dir) {
auto stem = dir.stem().string();
return includedTests.empty() || Contains(includedTests, stem) ||
Contains(includedTests, stem.substr(0, stem.size() - 2));
};
auto runTests = [&](std::string_view directory, BarcodeFormat format, int total,
const std::vector<TestCase>& tests, const ReaderOptions& opts = ReaderOptions()) {
if (hasTest(directory) && format & (BarcodeFormat::AllReadable | BarcodeFormat::None))
doRunTests(testPathPrefix / directory, format, total, tests, opts);
};
auto runStructuredAppendTest = [&](std::string_view directory, BarcodeFormat format, int total,
const std::vector<TestCase>& tests) {
if (hasTest(directory) && format & (BarcodeFormat::AllReadable | BarcodeFormat::None))
doRunStructuredAppendTest(testPathPrefix / directory, format, total, tests);
};
try
{
auto startTime = std::chrono::steady_clock::now();
using enum BarcodeFormat;
// clang-format off
// Expected failures:
// abc-inverted.png (fast) - fast does not try inverted
// az-thick.png (pure)
runTests("aztec-1", Aztec, 32, {
{ 31, 32, 0 },
{ 31, 32, 90 },
{ 31, 32, 180 },
{ 31, 32, 270 },
{ 30, 0, pure },
});
runTests("aztec-2", Aztec, 16, {
{ 16, 16, 0 },
{ 16, 16, 90 },
{ 16, 16, 180 },
{ 16, 16, 270 },
});
runTests("datamatrix-1", DataMatrix, 30, {
{ 30, 30, 0 },
{ 0, 28, 90 },
{ 0, 28, 180 },
{ 0, 28, 270 },
{ 28, 0, pure },
});
runTests("datamatrix-2", DataMatrix, 13, {
{ 13, 13, 0 },
{ 0, 13, 90 },
{ 0, 13, 180 },
{ 0, 13, 270 },
});
runTests("datamatrix-3", DataMatrix, 28, {
{ 26, 27, 0 },
{ 0, 27, 90 },
{ 0, 27, 180 },
{ 0, 27, 270 },
});
runTests("datamatrix-4", DataMatrix, 21, {
{ 21, 21, 0 },
{ 0, 21, 90 },
{ 0, 21, 180 },
{ 0, 21, 270 },
{ 19, 0, pure },
});
runTests("dxfilmedge-1", DXFilmEdge, 3, {
{ 1, 3, 0 },
{ 0, 3, 180 },
});
runTests("codabar-1", Codabar, 11, {
{ 11, 11, 0 },
{ 11, 11, 180 },
});
runTests("codabar-2", Codabar, 4, {
{ 3, 4, 0 },
{ 3, 4, 180 },
});
runTests("code39-1", Code39, 6, {
{ 6, 6, 0 },
{ 6, 6, 180 },
});
runTests("code39-2", Code39Ext, 3, {
{ 3, 3, 0 },
{ 3, 3, 180 },
});
runTests("code39-3", Code39, 5, {
{ 5, 5, 0 },
{ 5, 5, 180 },
});
runTests("code93-1", Code93, 3, {
{ 3, 3, 0 },
{ 3, 3, 180 },
});
runTests("code128-1", Code128, 6, {
{ 6, 6, 0 },
{ 6, 6, 180 },
});
runTests("code128-2", Code128, 14, {
{ 13, 14, 0 },
{ 13, 14, 180 },
});
runTests("code128-3", Code128, 2, {
{ 2, 2, 0 },
{ 2, 2, 180 },
});
runTests("ean8-1", EAN8, 9, {
{ 9, 9, 0 },
{ 9, 9, 180 },
{ 8, 0, pure },
});
runTests("ean13-1", EAN13, 16, {
{ 10, 14, 0 },
{ 11, 14, 180 },
});
runTests("ean13-2", EAN13, 16, {
{ 1, 6, 0 },
{ 1, 6, 180 },
});
runTests("ean13-3", EAN13, 8, {
{ 8, 8, 0 },
{ 8, 8, 180 },
});
runTests("ean13-4", EAN13, 20, {
{ 8, 12, 0 },
{ 7, 12, 180 },
});
runTests("ean13-extension-1", EAN13, 5, {
{ 4, 5, 0 },
{ 4, 5, 180 },
}, ReaderOptions().eanAddOnSymbol(EanAddOnSymbol::Require));
runTests("itf-1", ITF, 14, {
{ 13, 14, 0 },
{ 13, 14, 180 },
});
runTests("itf-2", ITF, 6, {
{ 6, 6, 0 },
{ 6, 6, 180 },
});
runTests("telepen-1", Telepen, 4, {
{ 4, 4, 0 },
{ 4, 4, 180 },
});
runTests("upca-1", UPCA, 5, {
{ 4, 5, 0 },
{ 4, 5, 180 },
}, ReaderOptions().formats(UPCA));
runTests("upca-2", UPCA, 26, {
{ 10, 16, 0 },
{ 12, 16, 180 },
}, ReaderOptions().formats(UPCA));
runTests("upca-3", UPCA, 6, {
{ 3, 6, 0 },
{ 3, 6, 180 },
}, ReaderOptions().formats(UPCA));
runTests("upca-4", UPCA, 13, {
{ 5, 9, 0 },
{ 6, 9, 180 },
}, ReaderOptions().formats(UPCA));
runTests("upca-5", UPCA, 20, {
{ 7, 9, 0 },
{ 7, 9, 180 },
}, ReaderOptions().formats(UPCA));
runTests("upca-extension-1", UPCA, 6, {
{ 4, 5, 0 },
{ 4, 5, 180 },
}, ReaderOptions().eanAddOnSymbol(EanAddOnSymbol::Require).formats(UPCA));
runTests("upce-1", UPCE, 3, {
{ 3, 3, 0 },
{ 3, 3, 180 },
{ 3, 0, pure },
});
runTests("upce-2", UPCE, 15, {
{ 9, 10, 0 },
{ 9, 10, 180 },
});
runTests("upce-3", UPCE, 11, {
{ 5, 7, 0 },
{ 5, 7, 180 },
});
runTests("rss14-1", DataBarOmni, 13, {
{ 11, 11, 0 },
{ 11, 11, 180 },
});
runTests("rss14-2", DataBarStk, 7, {
{ 5, 6, 0 },
{ 6, 6, 180 },
});
runTests("rssexpanded-1", DataBarExp, 37, {
{ 37, 37, 0 },
{ 37, 37, 180 },
{ 37, 0, pure },
});
runTests("rssexpanded-2", DataBarExp, 7, {
{ 5, 7, 0 },
{ 5, 7, 180 },
});
runTests("rssexpanded-3", DataBarExp, 118, {
{ 118, 118, 0 },
{ 118, 118, 180 },
{ 118, 0, pure },
});
runTests("rssexpandedstacked-1", DataBarExpStk, 62, {
{ 52, 62, 0 },
{ 52, 62, 180 },
{ 57, 0, pure },
});
runTests("rssexpandedstacked-2", DataBarExpStk, 2, {
{ 2, 2, 0 },
{ 2, 2, 180 },
});
runTests("databarltd-1", DataBarLtd, 2, {
{ 2, 2, 0 },
{ 2, 2, 180 },
{ 1, 0, pure },
});
runTests("maxicode-1", MaxiCode, 9, {
{ 9, 9, 0 },
});
runTests("maxicode-2", MaxiCode, 4, {
{ 0, 0, 0 },
});
runTests("qrcode-1", QRCode, 8, {
{ 8, 8, 0 },
{ 8, 8, 90 },
{ 8, 8, 180 },
{ 8, 8, 270 },
});
runTests("qrcode-2", QRCode, 54, {
{ 48, 51, 0 },
{ 49, 52, 90 },
{ 48, 51, 180 },
{ 48, 51, 270 },
{ 23, 1, pure }, // the misread is the 'outer' symbol in 16.png
});
runTests("qrcode-3", QRCode, 18, {
{ 18, 18, 0 },
{ 18, 18, 90 },
{ 18, 18, 180 },
{ 18, 18, 270 },
});
runTests("qrcode-4", QRCode, 24, {
{ 15, 15, 0 },
{ 15, 15, 90 },
{ 15, 15, 180 },
{ 15, 15, 270 },
});
runTests("qrcode-5", QRCode, 16, {
{ 16, 16, 0 },
{ 16, 16, 90 },
{ 16, 16, 180 },
{ 16, 16, 270 },
{ 4, 0, pure },
});
runTests("qrcode-6", QRCode, 15, {
{ 15, 15, 0 },
{ 15, 15, 90 },
{ 15, 15, 180 },
{ 15, 15, 270 },
});
runStructuredAppendTest("qrcode-7", QRCode, 1, {
{ 1, 1, 0 },
});
runTests("microqrcode-1", MicroQRCode, 16, {
{ 15, 15, 0 },
{ 14, 14, 90 },
{ 14, 14, 180 }, // ughs: 1 result is platform/compiler dependent (e.g. -march=core2 vs. haswell)
{ 15, 15, 270 },
{ 9, 0, pure },
});
runTests("rmqrcode-1", RMQRCode, 3, {
{ 2, 3, 0 },
{ 2, 3, 90 },
{ 2, 3, 180 },
{ 2, 3, 270 },
{ 2, 2, pure },
});
runTests("pdf417-1", PDF417, 17, {
{ 16, 17, 0 },
{ 1, 17, 90 },
{ 16, 17, 180 },
{ 1, 17, 270 },
{ 16, 0, pure },
});
runTests("pdf417-2", PDF417, 25, {
{ 25, 25, 0 },
{ 0, 25, 90 },
{ 25, 25, 180 },
{ 0, 25, 270 },
});
runTests("pdf417-3", PDF417, 16, {
{ 16, 16, 0 },
{ 0, 16, 90 },
{ 16, 16, 180 },
{ 0, 16, 270 },
{ 7, 0, pure },
});
runStructuredAppendTest("pdf417-4", PDF417, 3, {
{ 3, 3, 0 },
});
runTests("micropdf417-1", MicroPDF417, 5, {
{ 4, 4, 0 },
{ 0, 4, 90 },
{ 4, 4, 180 },
{ 0, 4, 270 },
{ 1, 0, pure },
});
runTests("falsepositives-1", None, 21, {
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 90 },
{ 0, 0, 0, 0, 180 },
{ 0, 0, 0, 0, 270 },
{ 0, 0, pure },
});
runTests("falsepositives-2", None, 25, {
{ 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 90 },
{ 0, 0, 0, 0, 180 },
{ 0, 0, 0, 0, 270 },
{ 0, 0, pure },
});
// clang-format on
int totalTime = timeSince(startTime);
int decodeTime = totalTime - totalImageLoadTime;
std::println("load time: {} ms.", totalImageLoadTime);
std::println("decode time: {} ms.", decodeTime);
std::println("total time: {} ms.", totalTime);
if (failed)
std::println("WARNING: {} tests failed.", failed);
if (extra)
std::println("INFO: {} tests succeeded unexpectedly.", extra);
return failed;
}
catch (const std::exception& e) {
std::println("{}", e.what());
}
catch (...) {
std::println("Internal error");
}
return -1;
}
} // ZXing::Test
@@ -0,0 +1,18 @@
/*
* Copyright 2016 Nu-book Inc.
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <filesystem>
#include <set>
#include <string>
namespace fs = std::filesystem;
namespace ZXing::Test {
int runBlackBoxTests(const fs::path& testPathPrefix, const std::set<std::string>& includedTests);
} // ZXing::Test
@@ -0,0 +1,37 @@
zxing_add_package_stb()
zxing_add_package(WebP WebP https://github.com/webmproject/libwebp.git v1.6.0)
if (ZXING_READERS)
add_executable (ReaderTest
TestReaderMain.cpp
ImageLoader.h
ImageLoader.cpp
BlackboxTestRunner.h
BlackboxTestRunner.cpp
)
target_link_libraries(ReaderTest
ZXing::ZXing stb::stb WebP::webp
$<$<AND:$<CXX_COMPILER_ID:GNU>,$<VERSION_LESS:$<CXX_COMPILER_VERSION>,9.0>>:stdc++fs>
)
if (WIN32)
add_custom_command(TARGET ReaderTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_RUNTIME_DLLS:ReaderTest>
$<TARGET_FILE_DIR:ReaderTest>
COMMAND_EXPAND_LISTS
VERBATIM
)
endif()
add_test(NAME ReaderTest COMMAND ReaderTest ${CMAKE_CURRENT_SOURCE_DIR}/../samples)
endif()
if (ZXING_WRITERS)
add_executable (WriterTest TestWriterMain.cpp)
target_link_libraries (WriterTest ZXing::ZXing stb::stb)
add_test(NAME WriterTest COMMAND WriterTest)
endif()
@@ -0,0 +1,105 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2019 Axel Waggersauser.
*/
// SPDX-License-Identifier: Apache-2.0
#include "ImageLoader.h"
#include "BinaryBitmap.h"
#include "ImageView.h"
#include <array>
#include <cctype>
#include <cstdlib>
#include <fstream>
#include <iterator>
#include <map>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <vector>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <webp/decode.h>
namespace ZXing::Test {
class STBImage : public ImageView
{
std::unique_ptr<uint8_t[], void (*)(void*)> _memory = {nullptr, nullptr};
public:
void load(const fs::path& imgPath)
{
int width = 0, height = 0, channels = 0;
int rowStride = 0;
if (imgPath.extension() == ".webp") {
std::ifstream file(imgPath, std::ios::binary);
if (!file)
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (failed to open file)");
std::vector<uint8_t> compressedData(std::istreambuf_iterator<char>(file), {});
if (compressedData.empty())
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (empty file)");
#if 0
// directly decoding to YUV is faster and makes more sense but a couple of tests fail with this, so we decode to RGB and convert to Lum instead
channels = 1;
uint8_t *u = nullptr, *v = nullptr;
int uvStride = 0;
_memory = std::unique_ptr<uint8_t[], void (*)(void*)>(
WebPDecodeYUV(compressedData.data(), compressedData.size(), &width, &height, &u, &v, &rowStride, &uvStride), WebPFree);
#else
channels = 3;
_memory = std::unique_ptr<uint8_t[], void (*)(void*)>(
WebPDecodeRGB(compressedData.data(), compressedData.size(), &width, &height), WebPFree);
#endif
if (_memory == nullptr)
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (WebP decode failed)");
} else {
_memory = std::unique_ptr<uint8_t[], void (*)(void*)>(stbi_load(imgPath.string().c_str(), &width, &height, &channels, 0), stbi_image_free);
if (_memory == nullptr)
throw std::runtime_error("Failed to read image: " + imgPath.string() + " (" + stbi_failure_reason() + ")");
rowStride = width * channels;
}
auto ImageFormatFromChannels = std::array{ImageFormat::None, ImageFormat::Lum, ImageFormat::LumA, ImageFormat::RGB, ImageFormat::RGBA};
ImageView::operator=({_memory.get(), width, height, ImageFormatFromChannels.at(channels), rowStride});
// preconvert from RGB -> Lum to do this only once instead of for each rotation
if (_format == ImageFormat::RGB) {
auto* img = _memory.get();
for (int i = 0; i < width * height; ++i)
img[i] = RGBToLum(img[3 * i + 0], img[3 * i + 1], img[3 * i + 2]);
ImageView::operator=({_memory.get(), width, height, ImageFormat::Lum});
}
}
operator bool() const { return _data; }
};
std::map<fs::path, STBImage> cache;
std::mutex cacheMutex;
void ImageLoader::clearCache()
{
std::scoped_lock lock(cacheMutex);
cache.clear();
}
const ImageView& ImageLoader::load(const fs::path& imgPath)
{
std::scoped_lock lock(cacheMutex);
auto& binImg = cache[imgPath];
if (!binImg)
binImg.load(imgPath);
return binImg;
}
} // namespace ZXing::Test
@@ -0,0 +1,18 @@
/*
* Copyright 2016 Nu-book Inc.
*/
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "ImageView.h"
#include <filesystem>
namespace fs = std::filesystem;
namespace ZXing::Test::ImageLoader {
void clearCache();
const ImageView& load(const fs::path& imgPath);
} // namespace ZXing::Test::ImageLoader
@@ -0,0 +1,65 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2017 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "BlackboxTestRunner.h"
#include "ImageLoader.h"
#include "ReadBarcode.h"
#include "StdPrint.h"
#include "ZXAlgorithms.h"
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <set>
using namespace ZXing;
using namespace ZXing::Test;
int getEnv(const char* name, int fallback = 0)
{
auto var = getenv(name);
return var ? std::stoi(var) : fallback;
}
int main(int argc, char** argv)
{
if (argc <= 1) {
std::println("Usage: {} <test_path_prefix>", argv[0]);
return 0;
}
fs::path pathPrefix = argv[1];
if (Contains({".png", ".jpg", ".pgm", ".gif", ".webp", ".jxl"}, pathPrefix.extension())) {
auto opts = ReaderOptions().tryHarder(!getEnv("FAST", false)).tryRotate(true).isPure(getEnv("IS_PURE"));
if (getenv("FORMATS"))
opts.formats(BarcodeFormatsFromString(getenv("FORMATS")));
int rotation = getEnv("ROTATION");
for (int i = 1; i < argc; ++i) {
Barcode barcode = ReadBarcode(ImageLoader::load(argv[i]).rotated(rotation), opts);
std::print("{}: ", argv[i]);
if (barcode.isValid())
std::println("{}: {}", ToString(barcode.format()), barcode.text());
else
std::println("FAILED");
if (barcode.isValid() && getenv("WRITE_TEXT")) {
std::ofstream f(fs::path(argv[i]).replace_extension(".txt"));
f << barcode.text();
}
}
return 0;
} else {
std::set<std::string> includedTests;
for (int i = 2; i < argc; ++i) {
if (std::strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == 't') {
includedTests.insert(argv[i] + 2);
}
}
return runBlackBoxTests(pathPrefix, includedTests);
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2016 Nu-book Inc.
* Copyright 2019 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "ZXingCpp.h"
#include <vector>
using namespace ZXing;
using namespace std::literals;
using enum BarcodeFormat;
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
void savePng(ImageView iv, BarcodeFormat format)
{
stbi_write_png((ToString(format) + ".png"s).c_str(), iv.width(), iv.height(), iv.pixStride(), iv.data(), iv.rowStride());
}
int main()
{
std::string text = "zxing-cpp";
for (auto format : BarcodeFormats::list(AllMatrix))
if (format & AllCreatable && format != AztecRune)
savePng(CreateBarcodeFromText(text, format).symbol(), format);
text = "012345678901234567890123456789";
using FormatSpecs = std::vector<std::pair<BarcodeFormat, size_t>>;
for (const auto& [format, length] : FormatSpecs({
// {Codabar, 0}, // needs to start with A, B, C or D
{Code39, 0},
{Code93, 0},
{Code128, 0},
{EAN8, 7},
{EAN13, 12},
{ITF, 0},
{UPCA, 11},
{UPCE, 7}
}))
{
auto input = length > 0 ? text.substr(0, length) : text;
if (format & AllCreatable)
savePng(WriteBarcodeToImage(CreateBarcodeFromText(input, format)), format);
}
}
@@ -0,0 +1,34 @@
cmake_minimum_required (VERSION 3.16)
# -fsanitize=fuzzer only available in clang / llvm
# cmake -DCMAKE_CXX_COMPILER=/path/to/your/clang++
project (ZXingFuzz)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -march=native -fsanitize=address,undefined,fuzzer")
set (ZXING_WRITERS BOTH)
set (ZXING_READERS ON)
add_definitions (-DZXING_BUILD_FOR_TEST)
add_subdirectory (${CMAKE_CURRENT_SOURCE_DIR}/../../core ${CMAKE_BINARY_DIR}/ZXing)
set (TESTS
DBEDecoder
DMEncoder
ReadLinear
ReadMatrix
DecodeMatrix
)
foreach (test ${TESTS})
set (name "fuzz${test}")
add_executable (${name} "${name}.cpp")
target_compile_options (${name} PRIVATE -DZXING_INTERNAL)
target_link_libraries (${name} ZXing::ZXing)
endforeach()
@@ -0,0 +1,31 @@
/*
* Copyright 2021 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include <stdint.h>
#include <stddef.h>
#include "BitArray.h"
#include "Error.h"
#include "oned/ODDataBarExpandedBitDecoder.h"
using namespace ZXing;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
if (size < 2)
return 0;
BitArray bits;
for (size_t i = 0; i < size; ++i)
bits.appendBits(data[i], 8);
try {
OneD::DataBar::DecodeExpandedBits(bits);
} catch (std::out_of_range) {
} catch (Error) {
}
return 0;
}
@@ -0,0 +1,24 @@
/*
* Copyright 2021 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include <stdint.h>
#include <stddef.h>
#include "ByteArray.h"
#include "datamatrix/DMHighLevelEncoder.h"
using namespace ZXing;
using namespace ZXing::DataMatrix;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::wstring txt(data, data + size);
try {
Encode(txt);
} catch (...) {
}
return 0;
}
@@ -0,0 +1,87 @@
/*
* Copyright 2023 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "BitArray.h"
#include "ByteArray.h"
#include "DecoderResult.h"
#include "qrcode/QRErrorCorrectionLevel.h"
#include "qrcode/QRVersion.h"
#include "pdf417/PDFDecoder.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
using namespace ZXing;
namespace ZXing::Aztec {
DecoderResult Decode(const BitArray& bits);
}
namespace ZXing::DataMatrix::DecodedBitStreamParser {
DecoderResult Decode(ByteArray&& bytes, const bool isDMRE);
}
namespace ZXing::QRCode {
DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel);
}
void az(const uint8_t* data, size_t size)
{
BitArray bits;
for (size_t i = 1; i < size - 1; ++i)
bits.appendBits(data[i], 8);
bits.appendBits(data[size - 1], (data[0] & 0x7) + 1);
Aztec::Decode(bits);
}
void dm(const uint8_t* data, size_t size)
{
ByteArray ba;
ba.insert(ba.begin(), data, data + size);
try {
DataMatrix::DecodedBitStreamParser::Decode(std::move(ba), false);
} catch (...) {
}
}
void qr(const uint8_t* data, size_t size)
{
auto version = QRCode::Version::Model2(std::clamp(data[0] & 0x3F, 1, 40));
auto ecLevel = QRCode::ECLevelFromBits(data[0] >> 6);
ByteArray ba;
ba.insert(ba.begin(), data, data + size);
QRCode::DecodeBitStream(std::move(ba), *version, ecLevel);
}
void pd(const uint8_t* data, size_t size)
{
auto codewords = std::vector<int>(size / 2);
auto u16 = reinterpret_cast<const uint16_t*>(data);
for (int i = 0; i < static_cast<int>(codewords.size()); ++i)
codewords[i] = u16[i] % 929;
codewords[0] = static_cast<int>(codewords.size());
Pdf417::Decode(codewords);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
if (size < 3)
return 0;
az(data, size);
dm(data, size);
qr(data, size);
pd(data, size);
return 0;
}
@@ -0,0 +1,68 @@
/*
* Copyright 2021 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include <stdint.h>
#include <stddef.h>
#include "oned/ODMultiUPCEANReader.h"
#include "oned/ODCode39Reader.h"
#include "oned/ODCode93Reader.h"
#include "oned/ODCode128Reader.h"
#include "oned/ODDataBarReader.h"
#include "oned/ODDataBarExpandedReader.h"
#include "oned/ODDXFilmEdgeReader.h"
#include "oned/ODITFReader.h"
#include "oned/ODCodabarReader.h"
#include "ReaderOptions.h"
#include "Barcode.h"
using namespace ZXing;
using namespace ZXing::OneD;
static std::vector<std::unique_ptr<RowReader>> readers;
bool init()
{
static auto opts = ReaderOptions().returnErrors(true);
readers.emplace_back(new MultiUPCEANReader(opts));
readers.emplace_back(new Code39Reader(opts));
readers.emplace_back(new Code93Reader(opts));
readers.emplace_back(new Code128Reader(opts));
readers.emplace_back(new ITFReader(opts));
readers.emplace_back(new CodabarReader(opts));
readers.emplace_back(new DataBarReader(opts));
readers.emplace_back(new DataBarExpandedReader(opts));
readers.emplace_back(new DXFilmEdgeReader(opts));
return true;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
if (size < 1)
return 0;
static bool inited [[maybe_unused]] = init();
std::vector<std::unique_ptr<RowReader::DecodingState>> decodingState(readers.size());
PatternRow row(size * 2 + 1);
for (size_t i = 0; i < size; ++i){
auto v = data[i];
row[i * 2 + 0] = (v & 0xf) + 1;
row[i * 2 + 1] = (v >> 4) + 1;
}
row.back() = 0;
for (size_t r = 0; r < readers.size(); ++r) {
PatternView next(row);
while (next.isValid()) {
readers[r]->decodePattern(0, next, decodingState[r]);
// make sure we make progress and we start the next try on a bar
next.shift(2 - (next.index() % 2));
next.extend();
}
}
return 0;
}
@@ -0,0 +1,64 @@
/*
* Copyright 2023 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0
#include "ReadBarcode.h"
#include <algorithm>
#include <cassert>
#include <csignal>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
using namespace ZXing;
uint64_t Expand(uint8_t b)
{
uint64_t shift = 0x0000040810204081ul; // bits set: 0, 7, 14, 21, 28, 35, 42
uint64_t mask = 0x0001010101010101ul; // bits set: 0, 8, 16, 24, 32, 40, 48
return ((uint64_t)(b & 127) * shift & mask) | (uint64_t)(b & 128) << 49;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
if (size < 3)
return 0;
static auto opts = ReaderOptions()
.formats(BarcodeFormat::AllMatrix)
.binarizer(Binarizer::BoolCast)
.returnErrors(true)
.tryInvert(false)
.tryRotate(false);
int ratio = data[0] + 1;
int nBits = (size - 1) * 8;
int width = std::clamp(nBits * ratio / 256, 1, nBits);
int height = std::clamp(nBits / width, 1, nBits);
assert(width * height <= nBits);
std::vector<uint8_t> buffer(nBits);
for (size_t i = 1; i < size; ++i)
*reinterpret_cast<uint64_t*>(&buffer[(i - 1) * 8]) = Expand(data[i]);
#ifdef PRINT_DEBUG
printf("s: %zu, r: %d, n: %d -> %d x %d\n", size, ratio, nBits, width, height);
#endif
auto image = ImageView(buffer.data(), width, height, ImageFormat::Lum);
auto res = ReadBarcodes(image, opts);
#ifdef PRINT_DEBUG
for (const auto& r : res)
printf("%s: %s / %s\n", ToString(r.format()).c_str(), r.text().c_str(), ToString(r.error()).c_str());
#endif
static int detectedSybols = 0;
detectedSybols += static_cast<int>(res.size());
if (!res.empty() && detectedSybols % 100 == 0)
printf("detected barcode symbols: %d\n", detectedSybols);
return 0;
}
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.16)
project(ZXingInstallTest LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17) # make sure the public API is still c++17 compatible
find_package(ZXing CONFIG REQUIRED)
add_executable(InstallTest main.cpp)
target_link_libraries(InstallTest PRIVATE ZXing::ZXing)
if (ZXING_WRITERS MATCHES "BOTH|OLD")
add_executable(InstallTestOLD mainOLD.cpp)
target_link_libraries(InstallTestOLD PRIVATE ZXing::ZXing)
endif()
@@ -0,0 +1,22 @@
#include "ZXing/ZXingCpp.h"
#include <iostream>
int main(int argc, char** argv)
{
using namespace ZXing;
auto copts = CreatorOptions(BarcodeFormat::QRCode, "eclevel=L");
auto barcode = CreateBarcodeFromText("Test", copts);
auto wopts = WriterOptions().scale(2);
auto img = WriteBarcodeToImage(barcode, wopts);
auto iv = ImageView(img.data(), img.width(), img.height(), img.format());
auto ropts = ReaderOptions().formats(BarcodeFormat::AllLinear);
auto barcodes = ReadBarcodes(iv, ropts);
for (const auto& b : barcodes)
std::cout << ToString(b.format()) << ": " << b.text() << "\n";
return 0;
}
@@ -0,0 +1,20 @@
#include "ZXing/MultiFormatWriter.h"
#include "ZXing/BitMatrix.h"
#include "ZXing/BitMatrixIO.h"
#include "ZXing/ZXVersion.h"
#include <iostream>
int main(int argc, char** argv)
{
using namespace ZXing;
std::cout << "zxing-cpp version " << ZXING_VERSION_STR << "\n";
MultiFormatWriter writer(BarcodeFormat::QRCode);
BitMatrix bitmatrix = writer.encode("Hello, World!", 200, 200);
auto matrix = ToMatrix<int8_t>(bitmatrix);
std::cout << ToString(bitmatrix) << "\n";
return 0;
}
@@ -0,0 +1,34 @@
set(install_prefix "${PROJECT_BINARY_DIR}/InstallTest-prefix")
set(consumer_build "${PROJECT_BINARY_DIR}/InstallTest-build")
# 1. Install zxing-cpp
execute_process(
COMMAND "${CMAKE_COMMAND}" --install "${PROJECT_BINARY_DIR}" --prefix "${install_prefix}"
RESULT_VARIABLE install_result
)
if(NOT install_result EQUAL 0)
message(FATAL_ERROR "Install step failed")
endif()
# 2. Configure consumer project
execute_process(
COMMAND
"${CMAKE_COMMAND}"
-S "${SOURCE_DIR}"
-B "${consumer_build}"
-DCMAKE_PREFIX_PATH=${install_prefix}
-DZXING_WRITERS=${ZXING_WRITERS}
RESULT_VARIABLE configure_result
)
if(NOT configure_result EQUAL 0)
message(FATAL_ERROR "Consumer configure failed")
endif()
# 3. Build consumer project
execute_process(
COMMAND "${CMAKE_COMMAND}" --build "${consumer_build}"
RESULT_VARIABLE build_result
)
if(NOT build_result EQUAL 0)
message(FATAL_ERROR "Consumer build failed")
endif()
@@ -0,0 +1 @@
* binary
@@ -0,0 +1 @@
This is an example Aztec symbol for Wikipedia.
Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

@@ -0,0 +1 @@
I have the best words.
Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

@@ -0,0 +1 @@
symbologyIdentifier=]z0
@@ -0,0 +1 @@
Code 2D!
Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

@@ -0,0 +1 @@
This is an example Aztec symbol for Wikipedia.
Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

@@ -0,0 +1 @@
Histórico
Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

@@ -0,0 +1 @@
Históóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóóórico
Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

@@ -0,0 +1,4 @@
symbologyIdentifier=]z6
sequenceSize=7
sequenceIndex=3
sequenceId=Z1.txt
@@ -0,0 +1 @@
3456
Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

@@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyz
Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

@@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

@@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyz
Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

@@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyz
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1 @@
Aztec Code is a public domain 2D matrix barcode symbology of nominally square symbols built on a square grid with a distinctive square bullseye pattern at their center.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1 @@
Aztec Code is a public domain 2D matrix barcode symbology of nominally square symbols built on a square grid with a distinctive square bullseye pattern at their center.
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

@@ -0,0 +1 @@
3333h3i3jITIT
Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

@@ -0,0 +1 @@
symbologyIdentifier=]z1
@@ -0,0 +1 @@
01095040000591012112345678p901101234567p171411208200http://www.gs1.org/demo/
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1 @@
hello
Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

@@ -0,0 +1 @@
In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1 @@
In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tris. In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo e.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1 @@
In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu tris. In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem.
Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1 @@
KF5::Prison - The KDE barcode generation framework.
Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

@@ -0,0 +1,2 @@
symbologyIdentifier=]z0
readerInit=true
Loaded 100 of 2206 files, more files were not shown because too many files have changed in this diff. Show more