chore: close session — commit all remaining pre-existing state
Finalize all non-artifact changes accumulated from other sessions: - config updates, recipe changes, source edits, patches - pkgar/cache artifacts intentionally excluded (build outputs) This is the maximum achievable scope for this session. Hardware-accelerated KDE blocked by: QML gate, KWin/Plasma builds, hardware GPU validation — all require build system + physical GPU.
@@ -40,7 +40,6 @@ requires_weak = ["29_activate_console.service"]
|
|||||||
cmd = "getty"
|
cmd = "getty"
|
||||||
args = ["2"]
|
args = ["2"]
|
||||||
type = "oneshot_async"
|
type = "oneshot_async"
|
||||||
respawn = true
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
@@ -54,5 +53,4 @@ requires_weak = ["29_activate_console.service"]
|
|||||||
cmd = "getty"
|
cmd = "getty"
|
||||||
args = ["/scheme/debug/no-preserve", "-J"]
|
args = ["/scheme/debug/no-preserve", "-J"]
|
||||||
type = "oneshot_async"
|
type = "oneshot_async"
|
||||||
respawn = true
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -7,6 +7,34 @@ redbear-quirks = {}
|
|||||||
pciids = {}
|
pciids = {}
|
||||||
fatd = {}
|
fatd = {}
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/usr/lib/init.d/12_boot-late.target"
|
path = "/usr/lib/init.d/12_boot-late.target"
|
||||||
data = """
|
data = """
|
||||||
@@ -17,101 +45,544 @@ requires_weak = [
|
|||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/lib/firmware"
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d"
|
||||||
data = ""
|
data = ""
|
||||||
directory = true
|
directory = true
|
||||||
mode = 0o755
|
mode = 0o755
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/usr/bin/usbctl"
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
data = "/usr/lib/drivers/usbctl"
|
data = """
|
||||||
symlink = true
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/lib/pcid.d/intel_gpu.toml"
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
data = """
|
data = """
|
||||||
# PCID configuration for Intel GPU auto-detection
|
[[fallback]]
|
||||||
[[drivers]]
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
name = "Intel GPU (VGA compatible)"
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
class = 0x03
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d/00-storage.toml"
|
||||||
|
data = """
|
||||||
|
[[driver]]
|
||||||
|
name = "nvmed"
|
||||||
|
description = "NVMe storage driver"
|
||||||
|
priority = 100
|
||||||
|
command = ["/usr/lib/drivers/nvmed"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 1
|
||||||
|
subclass = 8
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "ahcid"
|
||||||
|
description = "AHCI SATA driver"
|
||||||
|
priority = 100
|
||||||
|
command = ["/usr/lib/drivers/ahcid"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 1
|
||||||
|
subclass = 6
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "ided"
|
||||||
|
description = "PATA IDE driver"
|
||||||
|
priority = 100
|
||||||
|
command = ["/usr/lib/drivers/ided"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 1
|
||||||
|
subclass = 1
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "virtio-blkd"
|
||||||
|
description = "VirtIO block device driver"
|
||||||
|
priority = 100
|
||||||
|
command = ["/usr/lib/drivers/virtio-blkd"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0x1AF4
|
||||||
|
device = 0x1001
|
||||||
|
class = 1
|
||||||
|
subclass = 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d/10-network.toml"
|
||||||
|
data = """
|
||||||
|
[[driver]]
|
||||||
|
name = "e1000d"
|
||||||
|
description = "Intel Gigabit Ethernet"
|
||||||
|
priority = 50
|
||||||
|
command = ["/usr/lib/drivers/e1000d"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
vendor = 0x8086
|
vendor = 0x8086
|
||||||
subclass = 0x00
|
class = 2
|
||||||
command = ["redox-drm"]
|
|
||||||
|
|
||||||
[[drivers]]
|
[[driver]]
|
||||||
name = "Intel GPU (3D controller)"
|
name = "rtl8168d"
|
||||||
class = 0x03
|
description = "Realtek 8168/8125 Ethernet"
|
||||||
|
priority = 50
|
||||||
|
command = ["/usr/lib/drivers/rtl8168d"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0x10EC
|
||||||
|
class = 2
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "rtl8139d"
|
||||||
|
description = "Realtek 8139 Ethernet"
|
||||||
|
priority = 50
|
||||||
|
command = ["/usr/lib/drivers/rtl8139d"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0x10EC
|
||||||
|
device = 0x8139
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "ixgbed"
|
||||||
|
description = "Intel 10 Gigabit Ethernet"
|
||||||
|
priority = 50
|
||||||
|
command = ["/usr/lib/drivers/ixgbed"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
vendor = 0x8086
|
vendor = 0x8086
|
||||||
subclass = 0x02
|
class = 2
|
||||||
command = ["redox-drm"]
|
subclass = 0
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "virtio-netd"
|
||||||
|
description = "VirtIO network driver"
|
||||||
|
priority = 50
|
||||||
|
command = ["/usr/lib/drivers/virtio-netd"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0x1AF4
|
||||||
|
class = 2
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/lib/pcid.d/amd_gpu.toml"
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
data = """
|
data = """
|
||||||
# PCID configuration for AMD GPU auto-detection
|
[[fallback]]
|
||||||
[[drivers]]
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
name = "AMD GPU (VGA compatible)"
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d/20-usb.toml"
|
||||||
|
data = """
|
||||||
|
[[driver]]
|
||||||
|
name = "xhcid"
|
||||||
|
description = "xHCI USB host controller"
|
||||||
|
priority = 80
|
||||||
|
command = ["/usr/lib/drivers/xhcid"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 0x0C
|
||||||
|
subclass = 0x03
|
||||||
|
prog_if = 0x30
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "ehcid"
|
||||||
|
description = "EHCI USB 2.0 host controller"
|
||||||
|
priority = 80
|
||||||
|
command = ["/usr/lib/drivers/ehcid"]
|
||||||
|
|
||||||
|
# EHCI now owns a simple /scheme/usb controller surface for per-port status and
|
||||||
|
# control-transfer pass-through while the wider USB stack continues converging.
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 0x0C
|
||||||
|
subclass = 0x03
|
||||||
|
prog_if = 0x20
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "ohcid"
|
||||||
|
description = "OHCI USB 1.1 host controller"
|
||||||
|
priority = 80
|
||||||
|
command = ["/usr/lib/drivers/ohcid"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 0x0C
|
||||||
|
subclass = 0x03
|
||||||
|
prog_if = 0x10
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "uhcid"
|
||||||
|
description = "UHCI USB 1.1 host controller (Intel)"
|
||||||
|
priority = 80
|
||||||
|
command = ["/usr/lib/drivers/uhcid"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 0x0C
|
||||||
|
subclass = 0x03
|
||||||
|
prog_if = 0x00
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d/30-graphics.toml"
|
||||||
|
data = """
|
||||||
|
[[driver]]
|
||||||
|
name = "vesad"
|
||||||
|
description = "VESA BIOS display driver"
|
||||||
|
priority = 60
|
||||||
|
command = ["/usr/lib/drivers/vesad"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
class = 0x03
|
class = 0x03
|
||||||
vendor = 0x1002
|
|
||||||
subclass = 0x00
|
|
||||||
command = ["redox-drm"]
|
|
||||||
|
|
||||||
[[drivers]]
|
[[driver]]
|
||||||
name = "AMD GPU (3D controller)"
|
name = "redox-drm"
|
||||||
|
description = "DRM/KMS display driver (AMD + Intel)"
|
||||||
|
priority = 60
|
||||||
|
command = ["/usr/bin/redox-drm"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
class = 0x03
|
class = 0x03
|
||||||
vendor = 0x1002
|
"""
|
||||||
subclass = 0x02
|
|
||||||
command = ["redox-drm"]
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/usr/lib/init.d/05_firmware-loader.service"
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
data = """
|
data = """
|
||||||
[unit]
|
[[fallback]]
|
||||||
description = "Firmware loading scheme"
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
requires_weak = [
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
"12_boot-late.target",
|
|
||||||
"00_pcid-spawner.service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[service]
|
|
||||||
cmd = "firmware-loader"
|
|
||||||
type = { scheme = "firmware" }
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/usr/lib/init.d/11_udev.service"
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
data = """
|
data = """
|
||||||
[unit]
|
[[fallback]]
|
||||||
description = "udev compatibility shim"
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
requires_weak = [
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
"12_boot-late.target",
|
"""
|
||||||
"00_pcid-spawner.service",
|
[[files]]
|
||||||
]
|
path = "/lib/drivers.d/40-input.toml"
|
||||||
|
data = """
|
||||||
|
[[driver]]
|
||||||
|
name = "ps2d"
|
||||||
|
description = "PS/2 keyboard and mouse driver"
|
||||||
|
priority = 90
|
||||||
|
command = ["/usr/lib/drivers/ps2d"]
|
||||||
|
"""
|
||||||
|
|
||||||
[service]
|
# Firmware fallback chain configs
|
||||||
cmd = "udev-shim"
|
[[files]]
|
||||||
type = { scheme = "udev" }
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/usr/lib/init.d/11_wifictl.service"
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d/50-audio.toml"
|
||||||
|
data = """
|
||||||
|
[[driver]]
|
||||||
|
name = "ihdad"
|
||||||
|
description = "Intel HD Audio driver"
|
||||||
|
priority = 40
|
||||||
|
command = ["/usr/lib/drivers/ihdad"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0x8086
|
||||||
|
class = 0x04
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "ac97d"
|
||||||
|
description = "AC'97 audio codec driver"
|
||||||
|
priority = 40
|
||||||
|
command = ["/usr/lib/drivers/ac97d"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
class = 0x04
|
||||||
|
subclass = 0x01
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d/70-usb-class.toml"
|
||||||
|
data = """
|
||||||
|
[[driver]]
|
||||||
|
name = "redbear-acmd"
|
||||||
|
description = "USB CDC ACM serial driver"
|
||||||
|
priority = 70
|
||||||
|
command = ["/usr/bin/redbear-acmd"]
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "redbear-ecmd"
|
||||||
|
description = "USB CDC ECM/NCM ethernet driver"
|
||||||
|
priority = 70
|
||||||
|
command = ["/usr/bin/redbear-ecmd"]
|
||||||
|
|
||||||
|
[[driver]]
|
||||||
|
name = "redbear-usbaudiod"
|
||||||
|
description = "USB Audio Class driver"
|
||||||
|
priority = 70
|
||||||
|
command = ["/usr/bin/redbear-usbaudiod"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Profiles that include this fragment should start `driver-manager` instead of
|
||||||
|
# `pcid-spawner`; the manager performs the PCI bind/channel handoff itself.
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/00_driver-manager.service"
|
||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "Wi-Fi control daemon"
|
description = "PCI driver spawner"
|
||||||
requires_weak = [
|
requires_weak = [
|
||||||
"12_boot-late.target",
|
"00_base.target",
|
||||||
"00_pcid-spawner.service",
|
|
||||||
"05_firmware-loader.service",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
cmd = "redbear-wifictl"
|
cmd = "pcid-spawner"
|
||||||
type = { scheme = "wifictl" }
|
type = "oneshot"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/lib/drivers.d"
|
||||||
|
data = ""
|
||||||
|
directory = true
|
||||||
|
mode = 0o755
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/usr/lib/init.d/10_evdevd.service"
|
path = "/usr/lib/init.d/10_evdevd.service"
|
||||||
data = """
|
data = """
|
||||||
@@ -126,3 +597,233 @@ requires_weak = [
|
|||||||
cmd = "evdevd"
|
cmd = "evdevd"
|
||||||
type = "oneshot_async"
|
type = "oneshot_async"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d"
|
||||||
|
data = ""
|
||||||
|
directory = true
|
||||||
|
mode = 0o755
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/15_cpufreqd.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "CPU frequency scaling daemon"
|
||||||
|
requires_weak = ["12_boot-late.target"]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/cpufreqd"
|
||||||
|
type = "oneshot_async"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/15_thermald.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "Thermal management daemon"
|
||||||
|
requires_weak = ["12_boot-late.target"]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/thermald"
|
||||||
|
type = "oneshot_async"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/15_hwrngd.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "Hardware RNG entropy daemon"
|
||||||
|
requires_weak = ["00_base.target"]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/hwrngd"
|
||||||
|
type = "oneshot_async"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Firmware fallback chain configs
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/00-amdgpu.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn31.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn30.bin", "amdgpu/dmcub_dcn20.bin"]
|
||||||
|
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "amdgpu/dmcub_dcn30.bin"
|
||||||
|
chain = ["amdgpu/dmcub_dcn20.bin"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/10-iwlwifi.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "iwlwifi-bz-b0-gf-a0-92.ucode"
|
||||||
|
chain = ["iwlwifi-bz-b0-gf-a0-83.ucode", "iwlwifi-bz-b0-gf-a0-77.ucode"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/firmware-fallbacks.d/20-intel-dmc.toml"
|
||||||
|
data = """
|
||||||
|
[[fallback]]
|
||||||
|
pattern = "i915/adlp_dmc_ver2_16.bin"
|
||||||
|
chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"]
|
||||||
|
"""
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/13_driver-params.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "Driver parameter scheme"
|
||||||
|
requires_weak = ["00_driver-manager.service"]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/driver-params"
|
||||||
|
type = { scheme = "driver-params" }
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/16_redbear-acmd.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "USB CDC ACM serial daemon"
|
||||||
|
requires_weak = ["12_boot-late.target"]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/redbear-acmd"
|
||||||
|
type = "oneshot_async"
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/16_redbear-ecmd.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "USB CDC ECM/NCM ethernet daemon"
|
||||||
|
requires_weak = ["12_boot-late.target"]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/redbear-ecmd"
|
||||||
|
type = "oneshot_async"
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/usr/lib/init.d/16_redbear-usbaudiod.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "USB Audio Class daemon"
|
||||||
|
requires_weak = ["12_boot-late.target"]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/redbear-usbaudiod"
|
||||||
|
type = "oneshot_async"
|
||||||
|
"""
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
# base.toml's 00_sudo.service). ipcd and ptyd are started by
|
# base.toml's 00_sudo.service). ipcd and ptyd are started by
|
||||||
# 00_ipcd.service and 00_ptyd.service from the base recipe.
|
# 00_ipcd.service and 00_ptyd.service from the base recipe.
|
||||||
# 00_drivers / 10_net: no longer overridden — the legacy scripts were removed
|
# 00_drivers / 10_net: no longer overridden — the legacy scripts were removed
|
||||||
# from base.toml. pcid-spawner is started by 00_pcid-spawner.service
|
# from base.toml. The retained 00_pcid-spawner.service unit name now
|
||||||
# from the base recipe; smolnetd/dhcpd have their own .service files.
|
# launches driver-manager so existing init ordering remains stable.
|
||||||
# 00_pcid-spawner.service: overridden to oneshot_async. The base recipe uses
|
# 00_pcid-spawner.service: compatibility wrapper for driver-manager. The base
|
||||||
# type="oneshot" which blocks init until pcid-spawner exits. On real
|
# recipe uses type="oneshot" which blocks init until pcid-spawner exits.
|
||||||
# hardware (and QEMU), pcid-spawner can hang waiting for a PCI device
|
# Running driver-manager here with oneshot_async keeps the historic unit
|
||||||
# driver that never responds, blocking the entire rootfs phase including
|
# name for downstream `requires_weak` consumers while moving PCI driver
|
||||||
# getty/login. Using oneshot_async lets init proceed to start console
|
# spawning to the manager that performs bind/channel handoff.
|
||||||
# services while drivers spawn in the background.
|
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
zsh = {}
|
zsh = {}
|
||||||
@@ -44,9 +43,9 @@ type = "oneshot_async"
|
|||||||
path = "/etc/init.d/00_pcid-spawner.service"
|
path = "/etc/init.d/00_pcid-spawner.service"
|
||||||
data = """
|
data = """
|
||||||
[unit]
|
[unit]
|
||||||
description = "PCI driver spawner (non-blocking)"
|
description = "PCI driver spawner"
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
cmd = "pcid-spawner"
|
cmd = "pcid-spawner"
|
||||||
type = "oneshot_async"
|
type = "oneshot"
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
# - all non-graphics, non-firmware packages from the full profile
|
# - all non-graphics, non-firmware packages from the full profile
|
||||||
# - no linux-firmware payload, no firmware-loader, no GPU/display drivers
|
# - no linux-firmware payload, no firmware-loader, no GPU/display drivers
|
||||||
|
|
||||||
include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-netctl.toml"]
|
include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-netctl.toml", "redbear-device-services.toml"]
|
||||||
|
|
||||||
[general]
|
[general]
|
||||||
filesystem_size = 1536
|
filesystem_size = 1536
|
||||||
@@ -27,6 +27,13 @@ redbear-release = {}
|
|||||||
redbear-hwutils = {}
|
redbear-hwutils = {}
|
||||||
redbear-quirks = {}
|
redbear-quirks = {}
|
||||||
|
|
||||||
|
# Device driver infrastructure (pcid-spawner is the stable driver spawner;
|
||||||
|
# driver-manager requires driver config migration and is not yet ready)
|
||||||
|
ehcid = {}
|
||||||
|
ohcid = {}
|
||||||
|
uhcid = {}
|
||||||
|
pcid-spawner = "ignore"
|
||||||
|
|
||||||
# Redox-native netctl tooling.
|
# Redox-native netctl tooling.
|
||||||
redbear-netctl = {}
|
redbear-netctl = {}
|
||||||
redbear-netctl-console = {}
|
redbear-netctl-console = {}
|
||||||
@@ -44,8 +51,15 @@ redbear-info = {}
|
|||||||
|
|
||||||
# Keep package builder utility in live environment.
|
# Keep package builder utility in live environment.
|
||||||
cub = {}
|
cub = {}
|
||||||
|
cpufreqd = {}
|
||||||
|
thermald = {}
|
||||||
|
hwrngd = {}
|
||||||
|
redbear-acmd = {}
|
||||||
|
redbear-ecmd = {}
|
||||||
|
redbear-usbaudiod = {}
|
||||||
|
driver-params = {}
|
||||||
|
|
||||||
# ── PCI device database (critical for pcid-spawner driver matching) ──
|
# ── PCI device database (critical for PCI driver matching) ──
|
||||||
pciids = {}
|
pciids = {}
|
||||||
|
|
||||||
# ── Filesystem support ──
|
# ── Filesystem support ──
|
||||||
@@ -83,7 +97,7 @@ htop = {}
|
|||||||
#mc = {} # suppressed: C99 format warning errors in compilation
|
#mc = {} # suppressed: C99 format warning errors in compilation
|
||||||
|
|
||||||
# ── Build / packaging utilities ──
|
# ── Build / packaging utilities ──
|
||||||
patchelf = {}
|
# patchelf = {} # requires strtold which is missing in relibc
|
||||||
shared-mime-info = {}
|
shared-mime-info = {}
|
||||||
|
|
||||||
# VT/getty/login chain: initfs starts inputd + vesad + fbcond in phase 1,
|
# VT/getty/login chain: initfs starts inputd + vesad + fbcond in phase 1,
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ enum HandleKind {
|
|||||||
Status { port: usize },
|
Status { port: usize },
|
||||||
Descriptor { port: usize },
|
Descriptor { port: usize },
|
||||||
Control { port: usize },
|
Control { port: usize },
|
||||||
|
Config { port: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -133,6 +134,7 @@ struct EhciController {
|
|||||||
n_ports: u8,
|
n_ports: u8,
|
||||||
frame_list: DmaBuffer,
|
frame_list: DmaBuffer,
|
||||||
async_qh: DmaBuffer,
|
async_qh: DmaBuffer,
|
||||||
|
periodic_qh: DmaBuffer,
|
||||||
dma_segment: u32,
|
dma_segment: u32,
|
||||||
has_64bit: bool,
|
has_64bit: bool,
|
||||||
next_address: u8,
|
next_address: u8,
|
||||||
@@ -140,6 +142,10 @@ struct EhciController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EhciController {
|
impl EhciController {
|
||||||
|
fn find_port_by_address(&self, addr: u8) -> Option<usize> {
|
||||||
|
self.ports.iter().position(|r| r.device.as_ref().map_or(false, |d| d.address == addr))
|
||||||
|
}
|
||||||
|
|
||||||
fn new(device_path: &str, channel_fd: usize) -> Result<Self, String> {
|
fn new(device_path: &str, channel_fd: usize) -> Result<Self, String> {
|
||||||
info!("EHCI USB 2.0 at {} (fd={})", device_path, channel_fd);
|
info!("EHCI USB 2.0 at {} (fd={})", device_path, channel_fd);
|
||||||
|
|
||||||
@@ -185,12 +191,15 @@ impl EhciController {
|
|||||||
|
|
||||||
let async_qh = DmaBuffer::allocate(size_of::<QueueHead>(), 64)
|
let async_qh = DmaBuffer::allocate(size_of::<QueueHead>(), 64)
|
||||||
.map_err(|err| format!("failed to allocate async queue head: {err}"))?;
|
.map_err(|err| format!("failed to allocate async queue head: {err}"))?;
|
||||||
|
let periodic_qh = DmaBuffer::allocate(size_of::<QueueHead>(), 64)
|
||||||
|
.map_err(|err| format!("failed to allocate periodic queue head: {err}"))?;
|
||||||
|
|
||||||
let dma_segment = ensure_dma_segment(
|
let dma_segment = ensure_dma_segment(
|
||||||
has_64bit,
|
has_64bit,
|
||||||
&[
|
&[
|
||||||
frame_list.physical_address() as u64,
|
frame_list.physical_address() as u64,
|
||||||
async_qh.physical_address() as u64,
|
async_qh.physical_address() as u64,
|
||||||
|
periodic_qh.physical_address() as u64,
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -201,6 +210,7 @@ impl EhciController {
|
|||||||
n_ports,
|
n_ports,
|
||||||
frame_list,
|
frame_list,
|
||||||
async_qh,
|
async_qh,
|
||||||
|
periodic_qh,
|
||||||
dma_segment,
|
dma_segment,
|
||||||
has_64bit,
|
has_64bit,
|
||||||
next_address: 1,
|
next_address: 1,
|
||||||
@@ -336,12 +346,12 @@ impl EhciController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_async_qh(&mut self, device_address: u8, max_packet_size: u16, first_td_phys: u32) {
|
fn prepare_async_qh(&mut self, device_address: u8, endpoint: u8, max_packet_size: u16, first_td_phys: u32) {
|
||||||
let qh_ptr = self.async_qh.as_mut_ptr() as *mut QueueHead;
|
let qh_ptr = self.async_qh.as_mut_ptr() as *mut QueueHead;
|
||||||
unsafe {
|
unsafe {
|
||||||
let qh = &mut *qh_ptr;
|
let qh = &mut *qh_ptr;
|
||||||
qh.horiz_link = qh_link_pointer(self.async_qh.physical_address() as u64);
|
qh.horiz_link = qh_link_pointer(self.async_qh.physical_address() as u64);
|
||||||
qh.caps[0] = qh_endpoint_characteristics(device_address, 0, max_packet_size, true);
|
qh.caps[0] = qh_endpoint_characteristics(device_address, endpoint, max_packet_size, true);
|
||||||
qh.caps[1] = qh_endpoint_capabilities();
|
qh.caps[1] = qh_endpoint_capabilities();
|
||||||
qh.current_qtd = 0;
|
qh.current_qtd = 0;
|
||||||
qh.overlay[0] = first_td_phys & !0x1F;
|
qh.overlay[0] = first_td_phys & !0x1F;
|
||||||
@@ -371,6 +381,30 @@ impl EhciController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn arm_periodic_qh(&mut self, device_address: u8, max_packet: u16, endpoint: u8, td_phys: u64) {
|
||||||
|
let fl = self.frame_list.as_mut_ptr() as *mut u32;
|
||||||
|
let qh_val = qh_endpoint_characteristics(device_address, endpoint, max_packet, false);
|
||||||
|
unsafe {
|
||||||
|
let qh_ptr = self.periodic_qh.as_mut_ptr() as *mut QueueHead;
|
||||||
|
let qh = &mut *qh_ptr;
|
||||||
|
qh.horiz_link = qh_link_pointer(self.periodic_qh.physical_address() as u64);
|
||||||
|
qh.caps[0] = qh_val;
|
||||||
|
qh.caps[1] = qh_endpoint_capabilities();
|
||||||
|
qh.current_qtd = 0;
|
||||||
|
qh.overlay[0] = (td_phys as u32) & !0x1F;
|
||||||
|
qh.overlay[1] = TD_TERMINATE;
|
||||||
|
qh.overlay[2] = 0; qh.overlay[3] = 0;
|
||||||
|
qh.overlay[4] = 0; qh.overlay[5] = 0;
|
||||||
|
qh.overlay[6] = 0; qh.overlay[7] = 0;
|
||||||
|
*fl = (self.periodic_qh.physical_address() as u32) | 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disarm_periodic_qh(&mut self) {
|
||||||
|
let fl = self.frame_list.as_mut_ptr() as *mut u32;
|
||||||
|
unsafe { *fl = TD_TERMINATE; }
|
||||||
|
}
|
||||||
|
|
||||||
fn ensure_controller_running(&mut self) {
|
fn ensure_controller_running(&mut self) {
|
||||||
let status = self.read_op32(USBSTS);
|
let status = self.read_op32(USBSTS);
|
||||||
let command = self.read_op32(USBCMD);
|
let command = self.read_op32(USBCMD);
|
||||||
@@ -775,7 +809,7 @@ impl EhciController {
|
|||||||
)
|
)
|
||||||
.ok_or(UsbError::IoError)?;
|
.ok_or(UsbError::IoError)?;
|
||||||
|
|
||||||
self.prepare_async_qh(device_address, max_packet_size, first_td_phys);
|
self.prepare_async_qh(device_address, 0, max_packet_size, first_td_phys);
|
||||||
self.clear_interrupt_status();
|
self.clear_interrupt_status();
|
||||||
fence(Ordering::SeqCst);
|
fence(Ordering::SeqCst);
|
||||||
|
|
||||||
@@ -958,21 +992,72 @@ impl UsbHostController for EhciController {
|
|||||||
|
|
||||||
fn bulk_transfer(
|
fn bulk_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
_device_address: u8,
|
device_address: u8,
|
||||||
_endpoint: u8,
|
endpoint: u8,
|
||||||
_data: &mut [u8],
|
data: &mut [u8],
|
||||||
_direction: TransferDirection,
|
direction: TransferDirection,
|
||||||
) -> Result<usize, UsbError> {
|
) -> Result<usize, UsbError> {
|
||||||
Err(UsbError::Unsupported)
|
let port = self.find_port_by_address(device_address).ok_or(UsbError::NoDevice)?;
|
||||||
|
let max_packet = self.ports[port].device.as_ref().map(|d| d.max_packet_size0).unwrap_or(512);
|
||||||
|
self.ensure_controller_running();
|
||||||
|
if data.len() > 0x7FFF { return Err(UsbError::Unsupported); }
|
||||||
|
let mut data_dma = if data.is_empty() { None }
|
||||||
|
else { Some(DmaBuffer::allocate(data.len(), 4096).map_err(|_| UsbError::IoError)?) };
|
||||||
|
if let Some(buf) = data_dma.as_mut() {
|
||||||
|
self.ensure_dma_segment_matches(buf.physical_address() as u64, "bulk_data")?;
|
||||||
|
if direction != TransferDirection::In { dma_write_bytes(buf, data); }
|
||||||
|
}
|
||||||
|
let mut td_dma = DmaBuffer::allocate(2 * size_of::<TransferDescriptor>(), 32).map_err(|_| UsbError::IoError)?;
|
||||||
|
self.ensure_dma_segment_matches(td_dma.physical_address() as u64, "bulk_td")?;
|
||||||
|
let tds = unsafe { std::slice::from_raw_parts_mut(td_dma.as_mut_ptr() as *mut TransferDescriptor, 2) };
|
||||||
|
let dma_phys = data_dma.as_ref().map(|b| b.physical_address() as u64).unwrap_or(0);
|
||||||
|
let first = build_bulk_transfer(dma_phys, data.len(), direction == TransferDirection::In, tds, td_dma.physical_address() as u64);
|
||||||
|
self.prepare_async_qh(device_address, endpoint, max_packet, first);
|
||||||
|
fence(Ordering::SeqCst);
|
||||||
|
for _ in 0..CONTROL_TRANSFER_TIMEOUT_POLLS {
|
||||||
|
let t0 = read_td_token(&td_dma, 0); let t1 = read_td_token(&td_dma, 1);
|
||||||
|
if (t0 | t1) & (TD_HALTED | TD_BUFERR | TD_BABBLE | TD_XACTERR | TD_MISSED) != 0 { self.disarm_async_qh(); return Err(map_td_error(t0 | t1)); }
|
||||||
|
if (t0 | t1) & TD_ACTIVE == 0 {
|
||||||
|
let rem = ((t0 & TD_TOTAL_BYTES_MASK) >> TD_TOTAL_BYTES_SHIFT) as usize;
|
||||||
|
let actual = data.len().saturating_sub(rem);
|
||||||
|
if direction == TransferDirection::In && actual > 0 { if let Some(buf) = data_dma.as_ref() { dma_read_bytes(buf, &mut data[..actual]); } }
|
||||||
|
self.disarm_async_qh(); return Ok(actual);
|
||||||
|
}
|
||||||
|
thread::sleep(WAIT_STEP);
|
||||||
|
}
|
||||||
|
self.disarm_async_qh(); Err(UsbError::Timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interrupt_transfer(
|
fn interrupt_transfer(
|
||||||
&mut self,
|
&mut self,
|
||||||
_device_address: u8,
|
device_address: u8,
|
||||||
_endpoint: u8,
|
endpoint: u8,
|
||||||
_data: &mut [u8],
|
data: &mut [u8],
|
||||||
) -> Result<usize, UsbError> {
|
) -> Result<usize, UsbError> {
|
||||||
Err(UsbError::Unsupported)
|
let port = self.find_port_by_address(device_address).ok_or(UsbError::NoDevice)?;
|
||||||
|
let max_packet = self.ports[port].device.as_ref().map(|d| d.max_packet_size0).unwrap_or(64);
|
||||||
|
self.ensure_controller_running();
|
||||||
|
if data.len() > 64 { return Err(UsbError::Unsupported); }
|
||||||
|
let data_dma = DmaBuffer::allocate(data.len(), 64).map_err(|_| UsbError::IoError)?;
|
||||||
|
self.ensure_dma_segment_matches(data_dma.physical_address() as u64, "intr_data")?;
|
||||||
|
let mut td_dma = DmaBuffer::allocate(size_of::<TransferDescriptor>(), 32).map_err(|_| UsbError::IoError)?;
|
||||||
|
self.ensure_dma_segment_matches(td_dma.physical_address() as u64, "intr_td")?;
|
||||||
|
let td = unsafe { &mut *(td_dma.as_mut_ptr() as *mut TransferDescriptor) };
|
||||||
|
*td = build_intr_td(data_dma.physical_address() as u64, data.len(), td_dma.physical_address() as u64);
|
||||||
|
self.arm_periodic_qh(device_address, max_packet, endpoint, td_dma.physical_address() as u64);
|
||||||
|
fence(Ordering::SeqCst);
|
||||||
|
for _ in 0..(CONTROL_TRANSFER_TIMEOUT_POLLS / 2) {
|
||||||
|
let token = read_td_token(&td_dma, 0);
|
||||||
|
if token & (TD_HALTED | TD_BUFERR | TD_BABBLE | TD_XACTERR | TD_MISSED) != 0 { self.disarm_periodic_qh(); return Err(map_td_error(token)); }
|
||||||
|
if token & TD_ACTIVE == 0 {
|
||||||
|
let rem = ((token & TD_TOTAL_BYTES_MASK) >> TD_TOTAL_BYTES_SHIFT) as usize;
|
||||||
|
let actual = data.len().saturating_sub(rem);
|
||||||
|
if actual > 0 { dma_read_bytes(&data_dma, &mut data[..actual]); }
|
||||||
|
self.disarm_periodic_qh(); return Ok(actual);
|
||||||
|
}
|
||||||
|
thread::sleep(WAIT_STEP);
|
||||||
|
}
|
||||||
|
self.disarm_periodic_qh(); Err(UsbError::Timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_address(&mut self, device_address: u8) -> bool {
|
fn set_address(&mut self, device_address: u8) -> bool {
|
||||||
@@ -1043,6 +1128,7 @@ impl EhciScheme {
|
|||||||
(Some("status"), None) => Ok(HandleKind::Status { port }),
|
(Some("status"), None) => Ok(HandleKind::Status { port }),
|
||||||
(Some("descriptor"), None) => Ok(HandleKind::Descriptor { port }),
|
(Some("descriptor"), None) => Ok(HandleKind::Descriptor { port }),
|
||||||
(Some("control"), None) => Ok(HandleKind::Control { port }),
|
(Some("control"), None) => Ok(HandleKind::Control { port }),
|
||||||
|
(Some("config"), None) => Ok(HandleKind::Config { port }),
|
||||||
_ => Err(SysError::new(ENOENT)),
|
_ => Err(SysError::new(ENOENT)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1052,6 +1138,7 @@ impl EhciScheme {
|
|||||||
"status" => Ok(HandleKind::Status { port }),
|
"status" => Ok(HandleKind::Status { port }),
|
||||||
"descriptor" => Ok(HandleKind::Descriptor { port }),
|
"descriptor" => Ok(HandleKind::Descriptor { port }),
|
||||||
"control" => Ok(HandleKind::Control { port }),
|
"control" => Ok(HandleKind::Control { port }),
|
||||||
|
"config" => Ok(HandleKind::Config { port }),
|
||||||
_ => Err(SysError::new(ENOENT)),
|
_ => Err(SysError::new(ENOENT)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1065,6 +1152,18 @@ impl EhciScheme {
|
|||||||
listing.into_bytes()
|
listing.into_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn config_bytes(&self, port: usize) -> SysResult<Vec<u8>> {
|
||||||
|
let ctrl = self.controller.lock().map_err(|_| SysError::new(syscall::EIO))?;
|
||||||
|
let record = ctrl.ports.get(port).ok_or(SysError::new(syscall::ENOENT))?;
|
||||||
|
if let Some(ref dev) = record.device {
|
||||||
|
Ok(format!("class={:02x} subclass={:02x} vendor={:04x} device={:04x}\n",
|
||||||
|
dev.device_class, dev.device_subclass, dev.vendor_id, dev.product_id,
|
||||||
|
).into_bytes())
|
||||||
|
} else {
|
||||||
|
Ok(b"no device\n".to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn status_bytes(&self, port: usize) -> SysResult<Vec<u8>> {
|
fn status_bytes(&self, port: usize) -> SysResult<Vec<u8>> {
|
||||||
let controller = lock_controller(&self.controller);
|
let controller = lock_controller(&self.controller);
|
||||||
let Some(record) = controller.port_record(port) else {
|
let Some(record) = controller.port_record(port) else {
|
||||||
@@ -1158,10 +1257,11 @@ impl EhciScheme {
|
|||||||
|
|
||||||
let handle = self.handle(id)?;
|
let handle = self.handle(id)?;
|
||||||
match &handle.kind {
|
match &handle.kind {
|
||||||
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ncontrol\n".to_vec()),
|
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ncontrol\nconfig\n".to_vec()),
|
||||||
HandleKind::Status { port } => self.status_bytes(*port),
|
HandleKind::Status { port } => self.status_bytes(*port),
|
||||||
HandleKind::Descriptor { port } => self.descriptor_bytes(*port),
|
HandleKind::Descriptor { port } => self.descriptor_bytes(*port),
|
||||||
HandleKind::Control { .. } => Ok(handle.response.clone()),
|
HandleKind::Control { .. } => Ok(handle.response.clone()),
|
||||||
|
HandleKind::Config { port } => self.config_bytes(*port),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1178,6 +1278,7 @@ impl EhciScheme {
|
|||||||
format!("{SCHEME_NAME}:/port{}/descriptor", port + 1)
|
format!("{SCHEME_NAME}:/port{}/descriptor", port + 1)
|
||||||
}
|
}
|
||||||
HandleKind::Control { port } => format!("{SCHEME_NAME}:/port{}/control", port + 1),
|
HandleKind::Control { port } => format!("{SCHEME_NAME}:/port{}/control", port + 1),
|
||||||
|
HandleKind::Config { port } => format!("{SCHEME_NAME}:/port{}/config", port + 1),
|
||||||
};
|
};
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
@@ -1277,7 +1378,7 @@ impl SchemeSync for EhciScheme {
|
|||||||
match self.handle(id)?.kind {
|
match self.handle(id)?.kind {
|
||||||
HandleKind::PortDir { .. } => MODE_DIR | 0o755,
|
HandleKind::PortDir { .. } => MODE_DIR | 0o755,
|
||||||
HandleKind::Status { .. } | HandleKind::Descriptor { .. } => MODE_FILE | 0o444,
|
HandleKind::Status { .. } | HandleKind::Descriptor { .. } => MODE_FILE | 0o444,
|
||||||
HandleKind::Control { .. } => MODE_FILE | 0o644,
|
HandleKind::Control { .. } | HandleKind::Config { .. } => MODE_FILE | 0o644,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -328,3 +328,19 @@ pub fn build_control_transfer(
|
|||||||
|
|
||||||
Some(setup_td_phys)
|
Some(setup_td_phys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_bulk_transfer(data_phys: u64, data_len: usize, dir_in: bool, tds: &mut [TransferDescriptor], td_phys: u64) -> u32 {
|
||||||
|
let pid = if dir_in { TD_PID_IN } else { TD_PID_OUT };
|
||||||
|
tds[0] = TransferDescriptor { next_qtd: 0, alt_qtd: 0, token: TD_ACTIVE | pid | ((data_len as u32 & 0x7FFF) << 16), buffers: [0; 5] };
|
||||||
|
tds[0].buffers[0] = data_phys as u32;
|
||||||
|
let next = (td_phys as u32 + size_of::<TransferDescriptor>() as u32) & !0x1F;
|
||||||
|
tds[0].next_qtd = next;
|
||||||
|
tds[1] = TransferDescriptor { next_qtd: TD_TERMINATE, alt_qtd: TD_TERMINATE, token: TD_ACTIVE | (1 << 15) | 0x8000_0000 /* IOC */, buffers: [0; 5] };
|
||||||
|
td_phys as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_intr_td(data_phys: u64, data_len: usize, _td_phys: u64) -> TransferDescriptor {
|
||||||
|
let mut td = TransferDescriptor { next_qtd: TD_TERMINATE, alt_qtd: TD_TERMINATE, token: TD_ACTIVE | TD_PID_IN | ((data_len as u32 & 0x7FFF) << 16), buffers: [0; 5] };
|
||||||
|
td.buffers[0] = data_phys as u32;
|
||||||
|
td
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ mod registers;
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process;
|
use std::process;
|
||||||
use log::{info, error, LevelFilter};
|
use std::fs;
|
||||||
|
use log::{info, error, warn, LevelFilter};
|
||||||
|
use registers::*;
|
||||||
|
|
||||||
struct StderrLogger;
|
struct StderrLogger;
|
||||||
impl log::Log for StderrLogger {
|
impl log::Log for StderrLogger {
|
||||||
@@ -14,10 +16,20 @@ impl log::Log for StderrLogger {
|
|||||||
fn main() {
|
fn main() {
|
||||||
log::set_logger(&StderrLogger).ok();
|
log::set_logger(&StderrLogger).ok();
|
||||||
log::set_max_level(LevelFilter::Info);
|
log::set_max_level(LevelFilter::Info);
|
||||||
let channel_fd: usize = match env::var("PCID_CLIENT_CHANNEL") {
|
let _fd = match env::var("PCID_CLIENT_CHANNEL") {
|
||||||
Ok(s) => match s.parse() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
Ok(s) => match s.parse::<usize>() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
||||||
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
||||||
};
|
};
|
||||||
info!("OHCI USB 1.1 controller (PCI fd: {})", channel_fd);
|
let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default();
|
||||||
info!("ohcid: ready");
|
info!("OHCI USB 1.1 at {}", device_path);
|
||||||
|
let config_path = format!("{}/config", device_path);
|
||||||
|
match fs::read(&config_path) {
|
||||||
|
Ok(data) if data.len() >= 0x14 => {
|
||||||
|
let bar0 = u32::from_le_bytes([data[0x10], data[0x11], data[0x12], data[0x13]]);
|
||||||
|
info!("OHCI MMIO base: 0x{:08X} (BAR0)", bar0 & 0xFFFFFFF0);
|
||||||
|
info!("ohcid: MMIO detected, ready for port enumeration");
|
||||||
|
}
|
||||||
|
_ => warn!("cannot read PCI config"),
|
||||||
|
}
|
||||||
|
loop { std::thread::sleep(std::time::Duration::from_secs(10)); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ mod registers;
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process;
|
use std::process;
|
||||||
use log::{info, error, LevelFilter};
|
use std::fs;
|
||||||
|
use log::{info, error, warn, LevelFilter};
|
||||||
|
use registers::*;
|
||||||
|
|
||||||
struct StderrLogger;
|
struct StderrLogger;
|
||||||
impl log::Log for StderrLogger {
|
impl log::Log for StderrLogger {
|
||||||
@@ -14,10 +16,20 @@ impl log::Log for StderrLogger {
|
|||||||
fn main() {
|
fn main() {
|
||||||
log::set_logger(&StderrLogger).ok();
|
log::set_logger(&StderrLogger).ok();
|
||||||
log::set_max_level(LevelFilter::Info);
|
log::set_max_level(LevelFilter::Info);
|
||||||
let channel_fd: usize = match env::var("PCID_CLIENT_CHANNEL") {
|
let _fd = match env::var("PCID_CLIENT_CHANNEL") {
|
||||||
Ok(s) => match s.parse() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
Ok(s) => match s.parse::<usize>() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
|
||||||
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
|
||||||
};
|
};
|
||||||
info!("UHCI USB 1.1 controller (PCI fd: {})", channel_fd);
|
let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default();
|
||||||
info!("uhcid: ready");
|
info!("UHCI USB 1.1 at {}", device_path);
|
||||||
|
let config_path = format!("{}/config", device_path);
|
||||||
|
match fs::read(&config_path) {
|
||||||
|
Ok(data) if data.len() >= 0x14 => {
|
||||||
|
let bar4 = u32::from_le_bytes([data[0x20], data[0x21], data[0x22], data[0x23]]);
|
||||||
|
info!("UHCI I/O base: 0x{:04X} (BAR4)", bar4 & 0xFFE0);
|
||||||
|
info!("uhcid: I/O port detected, ready for port enumeration");
|
||||||
|
}
|
||||||
|
_ => warn!("cannot read PCI config"),
|
||||||
|
}
|
||||||
|
loop { std::thread::sleep(std::time::Duration::from_secs(10)); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ add_subdirectory(src)
|
|||||||
|
|
||||||
# Enable unit testing
|
# Enable unit testing
|
||||||
if (BUILD_TESTING)
|
if (BUILD_TESTING)
|
||||||
# add_subdirectory(autotests)
|
## add_subdirectory(autotests)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
# shall we use DBus?
|
# shall we use DBus?
|
||||||
# enabled per default on Linux & BSD systems
|
# enabled per default on Linux & BSD systems
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||||
|
|
||||||
|
|||||||
@@ -36,12 +36,18 @@ sed -i 's/^ add_subdirectory(localedata-qml)/# add_subdirectory(localedata
|
|||||||
rm -f CMakeCache.txt
|
rm -f CMakeCache.txt
|
||||||
rm -rf CMakeFiles
|
rm -rf CMakeFiles
|
||||||
|
|
||||||
|
export LDFLAGS+=" -liconv"
|
||||||
|
|
||||||
cmake "${COOKBOOK_SOURCE}" \
|
cmake "${COOKBOOK_SOURCE}" \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||||
|
-DCMAKE_C_STANDARD_LIBRARIES="-liconv" \
|
||||||
|
-DCMAKE_CXX_STANDARD_LIBRARIES="-liconv" \
|
||||||
|
-DCMAKE_SHARED_LINKER_FLAGS="-liconv" \
|
||||||
|
-DCMAKE_EXE_LINKER_FLAGS="-liconv" \
|
||||||
-DBUILD_TESTING=OFF \
|
-DBUILD_TESTING=OFF \
|
||||||
-DBUILD_QCH=OFF \
|
-DBUILD_QCH=OFF \
|
||||||
-DBUILD_WITH_QML=OFF \
|
-DBUILD_WITH_QML=OFF \
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ set_package_properties(Qt6Qml PROPERTIES
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (TARGET Qt6::Qml)
|
if (TARGET Qt6::Qml)
|
||||||
### include(ECMQmlModule)
|
#### include(ECMQmlModule)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
if (TARGET Qt6::Qml)
|
if (TARGET Qt6::Qml)
|
||||||
## add_subdirectory(qml)
|
### add_subdirectory(qml)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ecm_qt_install_logging_categories(
|
ecm_qt_install_logging_categories(
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||||
|
|
||||||
|
|||||||
@@ -41,12 +41,16 @@ sed -i '/^find_package(Wayland 1.15/a find_package(Qt6WaylandClientPrivate REQUI
|
|||||||
rm -f CMakeCache.txt
|
rm -f CMakeCache.txt
|
||||||
rm -rf CMakeFiles
|
rm -rf CMakeFiles
|
||||||
|
|
||||||
|
export LDFLAGS+=" -lffi"
|
||||||
|
|
||||||
cmake "${COOKBOOK_SOURCE}" \
|
cmake "${COOKBOOK_SOURCE}" \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||||
|
-DCMAKE_SHARED_LINKER_FLAGS="-lffi" \
|
||||||
|
-DCMAKE_EXE_LINKER_FLAGS="-lffi" \
|
||||||
-DWaylandScanner_EXECUTABLE=/usr/bin/wayland-scanner \
|
-DWaylandScanner_EXECUTABLE=/usr/bin/wayland-scanner \
|
||||||
-DWaylandProtocols_DATADIR="${COOKBOOK_SYSROOT}/share/wayland-protocols" \
|
-DWaylandProtocols_DATADIR="${COOKBOOK_SYSROOT}/share/wayland-protocols" \
|
||||||
-DPLASMA_WAYLAND_PROTOCOLS_DIR="${COOKBOOK_SYSROOT}/share/plasma-wayland-protocols" \
|
-DPLASMA_WAYLAND_PROTOCOLS_DIR="${COOKBOOK_SYSROOT}/share/plasma-wayland-protocols" \
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ find_package(Qt6WaylandClientPrivate REQUIRED)
|
|||||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
set_package_properties(Wayland PROPERTIES
|
set_package_properties(Wayland PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#clang-format
|
||||||
|
7dd564adbcfc6151473c97a3a7737cfaef5694b7
|
||||||
|
#clang-tidy
|
||||||
|
4c41ac844a27089083578eea1b947d7bbbd517c7
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Ignore the following files
|
||||||
|
*~
|
||||||
|
*.[oa]
|
||||||
|
*.diff
|
||||||
|
*.kate-swp
|
||||||
|
*.kdev4
|
||||||
|
.kdev_include_paths
|
||||||
|
*.kdevelop.pcs
|
||||||
|
*.moc
|
||||||
|
*.moc.cpp
|
||||||
|
*.orig
|
||||||
|
*.user
|
||||||
|
.*.swp
|
||||||
|
.swp.*
|
||||||
|
Doxyfile
|
||||||
|
Makefile
|
||||||
|
avail
|
||||||
|
random_seed
|
||||||
|
/build*/
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
*.unc-backup*
|
||||||
|
.cmake/
|
||||||
|
/.clang-format
|
||||||
|
/compile_commands.json
|
||||||
|
.clangd
|
||||||
|
.idea
|
||||||
|
/cmake-build*
|
||||||
|
.cache
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
include:
|
||||||
|
- project: sysadmin/ci-utilities
|
||||||
|
file:
|
||||||
|
- /gitlab-templates/linux-qt6.yml
|
||||||
|
- /gitlab-templates/linux-qt6-static.yml
|
||||||
|
- /gitlab-templates/android-qt6.yml
|
||||||
|
- /gitlab-templates/freebsd-qt6.yml
|
||||||
|
- /gitlab-templates/windows-qt6.yml
|
||||||
|
- /gitlab-templates/alpine-qt6.yml
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Dependencies:
|
||||||
|
- 'on': ['Linux', 'FreeBSD', 'Windows', 'Android']
|
||||||
|
'require':
|
||||||
|
'frameworks/extra-cmake-modules': '@same'
|
||||||
|
'third-party/zxing-cpp': '@latest'
|
||||||
|
|
||||||
|
Options:
|
||||||
|
test-before-installing: True
|
||||||
|
require-passing-tests-on: [ 'Linux', 'FreeBSD', 'Windows' ]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: KF5::Prison
|
||||||
|
Source: https://invent.kde.org/frameworks/prison
|
||||||
|
|
||||||
|
Files: autotests/aztec/encoding/*.png autotests/aztec/rendering/*.png autotests/code128/*.png autotests/datamatrix/*.png autotests/qr/*.png
|
||||||
|
Copyright: none
|
||||||
|
License: CC0-1.0
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(KF_VERSION "6.10.0") # handled by release scripts
|
||||||
|
project(prison VERSION ${KF_VERSION})
|
||||||
|
|
||||||
|
# ECM setup
|
||||||
|
include(FeatureSummary)
|
||||||
|
find_package(ECM 6.10.0 NO_MODULE)
|
||||||
|
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
||||||
|
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${ECM_MODULE_PATH}")
|
||||||
|
|
||||||
|
include(KDEInstallDirs)
|
||||||
|
include(KDECMakeSettings)
|
||||||
|
include(KDEGitCommitHooks)
|
||||||
|
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
|
||||||
|
|
||||||
|
include(GenerateExportHeader)
|
||||||
|
include(ECMGenerateHeaders)
|
||||||
|
include(ECMGenerateExportHeader)
|
||||||
|
include(ECMAddQch)
|
||||||
|
include(ECMAddTests)
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
include(ECMSetupVersion)
|
||||||
|
include(ECMQtDeclareLoggingCategory)
|
||||||
|
include(ECMDeprecationSettings)
|
||||||
|
|
||||||
|
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||||
|
|
||||||
|
option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
|
||||||
|
add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
|
||||||
|
|
||||||
|
set(REQUIRED_QT_VERSION 6.6.0)
|
||||||
|
|
||||||
|
option(WITH_QUICK "Build QtQuick components" ON)
|
||||||
|
option(WITH_MULTIMEDIA "Build scanner support" ON)
|
||||||
|
|
||||||
|
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Core Gui)
|
||||||
|
|
||||||
|
if (WITH_QUICK)
|
||||||
|
include(ECMQmlModule)
|
||||||
|
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Quick)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WITH_MULTIMEDIA)
|
||||||
|
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Multimedia)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(QRencode)
|
||||||
|
set_package_properties(QRencode PROPERTIES
|
||||||
|
PURPOSE "Required for generation of QRCode barcodes."
|
||||||
|
TYPE REQUIRED)
|
||||||
|
find_package(Dmtx)
|
||||||
|
set_package_properties(Dmtx PROPERTIES
|
||||||
|
PURPOSE "Required for generation of Data Matrix barcodes."
|
||||||
|
TYPE RECOMMENDED)
|
||||||
|
find_package(ZXing 2.0)
|
||||||
|
if (NOT TARGET ZXing::ZXing)
|
||||||
|
find_package(ZXing 1.2.0)
|
||||||
|
endif()
|
||||||
|
set_package_properties(ZXing PROPERTIES
|
||||||
|
PURPOSE "Required for generation of PDF417 barcodes and for scanning of barcodes from live video feed."
|
||||||
|
TYPE RECOMMENDED)
|
||||||
|
|
||||||
|
ecm_setup_version(PROJECT VARIABLE_PREFIX PRISON
|
||||||
|
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/prison_version.h"
|
||||||
|
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF6PrisonConfigVersion.cmake"
|
||||||
|
SOVERSION 6
|
||||||
|
)
|
||||||
|
ecm_set_disabled_deprecation_versions(
|
||||||
|
QT 6.8
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test Concurrent Widgets)
|
||||||
|
add_subdirectory(autotests)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF6Prison")
|
||||||
|
|
||||||
|
if (BUILD_QCH)
|
||||||
|
ecm_install_qch_export(
|
||||||
|
TARGETS KF6Prison_QCH
|
||||||
|
FILE KF6PrisonQchTargets.cmake
|
||||||
|
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||||
|
COMPONENT Devel
|
||||||
|
)
|
||||||
|
set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF6PrisonQchTargets.cmake\")")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_package_config_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/KF6PrisonConfig.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/KF6PrisonConfig.cmake"
|
||||||
|
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/KF6PrisonConfig.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/KF6PrisonConfigVersion.cmake"
|
||||||
|
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||||
|
COMPONENT Devel
|
||||||
|
)
|
||||||
|
|
||||||
|
install(EXPORT KF6PrisonTargets
|
||||||
|
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||||
|
FILE KF6PrisonTargets.cmake
|
||||||
|
NAMESPACE KF6::
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/prison_version.h
|
||||||
|
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/Prison
|
||||||
|
COMPONENT Devel
|
||||||
|
)
|
||||||
|
|
||||||
|
include(ECMFeatureSummary)
|
||||||
|
ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
||||||
|
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
find_dependency(Qt6Gui @REQUIRED_QT_VERSION@)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/KF6PrisonTargets.cmake")
|
||||||
|
@PACKAGE_INCLUDE_QCHTARGETS@
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
Copyright (c) <year> <owner>. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
MIT License Copyright (c) <year> <copyright holders>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||||
|
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Prison
|
||||||
|
|
||||||
|
A barcode abstraction layer providing uniform access to generation of barcodes with data.
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Prison has a Prison::AbstractBarcode, which is the base class for the actual
|
||||||
|
barcode generators. Prison currently implements barcode generators for the following formats:
|
||||||
|
|
||||||
|
- QRCode
|
||||||
|
- Data Matrix
|
||||||
|
- Aztec
|
||||||
|
- Code39
|
||||||
|
- Code93
|
||||||
|
- [Code128](https://en.wikipedia.org/wiki/Code_128)
|
||||||
|
- [PDF417](https://en.wikipedia.org/wiki/PDF417) (ISO/IEC 15438)
|
||||||
|
- [EAN13](https://en.wikipedia.org/wiki/International_Article_Number)
|
||||||
|
|
||||||
|
Prison currently ships the org.kde.prison.Barcode QML element that can be used to render barcodes in QML code.
|
||||||
|
|
||||||
|
## Supported Barcode types
|
||||||
|
|
||||||
|
There are basically two types of barcodes:
|
||||||
|
* barcodes that carry the data
|
||||||
|
* barcodes that carry a lookup number, and require a specific server to
|
||||||
|
look up the actual data.
|
||||||
|
|
||||||
|
Prison isn't as such designed for the latter, it will probably work, but
|
||||||
|
patches implementing barcode support for such barcodes will not be accepted.
|
||||||
|
An example is [EZCode](https://en.wikipedia.org/wiki/EZcode).
|
||||||
|
|
||||||
|
Prison is currently using [libdmtx](https://github.com/dmtx/libdmtx) for generation of
|
||||||
|
[DataMatrix](https://en.wikipedia.org/wiki/Datamatrix) barcodes,
|
||||||
|
[libqrencode](https://fukuchi.org/works/qrencode/) for generation
|
||||||
|
of [QRCode](https://en.wikipedia.org/wiki/QR_Code) barcodes and
|
||||||
|
[ZXing](https://github.com/nu-book/zxing-cpp) for generating
|
||||||
|
[EAN13 and PDF417](https://en.wikipedia.org/wiki/PDF417) barcodes.
|
||||||
|
|
||||||
|
# Prison Scanner
|
||||||
|
|
||||||
|
A barcode scanner consuming a live video feed from QtMultimedia.
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Prison's barcode scanner can be used from C++ code using the Prison::VideoScanner
|
||||||
|
class, or from QML using the org.kde.prison.scanner.VideoScanner QML element.
|
||||||
|
|
||||||
|
There are standalone QML examples in tests/scanner-qt(5|6).qml demonstrating
|
||||||
|
the QML API.
|
||||||
|
|
||||||
|
## Supported barcode formats
|
||||||
|
|
||||||
|
Barcode detection is implemented using the [ZXing](https://github.com/nu-book/zxing-cpp)
|
||||||
|
library, all formats supported by ZXing can be detected.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2017-2018 Volker Krause <vkrause@kde.org>
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
set(aztecbarcodetest_srcs
|
||||||
|
aztecbarcodetest.cpp
|
||||||
|
aztec/aztec.qrc
|
||||||
|
../src/lib/aztecbarcode.cpp
|
||||||
|
../src/lib/abstractbarcode_p.cpp
|
||||||
|
../src/lib/barcodeutil.cpp
|
||||||
|
../src/lib/bitvector.cpp
|
||||||
|
../src/lib/reedsolomon.cpp
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/../src/lib/prison_debug.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_add_test(${aztecbarcodetest_srcs} TEST_NAME prison-aztecbarcodetest LINK_LIBRARIES Qt6::Test KF6::Prison)
|
||||||
|
|
||||||
|
ecm_add_test(
|
||||||
|
reedsolomontest.cpp
|
||||||
|
../src/lib/bitvector.cpp
|
||||||
|
../src/lib/reedsolomon.cpp
|
||||||
|
TEST_NAME prison-reedsolomontest
|
||||||
|
LINK_LIBRARIES Qt6::Test KF6::Prison
|
||||||
|
)
|
||||||
|
|
||||||
|
set(code128barcodetest_srcs
|
||||||
|
code128barcodetest.cpp
|
||||||
|
code128/code128.qrc
|
||||||
|
../src/lib/abstractbarcode_p.cpp
|
||||||
|
../src/lib/barcodeutil.cpp
|
||||||
|
../src/lib/code128barcode.cpp
|
||||||
|
../src/lib/bitvector.cpp
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/../src/lib/prison_debug.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_add_test(${code128barcodetest_srcs} TEST_NAME prison-code128barcodetest LINK_LIBRARIES Qt6::Test KF6::Prison)
|
||||||
|
|
||||||
|
if(TARGET Dmtx::Dmtx)
|
||||||
|
ecm_add_test(datamatrixtest.cpp datamatrix/datamatrix.qrc TEST_NAME prison-datamatrixtest LINK_LIBRARIES Qt6::Test KF6::Prison)
|
||||||
|
endif()
|
||||||
|
ecm_add_test(qrtest.cpp qr/qr.qrc TEST_NAME prison-qrtest LINK_LIBRARIES Qt6::Test KF6::Prison)
|
||||||
|
|
||||||
|
if (TARGET ZXing::ZXing)
|
||||||
|
ecm_add_test(zxingutiltest.cpp ${PROJECT_SOURCE_DIR}/src/lib/zxingutil.cpp TEST_NAME prison-zxingutiltest LINK_LIBRARIES Qt6::Test ZXing::ZXing KF6::Prison)
|
||||||
|
kde_source_files_enable_exceptions(${PROJECT_SOURCE_DIR}/src/lib/zxingutil.cpp )
|
||||||
|
ecm_add_test(imagescannertest.cpp qr/qr.qrc TEST_NAME prison-imagescannertest LINK_LIBRARIES Qt6::Test Qt6::Concurrent KF6::PrisonScanner)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
ecm_add_test(
|
||||||
|
mecardtest.cpp
|
||||||
|
TEST_NAME prison-mecardtest
|
||||||
|
LINK_LIBRARIES Qt6::Test KF6::Prison
|
||||||
|
)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: none
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
|
-->
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/aztec/">
|
||||||
|
<file>rendering/aztec-full-grid.png</file>
|
||||||
|
<file>rendering/aztec-full-data-0011.png</file>
|
||||||
|
<file>rendering/aztec-full-data-0101.png</file>
|
||||||
|
<file>rendering/aztec-full-data-1001.png</file>
|
||||||
|
<file>rendering/aztec-full-data-1010.png</file>
|
||||||
|
<file>rendering/aztec-full-data-1111.png</file>
|
||||||
|
<file>rendering/aztec-full-mode-1111.png</file>
|
||||||
|
<file>rendering/aztec-full-mode-1234.png</file>
|
||||||
|
<file>rendering/aztec-full-mode-1234-rev.png</file>
|
||||||
|
|
||||||
|
<file>rendering/aztec-compact-grid.png</file>
|
||||||
|
<file>rendering/aztec-compact-data-0011.png</file>
|
||||||
|
<file>rendering/aztec-compact-data-0101.png</file>
|
||||||
|
<file>rendering/aztec-compact-data-1001.png</file>
|
||||||
|
<file>rendering/aztec-compact-data-1010.png</file>
|
||||||
|
<file>rendering/aztec-compact-data-1111.png</file>
|
||||||
|
<file>rendering/aztec-compact-mode-1111.png</file>
|
||||||
|
<file>rendering/aztec-compact-mode-1234.png</file>
|
||||||
|
<file>rendering/aztec-compact-mode-1234-rev.png</file>
|
||||||
|
|
||||||
|
<file>encoding/aztec-complete-compact1.png</file>
|
||||||
|
<file>encoding/aztec-complete-compact3.png</file>
|
||||||
|
<file>encoding/aztec-complete-compact4.png</file>
|
||||||
|
<file>encoding/aztec-complete-full5.png</file>
|
||||||
|
<file>encoding/aztec-complete-big.png</file>
|
||||||
|
<file>encoding/aztec-binary.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
|
After Width: | Height: | Size: 230 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 246 B |
|
After Width: | Height: | Size: 315 B |
|
After Width: | Height: | Size: 369 B |
|
After Width: | Height: | Size: 434 B |
|
After Width: | Height: | Size: 160 B |
|
After Width: | Height: | Size: 219 B |
|
After Width: | Height: | Size: 308 B |
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 141 B |
|
After Width: | Height: | Size: 190 B |
|
After Width: | Height: | Size: 527 B |
|
After Width: | Height: | Size: 528 B |
|
After Width: | Height: | Size: 527 B |
|
After Width: | Height: | Size: 582 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 580 B |
|
After Width: | Height: | Size: 767 B |
|
After Width: | Height: | Size: 526 B |
|
After Width: | Height: | Size: 537 B |
|
After Width: | Height: | Size: 537 B |
@@ -0,0 +1,528 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../src/lib/aztecbarcode_p.h"
|
||||||
|
#include "../src/lib/bitvector_p.h"
|
||||||
|
|
||||||
|
#include <prison.h>
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Prison::BitVector)
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class AztecBarcodeTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testAztecEncode_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
QTest::addColumn<BitVector>("output");
|
||||||
|
|
||||||
|
QTest::newRow("empty") << QByteArray() << BitVector();
|
||||||
|
BitVector v;
|
||||||
|
v.appendMSB(12, 5);
|
||||||
|
v.appendMSB(5, 5);
|
||||||
|
v.appendMSB(6, 5);
|
||||||
|
QTest::newRow("all uppper") << QByteArray("KDE") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(28, 5);
|
||||||
|
v.appendMSB(12, 5);
|
||||||
|
v.appendMSB(5, 5);
|
||||||
|
v.appendMSB(6, 5);
|
||||||
|
QTest::newRow("all lower") << QByteArray("kde") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(12, 5);
|
||||||
|
v.appendMSB(28, 5);
|
||||||
|
v.appendMSB(5, 5);
|
||||||
|
v.appendMSB(6, 5);
|
||||||
|
QTest::newRow("upper -> lower latch") << QByteArray("Kde") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(28, 5);
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(16, 5);
|
||||||
|
v.appendMSB(16, 5);
|
||||||
|
QTest::newRow("lower -> punct latch") << QByteArray("a++") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(6, 4);
|
||||||
|
v.appendMSB(4, 4);
|
||||||
|
QTest::newRow("digit") << QByteArray("42") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(25, 5);
|
||||||
|
v.appendMSB(24, 5);
|
||||||
|
v.appendMSB(31, 5);
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(11, 4);
|
||||||
|
v.appendMSB(2, 4);
|
||||||
|
QTest::newRow("punct -> digit latch") << QByteArray(">=90") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(10, 4);
|
||||||
|
v.appendMSB(3, 4);
|
||||||
|
v.appendMSB(14, 4);
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(13, 5);
|
||||||
|
v.appendMSB(14, 5);
|
||||||
|
QTest::newRow("digit -> punct latch") << QByteArray("81()") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(11, 5);
|
||||||
|
v.appendMSB(8, 5);
|
||||||
|
QTest::newRow("mixed") << QByteArray("\n\a") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
QTest::newRow("CR LF") << QByteArray("\r\n\r\n") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(31, 5);
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
v.appendMSB(128, 8);
|
||||||
|
v.appendMSB(129, 8);
|
||||||
|
QTest::newRow("binary") << QByteArray("\x80\x81") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(31, 5);
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
v.appendMSB(255, 8);
|
||||||
|
v.appendMSB(254, 8);
|
||||||
|
v.appendMSB(28, 5);
|
||||||
|
v.appendMSB(3, 5);
|
||||||
|
QTest::newRow("binary/lower") << QByteArray(
|
||||||
|
"\xff\xfe"
|
||||||
|
"b") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(12, 5);
|
||||||
|
v.appendMSB(0, 5);
|
||||||
|
v.appendMSB(6, 5);
|
||||||
|
QTest::newRow("upper -> punct shift") << QByteArray("K!") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(9, 4);
|
||||||
|
v.appendMSB(4, 4);
|
||||||
|
v.appendMSB(15, 4);
|
||||||
|
v.appendMSB(6, 5);
|
||||||
|
v.appendMSB(5, 4);
|
||||||
|
QTest::newRow("digit -> upper shift") << QByteArray("72E3") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(25, 5);
|
||||||
|
v.appendMSB(1, 5);
|
||||||
|
v.appendMSB(26, 5);
|
||||||
|
QTest::newRow("upper space") << QByteArray("X Y") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(20, 5);
|
||||||
|
v.appendMSB(0, 5);
|
||||||
|
v.appendMSB(4, 5);
|
||||||
|
v.appendMSB(23, 5);
|
||||||
|
QTest::newRow("upper punct double char shift") << QByteArray("S, V") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(17, 5);
|
||||||
|
v.appendMSB(0, 5);
|
||||||
|
v.appendMSB(17, 5);
|
||||||
|
v.appendMSB(18, 5);
|
||||||
|
QTest::newRow("upper ambiguous punct shift") << QByteArray("P,Q") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(13, 4);
|
||||||
|
v.appendMSB(7, 4);
|
||||||
|
QTest::newRow("digit ambiguous punct latch") << QByteArray(".5") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(25, 5);
|
||||||
|
v.appendMSB(26, 5);
|
||||||
|
v.appendMSB(31, 5);
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(20, 5);
|
||||||
|
v.appendMSB(29, 5);
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
QTest::newRow("punct/mixed/upper sequence") << QByteArray(">?@A") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
v.appendMSB(3, 5);
|
||||||
|
v.appendMSB(4, 5);
|
||||||
|
v.appendMSB(29, 5); // latch to Punct via latch to Mixed, shift to Punct directly would be more efficient here
|
||||||
|
v.appendMSB(30, 5);
|
||||||
|
v.appendMSB(19, 5);
|
||||||
|
v.appendMSB(31, 5); // latch to Upper due to shift to Binary not available in Punct
|
||||||
|
v.appendMSB(31, 5);
|
||||||
|
v.appendMSB(2, 5);
|
||||||
|
v.appendMSB(0, 8);
|
||||||
|
v.appendMSB(0, 8);
|
||||||
|
QTest::newRow("upper/special -> binary") << QByteArray("ABC.\x00\x00", 6) << v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void testAztecEncode()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, input);
|
||||||
|
QFETCH(BitVector, output);
|
||||||
|
|
||||||
|
AztecBarcode code;
|
||||||
|
const auto v = code.aztecEncode(input);
|
||||||
|
if (v != output) {
|
||||||
|
qDebug() << "Actual :" << v;
|
||||||
|
qDebug() << "Expected:" << output;
|
||||||
|
}
|
||||||
|
QCOMPARE(v, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testStuffAndPad_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<BitVector>("input");
|
||||||
|
QTest::addColumn<BitVector>("output");
|
||||||
|
QTest::addColumn<int>("codeWordSize");
|
||||||
|
|
||||||
|
BitVector in;
|
||||||
|
BitVector out;
|
||||||
|
QTest::newRow("empty") << in << out << 4;
|
||||||
|
in.appendMSB(0x2, 2);
|
||||||
|
out.appendMSB(0xB, 4);
|
||||||
|
QTest::newRow("pad only") << in << out << 4;
|
||||||
|
in.clear();
|
||||||
|
out.clear();
|
||||||
|
in.appendMSB(0x3, 2);
|
||||||
|
out.appendMSB(0xE, 4);
|
||||||
|
QTest::newRow("pad only inverted") << in << out << 4;
|
||||||
|
in.clear();
|
||||||
|
out.clear();
|
||||||
|
in.appendMSB(0xe0, 8);
|
||||||
|
out.appendMSB(0xe13, 12);
|
||||||
|
QTest::newRow("stuff and pad") << in << out << 4;
|
||||||
|
in.clear();
|
||||||
|
out.clear();
|
||||||
|
in.appendMSB(0, 6);
|
||||||
|
out.appendMSB(0x11, 8);
|
||||||
|
QTest::newRow("stuff only") << in << out << 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void testStuffAndPad()
|
||||||
|
{
|
||||||
|
QFETCH(BitVector, input);
|
||||||
|
QFETCH(BitVector, output);
|
||||||
|
QFETCH(int, codeWordSize);
|
||||||
|
AztecBarcode code;
|
||||||
|
const auto res = code.bitStuffAndPad(input, codeWordSize);
|
||||||
|
QCOMPARE(res.size(), output.size());
|
||||||
|
if (res != output) {
|
||||||
|
qDebug() << "Actual :" << res;
|
||||||
|
qDebug() << "Expected:" << output;
|
||||||
|
}
|
||||||
|
QCOMPARE(res, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFullGrid()
|
||||||
|
{
|
||||||
|
AztecBarcode code;
|
||||||
|
QImage img(151, 151, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(code.m_background);
|
||||||
|
code.paintFullGrid(&img);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/rendering/aztec-full-grid.png"));
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCompactGrid()
|
||||||
|
{
|
||||||
|
AztecBarcode code;
|
||||||
|
QImage img(27, 27, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(code.m_background);
|
||||||
|
code.paintCompactGrid(&img);
|
||||||
|
img.save(QStringLiteral("aztec-compact-grid.png"));
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/rendering/aztec-compact-grid.png"));
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFullData_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<BitVector>("data");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
QTest::addColumn<int>("layer");
|
||||||
|
|
||||||
|
BitVector v;
|
||||||
|
for (int i = 0; i < 1248; ++i) {
|
||||||
|
v.appendLSB(0x9249, 16);
|
||||||
|
}
|
||||||
|
QTest::newRow("1001-31") << v << QStringLiteral("aztec-full-data-1001.png") << 32;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 1248 * 8; ++i) {
|
||||||
|
v.appendLSB(0x2, 2);
|
||||||
|
}
|
||||||
|
QTest::newRow("0101-31") << v << QStringLiteral("aztec-full-data-0101.png") << 32;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 1248; ++i) {
|
||||||
|
v.appendLSB(0xffff, 16);
|
||||||
|
}
|
||||||
|
QTest::newRow("1111-31") << v << QStringLiteral("aztec-full-data-1111.png") << 32;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 704 * 4; ++i) {
|
||||||
|
v.appendLSB(0x1, 2);
|
||||||
|
}
|
||||||
|
QTest::newRow("1010-15") << v << QStringLiteral("aztec-full-data-1010.png") << 16;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
v.appendLSB(0xCC, 8);
|
||||||
|
}
|
||||||
|
QTest::newRow("0011-0") << v << QStringLiteral("aztec-full-data-0011.png") << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFullData()
|
||||||
|
{
|
||||||
|
QFETCH(BitVector, data);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
QFETCH(int, layer);
|
||||||
|
|
||||||
|
AztecBarcode code;
|
||||||
|
QImage img(151, 151, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(code.m_background);
|
||||||
|
code.paintFullData(&img, data, layer);
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/rendering/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFullModeMessage_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<BitVector>("data");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
|
||||||
|
BitVector v;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
v.appendMSB(i + 1, 5);
|
||||||
|
}
|
||||||
|
QTest::newRow("1234") << v << QStringLiteral("aztec-full-mode-1234.png");
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
v.appendLSB(i + 1, 5);
|
||||||
|
}
|
||||||
|
QTest::newRow("1234-rev") << v << QStringLiteral("aztec-full-mode-1234-rev.png");
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
v.appendMSB(0xffff, 10);
|
||||||
|
}
|
||||||
|
QTest::newRow("1111") << v << QStringLiteral("aztec-full-mode-1111.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
void testFullModeMessage()
|
||||||
|
{
|
||||||
|
QFETCH(BitVector, data);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
|
||||||
|
AztecBarcode code;
|
||||||
|
QImage img(151, 151, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(code.m_background);
|
||||||
|
code.paintFullModeMessage(&img, data);
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/rendering/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCompactData_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<BitVector>("data");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
QTest::addColumn<int>("layer");
|
||||||
|
|
||||||
|
BitVector v;
|
||||||
|
for (int i = 0; i < 304; ++i) {
|
||||||
|
v.appendLSB(0x9249, 16);
|
||||||
|
}
|
||||||
|
QTest::newRow("1001-3") << v << QStringLiteral("aztec-compact-data-1001.png") << 4;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 608 * 4; ++i) {
|
||||||
|
v.appendLSB(0x2, 2);
|
||||||
|
}
|
||||||
|
QTest::newRow("0101-3") << v << QStringLiteral("aztec-compact-data-0101.png") << 4;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 304; ++i) {
|
||||||
|
v.appendLSB(0xffff, 16);
|
||||||
|
}
|
||||||
|
QTest::newRow("1111-3") << v << QStringLiteral("aztec-compact-data-1111.png") << 4;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 102 * 4; ++i) {
|
||||||
|
v.appendLSB(0x1, 2);
|
||||||
|
}
|
||||||
|
QTest::newRow("1010-2") << v << QStringLiteral("aztec-compact-data-1010.png") << 3;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 13; ++i) {
|
||||||
|
v.appendLSB(0xCC, 8);
|
||||||
|
}
|
||||||
|
QTest::newRow("0011-0") << v << QStringLiteral("aztec-compact-data-0011.png") << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCompactData()
|
||||||
|
{
|
||||||
|
QFETCH(BitVector, data);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
QFETCH(int, layer);
|
||||||
|
|
||||||
|
AztecBarcode code;
|
||||||
|
QImage img(27, 27, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(code.m_background);
|
||||||
|
code.paintCompactData(&img, data, layer);
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/rendering/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCompactModeMessage_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<BitVector>("data");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
|
||||||
|
BitVector v;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
v.appendMSB(i + 1, 7);
|
||||||
|
}
|
||||||
|
QTest::newRow("1234") << v << QStringLiteral("aztec-compact-mode-1234.png");
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
v.appendLSB(i + 1, 7);
|
||||||
|
}
|
||||||
|
QTest::newRow("1234-rev") << v << QStringLiteral("aztec-compact-mode-1234-rev.png");
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
v.appendMSB(0xffff, 7);
|
||||||
|
}
|
||||||
|
QTest::newRow("1111") << v << QStringLiteral("aztec-compact-mode-1111.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCompactModeMessage()
|
||||||
|
{
|
||||||
|
QFETCH(BitVector, data);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
|
||||||
|
AztecBarcode code;
|
||||||
|
QImage img(151, 151, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(code.m_background);
|
||||||
|
code.paintCompactModeMessage(&img, data);
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/rendering/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCodeGen_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
|
||||||
|
QTest::newRow("short compact") << QByteArray("KF5::Prison") << "aztec-complete-compact1.png";
|
||||||
|
QTest::newRow("compact 3 layer") << QByteArray("M1KRAUSE/VOLKER ABCDEFG TXLRIXBT 0212 309Y014E0063 100") << "aztec-complete-compact3.png";
|
||||||
|
QTest::newRow("long compact") << QByteArray("KF5::Prison - the barcode generation library of KDE Frameworks 5!") << "aztec-complete-compact4.png";
|
||||||
|
QTest::newRow("short full") << QByteArray("KF5::Prison - the MIT licensed free software barcode generation library of KDE Frameworks 5!")
|
||||||
|
<< "aztec-complete-full5.png";
|
||||||
|
QTest::newRow("long full") << QByteArray(
|
||||||
|
"Permission is hereby granted, free of charge, to any person\n"
|
||||||
|
"obtaining a copy of this software and associated documentation\n"
|
||||||
|
"files (the \"Software\"), to deal in the Software without\n"
|
||||||
|
"restriction, including without limitation the rights to use,\n"
|
||||||
|
"copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
|
||||||
|
"copies of the Software, and to permit persons to whom the\n"
|
||||||
|
"Software is furnished to do so, subject to the following\n"
|
||||||
|
"conditions:\n\n"
|
||||||
|
"The above copyright notice and this permission notice shall be\n"
|
||||||
|
"included in all copies or substantial portions of the Software.\n\n"
|
||||||
|
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n"
|
||||||
|
"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n"
|
||||||
|
"OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n"
|
||||||
|
"NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n"
|
||||||
|
"HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n"
|
||||||
|
"WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n"
|
||||||
|
"FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n"
|
||||||
|
"OTHER DEALINGS IN THE SOFTWARE.")
|
||||||
|
<< "aztec-complete-big.png";
|
||||||
|
QTest::newRow("binary") << QByteArray("KDE\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9kde", 16) << "aztec-binary.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCodeGen()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, input);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto code = Prison::Barcode::create(Prison::Aztec);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(QString::fromLatin1(input.constData(), input.size()));
|
||||||
|
const auto img = code->toImage(code->minimumSize());
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/encoding/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto code = Prison::Barcode::create(Prison::Aztec);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(input);
|
||||||
|
const auto img = code->toImage(code->minimumSize());
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/aztec/encoding/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testDimension()
|
||||||
|
{
|
||||||
|
auto barcode = Prison::Barcode::create(Prison::Aztec);
|
||||||
|
QVERIFY(barcode);
|
||||||
|
QCOMPARE(barcode->format(), Prison::Aztec);
|
||||||
|
QCOMPARE(barcode->dimensions(), Prison::Barcode::TwoDimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testSize()
|
||||||
|
{
|
||||||
|
auto barcode = Prison::Barcode::create(Prison::Aztec);
|
||||||
|
QVERIFY(barcode);
|
||||||
|
QCOMPARE(barcode->format(), Prison::Aztec);
|
||||||
|
barcode->setData(QStringLiteral("UNIT TEST"));
|
||||||
|
QCOMPARE(barcode->minimumSize(), QSize(15, 15));
|
||||||
|
QCOMPARE(barcode->preferredSize(1), QSize(60, 60));
|
||||||
|
QCOMPARE(barcode->preferredSize(2), QSize(30, 30));
|
||||||
|
QCOMPARE(barcode->toImage(barcode->preferredSize(1)).size(), QSize(60, 60));
|
||||||
|
QCOMPARE(barcode->toImage({1, 1}).isNull(), true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(AztecBarcodeTest)
|
||||||
|
|
||||||
|
#include "aztecbarcodetest.moc"
|
||||||
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 147 B |
@@ -0,0 +1,10 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: none
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
|
-->
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/code128/">
|
||||||
|
<file>code128-text.png</file>
|
||||||
|
<file>code128-binary.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../src/lib/bitvector_p.h"
|
||||||
|
#include "../src/lib/code128barcode_p.h"
|
||||||
|
|
||||||
|
#include <prison.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Prison::BitVector)
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class Code128BarcodeTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testEncode_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
QTest::addColumn<BitVector>("output");
|
||||||
|
|
||||||
|
BitVector v;
|
||||||
|
QTest::newRow("empty") << QByteArray() << v;
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1554, 11);
|
||||||
|
v.appendMSB(1062, 11);
|
||||||
|
v.appendMSB(1424, 11);
|
||||||
|
v.appendMSB(1220, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("all lower") << QByteArray("kde") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1422, 11);
|
||||||
|
v.appendMSB(1416, 11);
|
||||||
|
v.appendMSB(1128, 11);
|
||||||
|
v.appendMSB(1764, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("all uppper") << QByteArray("KDE") << v;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1614, 11);
|
||||||
|
v.appendMSB(1764, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("1 digit") << QByteArray("4") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1692, 11);
|
||||||
|
v.appendMSB(1464, 11);
|
||||||
|
v.appendMSB(1134, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("2 digits") << QByteArray("42") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1614, 11);
|
||||||
|
v.appendMSB(1650, 11);
|
||||||
|
v.appendMSB(1650, 11);
|
||||||
|
v.appendMSB(1124, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("3 digits") << QByteArray("422") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1692, 11);
|
||||||
|
v.appendMSB(1464, 11);
|
||||||
|
v.appendMSB(1902, 11);
|
||||||
|
v.appendMSB(1782, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("4 digits") << QByteArray("4223") << v;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1814, 11);
|
||||||
|
v.appendMSB(1260, 11);
|
||||||
|
v.appendMSB(1260, 11);
|
||||||
|
v.appendMSB(1896, 11);
|
||||||
|
v.appendMSB(1814, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("mixed") << QByteArray("X00Y") << v;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1668, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("null") << QByteArray("\0", 1) << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1668, 11);
|
||||||
|
v.appendMSB(1422, 11);
|
||||||
|
v.appendMSB(1416, 11);
|
||||||
|
v.appendMSB(1128, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1412, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("Code A only") << QByteArray("KDE\0", 4) << v;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1668, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1518, 11);
|
||||||
|
v.appendMSB(1554, 11);
|
||||||
|
v.appendMSB(1062, 11);
|
||||||
|
v.appendMSB(1424, 11);
|
||||||
|
v.appendMSB(1616, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("Start A -> Latch B") << QByteArray("\0kde", 4) << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1554, 11);
|
||||||
|
v.appendMSB(1062, 11);
|
||||||
|
v.appendMSB(1886, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1602, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("Start B -> Latch A") << QByteArray("kd\0\0", 4) << v;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1668, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1954, 11);
|
||||||
|
v.appendMSB(1118, 11);
|
||||||
|
v.appendMSB(1590, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1328, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("Start A -> Shift B") << QByteArray("\0~@\0", 4) << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1974, 11);
|
||||||
|
v.appendMSB(1954, 11);
|
||||||
|
v.appendMSB(1292, 11);
|
||||||
|
v.appendMSB(1310, 11);
|
||||||
|
v.appendMSB(1844, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("Start B -> Shift A") << QByteArray("{\0}", 3) << v;
|
||||||
|
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1692, 11);
|
||||||
|
v.appendMSB(1436, 11);
|
||||||
|
v.appendMSB(1112, 11);
|
||||||
|
v.appendMSB(1518, 11);
|
||||||
|
v.appendMSB(1304, 11);
|
||||||
|
v.appendMSB(1112, 11);
|
||||||
|
v.appendMSB(1158, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("Start C -> Latch B") << QByteArray("1234AB") << v;
|
||||||
|
v.clear();
|
||||||
|
v.appendMSB(1680, 11);
|
||||||
|
v.appendMSB(1304, 11);
|
||||||
|
v.appendMSB(1112, 11);
|
||||||
|
v.appendMSB(1502, 11);
|
||||||
|
v.appendMSB(1436, 11);
|
||||||
|
v.appendMSB(1112, 11);
|
||||||
|
v.appendMSB(1966, 11);
|
||||||
|
v.appendMSB(6379, 13);
|
||||||
|
QTest::newRow("Start B -> Latch C") << QByteArray("AB1234") << v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void testEncode()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, input);
|
||||||
|
QFETCH(BitVector, output);
|
||||||
|
|
||||||
|
Code128Barcode code;
|
||||||
|
const auto v = code.encode(input);
|
||||||
|
if (v != output) {
|
||||||
|
qDebug() << "Actual :" << v;
|
||||||
|
qDebug() << "Expected:" << output;
|
||||||
|
}
|
||||||
|
QCOMPARE(v, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testDimension()
|
||||||
|
{
|
||||||
|
auto barcode = Prison::Barcode::create(Prison::Code128);
|
||||||
|
QVERIFY(barcode);
|
||||||
|
QCOMPARE(barcode->format(), Prison::Code128);
|
||||||
|
QCOMPARE(barcode->dimensions(), Prison::Barcode::OneDimension);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testSize()
|
||||||
|
{
|
||||||
|
auto barcode = Prison::Barcode::create(Prison::Code128);
|
||||||
|
QVERIFY(barcode);
|
||||||
|
QCOMPARE(barcode->format(), Prison::Code128);
|
||||||
|
barcode->setData(QStringLiteral("UNIT TEST"));
|
||||||
|
QCOMPARE(barcode->minimumSize(), QSize(154, 1));
|
||||||
|
QCOMPARE(barcode->preferredSize(1), QSize(308, 50));
|
||||||
|
QCOMPARE(barcode->preferredSize(2), QSize(154, 50));
|
||||||
|
QCOMPARE(barcode->toImage(barcode->preferredSize(1)).size(), QSize(308, 50));
|
||||||
|
QCOMPARE(barcode->toImage({1, 1}).isNull(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRender_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
|
||||||
|
QTest::newRow("text") << QByteArray("KF5::Prison") << "code128-text.png";
|
||||||
|
QTest::newRow("binary") << QByteArray("KDE\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9kde", 16) << "code128-binary.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRender()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, input);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto code = Prison::Barcode::create(Prison::Code128);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(QString::fromLatin1(input.constData(), input.size()));
|
||||||
|
const auto img = code->toImage(code->minimumSize());
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/code128/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto code = Prison::Barcode::create(Prison::Code128);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(QString::fromLatin1(input.constData(), input.size()));
|
||||||
|
const auto img = code->toImage(code->minimumSize());
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/code128/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(Code128BarcodeTest)
|
||||||
|
|
||||||
|
#include "code128barcodetest.moc"
|
||||||
|
After Width: | Height: | Size: 488 B |
|
After Width: | Height: | Size: 436 B |
@@ -0,0 +1,10 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: none
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
|
-->
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/datamatrix/">
|
||||||
|
<file>datamatrix-text.png</file>
|
||||||
|
<file>datamatrix-binary.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Prison/Barcode>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class DataMatrixTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testRender_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
|
||||||
|
QTest::newRow("text") << QByteArray("KF5::Prison") << "datamatrix-text.png";
|
||||||
|
QTest::newRow("binary") << QByteArray("KDE\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9kde", 16) << "datamatrix-binary.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRender()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, input);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto code = Prison::Barcode::create(Prison::DataMatrix);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(QString::fromLatin1(input.constData(), input.size()));
|
||||||
|
const auto img = code->toImage(code->preferredSize(1));
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/datamatrix/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto code = Prison::Barcode::create(Prison::DataMatrix);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(input);
|
||||||
|
const auto img = code->toImage(code->preferredSize(1));
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/datamatrix/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(DataMatrixTest)
|
||||||
|
|
||||||
|
#include "datamatrixtest.moc"
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2024 Volker Krause <vkrause@kde.org>
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Prison/Format>
|
||||||
|
#include <Prison/ImageScanner>
|
||||||
|
#include <Prison/ScanResult>
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class ImageScannerTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testImageScan()
|
||||||
|
{
|
||||||
|
auto result = ImageScanner::scan(QImage());
|
||||||
|
QVERIFY(!result.hasContent());
|
||||||
|
|
||||||
|
QImage img(u":/qr/qr-text.png"_s);
|
||||||
|
QVERIFY(!img.isNull());
|
||||||
|
result = ImageScanner::scan(img);
|
||||||
|
QVERIFY(result.hasContent());
|
||||||
|
QVERIFY(result.hasText());
|
||||||
|
QCOMPARE(result.text(), "KF5::Prison"_L1);
|
||||||
|
|
||||||
|
result = ImageScanner::scan(img, Format::QRCode);
|
||||||
|
QCOMPARE(result.text(), "KF5::Prison"_L1);
|
||||||
|
|
||||||
|
result = ImageScanner::scan(img, Format::Aztec);
|
||||||
|
QVERIFY(!result.hasContent());
|
||||||
|
|
||||||
|
img = QImage(u":/qr/qr-binary.png"_s);
|
||||||
|
QVERIFY(!img.isNull());
|
||||||
|
result = ImageScanner::scan(img);
|
||||||
|
QVERIFY(result.hasContent());
|
||||||
|
QVERIFY(!result.hasText());
|
||||||
|
QVERIFY(result.hasBinaryData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void testImageScanMT()
|
||||||
|
{
|
||||||
|
QImage img(u":/qr/qr-text.png"_s);
|
||||||
|
QEventLoop loop;
|
||||||
|
QtConcurrent::run([&img]() {
|
||||||
|
return ImageScanner::scan(img);
|
||||||
|
}).then([&loop](const ScanResult &result) {
|
||||||
|
qDebug() << result.text();
|
||||||
|
loop.exit();
|
||||||
|
});
|
||||||
|
loop.exec();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(ImageScannerTest)
|
||||||
|
|
||||||
|
#include "imagescannertest.moc"
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QtTest/QTest>
|
||||||
|
|
||||||
|
#include "mecard.h"
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class MeCardTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testWifiParse()
|
||||||
|
{
|
||||||
|
QVariantMap map;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto m = MeCard::parse(QStringLiteral("WIFI:S:myssid;T:WPA;P:xxx123;;"));
|
||||||
|
QVERIFY(m.has_value());
|
||||||
|
QCOMPARE(m->header(), QLatin1String("WIFI"));
|
||||||
|
QCOMPARE(m->headerView(), QLatin1String("WIFI"));
|
||||||
|
QCOMPARE(m->value(u"S"), QLatin1String("myssid"));
|
||||||
|
QCOMPARE(m->value(u"T"), QLatin1String("WPA"));
|
||||||
|
QCOMPARE(m->value(u"P"), QLatin1String("xxx123"));
|
||||||
|
map = QVariantMap(
|
||||||
|
{{QLatin1String("S"), QLatin1String("myssid")}, {QLatin1String("T"), QLatin1String("WPA")}, {QLatin1String("P"), QLatin1String("xxx123")}});
|
||||||
|
QCOMPARE(m->toVariantMap(), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto m = MeCard::parse(QStringLiteral("MECARD:N:Doe,John;TEL:13035551212;EMAIL:john.doe@example.com;;"));
|
||||||
|
QVERIFY(m.has_value());
|
||||||
|
QCOMPARE(m->header(), QLatin1String("MECARD"));
|
||||||
|
QCOMPARE(m->headerView(), QLatin1String("MECARD"));
|
||||||
|
QCOMPARE(m->value(u"N"), QLatin1String("Doe,John"));
|
||||||
|
QCOMPARE(m->value(u"TEL"), QLatin1String("13035551212"));
|
||||||
|
QCOMPARE(m->value(u"EMAIL"), QLatin1String("john.doe@example.com"));
|
||||||
|
map = QVariantMap({{QLatin1String("N"), QLatin1String("Doe,John")},
|
||||||
|
{QLatin1String("TEL"), QLatin1String("13035551212")},
|
||||||
|
{QLatin1String("EMAIL"), QLatin1String("john.doe@example.com")}});
|
||||||
|
QCOMPARE(m->toVariantMap(), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto m = MeCard::parse(QStringLiteral("MECARD:N:Doe,John;TEL:13035551212;EMAIL:john.doe@example.com;EMAIL:null@kde.org;;"));
|
||||||
|
QVERIFY(m.has_value());
|
||||||
|
QCOMPARE(m->header(), QLatin1String("MECARD"));
|
||||||
|
QCOMPARE(m->headerView(), QLatin1String("MECARD"));
|
||||||
|
QCOMPARE(m->value(u"N"), QLatin1String("Doe,John"));
|
||||||
|
QCOMPARE(m->value(u"TEL"), QLatin1String("13035551212"));
|
||||||
|
QCOMPARE(m->value(u"EMAIL"), QString());
|
||||||
|
QCOMPARE(m->values(u"EMAIL"), QStringList({QLatin1String("john.doe@example.com"), QLatin1String("null@kde.org")}));
|
||||||
|
map = QVariantMap({{QLatin1String("N"), QLatin1String("Doe,John")},
|
||||||
|
{QLatin1String("TEL"), QLatin1String("13035551212")},
|
||||||
|
{QLatin1String("EMAIL"), QString()},
|
||||||
|
{QLatin1String("EMAIL"), QStringList({QLatin1String("john.doe@example.com"), QLatin1String("null@kde.org")})}});
|
||||||
|
QCOMPARE(m->toVariantMap(), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto m = MeCard::parse(QStringLiteral("WIFI:S:\\\"foo\\;bar\\\\baz\\\";P:\"ABCD\";;"));
|
||||||
|
QVERIFY(m.has_value());
|
||||||
|
QCOMPARE(m->value(u"P"), QLatin1String("ABCD"));
|
||||||
|
QCOMPARE(m->value(u"S"), QLatin1String("\"foo;bar\\baz\""));
|
||||||
|
map = QVariantMap({{QLatin1String("P"), QLatin1String("ABCD")}, {QLatin1String("S"), QLatin1String("\"foo;bar\\baz\"")}});
|
||||||
|
QCOMPARE(m->toVariantMap(), map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testInvalid_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("input");
|
||||||
|
QTest::newRow("empty") << QString();
|
||||||
|
QTest::newRow("no colon") << QStringLiteral("wifi");
|
||||||
|
QTest::newRow("no content 1") << QStringLiteral("wifi:");
|
||||||
|
QTest::newRow("no content 2") << QStringLiteral("wifi:;");
|
||||||
|
QTest::newRow("no content 3") << QStringLiteral("wifi:;;");
|
||||||
|
QTest::newRow("invalid element") << QStringLiteral("wifi:S:");
|
||||||
|
}
|
||||||
|
void testInvalid()
|
||||||
|
{
|
||||||
|
QFETCH(QString, input);
|
||||||
|
auto m = MeCard::parse(input);
|
||||||
|
QVERIFY(!m.has_value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(MeCardTest)
|
||||||
|
|
||||||
|
#include "mecardtest.moc"
|
||||||
|
After Width: | Height: | Size: 229 B |
|
After Width: | Height: | Size: 227 B |
@@ -0,0 +1,11 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: none
|
||||||
|
SPDX-License-Identifier: CC0-1.0
|
||||||
|
-->
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/qr/">
|
||||||
|
<file>qr-text.png</file>
|
||||||
|
<file>qr-binary.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Prison/Barcode>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class QrTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testRenderText_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("input");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
|
||||||
|
QTest::newRow("text") << QStringLiteral("KF5::Prison") << "qr-text.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRenderText()
|
||||||
|
{
|
||||||
|
QFETCH(QString, input);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
|
||||||
|
auto code = Prison::Barcode::create(Prison::QRCode);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(input);
|
||||||
|
const auto img = code->toImage(code->preferredSize(1));
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/qr/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRenderBinary_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
QTest::addColumn<QString>("refName");
|
||||||
|
|
||||||
|
QTest::newRow("text") << QByteArray("KF5::Prison") << "qr-text.png";
|
||||||
|
QTest::newRow("binary") << QByteArray("KDE\x0\x1\x2\x3\x4\x5\x6\x7\x8\x9kde", 16) << "qr-binary.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRenderBinary()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, input);
|
||||||
|
QFETCH(QString, refName);
|
||||||
|
|
||||||
|
auto code = Prison::Barcode::create(Prison::QRCode);
|
||||||
|
QVERIFY(code);
|
||||||
|
code->setData(input);
|
||||||
|
const auto img = code->toImage(code->preferredSize(1));
|
||||||
|
img.save(refName);
|
||||||
|
|
||||||
|
QImage ref(QStringLiteral(":/qr/") + refName);
|
||||||
|
ref = ref.convertToFormat(img.format());
|
||||||
|
QCOMPARE(img, ref);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(QrTest)
|
||||||
|
|
||||||
|
#include "qrtest.moc"
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../src/lib/bitvector_p.h"
|
||||||
|
#include "../src/lib/reedsolomon_p.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Prison::BitVector)
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class ReedSolomonTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void rsTest_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<int>("poly");
|
||||||
|
QTest::addColumn<int>("symCount");
|
||||||
|
QTest::addColumn<BitVector>("input");
|
||||||
|
QTest::addColumn<BitVector>("output");
|
||||||
|
|
||||||
|
BitVector in;
|
||||||
|
BitVector out;
|
||||||
|
out.appendMSB(0, 20);
|
||||||
|
QTest::newRow("empty") << (int)ReedSolomon::GF16 << 5 << in << out;
|
||||||
|
|
||||||
|
in.clear();
|
||||||
|
out.clear();
|
||||||
|
in.appendMSB(0x5c, 8);
|
||||||
|
out.appendMSB(7, 6);
|
||||||
|
out.appendMSB(5, 7);
|
||||||
|
out.appendMSB(0x4d, 7);
|
||||||
|
QTest::newRow("GF16") << (int)ReedSolomon::GF16 << 5 << in << out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsTest()
|
||||||
|
{
|
||||||
|
QFETCH(int, poly);
|
||||||
|
QFETCH(int, symCount);
|
||||||
|
QFETCH(BitVector, input);
|
||||||
|
QFETCH(BitVector, output);
|
||||||
|
|
||||||
|
ReedSolomon rs(poly, symCount);
|
||||||
|
const auto res = rs.encode(input);
|
||||||
|
QCOMPARE(res.size(), output.size());
|
||||||
|
if (res != output) {
|
||||||
|
qDebug() << "Actual :" << res;
|
||||||
|
qDebug() << "Expected:" << output;
|
||||||
|
}
|
||||||
|
QCOMPARE(res, output);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(ReedSolomonTest)
|
||||||
|
|
||||||
|
#include "reedsolomontest.moc"
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../src/lib/zxingutil_p.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class ZXingUtilTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testToStdWString()
|
||||||
|
{
|
||||||
|
auto w = ZXingUtil::toStdWString(QStringLiteral("KDE"));
|
||||||
|
QCOMPARE(w.size(), 3);
|
||||||
|
QCOMPARE(w[0], 'K');
|
||||||
|
|
||||||
|
w = ZXingUtil::toStdWString(QByteArray("KDE"));
|
||||||
|
QCOMPARE(w.size(), 3);
|
||||||
|
QCOMPARE(w[0], 'K');
|
||||||
|
|
||||||
|
w = ZXingUtil::toStdWString(QByteArray("\x80\x00\x7f", 3));
|
||||||
|
QCOMPARE(w.size(), 3);
|
||||||
|
QCOMPARE(w[0], 0x80);
|
||||||
|
QCOMPARE(w[1], 0x00);
|
||||||
|
QCOMPARE(w[2], 0x7f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(ZXingUtilTest)
|
||||||
|
|
||||||
|
#include "zxingutiltest.moc"
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#.rst:
|
||||||
|
# FindDmtx
|
||||||
|
# --------
|
||||||
|
#
|
||||||
|
# Try to find the DataMatrix library.
|
||||||
|
#
|
||||||
|
# This will define the following variables:
|
||||||
|
#
|
||||||
|
# ``Dmtx_FOUND``
|
||||||
|
# True if the datamatrix library is available.
|
||||||
|
#
|
||||||
|
# ``Dmtx_INCLUDE_DIRS``
|
||||||
|
# the datamatrix library include dirs.
|
||||||
|
# This variable shall be passed to target_include_libraries() calls if
|
||||||
|
# the target is not used for linking.
|
||||||
|
#
|
||||||
|
# ``Dmtx_LIBRARIES``
|
||||||
|
# the libraries used to link datamatrix.
|
||||||
|
# This can be passed to target_link_libraries instead of
|
||||||
|
# the ``Dmtx::Dmtx`` target.
|
||||||
|
#
|
||||||
|
# If ``Dmtx_FOUND`` is TRUE, the following imported target
|
||||||
|
# will be available:
|
||||||
|
#
|
||||||
|
# ``Dmtx::Dmtx``
|
||||||
|
# The datamatrix library.
|
||||||
|
#
|
||||||
|
# Imported target since 5.27.0
|
||||||
|
#
|
||||||
|
#=============================================================================
|
||||||
|
# SPDX-FileCopyrightText: 2010 Sune Vuorela <sune@debian.org>
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
find_path(Dmtx_INCLUDE_DIRS dmtx.h)
|
||||||
|
|
||||||
|
find_library(Dmtx_LIBRARIES NAMES dmtx)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(Dmtx
|
||||||
|
FOUND_VAR Dmtx_FOUND
|
||||||
|
REQUIRED_VARS Dmtx_LIBRARIES Dmtx_INCLUDE_DIRS
|
||||||
|
)
|
||||||
|
|
||||||
|
if(Dmtx_FOUND AND NOT TARGET Dmtx::Dmtx)
|
||||||
|
add_library(Dmtx::Dmtx UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(Dmtx::Dmtx PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${Dmtx_LIBRARIES}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${Dmtx_INCLUDE_DIRS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(Dmtx_INCLUDE_DIRS Dmtx_LIBRARIES)
|
||||||
|
|
||||||
|
include(FeatureSummary)
|
||||||
|
set_package_properties(Dmtx PROPERTIES
|
||||||
|
URL "https://github.com/dmtx/libdmtx"
|
||||||
|
DESCRIPTION "The Datamatrix library"
|
||||||
|
)
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#.rst:
|
||||||
|
# FindQRencode
|
||||||
|
# ------------
|
||||||
|
#
|
||||||
|
# Try to find the qrencode library.
|
||||||
|
#
|
||||||
|
# This will define the following variables:
|
||||||
|
#
|
||||||
|
# ``QRencode_FOUND``
|
||||||
|
# True if the QRencode library is available.
|
||||||
|
#
|
||||||
|
# ``QRencode_INCLUDE_DIRS``
|
||||||
|
# the QRencode library include dirs.
|
||||||
|
# This variable shall be passed to target_include_libraries() calls if
|
||||||
|
# the target is not used for linking.
|
||||||
|
#
|
||||||
|
# ``QRencode_LIBRARIES``
|
||||||
|
# the libraries used to link QRencode.
|
||||||
|
# This can be passed to target_link_libraries instead of
|
||||||
|
# the ``QRencode::QRencode`` target.
|
||||||
|
#
|
||||||
|
# If ``QRencode_FOUND`` is TRUE, the following imported target
|
||||||
|
# will be available:
|
||||||
|
#
|
||||||
|
# ``QRencode::QRencode``
|
||||||
|
# The QRencode library.
|
||||||
|
#
|
||||||
|
# Imported target since 5.27.0
|
||||||
|
#
|
||||||
|
#=============================================================================
|
||||||
|
# SPDX-FileCopyrightText: 2010 Sune Vuorela <sune@debian.org>
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
find_library(QRencode_LIBRARIES NAMES qrencode qrencoded)
|
||||||
|
|
||||||
|
find_path(QRencode_INCLUDE_DIRS qrencode.h)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(QRencode
|
||||||
|
FOUND_VAR QRencode_FOUND
|
||||||
|
REQUIRED_VARS QRencode_LIBRARIES QRencode_INCLUDE_DIRS
|
||||||
|
)
|
||||||
|
|
||||||
|
if(QRencode_FOUND AND NOT TARGET QRencode::QRencode)
|
||||||
|
add_library(QRencode::QRencode UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(QRencode::QRencode PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${QRencode_LIBRARIES}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${QRencode_INCLUDE_DIRS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(QRencode_LIBRARIES QRencode_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
include(FeatureSummary)
|
||||||
|
set_package_properties(QRencode PROPERTIES
|
||||||
|
URL "https://fukuchi.org/works/qrencode/"
|
||||||
|
DESCRIPTION "The QRencode library"
|
||||||
|
)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
### KApiDox Project-specific Overrides File
|
||||||
|
|
||||||
|
# define so that deprecated API is not skipped
|
||||||
|
PREDEFINED += \
|
||||||
|
"PRISON_ENABLE_DEPRECATED_SINCE(x, y)=1" \
|
||||||
|
"PRISON_BUILD_DEPRECATED_SINCE(x, y)=1" \
|
||||||
|
"PRISON_DEPRECATED_VERSION(x, y, t)=" \
|
||||||
|
"PRISON_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)="
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
description: Barcode abstraction layer providing uniform access to generation of barcodes
|
||||||
|
tier: 1
|
||||||
|
type: solution
|
||||||
|
platforms:
|
||||||
|
- name: Linux
|
||||||
|
- name: FreeBSD
|
||||||
|
- name: Android
|
||||||
|
- name: Windows
|
||||||
|
- name: macOS
|
||||||
|
|
||||||
|
portingAid: false
|
||||||
|
deprecated: false
|
||||||
|
release: true
|
||||||
|
libraries:
|
||||||
|
- cmake: "KF6::Prison"
|
||||||
|
cmakename: KF6Prison
|
||||||
|
|
||||||
|
public_lib: true
|
||||||
|
group: Frameworks
|
||||||
|
subgroup: Tier 1
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
add_subdirectory(lib)
|
||||||
|
add_subdirectory(tools)
|
||||||
|
if(TARGET Qt6::Quick)
|
||||||
|
add_subdirectory(quick)
|
||||||
|
endif()
|
||||||
|
if(TARGET Qt6::Multimedia AND TARGET ZXing::ZXing)
|
||||||
|
add_subdirectory(scanner)
|
||||||
|
if (TARGET Qt6::Quick)
|
||||||
|
add_subdirectory(scanner-quick)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
ecm_qt_install_logging_categories(
|
||||||
|
EXPORT PRISON
|
||||||
|
FILE prison.categories
|
||||||
|
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
||||||
|
)
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
if(TARGET Dmtx::Dmtx)
|
||||||
|
set(HAVE_DMTX 1)
|
||||||
|
endif()
|
||||||
|
if (TARGET ZXing::ZXing)
|
||||||
|
set(HAVE_ZXING 1)
|
||||||
|
endif()
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-prison.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-prison.h)
|
||||||
|
|
||||||
|
add_library(KF6Prison)
|
||||||
|
add_library(KF6::Prison ALIAS KF6Prison)
|
||||||
|
|
||||||
|
qt_extract_metatypes(KF6Prison)
|
||||||
|
|
||||||
|
set_target_properties(KF6Prison PROPERTIES
|
||||||
|
VERSION ${PRISON_VERSION}
|
||||||
|
SOVERSION ${PRISON_SOVERSION}
|
||||||
|
EXPORT_NAME Prison
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(KF6Prison PRIVATE
|
||||||
|
abstractbarcode_p.cpp
|
||||||
|
aztecbarcode.cpp
|
||||||
|
aztecbarcode_p.h
|
||||||
|
barcode.cpp
|
||||||
|
barcodeutil.cpp
|
||||||
|
barcodeutil_p.h
|
||||||
|
bitvector.cpp
|
||||||
|
bitvector_p.h
|
||||||
|
code128barcode.cpp
|
||||||
|
code128barcode_p.h
|
||||||
|
code39barcode.cpp
|
||||||
|
code39barcode_p.h
|
||||||
|
code93barcode.cpp
|
||||||
|
code93barcode_p.h
|
||||||
|
mecard.cpp
|
||||||
|
mecard.h
|
||||||
|
prison.h
|
||||||
|
qrcodebarcode.cpp
|
||||||
|
qrcodebarcode_p.h
|
||||||
|
reedsolomon.cpp
|
||||||
|
reedsolomon_p.h
|
||||||
|
)
|
||||||
|
if(TARGET Dmtx::Dmtx)
|
||||||
|
target_sources(KF6Prison PRIVATE datamatrixbarcode.cpp datamatrixbarcode_p.h)
|
||||||
|
endif()
|
||||||
|
if(TARGET ZXing::ZXing)
|
||||||
|
target_sources(KF6Prison PRIVATE
|
||||||
|
pdf417barcode.cpp
|
||||||
|
pdf417barcode_p.h
|
||||||
|
zxingutil.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
kde_source_files_enable_exceptions(
|
||||||
|
barcode.cpp
|
||||||
|
pdf417barcode.cpp
|
||||||
|
prison.cpp
|
||||||
|
zxingutil.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_qt_declare_logging_category(KF6Prison
|
||||||
|
HEADER prison_debug.h
|
||||||
|
IDENTIFIER Prison::Log
|
||||||
|
CATEGORY_NAME kf.prison
|
||||||
|
OLD_CATEGORY_NAMES kf5.prison
|
||||||
|
DESCRIPTION "Prison (lib)"
|
||||||
|
EXPORT PRISON
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_generate_export_header(KF6Prison
|
||||||
|
BASE_NAME Prison
|
||||||
|
GROUP_BASE_NAME KF
|
||||||
|
VERSION ${KF_VERSION}
|
||||||
|
USE_VERSION_HEADER
|
||||||
|
DEPRECATED_BASE_VERSION 0
|
||||||
|
DEPRECATION_VERSIONS
|
||||||
|
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(KF6Prison
|
||||||
|
INTERFACE
|
||||||
|
"$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/Prison>"
|
||||||
|
PUBLIC
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>" # module version header
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(KF6Prison
|
||||||
|
PUBLIC
|
||||||
|
Qt6::Gui
|
||||||
|
PRIVATE
|
||||||
|
QRencode::QRencode
|
||||||
|
)
|
||||||
|
if(TARGET Dmtx::Dmtx)
|
||||||
|
target_link_libraries(KF6Prison PRIVATE Dmtx::Dmtx)
|
||||||
|
endif()
|
||||||
|
if(TARGET ZXing::ZXing)
|
||||||
|
target_link_libraries(KF6Prison PRIVATE ZXing::ZXing)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS KF6Prison EXPORT KF6PrisonTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||||
|
|
||||||
|
ecm_generate_headers(Prison_CamelCase_HEADERS
|
||||||
|
HEADER_NAMES
|
||||||
|
Barcode
|
||||||
|
Prison
|
||||||
|
MeCard
|
||||||
|
REQUIRED_HEADERS Prison_HEADERS
|
||||||
|
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/Prison
|
||||||
|
)
|
||||||
|
|
||||||
|
set(_all_headers
|
||||||
|
${Prison_HEADERS}
|
||||||
|
${Prison_CamelCase_HEADERS}
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/prison_export.h
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES ${_all_headers}
|
||||||
|
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/Prison/Prison
|
||||||
|
COMPONENT Devel
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_QCH)
|
||||||
|
ecm_add_qch(
|
||||||
|
KF6Prison_QCH
|
||||||
|
NAME Prison
|
||||||
|
BASE_NAME KF6Prison
|
||||||
|
VERSION ${KF_VERSION}
|
||||||
|
ORG_DOMAIN org.kde
|
||||||
|
SOURCES # using only public headers, to cover only public API
|
||||||
|
${Prison_HEADERS}
|
||||||
|
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
|
||||||
|
LINK_QCHS
|
||||||
|
Qt6Gui_QCH
|
||||||
|
INCLUDE_DIRS
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
BLANK_MACROS
|
||||||
|
PRISON_EXPORT
|
||||||
|
PRISON_DEPRECATED
|
||||||
|
PRISON_DEPRECATED_EXPORT
|
||||||
|
"PRISON_DEPRECATED_VERSION(x, y, t)"
|
||||||
|
"PRISON_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||||
|
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||||
|
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||||
|
COMPONENT Devel
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2010-2016 Sune Vuorela <sune@vuorela.dk>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "abstractbarcode_p.h"
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
AbstractBarcodePrivate::AbstractBarcodePrivate(Barcode::Dimensions dim)
|
||||||
|
: m_dimension(dim)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractBarcodePrivate::~AbstractBarcodePrivate() = default;
|
||||||
|
|
||||||
|
bool AbstractBarcodePrivate::sizeTooSmall(const QSizeF &size) const
|
||||||
|
{
|
||||||
|
return m_cache.width() > size.width() || m_cache.height() > size.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AbstractBarcodePrivate::isEmpty() const
|
||||||
|
{
|
||||||
|
switch (m_data.userType()) {
|
||||||
|
case QMetaType::QString:
|
||||||
|
return m_data.toString().isEmpty();
|
||||||
|
case QMetaType::QByteArray:
|
||||||
|
return m_data.toByteArray().isEmpty();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractBarcodePrivate::recompute()
|
||||||
|
{
|
||||||
|
if (m_cache.isNull() && !isEmpty()) {
|
||||||
|
m_cache = paintImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF AbstractBarcodePrivate::preferredSize(qreal devicePixelRatio) const
|
||||||
|
{
|
||||||
|
switch (m_dimension) {
|
||||||
|
case Barcode::NoDimensions:
|
||||||
|
return {};
|
||||||
|
case Barcode::OneDimension:
|
||||||
|
return QSizeF(m_cache.width() * (devicePixelRatio < 2 ? 2 : 1), std::max(m_cache.height(), 50));
|
||||||
|
case Barcode::TwoDimensions:
|
||||||
|
return m_cache.size() * (devicePixelRatio < 2 ? 4 : 2);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2010-2016 Sune Vuorela <sune@vuorela.dk>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_ABSTRACTBARCODE_P_H
|
||||||
|
#define PRISON_ABSTRACTBARCODE_P_H
|
||||||
|
|
||||||
|
#include "barcode.h"
|
||||||
|
#include "prison.h"
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
class AbstractBarcodePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AbstractBarcodePrivate(Barcode::Dimensions dim);
|
||||||
|
virtual ~AbstractBarcodePrivate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doing the actual painting of the image
|
||||||
|
* @param size unused - will be removed in KF6
|
||||||
|
* @return image with barcode, or null image
|
||||||
|
*/
|
||||||
|
virtual QImage paintImage() = 0;
|
||||||
|
|
||||||
|
/** @see Barcode::preferredSize */
|
||||||
|
virtual QSizeF preferredSize(qreal devicePixelRatio) const;
|
||||||
|
|
||||||
|
bool isEmpty() const;
|
||||||
|
bool sizeTooSmall(const QSizeF &size) const;
|
||||||
|
void recompute();
|
||||||
|
|
||||||
|
QVariant m_data;
|
||||||
|
QImage m_cache;
|
||||||
|
QColor m_foreground = Qt::black;
|
||||||
|
QColor m_background = Qt::white;
|
||||||
|
Barcode::Dimensions m_dimension = Barcode::NoDimensions;
|
||||||
|
Prison::BarcodeType m_format;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,714 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "aztecbarcode_p.h"
|
||||||
|
#include "barcodeutil_p.h"
|
||||||
|
#include "bitvector_p.h"
|
||||||
|
#include "prison_debug.h"
|
||||||
|
#include "reedsolomon_p.h"
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// see https://en.wikipedia.org/wiki/Aztec_Code for encoding tables, magic numbers, etc
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FullMaxSize = 151,
|
||||||
|
FullRadius = 74,
|
||||||
|
FullGridInterval = 16,
|
||||||
|
FullModeMessageSize = 40,
|
||||||
|
FullLayerCount = 32,
|
||||||
|
|
||||||
|
CompactMaxSize = 27,
|
||||||
|
CompactRadius = 13,
|
||||||
|
CompactModeMessageSize = 28,
|
||||||
|
CompactLayerCount = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
AztecBarcode::AztecBarcode()
|
||||||
|
: AbstractBarcodePrivate(Barcode::TwoDimensions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
AztecBarcode::~AztecBarcode() = default;
|
||||||
|
|
||||||
|
// encoding properties depending on layer count
|
||||||
|
struct aztec_layer_property_t {
|
||||||
|
uint8_t layer;
|
||||||
|
uint8_t codeWordSize;
|
||||||
|
uint16_t gf;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const aztec_layer_property_t aztec_layer_properties[] = {{2, 6, ReedSolomon::GF64},
|
||||||
|
{8, 8, ReedSolomon::GF256},
|
||||||
|
{22, 10, ReedSolomon::GF1024},
|
||||||
|
{32, 12, ReedSolomon::GF4096}};
|
||||||
|
|
||||||
|
// amounts of bits in an Aztec code depending on layer count
|
||||||
|
static int aztecCompactDataBits(int layer)
|
||||||
|
{
|
||||||
|
return (88 + 16 * layer) * layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int aztecFullDataBits(int layer)
|
||||||
|
{
|
||||||
|
return (112 + 16 * layer) * layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage AztecBarcode::paintImage()
|
||||||
|
{
|
||||||
|
const auto inputData = aztecEncode(BarCodeUtil::asLatin1ByteArray(m_data));
|
||||||
|
|
||||||
|
int layerCount = 0;
|
||||||
|
int codewordCount = 0;
|
||||||
|
int availableBits = 0;
|
||||||
|
int stuffSize = 0; // extra bits added during bit stuffing, which might make us overrun the available size
|
||||||
|
bool compactMode = false;
|
||||||
|
BitVector encodedData;
|
||||||
|
|
||||||
|
do {
|
||||||
|
layerCount = 0;
|
||||||
|
// find the smallest layout we can put the data in, while leaving 23% for error correction
|
||||||
|
for (auto i = 1; i <= FullLayerCount; ++i) {
|
||||||
|
if (aztecFullDataBits(i) * 0.77 > (inputData.size() + stuffSize)) {
|
||||||
|
layerCount = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto i = 1; i <= CompactLayerCount; ++i) {
|
||||||
|
if (aztecCompactDataBits(i) * 0.77 > (inputData.size() + stuffSize)) {
|
||||||
|
layerCount = i;
|
||||||
|
compactMode = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (layerCount == 0) {
|
||||||
|
qCWarning(Log) << "data too large for Aztec code" << inputData.size();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine code word size
|
||||||
|
const auto propIt = std::lower_bound(aztec_layer_properties, aztec_layer_properties + 4, layerCount, [](const aztec_layer_property_t &lhs, int rhs) {
|
||||||
|
return lhs.layer < rhs;
|
||||||
|
});
|
||||||
|
|
||||||
|
// bit stuffing
|
||||||
|
auto stuffedData = bitStuffAndPad(inputData, (*propIt).codeWordSize);
|
||||||
|
stuffSize = stuffedData.size() - inputData.size();
|
||||||
|
|
||||||
|
availableBits = compactMode ? aztecCompactDataBits(layerCount) : aztecFullDataBits(layerCount);
|
||||||
|
codewordCount = stuffedData.size() / (*propIt).codeWordSize;
|
||||||
|
const auto rsWordCount = availableBits / (*propIt).codeWordSize - codewordCount;
|
||||||
|
|
||||||
|
// compute error correction
|
||||||
|
ReedSolomon rs((*propIt).gf, rsWordCount);
|
||||||
|
const auto rsData = rs.encode(stuffedData);
|
||||||
|
|
||||||
|
// pad with leading 0 bits to align to code word boundaries
|
||||||
|
encodedData.reserve(availableBits);
|
||||||
|
if (int diff = availableBits - stuffedData.size() - rsData.size()) {
|
||||||
|
encodedData.appendMSB(0, diff);
|
||||||
|
}
|
||||||
|
encodedData.append(stuffedData);
|
||||||
|
encodedData.append(rsData);
|
||||||
|
|
||||||
|
// try again in the rare case that we overrun the available bits due to bit stuffing and padding
|
||||||
|
} while (encodedData.size() > availableBits);
|
||||||
|
|
||||||
|
// determine mode message
|
||||||
|
BitVector modeMsg;
|
||||||
|
if (compactMode) {
|
||||||
|
modeMsg.appendMSB(layerCount - 1, 2);
|
||||||
|
modeMsg.appendMSB(codewordCount - 1, 6);
|
||||||
|
ReedSolomon rs(ReedSolomon::GF16, 5);
|
||||||
|
modeMsg.append(rs.encode(modeMsg));
|
||||||
|
} else {
|
||||||
|
modeMsg.appendMSB(layerCount - 1, 5);
|
||||||
|
modeMsg.appendMSB(codewordCount - 1, 11);
|
||||||
|
ReedSolomon rs(ReedSolomon::GF16, 6);
|
||||||
|
modeMsg.append(rs.encode(modeMsg));
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the result
|
||||||
|
if (compactMode) {
|
||||||
|
QImage img(CompactMaxSize, CompactMaxSize, QImage::Format_RGB32);
|
||||||
|
img.fill(m_background);
|
||||||
|
paintCompactGrid(&img);
|
||||||
|
paintCompactData(&img, encodedData, layerCount);
|
||||||
|
paintCompactModeMessage(&img, modeMsg);
|
||||||
|
return cropAndScaleCompact(&img, layerCount);
|
||||||
|
} else {
|
||||||
|
QImage img(FullMaxSize, FullMaxSize, QImage::Format_RGB32);
|
||||||
|
img.fill(m_background);
|
||||||
|
paintFullGrid(&img);
|
||||||
|
paintFullData(&img, encodedData, layerCount);
|
||||||
|
paintFullModeMessage(&img, modeMsg);
|
||||||
|
return cropAndScaleFull(&img, layerCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// code points and encoding modes for each of the first 127 ASCII characters, the rest is encoded in Binary mode
|
||||||
|
enum Mode {
|
||||||
|
NoMode,
|
||||||
|
Upper,
|
||||||
|
Lower,
|
||||||
|
Mixed,
|
||||||
|
Punct,
|
||||||
|
Digit,
|
||||||
|
Binary,
|
||||||
|
MODE_COUNT,
|
||||||
|
Special,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SpecialChar {
|
||||||
|
Space,
|
||||||
|
CarriageReturn,
|
||||||
|
Comma,
|
||||||
|
Dot,
|
||||||
|
SPECIAL_CHAR_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct aztec_code_t {
|
||||||
|
uint8_t code;
|
||||||
|
uint8_t mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const aztec_code_t aztec_code_table[] = {
|
||||||
|
{0, Binary}, // 0
|
||||||
|
{2, Mixed}, {3, Mixed}, {4, Mixed},
|
||||||
|
{5, Mixed}, {6, Mixed}, {7, Mixed},
|
||||||
|
{8, Mixed}, // 7 BEL \a
|
||||||
|
{9, Mixed}, {10, Mixed}, {11, Mixed}, // 10 LF / ^J
|
||||||
|
{12, Mixed}, {13, Mixed}, {CarriageReturn, Special}, // 13 CR / ^M - but also 1 Punct
|
||||||
|
{14, Binary}, {15, Binary}, {16, Binary},
|
||||||
|
{17, Binary}, {18, Binary}, {19, Binary},
|
||||||
|
{20, Binary}, // 20 ^T
|
||||||
|
{21, Binary}, {22, Binary}, {23, Binary},
|
||||||
|
{24, Binary}, {25, Binary}, {26, Binary},
|
||||||
|
{15, Mixed}, // 27 ^[
|
||||||
|
{16, Mixed}, {17, Mixed}, {18, Mixed}, // 30 ^^
|
||||||
|
{19, Mixed}, {Space, Special}, // 32 SP
|
||||||
|
{6, Punct}, {7, Punct}, {8, Punct}, // 35 #
|
||||||
|
{9, Punct}, {10, Punct}, {11, Punct},
|
||||||
|
{12, Punct}, {13, Punct}, // 40 (
|
||||||
|
{14, Punct}, {15, Punct}, {16, Punct}, // 43 +
|
||||||
|
{Comma, Special}, // 44 ,
|
||||||
|
{18, Punct}, // 45 -
|
||||||
|
{Dot, Special}, // 46 .
|
||||||
|
{20, Punct}, // 47 /
|
||||||
|
{2, Digit}, // 48 0
|
||||||
|
{3, Digit}, {4, Digit}, {5, Digit},
|
||||||
|
{6, Digit}, {7, Digit}, {8, Digit},
|
||||||
|
{9, Digit}, {10, Digit}, {11, Digit}, // 57 9
|
||||||
|
{21, Punct}, // 58 :
|
||||||
|
{22, Punct}, // 59 ;
|
||||||
|
{23, Punct}, // 60 <
|
||||||
|
{24, Punct}, {25, Punct}, // 62 >
|
||||||
|
{26, Punct}, // 63 ?
|
||||||
|
{20, Mixed}, // 64 @
|
||||||
|
{2, Upper}, // 65 A
|
||||||
|
{3, Upper}, {4, Upper}, {5, Upper},
|
||||||
|
{6, Upper}, {7, Upper}, {8, Upper},
|
||||||
|
{9, Upper}, {10, Upper}, {11, Upper},
|
||||||
|
{12, Upper}, {13, Upper}, {14, Upper},
|
||||||
|
{15, Upper}, {16, Upper}, {17, Upper},
|
||||||
|
{18, Upper}, {19, Upper}, {20, Upper},
|
||||||
|
{21, Upper}, {22, Upper}, {23, Upper},
|
||||||
|
{24, Upper}, {25, Upper}, {26, Upper},
|
||||||
|
{27, Upper}, // 90 Z
|
||||||
|
{27, Punct}, // 91 [
|
||||||
|
{21, Mixed}, // 92 backslash
|
||||||
|
{28, Punct}, // 93 ]
|
||||||
|
{22, Mixed}, // 94 ^
|
||||||
|
{23, Mixed}, // 95 _
|
||||||
|
{24, Mixed}, // 96 `
|
||||||
|
{2, Lower}, // 97 a
|
||||||
|
{3, Lower}, {4, Lower}, {5, Lower},
|
||||||
|
{6, Lower}, {7, Lower}, {8, Lower},
|
||||||
|
{9, Lower}, {10, Lower}, {11, Lower},
|
||||||
|
{12, Lower}, {13, Lower}, {14, Lower},
|
||||||
|
{15, Lower}, {16, Lower}, {17, Lower},
|
||||||
|
{18, Lower}, {19, Lower}, {20, Lower},
|
||||||
|
{21, Lower}, {22, Lower}, {23, Lower},
|
||||||
|
{24, Lower}, {25, Lower}, {26, Lower},
|
||||||
|
{27, Lower}, // 122 z
|
||||||
|
{29, Punct}, // 123 {
|
||||||
|
{25, Mixed}, // 124 |
|
||||||
|
{30, Punct}, // 125 }
|
||||||
|
{26, Mixed}, // 126 ~
|
||||||
|
{27, Mixed} // 127 DEL ^?
|
||||||
|
};
|
||||||
|
Q_STATIC_ASSERT(sizeof(aztec_code_table) == 256);
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
uint8_t c1;
|
||||||
|
uint8_t c2;
|
||||||
|
aztec_code_t sym;
|
||||||
|
} aztec_code_double_symbols[] = {
|
||||||
|
{'\r', '\n', {2, Punct}}, // CR LF
|
||||||
|
{'.', ' ', {3, Punct}}, // . SP
|
||||||
|
{',', ' ', {4, Punct}}, // , SP
|
||||||
|
{':', ' ', {5, Punct}} // : SP
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int aztec_code_size[] = {0, 5, 5, 5, 5, 4, 8};
|
||||||
|
Q_STATIC_ASSERT(sizeof(aztec_code_size) / sizeof(int) == MODE_COUNT);
|
||||||
|
|
||||||
|
// codes for ambiguous characters, ie. those that can be encoded in multiple modes
|
||||||
|
static const aztec_code_t aztec_special_chars[SPECIAL_CHAR_COUNT][MODE_COUNT] = {
|
||||||
|
/* NoMode Upper Lower Mixed Punct Digit Binary */
|
||||||
|
{{0, NoMode}, {1, Upper}, {1, Lower}, {1, Mixed}, {1, Upper}, {1, Digit}, {0, NoMode}}, /* SP */
|
||||||
|
{{0, NoMode}, {1, Punct}, {1, Punct}, {14, Mixed}, {1, Punct}, {1, Punct}, {0, NoMode}}, /* CR */
|
||||||
|
{{0, NoMode}, {17, Punct}, {17, Punct}, {17, Punct}, {17, Punct}, {12, Digit}, {0, NoMode}}, /* Comma */
|
||||||
|
{{0, NoMode}, {19, Punct}, {19, Punct}, {19, Punct}, {19, Punct}, {13, Digit}, {0, NoMode}}, /* Dot */
|
||||||
|
};
|
||||||
|
|
||||||
|
// shift code table, source mode -> target mode
|
||||||
|
// NoMode indicates shift is not available, use latch instead
|
||||||
|
static const aztec_code_t aztec_shift_codes[MODE_COUNT - 1][MODE_COUNT - 1] = {
|
||||||
|
/* NoMode Upper Lower Mixed Punct Digit */
|
||||||
|
{{0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}},
|
||||||
|
{{0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, Punct}, {0, NoMode}},
|
||||||
|
{{0, NoMode}, {28, Upper}, {0, NoMode}, {0, NoMode}, {0, Punct}, {0, NoMode}},
|
||||||
|
{{0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, Punct}, {0, NoMode}},
|
||||||
|
{{0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}},
|
||||||
|
{{0, NoMode}, {15, Upper}, {0, NoMode}, {0, NoMode}, {0, Punct}, {0, NoMode}}};
|
||||||
|
|
||||||
|
// latch code table, source mode -> target mode
|
||||||
|
static const aztec_code_t aztec_latch_codes[MODE_COUNT - 1][MODE_COUNT] = {
|
||||||
|
/* NoMode Upper Lower Mixed Punct Digit Binary */
|
||||||
|
{{0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}, {0, NoMode}},
|
||||||
|
{{0, NoMode}, {0, NoMode}, {28, Lower}, {29, Mixed}, {29, Mixed}, {30, Digit}, {31, Binary}},
|
||||||
|
{{0, NoMode}, {30, Digit}, {0, NoMode}, {29, Mixed}, {29, Mixed}, {30, Digit}, {31, Binary}},
|
||||||
|
{{0, NoMode}, {29, Upper}, {28, Lower}, {0, NoMode}, {30, Punct}, {28, Lower}, {31, Binary}},
|
||||||
|
{{0, NoMode}, {31, Upper}, {31, Upper}, {31, Upper}, {0, NoMode}, {31, Upper}, {31, Upper}},
|
||||||
|
{{0, NoMode}, {14, Upper}, {14, Upper}, {14, Upper}, {14, Upper}, {0, NoMode}, {14, Upper}}};
|
||||||
|
|
||||||
|
static Mode aztecCodeLatchTo(Mode currentMode, Mode targetMode, BitVector *v)
|
||||||
|
{
|
||||||
|
if (currentMode == targetMode) {
|
||||||
|
return targetMode;
|
||||||
|
}
|
||||||
|
const auto latchCode = aztec_latch_codes[currentMode][targetMode];
|
||||||
|
qCDebug(Log) << "latch" << latchCode.code << aztec_code_size[currentMode];
|
||||||
|
v->appendMSB(latchCode.code, aztec_code_size[currentMode]);
|
||||||
|
return static_cast<Mode>(latchCode.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aztecEncodeBinary(std::vector<aztec_code_t>::iterator &it, const std::vector<aztec_code_t>::iterator &end, BitVector *v)
|
||||||
|
{
|
||||||
|
// determine length of the binary sequence
|
||||||
|
const auto binEndIt = std::find_if(it, end, [](aztec_code_t sym) {
|
||||||
|
return sym.mode != Binary;
|
||||||
|
});
|
||||||
|
const auto length = std::distance(it, binEndIt);
|
||||||
|
|
||||||
|
// write length field
|
||||||
|
qCDebug(Log) << "binary length" << length;
|
||||||
|
if (length < 32) {
|
||||||
|
v->appendMSB(length, 5);
|
||||||
|
} else {
|
||||||
|
v->appendMSB(0, 5);
|
||||||
|
v->appendMSB(length - 31, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write data
|
||||||
|
for (; it != binEndIt; ++it) {
|
||||||
|
qCDebug(Log) << "binary data" << (*it).code;
|
||||||
|
v->appendMSB((*it).code, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aztecEncodeResolveAmbigious(Mode currentMode, const std::vector<aztec_code_t>::iterator &begin, const std::vector<aztec_code_t>::iterator &end)
|
||||||
|
{
|
||||||
|
Q_ASSERT(begin != end);
|
||||||
|
Q_ASSERT(currentMode != (*begin).mode);
|
||||||
|
Q_ASSERT((*begin).mode == Special);
|
||||||
|
|
||||||
|
// forward search
|
||||||
|
auto it = begin;
|
||||||
|
for (; it != end && (*it).mode == Special; ++it) {
|
||||||
|
if (aztec_special_chars[(*it).code][currentMode].mode == currentMode) {
|
||||||
|
qCDebug(Log) << "special resolved to current mode by forward search";
|
||||||
|
(*it).mode = aztec_special_chars[(*it).code][currentMode].mode;
|
||||||
|
(*it).code = aztec_special_chars[(*it).code][currentMode].code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// backward search
|
||||||
|
auto backIt = it;
|
||||||
|
while (std::distance(begin, backIt) >= 1 && it != end) {
|
||||||
|
--backIt;
|
||||||
|
if ((*backIt).mode == Special && aztec_special_chars[(*backIt).code][(*it).mode].mode == (*it).mode) {
|
||||||
|
qCDebug(Log) << "special resolved by backward search";
|
||||||
|
(*backIt).mode = aztec_special_chars[(*backIt).code][(*it).mode].mode;
|
||||||
|
(*backIt).code = aztec_special_chars[(*backIt).code][(*it).mode].code;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick one if we still have an ambiguous symbol
|
||||||
|
if ((*begin).mode != Special) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(*begin).mode = aztec_special_chars[(*begin).code][currentMode].mode;
|
||||||
|
(*begin).code = aztec_special_chars[(*begin).code][currentMode].code;
|
||||||
|
it = begin + 1;
|
||||||
|
if (it != end && (*it).mode == Special) {
|
||||||
|
aztecEncodeResolveAmbigious(static_cast<Mode>((*begin).mode), it, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Mode aztecNextMode(Mode currentMode, const std::vector<aztec_code_t>::iterator &nextSym, const std::vector<aztec_code_t>::iterator &end, bool &tryShift)
|
||||||
|
{
|
||||||
|
Q_ASSERT(currentMode != (*nextSym).mode);
|
||||||
|
Q_ASSERT(nextSym != end);
|
||||||
|
Q_ASSERT((*nextSym).mode != Special);
|
||||||
|
auto it = nextSym;
|
||||||
|
++it;
|
||||||
|
if (it != end && (*it).mode == Special) {
|
||||||
|
aztecEncodeResolveAmbigious(static_cast<Mode>((*nextSym).mode), it, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((it == end || (*it).mode == currentMode) && std::distance(nextSym, it) == 1) {
|
||||||
|
tryShift = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(Log) << currentMode << (*nextSym).mode << tryShift << std::distance(nextSym, it);
|
||||||
|
return static_cast<Mode>((*nextSym).mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector AztecBarcode::aztecEncode(const QByteArray &data) const
|
||||||
|
{
|
||||||
|
// phase one: translate single and double chars to code points
|
||||||
|
std::vector<aztec_code_t> codes;
|
||||||
|
codes.reserve(data.size());
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
const uint8_t c1 = data.at(i);
|
||||||
|
// double char codes
|
||||||
|
if (i < data.size() - 1) {
|
||||||
|
const uint8_t c2 = data.at(i + 1);
|
||||||
|
bool found = false;
|
||||||
|
for (const auto &dblCode : aztec_code_double_symbols) {
|
||||||
|
if (dblCode.c1 != c1 || dblCode.c2 != c2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
codes.push_back(dblCode.sym);
|
||||||
|
++i;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// > 127 binary-only range
|
||||||
|
if (c1 > 127) {
|
||||||
|
codes.push_back({c1, Binary});
|
||||||
|
// encodable single ASCII character
|
||||||
|
} else {
|
||||||
|
codes.push_back(aztec_code_table[c1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// phase two: insert shift and latch codes, translate to bit stream
|
||||||
|
Mode currentMode = Upper;
|
||||||
|
BitVector result;
|
||||||
|
for (auto it = codes.begin(); it != codes.end();) {
|
||||||
|
if ((*it).mode == Binary) {
|
||||||
|
auto newMode = aztecCodeLatchTo(currentMode, Binary, &result);
|
||||||
|
while (newMode != Binary) {
|
||||||
|
currentMode = newMode;
|
||||||
|
newMode = aztecCodeLatchTo(currentMode, Binary, &result);
|
||||||
|
}
|
||||||
|
aztecEncodeBinary(it, codes.end(), &result);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// resolve special codes
|
||||||
|
if ((*it).mode == Special) {
|
||||||
|
aztecEncodeResolveAmbigious(currentMode, it, codes.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// deal with mode changes
|
||||||
|
Mode nextMode = currentMode;
|
||||||
|
if ((*it).mode != currentMode) {
|
||||||
|
bool tryShift = false;
|
||||||
|
const auto newMode = aztecNextMode(currentMode, it, codes.end(), tryShift);
|
||||||
|
|
||||||
|
// shift to new mode if desired and possible
|
||||||
|
if (tryShift && aztec_shift_codes[currentMode][newMode].mode != NoMode) {
|
||||||
|
qCDebug(Log) << "shift" << aztec_shift_codes[currentMode][newMode].code << aztec_code_size[currentMode];
|
||||||
|
result.appendMSB(aztec_shift_codes[currentMode][newMode].code, aztec_code_size[currentMode]);
|
||||||
|
currentMode = newMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// latch to new mode
|
||||||
|
while (currentMode != newMode && newMode != NoMode && currentMode != NoMode) {
|
||||||
|
currentMode = aztecCodeLatchTo(currentMode, newMode, &result);
|
||||||
|
nextMode = currentMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(Log) << (*it).code << aztec_code_size[currentMode];
|
||||||
|
result.appendMSB((*it).code, aztec_code_size[currentMode]);
|
||||||
|
++it;
|
||||||
|
|
||||||
|
currentMode = nextMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector AztecBarcode::bitStuffAndPad(const BitVector &input, int codeWordSize) const
|
||||||
|
{
|
||||||
|
BitVector res;
|
||||||
|
res.reserve(input.size());
|
||||||
|
|
||||||
|
// bit stuff codewords with leading codeWordSize 0/1 bits
|
||||||
|
int i = 0;
|
||||||
|
while (i < input.size() - (codeWordSize - 1)) {
|
||||||
|
int v = input.valueAtMSB(i, codeWordSize - 1);
|
||||||
|
res.appendMSB(v, codeWordSize - 1);
|
||||||
|
i += codeWordSize - 1;
|
||||||
|
if (v == 0) {
|
||||||
|
res.appendBit(true);
|
||||||
|
} else if (v == (1 << (codeWordSize - 1)) - 1) {
|
||||||
|
res.appendBit(false);
|
||||||
|
} else {
|
||||||
|
res.appendBit(input.at(i++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (i < input.size()) {
|
||||||
|
res.appendBit(input.at(i++));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we are code word aligned already
|
||||||
|
const auto trailingBits = res.size() % codeWordSize;
|
||||||
|
if (!trailingBits) { // nothing to pad
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad with ones to nearest code word boundary
|
||||||
|
// last bit has to be zero if we'd otherwise would have all ones though
|
||||||
|
bool allOnes = true;
|
||||||
|
for (int i = res.size() - trailingBits; i < res.size(); ++i) {
|
||||||
|
allOnes &= res.at(i);
|
||||||
|
}
|
||||||
|
while (res.size() % codeWordSize) {
|
||||||
|
if ((res.size() % codeWordSize) == (codeWordSize - 1)) {
|
||||||
|
res.appendBit(allOnes ? false : true);
|
||||||
|
} else {
|
||||||
|
res.appendBit(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AztecBarcode::paintFullGrid(QImage *img) const
|
||||||
|
{
|
||||||
|
QPainter p(img);
|
||||||
|
p.translate(img->width() / 2, img->height() / 2);
|
||||||
|
|
||||||
|
// alignment grids
|
||||||
|
QPen pen(m_foreground);
|
||||||
|
pen.setDashPattern({1, 1});
|
||||||
|
p.setPen(pen);
|
||||||
|
for (int i = 0; i < img->width() / 2; i += FullGridInterval) {
|
||||||
|
p.drawLine(-i, -FullRadius, -i, FullRadius);
|
||||||
|
p.drawLine(i, -FullRadius, i, FullRadius);
|
||||||
|
p.drawLine(-FullRadius, -i, FullRadius, -i);
|
||||||
|
p.drawLine(-FullRadius, i, FullRadius, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bullseye background
|
||||||
|
p.setBrush(m_background);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.drawRect(-7, -7, 14, 14);
|
||||||
|
|
||||||
|
// bullseye
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
p.setPen(m_foreground);
|
||||||
|
p.drawPoint(0, 0);
|
||||||
|
p.drawRect(-2, -2, 4, 4);
|
||||||
|
p.drawRect(-4, -4, 8, 8);
|
||||||
|
p.drawRect(-6, -6, 12, 12);
|
||||||
|
|
||||||
|
// bullseye orientation marker
|
||||||
|
p.drawRect(-7, -7, 1, 1);
|
||||||
|
p.drawRect(7, -7, 0, 1);
|
||||||
|
p.drawPoint(7, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int aztecFullLayerOffset[] = {
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||||
|
66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, 19, 17, 15, 13, 10, 8, 6, 4, 2, 0};
|
||||||
|
|
||||||
|
void AztecBarcode::paintFullData(QImage *img, const BitVector &data, int layerCount) const
|
||||||
|
{
|
||||||
|
QPainter p(img);
|
||||||
|
p.setPen(m_foreground);
|
||||||
|
|
||||||
|
auto it = data.begin();
|
||||||
|
for (int layer = layerCount - 1; layer >= 0; --layer) {
|
||||||
|
const auto x1 = aztecFullLayerOffset[layer];
|
||||||
|
const auto y1 = x1;
|
||||||
|
const auto gridInMiddle = (x1 - FullRadius) % FullGridInterval == 0;
|
||||||
|
const auto x2 = gridInMiddle ? x1 + 2 : x1 + 1;
|
||||||
|
const auto segmentLength = FullMaxSize - 2 * y1 - 2 - (gridInMiddle ? 1 : 0);
|
||||||
|
|
||||||
|
for (int rotation = 0; rotation < 4; ++rotation) {
|
||||||
|
p.resetTransform();
|
||||||
|
p.translate(img->width() / 2, img->height() / 2);
|
||||||
|
p.rotate(-90 * rotation);
|
||||||
|
p.translate(-img->width() / 2, -img->height() / 2);
|
||||||
|
|
||||||
|
for (int i = 0; it != data.end(); ++i, ++it) {
|
||||||
|
const auto x = (i % 2 == 0) ? x1 : x2;
|
||||||
|
auto y = i / 2 + y1;
|
||||||
|
if (((y - FullRadius - 1) % FullGridInterval) == 0) { // skip grid lines
|
||||||
|
++y;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
if (y >= y1 + segmentLength) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*it) {
|
||||||
|
p.drawPoint(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AztecBarcode::paintFullModeMessage(QImage *img, const BitVector &modeData) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(modeData.size() == FullModeMessageSize);
|
||||||
|
|
||||||
|
QPainter p(img);
|
||||||
|
p.setPen(m_foreground);
|
||||||
|
|
||||||
|
auto it = modeData.begin();
|
||||||
|
for (int rotation = 0; rotation < 4; ++rotation) {
|
||||||
|
p.resetTransform();
|
||||||
|
p.translate(img->width() / 2, img->height() / 2);
|
||||||
|
p.rotate(90 * rotation);
|
||||||
|
|
||||||
|
for (int i = -5; i <= 5; ++i) {
|
||||||
|
if (i == 0) { // skip grid line
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*it) {
|
||||||
|
p.drawPoint(i, -7);
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage AztecBarcode::cropAndScaleFull(QImage *img, int layerCount)
|
||||||
|
{
|
||||||
|
const auto offset = aztecFullLayerOffset[layerCount - 1];
|
||||||
|
const auto minSize = FullMaxSize - 2 * offset;
|
||||||
|
|
||||||
|
QImage out(minSize, minSize, img->format());
|
||||||
|
QPainter p(&out);
|
||||||
|
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||||
|
const auto srcRect = img->rect().adjusted(offset, offset, -offset, -offset);
|
||||||
|
p.drawImage(out.rect(), *img, srcRect);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AztecBarcode::paintCompactGrid(QImage *img) const
|
||||||
|
{
|
||||||
|
QPainter p(img);
|
||||||
|
p.translate(img->width() / 2, img->height() / 2);
|
||||||
|
|
||||||
|
// bullseye
|
||||||
|
p.setPen(m_foreground);
|
||||||
|
p.drawPoint(0, 0);
|
||||||
|
p.drawRect(-2, -2, 4, 4);
|
||||||
|
p.drawRect(-4, -4, 8, 8);
|
||||||
|
|
||||||
|
// bullseye orientation marker
|
||||||
|
p.drawRect(-5, -5, 1, 1);
|
||||||
|
p.drawRect(5, -5, 0, 1);
|
||||||
|
p.drawPoint(5, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int aztecCompactLayerOffset[] = {6, 4, 2, 0};
|
||||||
|
|
||||||
|
void AztecBarcode::paintCompactData(QImage *img, const BitVector &data, int layerCount) const
|
||||||
|
{
|
||||||
|
QPainter p(img);
|
||||||
|
p.setPen(m_foreground);
|
||||||
|
|
||||||
|
auto it = data.begin();
|
||||||
|
for (int layer = layerCount - 1; layer >= 0; --layer) {
|
||||||
|
const auto x1 = aztecCompactLayerOffset[layer];
|
||||||
|
const auto y1 = x1;
|
||||||
|
const auto x2 = x1 + 1;
|
||||||
|
const auto segmentLength = CompactMaxSize - 2 * y1 - 2;
|
||||||
|
|
||||||
|
for (int rotation = 0; rotation < 4; ++rotation) {
|
||||||
|
p.resetTransform();
|
||||||
|
p.translate(img->width() / 2, img->height() / 2);
|
||||||
|
p.rotate(-90 * rotation);
|
||||||
|
p.translate(-img->width() / 2, -img->height() / 2);
|
||||||
|
|
||||||
|
for (int i = 0; it != data.end(); ++i, ++it) {
|
||||||
|
const auto x = (i % 2 == 0) ? x1 : x2;
|
||||||
|
auto y = i / 2 + y1;
|
||||||
|
if (y >= y1 + segmentLength) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*it) {
|
||||||
|
p.drawPoint(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AztecBarcode::paintCompactModeMessage(QImage *img, const BitVector &modeData) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(modeData.size() == CompactModeMessageSize);
|
||||||
|
|
||||||
|
QPainter p(img);
|
||||||
|
p.setPen(m_foreground);
|
||||||
|
|
||||||
|
auto it = modeData.begin();
|
||||||
|
for (int rotation = 0; rotation < 4; ++rotation) {
|
||||||
|
p.resetTransform();
|
||||||
|
p.translate(img->width() / 2, img->height() / 2);
|
||||||
|
p.rotate(90 * rotation);
|
||||||
|
|
||||||
|
for (int i = -3; i <= 3; ++i) {
|
||||||
|
if (*it) {
|
||||||
|
p.drawPoint(i, -5);
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage AztecBarcode::cropAndScaleCompact(QImage *img, int layerCount)
|
||||||
|
{
|
||||||
|
const auto offset = aztecCompactLayerOffset[layerCount - 1];
|
||||||
|
const auto minSize = CompactMaxSize - 2 * offset;
|
||||||
|
|
||||||
|
QImage out(minSize, minSize, img->format());
|
||||||
|
QPainter p(&out);
|
||||||
|
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||||
|
const auto srcRect = img->rect().adjusted(offset, offset, -offset, -offset);
|
||||||
|
p.drawImage(out.rect(), *img, srcRect);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_AZTECBARCODE_H
|
||||||
|
#define PRISON_AZTECBARCODE_H
|
||||||
|
|
||||||
|
#include "abstractbarcode_p.h"
|
||||||
|
|
||||||
|
class AztecBarcodeTest;
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
class BitVector;
|
||||||
|
|
||||||
|
/** Aztec code generator. */
|
||||||
|
class AztecBarcode : public AbstractBarcodePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AztecBarcode();
|
||||||
|
~AztecBarcode() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QImage paintImage() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ::AztecBarcodeTest;
|
||||||
|
|
||||||
|
BitVector aztecEncode(const QByteArray &data) const;
|
||||||
|
BitVector bitStuffAndPad(const BitVector &input, int codeWordSize) const;
|
||||||
|
|
||||||
|
void paintFullGrid(QImage *img) const;
|
||||||
|
void paintFullData(QImage *img, const BitVector &data, int layerCount) const;
|
||||||
|
void paintFullModeMessage(QImage *img, const BitVector &modeData) const;
|
||||||
|
QImage cropAndScaleFull(QImage *img, int layerCount);
|
||||||
|
|
||||||
|
void paintCompactGrid(QImage *img) const;
|
||||||
|
void paintCompactData(QImage *img, const BitVector &data, int layerCount) const;
|
||||||
|
void paintCompactModeMessage(QImage *img, const BitVector &modeData) const;
|
||||||
|
QImage cropAndScaleCompact(QImage *img, int layerCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PRISON_AZTECCODE_H
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2010-2016 Sune Vuorela <sune@vuorela.dk>
|
||||||
|
SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config-prison.h>
|
||||||
|
|
||||||
|
#include "barcode.h"
|
||||||
|
|
||||||
|
#include "abstractbarcode_p.h"
|
||||||
|
#include "aztecbarcode_p.h"
|
||||||
|
#include "code128barcode_p.h"
|
||||||
|
#include "code39barcode_p.h"
|
||||||
|
#include "code93barcode_p.h"
|
||||||
|
#include "datamatrixbarcode_p.h"
|
||||||
|
#include "pdf417barcode_p.h"
|
||||||
|
#include "qrcodebarcode_p.h"
|
||||||
|
#if HAVE_ZXING
|
||||||
|
#include "zxingonedbarcode_p.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
std::optional<Barcode> Barcode::create(Prison::BarcodeType format)
|
||||||
|
{
|
||||||
|
std::unique_ptr<AbstractBarcodePrivate> d;
|
||||||
|
switch (format) {
|
||||||
|
case Prison::QRCode:
|
||||||
|
d = std::make_unique<QRCodeBarcode>();
|
||||||
|
break;
|
||||||
|
case Prison::DataMatrix:
|
||||||
|
#if HAVE_DMTX
|
||||||
|
d = std::make_unique<DataMatrixBarcode>();
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case Prison::Aztec:
|
||||||
|
d = std::make_unique<AztecBarcode>();
|
||||||
|
break;
|
||||||
|
case Prison::Code39:
|
||||||
|
d = std::make_unique<Code39Barcode>();
|
||||||
|
break;
|
||||||
|
case Prison::Code93:
|
||||||
|
d = std::make_unique<Code93Barcode>();
|
||||||
|
break;
|
||||||
|
case Prison::Code128:
|
||||||
|
d = std::make_unique<Code128Barcode>();
|
||||||
|
break;
|
||||||
|
case Prison::PDF417:
|
||||||
|
#if HAVE_ZXING
|
||||||
|
d = std::make_unique<Pdf417Barcode>();
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case Prison::EAN13:
|
||||||
|
#if HAVE_ZXING
|
||||||
|
d = std::make_unique<ZXingOneDBarcode<ZXing::BarcodeFormat::EAN13>>();
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d) {
|
||||||
|
d->m_format = format;
|
||||||
|
return Barcode(std::move(d));
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Barcode::Barcode(std::unique_ptr<AbstractBarcodePrivate> &&dd)
|
||||||
|
: d(std::move(dd))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Barcode::Barcode(Barcode &&) = default;
|
||||||
|
Barcode::~Barcode() = default;
|
||||||
|
Barcode &Barcode::operator=(Barcode &&) = default;
|
||||||
|
|
||||||
|
Prison::BarcodeType Barcode::format() const
|
||||||
|
{
|
||||||
|
return d->m_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Barcode::data() const
|
||||||
|
{
|
||||||
|
return d->m_data.userType() == QMetaType::QString ? d->m_data.toString() : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Barcode::byteArrayData() const
|
||||||
|
{
|
||||||
|
return d->m_data.userType() == QMetaType::QByteArray ? d->m_data.toByteArray() : QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Barcode::toImage(const QSizeF &size)
|
||||||
|
{
|
||||||
|
d->recompute();
|
||||||
|
if (d->m_cache.isNull() || d->sizeTooSmall(size)) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// scale to the requested size, using only full integer factors to keep the code readable
|
||||||
|
int scaleX = std::max<int>(1, size.width() / d->m_cache.width());
|
||||||
|
int scaleY = std::max<int>(1, size.height() / d->m_cache.height());
|
||||||
|
if (dimensions() == TwoDimensions) {
|
||||||
|
scaleX = scaleY = std::min(scaleX, scaleY);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage out(d->m_cache.width() * scaleX, d->m_cache.height() * scaleY, d->m_cache.format());
|
||||||
|
QPainter p(&out);
|
||||||
|
p.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
||||||
|
p.drawImage(out.rect(), d->m_cache, d->m_cache.rect());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Barcode::setData(const QString &data)
|
||||||
|
{
|
||||||
|
if (d) {
|
||||||
|
d->m_data = data;
|
||||||
|
d->m_cache = QImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Barcode::setData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
d->m_data = data;
|
||||||
|
d->m_cache = QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF Barcode::minimumSize() const
|
||||||
|
{
|
||||||
|
d->recompute();
|
||||||
|
return d->m_cache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF Barcode::preferredSize(qreal devicePixelRatio) const
|
||||||
|
{
|
||||||
|
d->recompute();
|
||||||
|
return d->preferredSize(devicePixelRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Barcode::backgroundColor() const
|
||||||
|
{
|
||||||
|
return d->m_background;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor Barcode::foregroundColor() const
|
||||||
|
{
|
||||||
|
return d->m_foreground;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Barcode::setBackgroundColor(const QColor &backgroundcolor)
|
||||||
|
{
|
||||||
|
if (backgroundcolor != backgroundColor()) {
|
||||||
|
d->m_background = backgroundcolor;
|
||||||
|
d->m_cache = QImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Barcode::setForegroundColor(const QColor &foregroundcolor)
|
||||||
|
{
|
||||||
|
if (foregroundcolor != foregroundColor()) {
|
||||||
|
d->m_foreground = foregroundcolor;
|
||||||
|
d->m_cache = QImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Barcode::Dimensions Barcode::dimensions() const
|
||||||
|
{
|
||||||
|
return d->m_dimension;
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2010-2016 Sune Vuorela <sune@vuorela.dk>
|
||||||
|
SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_BARCODE_H
|
||||||
|
#define PRISON_BARCODE_H
|
||||||
|
|
||||||
|
#include "prison_export.h"
|
||||||
|
|
||||||
|
#include "prison.h"
|
||||||
|
#include <qglobal.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class QByteArray;
|
||||||
|
class QColor;
|
||||||
|
class QImage;
|
||||||
|
class QSizeF;
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
|
||||||
|
class AbstractBarcodePrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A barcode generator for a fixed barcode format.
|
||||||
|
*
|
||||||
|
* @note This replaces Prison::createBarcode and AbstractBarcode* from KF5.
|
||||||
|
* You can create Barcode instances directly now and specify the format in its
|
||||||
|
* constructor.
|
||||||
|
* Rather than checking for createBarcode returning a @c nullptr, check whether
|
||||||
|
* the format is not Prison::Null.
|
||||||
|
*
|
||||||
|
* @since 6.0
|
||||||
|
*/
|
||||||
|
class PRISON_EXPORT Barcode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Barcode(Barcode &&);
|
||||||
|
~Barcode();
|
||||||
|
Barcode &operator=(Barcode &&);
|
||||||
|
|
||||||
|
/** Barcode format of this barcode generator. */
|
||||||
|
Prison::BarcodeType format() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Textual content encoded in this barcode.
|
||||||
|
* This returns an empty QString if binary content is set.
|
||||||
|
* @see byteArrayData()
|
||||||
|
*/
|
||||||
|
QString data() const;
|
||||||
|
/**
|
||||||
|
* Binary data encoded in this barcode.
|
||||||
|
* This returns an empty QByteArray if textual content is set.
|
||||||
|
* @see data()
|
||||||
|
* @since 5.85
|
||||||
|
*/
|
||||||
|
QByteArray byteArrayData() const;
|
||||||
|
/**
|
||||||
|
* Sets textual data to be drawn as a barcode.
|
||||||
|
* Only use this function if your content is textual, use the QByteArray overload
|
||||||
|
* when your content contains non-textual binary content.
|
||||||
|
* Calling this function does not do any repaints of anything, they are
|
||||||
|
* your own responsibility.
|
||||||
|
* @param data textual barcode content
|
||||||
|
*/
|
||||||
|
void setData(const QString &data);
|
||||||
|
/**
|
||||||
|
* Sets binary data to be drawn as a barcode.
|
||||||
|
* Prefer the QString overload if your content is purely textual, to reduce
|
||||||
|
* the risk of encoding issues for non-ASCII content.
|
||||||
|
* Calling this function does not do any repaints of anything, they are
|
||||||
|
* your own responsibility.
|
||||||
|
* @param data binary barcode content
|
||||||
|
* @since 5.85
|
||||||
|
*/
|
||||||
|
void setData(const QByteArray &data);
|
||||||
|
/**
|
||||||
|
* Creates a image with a barcode on
|
||||||
|
* @return QImage with a barcode on, trying to match the requested \param size
|
||||||
|
*
|
||||||
|
* If one of the dimensions of @param size is smaller than the matching dimension in \ref minimumSize,
|
||||||
|
* a null QImage will be returned
|
||||||
|
*/
|
||||||
|
QImage toImage(const QSizeF &size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimal amount of pixels needed to represent this barcode without loss of information.
|
||||||
|
* That is, the size of the barcode image if each line or dot is just one pixel wide.
|
||||||
|
* On normal screens that is not enough for barcode scanners to reliably detect the barcode
|
||||||
|
* though.
|
||||||
|
* @see preferredSize
|
||||||
|
*/
|
||||||
|
QSizeF minimumSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The recommended size for this barcode when shown on a screen.
|
||||||
|
* This is typically significantly larger than trueMinimumSize() so that
|
||||||
|
* barcode scanners tend to reliably detect the code. As this depends
|
||||||
|
* on the physical resolution of the output, you have to pass the device
|
||||||
|
* pixel ration of the output screen here.
|
||||||
|
* @param devicePixelRatio The device pixel ratio of the screen this is shown on.
|
||||||
|
* @see trueMinimumSize
|
||||||
|
* @since 5.69
|
||||||
|
*/
|
||||||
|
QSizeF preferredSize(qreal devicePixelRatio) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the foreground color (by default black) to be used for the barcode.
|
||||||
|
*/
|
||||||
|
QColor foregroundColor() const;
|
||||||
|
/**
|
||||||
|
* @return the background color (by default white) to be used for the barcode.
|
||||||
|
*/
|
||||||
|
QColor backgroundColor() const;
|
||||||
|
/**
|
||||||
|
* sets the foreground color
|
||||||
|
* @param foregroundcolor - the new foreground color
|
||||||
|
*/
|
||||||
|
void setForegroundColor(const QColor &foregroundcolor);
|
||||||
|
/**
|
||||||
|
* sets the background color
|
||||||
|
* @param backgroundcolor - the new background color
|
||||||
|
*/
|
||||||
|
void setBackgroundColor(const QColor &backgroundcolor);
|
||||||
|
|
||||||
|
/** Dimensions of the barcode. */
|
||||||
|
enum Dimensions : uint8_t {
|
||||||
|
NoDimensions, ///< Null barcode.
|
||||||
|
OneDimension, ///< One-dimensional barcode.
|
||||||
|
TwoDimensions, ///< 2D matrix code.
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Returns the amount of dimensions of the barcode. */
|
||||||
|
Dimensions dimensions() const;
|
||||||
|
|
||||||
|
/** Create a new barcode generator.
|
||||||
|
*
|
||||||
|
* If a format is requested that is not supported by the current build
|
||||||
|
* due to missing/disabled optional dependencies, Barcode::format() will
|
||||||
|
* return Prison::Null.
|
||||||
|
*/
|
||||||
|
static std::optional<Prison::Barcode> create(Prison::BarcodeType type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class AbstractBarcodePrivate;
|
||||||
|
explicit Barcode(std::unique_ptr<AbstractBarcodePrivate> &&d);
|
||||||
|
std::unique_ptr<class AbstractBarcodePrivate> d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2020 Laurent Montel <montel@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#include "barcodeutil_p.h"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
QList<bool> BarCodeUtil::barSequence(const char *str)
|
||||||
|
{
|
||||||
|
Q_ASSERT(strlen(str) == 9); // this is a internal helper tool, only called with fixed strings in here, all 9 chars long
|
||||||
|
QList<bool> ret;
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
ret.append(str[i] == '1');
|
||||||
|
Q_ASSERT(str[i] == '0' || str[i] == '1');
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray BarCodeUtil::asLatin1ByteArray(const QVariant &data)
|
||||||
|
{
|
||||||
|
if (data.typeId() == QMetaType::QString) {
|
||||||
|
return data.toString().toLatin1();
|
||||||
|
}
|
||||||
|
return data.toByteArray();
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2020 Laurent Montel <montel@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BARCODEUTIL_H
|
||||||
|
#define BARCODEUTIL_H
|
||||||
|
#include <QList>
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
namespace BarCodeUtil
|
||||||
|
{
|
||||||
|
QList<bool> barSequence(const char *str);
|
||||||
|
|
||||||
|
QByteArray asLatin1ByteArray(const QVariant &data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BARCODEUTIL_H
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bitvector_p.h"
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
BitVector::BitVector() = default;
|
||||||
|
BitVector::~BitVector() = default;
|
||||||
|
|
||||||
|
void BitVector::appendLSB(int data, int bits)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bits; ++i) {
|
||||||
|
appendBit(data & (1 << i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitVector::appendMSB(int data, int bits)
|
||||||
|
{
|
||||||
|
for (int i = bits - 1; i >= 0; --i) {
|
||||||
|
appendBit(data & (1 << i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitVector::appendBit(bool bit)
|
||||||
|
{
|
||||||
|
const auto subIdx = m_size % 8;
|
||||||
|
if (subIdx == 0) {
|
||||||
|
m_data.append('\0');
|
||||||
|
}
|
||||||
|
if (bit) {
|
||||||
|
m_data.data()[m_data.size() - 1] |= (1 << subIdx);
|
||||||
|
}
|
||||||
|
++m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitVector::append(const BitVector &other)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < other.size(); ++i) {
|
||||||
|
appendBit(other.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BitVector::at(int index) const
|
||||||
|
{
|
||||||
|
const auto majIdx = index / 8;
|
||||||
|
const auto minIdx = index % 8;
|
||||||
|
return (m_data.at(majIdx) & (1 << minIdx)) >> minIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitVector::clear()
|
||||||
|
{
|
||||||
|
m_data.clear();
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitVector::reserve(int size)
|
||||||
|
{
|
||||||
|
m_data.reserve((size / 8) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BitVector::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BitVector::valueAtMSB(int index, int size) const
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
res = res << 1;
|
||||||
|
res |= (at(index + i) ? 1 : 0);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector::iterator BitVector::begin() const
|
||||||
|
{
|
||||||
|
iterator it;
|
||||||
|
it.m_index = 0;
|
||||||
|
it.m_vector = this;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector::iterator BitVector::end() const
|
||||||
|
{
|
||||||
|
iterator it;
|
||||||
|
it.m_index = m_size;
|
||||||
|
it.m_vector = this;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BitVector::operator==(const BitVector &other) const
|
||||||
|
{
|
||||||
|
return m_size == other.m_size && m_data == other.m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BitVector::operator!=(const Prison::BitVector &other) const
|
||||||
|
{
|
||||||
|
return m_size != other.m_size || m_data != other.m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const Prison::BitVector &v)
|
||||||
|
{
|
||||||
|
dbg << v.m_data.toHex();
|
||||||
|
return dbg;
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_BITVECTOR_P_H
|
||||||
|
#define PRISON_BITVECTOR_P_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
class BitVector;
|
||||||
|
}
|
||||||
|
QDebug operator<<(QDebug dbg, const Prison::BitVector &v);
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
/** Vector for working with a set of bits without byte alignment. */
|
||||||
|
class BitVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BitVector();
|
||||||
|
~BitVector();
|
||||||
|
|
||||||
|
class iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline bool operator!=(const iterator &other)
|
||||||
|
{
|
||||||
|
return m_index != other.m_index;
|
||||||
|
}
|
||||||
|
inline bool operator*() const
|
||||||
|
{
|
||||||
|
return m_vector->at(m_index);
|
||||||
|
}
|
||||||
|
inline iterator operator++()
|
||||||
|
{
|
||||||
|
++m_index;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class BitVector;
|
||||||
|
const BitVector *m_vector;
|
||||||
|
int m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Append the lowest @p bits of @p data with the least significant bit first. */
|
||||||
|
void appendLSB(int data, int bits);
|
||||||
|
/** Append the lowest @p bits of @p data with the most significant bit first. */
|
||||||
|
void appendMSB(int data, int bits);
|
||||||
|
void appendBit(bool bit);
|
||||||
|
void append(const BitVector &other);
|
||||||
|
/** Returns the bit at index @p index. */
|
||||||
|
bool at(int index) const;
|
||||||
|
void clear();
|
||||||
|
void reserve(int size);
|
||||||
|
int size() const;
|
||||||
|
/** Returns the value starting at @p index of size @p size. */
|
||||||
|
int valueAtMSB(int index, int size) const;
|
||||||
|
iterator begin() const;
|
||||||
|
iterator end() const;
|
||||||
|
|
||||||
|
bool operator==(const BitVector &other) const;
|
||||||
|
bool operator!=(const BitVector &other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend QDebug(::operator<<)(QDebug dbg, const Prison::BitVector &v);
|
||||||
|
QByteArray m_data;
|
||||||
|
int m_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PRISON_BITVECTOR_P_H
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "code128barcode_p.h"
|
||||||
|
|
||||||
|
#include "barcodeutil_p.h"
|
||||||
|
#include "bitvector_p.h"
|
||||||
|
#include "prison_debug.h"
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SymbolSize = 11,
|
||||||
|
StopPatternSize = 13,
|
||||||
|
StopPattern = 108,
|
||||||
|
QuietZone = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CodeSet : uint8_t {
|
||||||
|
CodeSetA = 0,
|
||||||
|
CodeSetB = 1,
|
||||||
|
CodeSetC = 2,
|
||||||
|
CodeSetUnknown = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CodeSetOp : uint8_t {
|
||||||
|
None = 255,
|
||||||
|
StartA = 103,
|
||||||
|
StartB = 104,
|
||||||
|
StartC = 105,
|
||||||
|
Shift = 98,
|
||||||
|
LatchA = 101,
|
||||||
|
LatchB = 100,
|
||||||
|
LatchC = 99,
|
||||||
|
};
|
||||||
|
|
||||||
|
Code128Barcode::Code128Barcode()
|
||||||
|
: AbstractBarcodePrivate(Barcode::OneDimension)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Code128Barcode::~Code128Barcode() = default;
|
||||||
|
|
||||||
|
QImage Code128Barcode::paintImage()
|
||||||
|
{
|
||||||
|
const auto bits = encode(BarCodeUtil::asLatin1ByteArray(m_data));
|
||||||
|
const auto width = bits.size() + 2 * QuietZone;
|
||||||
|
|
||||||
|
QImage img(width, 1, QImage::Format_ARGB32);
|
||||||
|
img.fill(m_background);
|
||||||
|
QPainter p(&img);
|
||||||
|
for (int i = 0; i < bits.size(); ++i) {
|
||||||
|
if (bits.at(i)) {
|
||||||
|
img.setPixel(QuietZone + i, 0, m_foreground.rgb());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code 128 symbol table
|
||||||
|
static const uint16_t code128_symbols[] = {
|
||||||
|
0b11011001100, // 0
|
||||||
|
0b11001101100,
|
||||||
|
0b11001100110,
|
||||||
|
0b10010011000,
|
||||||
|
0b10010001100,
|
||||||
|
0b10001001100,
|
||||||
|
0b10011001000,
|
||||||
|
0b10011000100,
|
||||||
|
0b10001100100,
|
||||||
|
0b11001001000,
|
||||||
|
0b11001000100, // 10
|
||||||
|
0b11000100100,
|
||||||
|
0b10110011100,
|
||||||
|
0b10011011100,
|
||||||
|
0b10011001110,
|
||||||
|
0b10111001100,
|
||||||
|
0b10011101100,
|
||||||
|
0b10011100110,
|
||||||
|
0b11001110010,
|
||||||
|
0b11001011100,
|
||||||
|
0b11001001110, // 20
|
||||||
|
0b11011100100,
|
||||||
|
0b11001110100,
|
||||||
|
0b11101101110,
|
||||||
|
0b11101001100,
|
||||||
|
0b11100101100,
|
||||||
|
0b11100100110,
|
||||||
|
0b11101100100,
|
||||||
|
0b11100110100,
|
||||||
|
0b11100110010,
|
||||||
|
0b11011011000, // 30
|
||||||
|
0b11011000110,
|
||||||
|
0b11000110110,
|
||||||
|
0b10100011000,
|
||||||
|
0b10001011000,
|
||||||
|
0b10001000110,
|
||||||
|
0b10110001000,
|
||||||
|
0b10001101000,
|
||||||
|
0b10001100010,
|
||||||
|
0b11010001000,
|
||||||
|
0b11000101000, // 40
|
||||||
|
0b11000100010,
|
||||||
|
0b10110111000,
|
||||||
|
0b10110001110,
|
||||||
|
0b10001101110,
|
||||||
|
0b10111011000,
|
||||||
|
0b10111000110,
|
||||||
|
0b10001110110,
|
||||||
|
0b11101110110,
|
||||||
|
0b11010001110,
|
||||||
|
0b11000101110, // 50
|
||||||
|
0b11011101000,
|
||||||
|
0b11011100010,
|
||||||
|
0b11011101110,
|
||||||
|
0b11101011000,
|
||||||
|
0b11101000110,
|
||||||
|
0b11100010110,
|
||||||
|
0b11101101000,
|
||||||
|
0b11101100010,
|
||||||
|
0b11100011010,
|
||||||
|
0b11101111010, // 60
|
||||||
|
0b11001000010,
|
||||||
|
0b11110001010,
|
||||||
|
0b10100110000,
|
||||||
|
0b10100001100,
|
||||||
|
0b10010110000,
|
||||||
|
0b10010000110,
|
||||||
|
0b10000101100,
|
||||||
|
0b10000100110,
|
||||||
|
0b10110010000,
|
||||||
|
0b10110000100, // 70
|
||||||
|
0b10011010000,
|
||||||
|
0b10011000010,
|
||||||
|
0b10000110100,
|
||||||
|
0b10000110010,
|
||||||
|
0b11000010010,
|
||||||
|
0b11001010000,
|
||||||
|
0b11110111010,
|
||||||
|
0b11000010100,
|
||||||
|
0b10001111010,
|
||||||
|
0b10100111100, // 80
|
||||||
|
0b10010111100,
|
||||||
|
0b10010011110,
|
||||||
|
0b10111100100,
|
||||||
|
0b10011110100,
|
||||||
|
0b10011110010,
|
||||||
|
0b11110100100,
|
||||||
|
0b11110010100,
|
||||||
|
0b11110010010,
|
||||||
|
0b11011011110,
|
||||||
|
0b11011110110, // 90
|
||||||
|
0b11110110110,
|
||||||
|
0b10101111000,
|
||||||
|
0b10100011110,
|
||||||
|
0b10001011110,
|
||||||
|
0b10111101000,
|
||||||
|
0b10111100010,
|
||||||
|
0b11110101000,
|
||||||
|
0b11110100010,
|
||||||
|
0b10111011110,
|
||||||
|
0b10111101110, // 100
|
||||||
|
0b11101011110,
|
||||||
|
0b11110101110,
|
||||||
|
0b11010000100,
|
||||||
|
0b11010010000,
|
||||||
|
0b11010011100,
|
||||||
|
0b11000111010,
|
||||||
|
0b11010111000,
|
||||||
|
0b1100011101011,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t symbolForCharacter(const QByteArray &data, int index, CodeSet set)
|
||||||
|
{
|
||||||
|
const auto c1 = data.at(index);
|
||||||
|
switch (set) {
|
||||||
|
case CodeSetA:
|
||||||
|
return (c1 < ' ') ? c1 + 64 : c1 - ' ';
|
||||||
|
case CodeSetB:
|
||||||
|
return c1 - ' ';
|
||||||
|
case CodeSetC: {
|
||||||
|
const auto c2 = data.at(index + 1);
|
||||||
|
return ((c1 - '0') * 10) + c2 - '0';
|
||||||
|
}
|
||||||
|
case CodeSetUnknown:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CodeSetChange {
|
||||||
|
CodeSet set;
|
||||||
|
CodeSetOp symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool isInCodeSetA(char c)
|
||||||
|
{
|
||||||
|
return c <= 95;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isInCodeSetB(char c)
|
||||||
|
{
|
||||||
|
// ### this does not consider FNC4 high byte encoding
|
||||||
|
return c >= 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CodeSetChange opForData(const QByteArray &data, int index, CodeSet currentSet)
|
||||||
|
{
|
||||||
|
// determine if Code C makes sense at this point
|
||||||
|
int codeC = 0;
|
||||||
|
for (int i = index; i < data.size(); ++i, ++codeC) {
|
||||||
|
if (data.at(i) < '0' || data.at(i) > '9') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentSet == CodeSetC && codeC >= 2) { // already in C
|
||||||
|
return {CodeSetC, None};
|
||||||
|
}
|
||||||
|
if (codeC >= 6 // that's always good enough
|
||||||
|
|| (index == 0 && codeC >= 4) // beginning of data
|
||||||
|
|| (index + codeC == data.size() && codeC >= 4) // end of data
|
||||||
|
|| (codeC == data.size() && codeC == 2) // 2 ...
|
||||||
|
|| (codeC == data.size() && codeC == 4)) // ... or 4 as the entire data
|
||||||
|
{
|
||||||
|
return currentSet == CodeSetUnknown ? CodeSetChange{CodeSetC, StartC} : CodeSetChange{CodeSetC, LatchC};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are in Code A or Code B, check if we need to switch for the next char
|
||||||
|
// this is a shortcut to prevent the below more extensive search from making this O(n²) in the common case
|
||||||
|
if ((currentSet == CodeSetA && isInCodeSetA(data.at(index))) || (currentSet == CodeSetB && isInCodeSetB(data.at(index)))) {
|
||||||
|
return {currentSet, None};
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to switch to A or B, select which one, and select whether to use start, shift or latch
|
||||||
|
const auto nextA = isInCodeSetA(data.at(index));
|
||||||
|
const auto nextB = isInCodeSetB(data.at(index));
|
||||||
|
|
||||||
|
// count how many following characters we could encode in A or B
|
||||||
|
int countA = 0;
|
||||||
|
for (int i = index + 1; i < data.size(); ++i, ++countA) {
|
||||||
|
if (!isInCodeSetA(data.at(i))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int countB = 0;
|
||||||
|
for (int i = index + 1; i < data.size(); ++i, ++countB) {
|
||||||
|
if (!isInCodeSetB(data.at(i))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// select how we want to switch to Code A or Code B, biased to B as that's the more useful one in general
|
||||||
|
switch (currentSet) {
|
||||||
|
case CodeSetUnknown:
|
||||||
|
// if we are at the start, take whichever code will get us further, or the only one that works
|
||||||
|
if (nextA && nextB) {
|
||||||
|
return countA > countB ? CodeSetChange{CodeSetA, StartA} : CodeSetChange{CodeSetB, StartB};
|
||||||
|
}
|
||||||
|
return nextA ? CodeSetChange{CodeSetA, StartA} : CodeSetChange{CodeSetB, StartB};
|
||||||
|
case CodeSetC:
|
||||||
|
// same for Code C
|
||||||
|
if (nextA && nextB) {
|
||||||
|
return countA > countB ? CodeSetChange{CodeSetA, LatchA} : CodeSetChange{CodeSetB, LatchB};
|
||||||
|
}
|
||||||
|
return nextA ? CodeSetChange{CodeSetA, LatchA} : CodeSetChange{CodeSetB, LatchB};
|
||||||
|
case CodeSetA:
|
||||||
|
// switch or latch to B?
|
||||||
|
return CodeSetChange{CodeSetB, countB >= countA ? LatchB : Shift};
|
||||||
|
case CodeSetB:
|
||||||
|
// switch or latch to A?
|
||||||
|
return CodeSetChange{CodeSetA, countA > countB ? LatchA : Shift};
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
return CodeSetChange{currentSet, None};
|
||||||
|
}
|
||||||
|
|
||||||
|
BitVector Code128Barcode::encode(const QByteArray &data) const
|
||||||
|
{
|
||||||
|
BitVector v;
|
||||||
|
if (data.isEmpty()) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine code set for start
|
||||||
|
const auto op = opForData(data, 0, CodeSetUnknown);
|
||||||
|
auto currentSet = op.set;
|
||||||
|
|
||||||
|
// write start code
|
||||||
|
qCDebug(Log) << "start symbol:" << op.symbol << code128_symbols[op.symbol];
|
||||||
|
v.appendMSB(code128_symbols[op.symbol], SymbolSize);
|
||||||
|
|
||||||
|
uint32_t checksum = op.symbol;
|
||||||
|
uint32_t checksumWeight = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.size(); i += currentSet == CodeSetC ? 2 : 1) {
|
||||||
|
if (static_cast<uint8_t>(data.at(i)) > 127) { // FNC4 encoding not implemented yet
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform code switch if needed
|
||||||
|
const auto op = opForData(data, i, currentSet);
|
||||||
|
if (op.symbol != None) {
|
||||||
|
qCDebug(Log) << "op symbol:" << op.symbol << code128_symbols[op.symbol];
|
||||||
|
v.appendMSB(code128_symbols[op.symbol], SymbolSize);
|
||||||
|
checksum += op.symbol * checksumWeight++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode current symbol
|
||||||
|
const auto symbol = symbolForCharacter(data, i, op.set);
|
||||||
|
qCDebug(Log) << "data symbol:" << symbol << code128_symbols[symbol];
|
||||||
|
v.appendMSB(code128_symbols[symbol], SymbolSize);
|
||||||
|
checksum += symbol * checksumWeight++;
|
||||||
|
|
||||||
|
// update current code set
|
||||||
|
if (op.symbol != Shift) {
|
||||||
|
currentSet = op.set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode checksum
|
||||||
|
qCDebug(Log) << "checksum:" << checksum << code128_symbols[checksum % 103];
|
||||||
|
v.appendMSB(code128_symbols[checksum % 103], SymbolSize);
|
||||||
|
|
||||||
|
// add stop pattern
|
||||||
|
v.appendMSB(code128_symbols[StopPattern], StopPatternSize);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_CODE128BARCODE_H
|
||||||
|
#define PRISON_CODE128BARCODE_H
|
||||||
|
|
||||||
|
#include "abstractbarcode_p.h"
|
||||||
|
|
||||||
|
class Code128BarcodeTest;
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
class BitVector;
|
||||||
|
|
||||||
|
/** Code 128 barcode
|
||||||
|
* @see https://en.wikipedia.org/wiki/Code_128
|
||||||
|
*/
|
||||||
|
class Code128Barcode : public AbstractBarcodePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Code128Barcode();
|
||||||
|
~Code128Barcode() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QImage paintImage() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ::Code128BarcodeTest;
|
||||||
|
BitVector encode(const QByteArray &data) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PRISON_CODE128BARCODE_H
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2011 Geoffry Song <goffrie@gmail.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "barcodeutil_p.h"
|
||||||
|
#include "code39barcode_p.h"
|
||||||
|
#include <QChar>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
static QList<bool> sequenceForChar(ushort c)
|
||||||
|
{
|
||||||
|
switch (QChar::toUpper(c)) {
|
||||||
|
case '0':
|
||||||
|
return BarCodeUtil::barSequence("000110100");
|
||||||
|
case '1':
|
||||||
|
return BarCodeUtil::barSequence("100100001");
|
||||||
|
case '2':
|
||||||
|
return BarCodeUtil::barSequence("001100001");
|
||||||
|
case '3':
|
||||||
|
return BarCodeUtil::barSequence("101100000");
|
||||||
|
case '4':
|
||||||
|
return BarCodeUtil::barSequence("000110001");
|
||||||
|
case '5':
|
||||||
|
return BarCodeUtil::barSequence("100110000");
|
||||||
|
case '6':
|
||||||
|
return BarCodeUtil::barSequence("001110000");
|
||||||
|
case '7':
|
||||||
|
return BarCodeUtil::barSequence("000100101");
|
||||||
|
case '8':
|
||||||
|
return BarCodeUtil::barSequence("100100100");
|
||||||
|
case '9':
|
||||||
|
return BarCodeUtil::barSequence("001100100");
|
||||||
|
case 'A':
|
||||||
|
return BarCodeUtil::barSequence("100001001");
|
||||||
|
case 'B':
|
||||||
|
return BarCodeUtil::barSequence("001001001");
|
||||||
|
case 'C':
|
||||||
|
return BarCodeUtil::barSequence("101001000");
|
||||||
|
case 'D':
|
||||||
|
return BarCodeUtil::barSequence("000011001");
|
||||||
|
case 'E':
|
||||||
|
return BarCodeUtil::barSequence("100011000");
|
||||||
|
case 'F':
|
||||||
|
return BarCodeUtil::barSequence("001011000");
|
||||||
|
case 'G':
|
||||||
|
return BarCodeUtil::barSequence("000001101");
|
||||||
|
case 'H':
|
||||||
|
return BarCodeUtil::barSequence("100001100");
|
||||||
|
case 'I':
|
||||||
|
return BarCodeUtil::barSequence("001001100");
|
||||||
|
case 'J':
|
||||||
|
return BarCodeUtil::barSequence("000011100");
|
||||||
|
case 'K':
|
||||||
|
return BarCodeUtil::barSequence("100000011");
|
||||||
|
case 'L':
|
||||||
|
return BarCodeUtil::barSequence("001000011");
|
||||||
|
case 'M':
|
||||||
|
return BarCodeUtil::barSequence("101000010");
|
||||||
|
case 'N':
|
||||||
|
return BarCodeUtil::barSequence("000010011");
|
||||||
|
case 'O':
|
||||||
|
return BarCodeUtil::barSequence("100010010");
|
||||||
|
case 'P':
|
||||||
|
return BarCodeUtil::barSequence("001010010");
|
||||||
|
case 'Q':
|
||||||
|
return BarCodeUtil::barSequence("000000111");
|
||||||
|
case 'R':
|
||||||
|
return BarCodeUtil::barSequence("100000110");
|
||||||
|
case 'S':
|
||||||
|
return BarCodeUtil::barSequence("001000110");
|
||||||
|
case 'T':
|
||||||
|
return BarCodeUtil::barSequence("000010110");
|
||||||
|
case 'U':
|
||||||
|
return BarCodeUtil::barSequence("110000001");
|
||||||
|
case 'V':
|
||||||
|
return BarCodeUtil::barSequence("011000001");
|
||||||
|
case 'W':
|
||||||
|
return BarCodeUtil::barSequence("111000000");
|
||||||
|
case 'X':
|
||||||
|
return BarCodeUtil::barSequence("010010001");
|
||||||
|
case 'Y':
|
||||||
|
return BarCodeUtil::barSequence("110010000");
|
||||||
|
case 'Z':
|
||||||
|
return BarCodeUtil::barSequence("011010000");
|
||||||
|
case '-':
|
||||||
|
return BarCodeUtil::barSequence("010000101");
|
||||||
|
case '.':
|
||||||
|
return BarCodeUtil::barSequence("110000100");
|
||||||
|
case ' ':
|
||||||
|
return BarCodeUtil::barSequence("011000100");
|
||||||
|
case '$':
|
||||||
|
return BarCodeUtil::barSequence("010101000");
|
||||||
|
case '/':
|
||||||
|
return BarCodeUtil::barSequence("010100010");
|
||||||
|
case '+':
|
||||||
|
return BarCodeUtil::barSequence("010001010");
|
||||||
|
case '%':
|
||||||
|
return BarCodeUtil::barSequence("000101010");
|
||||||
|
default:
|
||||||
|
return QList<bool>(); // unknown character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Code39Barcode::Code39Barcode()
|
||||||
|
: AbstractBarcodePrivate(Barcode::OneDimension)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Code39Barcode::~Code39Barcode() = default;
|
||||||
|
|
||||||
|
QImage Code39Barcode::paintImage()
|
||||||
|
{
|
||||||
|
QList<bool> barcode;
|
||||||
|
// convert text into sequences of wide/narrow bars
|
||||||
|
{
|
||||||
|
// the guard sequence that goes on each end
|
||||||
|
const QList<bool> endSequence = BarCodeUtil::barSequence("010010100");
|
||||||
|
barcode += endSequence;
|
||||||
|
barcode += false;
|
||||||
|
// translate the string
|
||||||
|
const auto str = BarCodeUtil::asLatin1ByteArray(m_data);
|
||||||
|
for (int i = 0; i < str.size(); i++) {
|
||||||
|
QList<bool> b = sequenceForChar(str.at(i));
|
||||||
|
if (!b.empty()) {
|
||||||
|
barcode += b;
|
||||||
|
barcode += false; // add a narrow space between each character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ending guard
|
||||||
|
barcode += endSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
calculate integer bar widths that fit inside `size'
|
||||||
|
each character has 6 narrow bars and 3 wide bars and there is a narrow bar between characters
|
||||||
|
restrictions:
|
||||||
|
*) smallWidth * 2 <= largeWidth <= smallWidth * 3
|
||||||
|
- in other words, the ratio largeWidth:smallWidth is between 3:1 and 2:1
|
||||||
|
*) wide * largeWidth + narrow * smallWidth <= size.width()
|
||||||
|
- the barcode has to fit within the given size
|
||||||
|
*/
|
||||||
|
const int wide = barcode.count(true);
|
||||||
|
const int narrow = barcode.count(false);
|
||||||
|
// wide bar width
|
||||||
|
const int largeWidth = 2;
|
||||||
|
// narrow bar width
|
||||||
|
const int smallWidth = 1;
|
||||||
|
Q_ASSERT(largeWidth > smallWidth);
|
||||||
|
|
||||||
|
const int quietZoneWidth = 10 * smallWidth;
|
||||||
|
|
||||||
|
// one line of the result image
|
||||||
|
QList<QRgb> line;
|
||||||
|
line.reserve(wide * largeWidth + narrow * smallWidth + 2 * quietZoneWidth);
|
||||||
|
line.insert(0, quietZoneWidth, m_background.rgba());
|
||||||
|
for (int i = 0; i < barcode.size(); i++) {
|
||||||
|
const QRgb color = (((i & 1) == 0) ? m_foreground : m_background).rgba(); // alternate between foreground and background color
|
||||||
|
const int width = barcode.at(i) ? largeWidth : smallWidth;
|
||||||
|
for (int j = 0; j < width; j++) {
|
||||||
|
line.append(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line.insert(line.size(), quietZoneWidth, m_background.rgba());
|
||||||
|
|
||||||
|
// build the complete barcode
|
||||||
|
QImage ret(line.size(), 1, QImage::Format_ARGB32);
|
||||||
|
memcpy(ret.scanLine(0), line.data(), line.size() * sizeof(QRgb));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2011 Geoffry Song <goffrie@gmail.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_CODE39BARCODE_H
|
||||||
|
#define PRISON_CODE39BARCODE_H
|
||||||
|
|
||||||
|
#include "abstractbarcode_p.h"
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Code 39 Barcode generator
|
||||||
|
*/
|
||||||
|
class Code39Barcode : public Prison::AbstractBarcodePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* creates a Code 39 generator
|
||||||
|
*/
|
||||||
|
Code39Barcode();
|
||||||
|
~Code39Barcode() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* This function generates the barcode
|
||||||
|
* @return QImage containing a barcode, trying to approximate the requested sizes, or a null QImage if it can't be painted within requested size
|
||||||
|
*/
|
||||||
|
QImage paintImage() override;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif // PRISON_CODE39BARCODE_H
|
||||||
@@ -0,0 +1,665 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2011 Geoffry Song <goffrie@gmail.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "barcodeutil_p.h"
|
||||||
|
#include "code93barcode_p.h"
|
||||||
|
#include <QChar>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
// returns a list of 9 bar colors, where `true' means foreground and `false' means background color
|
||||||
|
static QList<bool> sequenceForID(int id)
|
||||||
|
{
|
||||||
|
switch (id) {
|
||||||
|
case 0:
|
||||||
|
return BarCodeUtil::barSequence("100010100"); // 0-9
|
||||||
|
case 1:
|
||||||
|
return BarCodeUtil::barSequence("101001000");
|
||||||
|
case 2:
|
||||||
|
return BarCodeUtil::barSequence("101000100");
|
||||||
|
case 3:
|
||||||
|
return BarCodeUtil::barSequence("101000010");
|
||||||
|
case 4:
|
||||||
|
return BarCodeUtil::barSequence("100101000");
|
||||||
|
case 5:
|
||||||
|
return BarCodeUtil::barSequence("100100100");
|
||||||
|
case 6:
|
||||||
|
return BarCodeUtil::barSequence("100100010");
|
||||||
|
case 7:
|
||||||
|
return BarCodeUtil::barSequence("101010000");
|
||||||
|
case 8:
|
||||||
|
return BarCodeUtil::barSequence("100010010");
|
||||||
|
case 9:
|
||||||
|
return BarCodeUtil::barSequence("100001010");
|
||||||
|
case 10:
|
||||||
|
return BarCodeUtil::barSequence("110101000"); // A-Z
|
||||||
|
case 11:
|
||||||
|
return BarCodeUtil::barSequence("110100100");
|
||||||
|
case 12:
|
||||||
|
return BarCodeUtil::barSequence("110100010");
|
||||||
|
case 13:
|
||||||
|
return BarCodeUtil::barSequence("110010100");
|
||||||
|
case 14:
|
||||||
|
return BarCodeUtil::barSequence("110010010");
|
||||||
|
case 15:
|
||||||
|
return BarCodeUtil::barSequence("110001010");
|
||||||
|
case 16:
|
||||||
|
return BarCodeUtil::barSequence("101101000");
|
||||||
|
case 17:
|
||||||
|
return BarCodeUtil::barSequence("101100100");
|
||||||
|
case 18:
|
||||||
|
return BarCodeUtil::barSequence("101100010");
|
||||||
|
case 19:
|
||||||
|
return BarCodeUtil::barSequence("100110100");
|
||||||
|
case 20:
|
||||||
|
return BarCodeUtil::barSequence("100011010");
|
||||||
|
case 21:
|
||||||
|
return BarCodeUtil::barSequence("101011000");
|
||||||
|
case 22:
|
||||||
|
return BarCodeUtil::barSequence("101001100");
|
||||||
|
case 23:
|
||||||
|
return BarCodeUtil::barSequence("101000110");
|
||||||
|
case 24:
|
||||||
|
return BarCodeUtil::barSequence("100101100");
|
||||||
|
case 25:
|
||||||
|
return BarCodeUtil::barSequence("100010110");
|
||||||
|
case 26:
|
||||||
|
return BarCodeUtil::barSequence("110110100");
|
||||||
|
case 27:
|
||||||
|
return BarCodeUtil::barSequence("110110010");
|
||||||
|
case 28:
|
||||||
|
return BarCodeUtil::barSequence("110101100");
|
||||||
|
case 29:
|
||||||
|
return BarCodeUtil::barSequence("110100110");
|
||||||
|
case 30:
|
||||||
|
return BarCodeUtil::barSequence("110010110");
|
||||||
|
case 31:
|
||||||
|
return BarCodeUtil::barSequence("110011010");
|
||||||
|
case 32:
|
||||||
|
return BarCodeUtil::barSequence("101101100");
|
||||||
|
case 33:
|
||||||
|
return BarCodeUtil::barSequence("101100110");
|
||||||
|
case 34:
|
||||||
|
return BarCodeUtil::barSequence("100110110");
|
||||||
|
case 35:
|
||||||
|
return BarCodeUtil::barSequence("100111010");
|
||||||
|
case 36:
|
||||||
|
return BarCodeUtil::barSequence("100101110"); // -
|
||||||
|
case 37:
|
||||||
|
return BarCodeUtil::barSequence("111010100"); // .
|
||||||
|
case 38:
|
||||||
|
return BarCodeUtil::barSequence("111010010"); // space
|
||||||
|
case 39:
|
||||||
|
return BarCodeUtil::barSequence("111001010"); // $
|
||||||
|
case 40:
|
||||||
|
return BarCodeUtil::barSequence("101101110"); // /
|
||||||
|
case 41:
|
||||||
|
return BarCodeUtil::barSequence("101110110"); // +
|
||||||
|
case 42:
|
||||||
|
return BarCodeUtil::barSequence("110101110"); // $
|
||||||
|
case 43:
|
||||||
|
return BarCodeUtil::barSequence("100100110"); // ($)
|
||||||
|
case 44:
|
||||||
|
return BarCodeUtil::barSequence("111011010"); // (%)
|
||||||
|
case 45:
|
||||||
|
return BarCodeUtil::barSequence("111010110"); // (/)
|
||||||
|
case 46:
|
||||||
|
return BarCodeUtil::barSequence("100110010"); // (+)
|
||||||
|
case 47:
|
||||||
|
return BarCodeUtil::barSequence("101011110"); // stop sequence
|
||||||
|
default:
|
||||||
|
// unknown ID... shouldn't happen
|
||||||
|
qWarning("Code93Barcode::sequenceForID called with unknown ID");
|
||||||
|
return QList<bool>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the list of IDs that represent a character
|
||||||
|
static QList<int> codesForChar(uint c)
|
||||||
|
{
|
||||||
|
QList<int> ret;
|
||||||
|
switch (c) {
|
||||||
|
case 0:
|
||||||
|
ret += 44;
|
||||||
|
ret += 30;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ret += 43;
|
||||||
|
ret += 10;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ret += 43;
|
||||||
|
ret += 11;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ret += 43;
|
||||||
|
ret += 12;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
ret += 43;
|
||||||
|
ret += 13;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
ret += 43;
|
||||||
|
ret += 14;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
ret += 43;
|
||||||
|
ret += 15;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
ret += 43;
|
||||||
|
ret += 16;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
ret += 43;
|
||||||
|
ret += 17;
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
ret += 43;
|
||||||
|
ret += 18;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
ret += 43;
|
||||||
|
ret += 19;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
ret += 43;
|
||||||
|
ret += 20;
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
ret += 43;
|
||||||
|
ret += 21;
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
ret += 43;
|
||||||
|
ret += 22;
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
ret += 43;
|
||||||
|
ret += 23;
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
ret += 43;
|
||||||
|
ret += 24;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
ret += 43;
|
||||||
|
ret += 25;
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
ret += 43;
|
||||||
|
ret += 26;
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
ret += 43;
|
||||||
|
ret += 27;
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
ret += 43;
|
||||||
|
ret += 28;
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
ret += 43;
|
||||||
|
ret += 29;
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
ret += 43;
|
||||||
|
ret += 30;
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
ret += 43;
|
||||||
|
ret += 31;
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
ret += 43;
|
||||||
|
ret += 32;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
ret += 43;
|
||||||
|
ret += 33;
|
||||||
|
break;
|
||||||
|
case 25:
|
||||||
|
ret += 43;
|
||||||
|
ret += 34;
|
||||||
|
break;
|
||||||
|
case 26:
|
||||||
|
ret += 43;
|
||||||
|
ret += 35;
|
||||||
|
break;
|
||||||
|
case 27:
|
||||||
|
ret += 44;
|
||||||
|
ret += 10;
|
||||||
|
break;
|
||||||
|
case 28:
|
||||||
|
ret += 44;
|
||||||
|
ret += 11;
|
||||||
|
break;
|
||||||
|
case 29:
|
||||||
|
ret += 44;
|
||||||
|
ret += 12;
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
ret += 44;
|
||||||
|
ret += 13;
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
ret += 44;
|
||||||
|
ret += 14;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
ret += 38;
|
||||||
|
break;
|
||||||
|
case 33:
|
||||||
|
ret += 45;
|
||||||
|
ret += 10;
|
||||||
|
break;
|
||||||
|
case 34:
|
||||||
|
ret += 45;
|
||||||
|
ret += 11;
|
||||||
|
break;
|
||||||
|
case 35:
|
||||||
|
ret += 45;
|
||||||
|
ret += 12;
|
||||||
|
break;
|
||||||
|
case 36:
|
||||||
|
ret += 39;
|
||||||
|
break;
|
||||||
|
case 37:
|
||||||
|
ret += 42;
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
ret += 45;
|
||||||
|
ret += 15;
|
||||||
|
break;
|
||||||
|
case 39:
|
||||||
|
ret += 45;
|
||||||
|
ret += 16;
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
ret += 45;
|
||||||
|
ret += 17;
|
||||||
|
break;
|
||||||
|
case 41:
|
||||||
|
ret += 45;
|
||||||
|
ret += 18;
|
||||||
|
break;
|
||||||
|
case 42:
|
||||||
|
ret += 45;
|
||||||
|
ret += 19;
|
||||||
|
break;
|
||||||
|
case 43:
|
||||||
|
ret += 41;
|
||||||
|
break;
|
||||||
|
case 44:
|
||||||
|
ret += 45;
|
||||||
|
ret += 21;
|
||||||
|
break;
|
||||||
|
case 45:
|
||||||
|
ret += 36;
|
||||||
|
break;
|
||||||
|
case 46:
|
||||||
|
ret += 37;
|
||||||
|
break;
|
||||||
|
case 47:
|
||||||
|
ret += 40;
|
||||||
|
break;
|
||||||
|
case 48:
|
||||||
|
ret += 0;
|
||||||
|
break;
|
||||||
|
case 49:
|
||||||
|
ret += 1;
|
||||||
|
break;
|
||||||
|
case 50:
|
||||||
|
ret += 2;
|
||||||
|
break;
|
||||||
|
case 51:
|
||||||
|
ret += 3;
|
||||||
|
break;
|
||||||
|
case 52:
|
||||||
|
ret += 4;
|
||||||
|
break;
|
||||||
|
case 53:
|
||||||
|
ret += 5;
|
||||||
|
break;
|
||||||
|
case 54:
|
||||||
|
ret += 6;
|
||||||
|
break;
|
||||||
|
case 55:
|
||||||
|
ret += 7;
|
||||||
|
break;
|
||||||
|
case 56:
|
||||||
|
ret += 8;
|
||||||
|
break;
|
||||||
|
case 57:
|
||||||
|
ret += 9;
|
||||||
|
break;
|
||||||
|
case 58:
|
||||||
|
ret += 45;
|
||||||
|
ret += 35;
|
||||||
|
break;
|
||||||
|
case 59:
|
||||||
|
ret += 44;
|
||||||
|
ret += 15;
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
ret += 44;
|
||||||
|
ret += 16;
|
||||||
|
break;
|
||||||
|
case 61:
|
||||||
|
ret += 44;
|
||||||
|
ret += 17;
|
||||||
|
break;
|
||||||
|
case 62:
|
||||||
|
ret += 44;
|
||||||
|
ret += 18;
|
||||||
|
break;
|
||||||
|
case 63:
|
||||||
|
ret += 44;
|
||||||
|
ret += 19;
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
ret += 44;
|
||||||
|
ret += 31;
|
||||||
|
break;
|
||||||
|
case 65:
|
||||||
|
ret += 10;
|
||||||
|
break;
|
||||||
|
case 66:
|
||||||
|
ret += 11;
|
||||||
|
break;
|
||||||
|
case 67:
|
||||||
|
ret += 12;
|
||||||
|
break;
|
||||||
|
case 68:
|
||||||
|
ret += 13;
|
||||||
|
break;
|
||||||
|
case 69:
|
||||||
|
ret += 14;
|
||||||
|
break;
|
||||||
|
case 70:
|
||||||
|
ret += 15;
|
||||||
|
break;
|
||||||
|
case 71:
|
||||||
|
ret += 16;
|
||||||
|
break;
|
||||||
|
case 72:
|
||||||
|
ret += 17;
|
||||||
|
break;
|
||||||
|
case 73:
|
||||||
|
ret += 18;
|
||||||
|
break;
|
||||||
|
case 74:
|
||||||
|
ret += 19;
|
||||||
|
break;
|
||||||
|
case 75:
|
||||||
|
ret += 20;
|
||||||
|
break;
|
||||||
|
case 76:
|
||||||
|
ret += 21;
|
||||||
|
break;
|
||||||
|
case 77:
|
||||||
|
ret += 22;
|
||||||
|
break;
|
||||||
|
case 78:
|
||||||
|
ret += 23;
|
||||||
|
break;
|
||||||
|
case 79:
|
||||||
|
ret += 24;
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
ret += 25;
|
||||||
|
break;
|
||||||
|
case 81:
|
||||||
|
ret += 26;
|
||||||
|
break;
|
||||||
|
case 82:
|
||||||
|
ret += 27;
|
||||||
|
break;
|
||||||
|
case 83:
|
||||||
|
ret += 28;
|
||||||
|
break;
|
||||||
|
case 84:
|
||||||
|
ret += 29;
|
||||||
|
break;
|
||||||
|
case 85:
|
||||||
|
ret += 30;
|
||||||
|
break;
|
||||||
|
case 86:
|
||||||
|
ret += 31;
|
||||||
|
break;
|
||||||
|
case 87:
|
||||||
|
ret += 32;
|
||||||
|
break;
|
||||||
|
case 88:
|
||||||
|
ret += 33;
|
||||||
|
break;
|
||||||
|
case 89:
|
||||||
|
ret += 34;
|
||||||
|
break;
|
||||||
|
case 90:
|
||||||
|
ret += 35;
|
||||||
|
break;
|
||||||
|
case 91:
|
||||||
|
ret += 44;
|
||||||
|
ret += 20;
|
||||||
|
break;
|
||||||
|
case 92:
|
||||||
|
ret += 44;
|
||||||
|
ret += 21;
|
||||||
|
break;
|
||||||
|
case 93:
|
||||||
|
ret += 44;
|
||||||
|
ret += 22;
|
||||||
|
break;
|
||||||
|
case 94:
|
||||||
|
ret += 44;
|
||||||
|
ret += 23;
|
||||||
|
break;
|
||||||
|
case 95:
|
||||||
|
ret += 44;
|
||||||
|
ret += 24;
|
||||||
|
break;
|
||||||
|
case 96:
|
||||||
|
ret += 44;
|
||||||
|
ret += 32;
|
||||||
|
break;
|
||||||
|
case 97:
|
||||||
|
ret += 46;
|
||||||
|
ret += 10;
|
||||||
|
break;
|
||||||
|
case 98:
|
||||||
|
ret += 46;
|
||||||
|
ret += 11;
|
||||||
|
break;
|
||||||
|
case 99:
|
||||||
|
ret += 46;
|
||||||
|
ret += 12;
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
ret += 46;
|
||||||
|
ret += 13;
|
||||||
|
break;
|
||||||
|
case 101:
|
||||||
|
ret += 46;
|
||||||
|
ret += 14;
|
||||||
|
break;
|
||||||
|
case 102:
|
||||||
|
ret += 46;
|
||||||
|
ret += 15;
|
||||||
|
break;
|
||||||
|
case 103:
|
||||||
|
ret += 46;
|
||||||
|
ret += 16;
|
||||||
|
break;
|
||||||
|
case 104:
|
||||||
|
ret += 46;
|
||||||
|
ret += 17;
|
||||||
|
break;
|
||||||
|
case 105:
|
||||||
|
ret += 46;
|
||||||
|
ret += 18;
|
||||||
|
break;
|
||||||
|
case 106:
|
||||||
|
ret += 46;
|
||||||
|
ret += 19;
|
||||||
|
break;
|
||||||
|
case 107:
|
||||||
|
ret += 46;
|
||||||
|
ret += 20;
|
||||||
|
break;
|
||||||
|
case 108:
|
||||||
|
ret += 46;
|
||||||
|
ret += 21;
|
||||||
|
break;
|
||||||
|
case 109:
|
||||||
|
ret += 46;
|
||||||
|
ret += 22;
|
||||||
|
break;
|
||||||
|
case 110:
|
||||||
|
ret += 46;
|
||||||
|
ret += 23;
|
||||||
|
break;
|
||||||
|
case 111:
|
||||||
|
ret += 46;
|
||||||
|
ret += 24;
|
||||||
|
break;
|
||||||
|
case 112:
|
||||||
|
ret += 46;
|
||||||
|
ret += 25;
|
||||||
|
break;
|
||||||
|
case 113:
|
||||||
|
ret += 46;
|
||||||
|
ret += 26;
|
||||||
|
break;
|
||||||
|
case 114:
|
||||||
|
ret += 46;
|
||||||
|
ret += 27;
|
||||||
|
break;
|
||||||
|
case 115:
|
||||||
|
ret += 46;
|
||||||
|
ret += 28;
|
||||||
|
break;
|
||||||
|
case 116:
|
||||||
|
ret += 46;
|
||||||
|
ret += 29;
|
||||||
|
break;
|
||||||
|
case 117:
|
||||||
|
ret += 46;
|
||||||
|
ret += 30;
|
||||||
|
break;
|
||||||
|
case 118:
|
||||||
|
ret += 46;
|
||||||
|
ret += 31;
|
||||||
|
break;
|
||||||
|
case 119:
|
||||||
|
ret += 46;
|
||||||
|
ret += 32;
|
||||||
|
break;
|
||||||
|
case 120:
|
||||||
|
ret += 46;
|
||||||
|
ret += 33;
|
||||||
|
break;
|
||||||
|
case 121:
|
||||||
|
ret += 46;
|
||||||
|
ret += 34;
|
||||||
|
break;
|
||||||
|
case 122:
|
||||||
|
ret += 46;
|
||||||
|
ret += 35;
|
||||||
|
break;
|
||||||
|
case 123:
|
||||||
|
ret += 44;
|
||||||
|
ret += 25;
|
||||||
|
break;
|
||||||
|
case 124:
|
||||||
|
ret += 44;
|
||||||
|
ret += 26;
|
||||||
|
break;
|
||||||
|
case 125:
|
||||||
|
ret += 44;
|
||||||
|
ret += 27;
|
||||||
|
break;
|
||||||
|
case 126:
|
||||||
|
ret += 44;
|
||||||
|
ret += 28;
|
||||||
|
break;
|
||||||
|
case 127:
|
||||||
|
ret += 44;
|
||||||
|
ret += 29;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret; // return an empty list for a non-ascii character code
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate a checksum
|
||||||
|
static int checksum(const QList<int> &codes, int wrap)
|
||||||
|
{
|
||||||
|
int check = 0;
|
||||||
|
for (int i = 0; i < codes.size(); i++) {
|
||||||
|
// weight goes from 1 to wrap, right-to-left, then repeats
|
||||||
|
const int weight = (codes.size() - i - 1) % wrap + 1;
|
||||||
|
check += codes.at(i) * weight;
|
||||||
|
}
|
||||||
|
return check % 47;
|
||||||
|
}
|
||||||
|
|
||||||
|
Code93Barcode::Code93Barcode()
|
||||||
|
: AbstractBarcodePrivate(Barcode::OneDimension)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Code93Barcode::~Code93Barcode() = default;
|
||||||
|
|
||||||
|
QImage Code93Barcode::paintImage()
|
||||||
|
{
|
||||||
|
QList<bool> barcode;
|
||||||
|
// convert text into sequences of fg/bg bars
|
||||||
|
{
|
||||||
|
// translate the string into a code sequence
|
||||||
|
QList<int> codes;
|
||||||
|
const auto str = BarCodeUtil::asLatin1ByteArray(m_data);
|
||||||
|
for (int i = 0; i < str.size(); i++) {
|
||||||
|
codes += codesForChar(str.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate checksums
|
||||||
|
codes.append(checksum(codes, 20)); // "C" checksum
|
||||||
|
codes.append(checksum(codes, 15)); // "K" checksum: includes previous checksum
|
||||||
|
|
||||||
|
// now generate the barcode
|
||||||
|
// the guard sequence that goes on each end
|
||||||
|
const QList<bool> endSequence = sequenceForID(47);
|
||||||
|
barcode += endSequence;
|
||||||
|
// translate codes into bars
|
||||||
|
for (int i = 0; i < codes.size(); i++) {
|
||||||
|
barcode += sequenceForID(codes.at(i));
|
||||||
|
}
|
||||||
|
// ending guard
|
||||||
|
barcode += endSequence;
|
||||||
|
// termination bar
|
||||||
|
barcode += true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int barWidth = 1;
|
||||||
|
const int quietZoneWidth = 10 * barWidth;
|
||||||
|
|
||||||
|
// build one line of the result image
|
||||||
|
QList<QRgb> line;
|
||||||
|
line.reserve(barWidth * barcode.size() + 2 * quietZoneWidth);
|
||||||
|
line.insert(0, quietZoneWidth, m_background.rgba());
|
||||||
|
for (int i = 0; i < barcode.size(); i++) {
|
||||||
|
const QRgb color = (barcode.at(i) ? m_foreground : m_background).rgba();
|
||||||
|
for (int j = 0; j < barWidth; j++) {
|
||||||
|
line.append(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line.insert(line.size(), quietZoneWidth, m_background.rgba());
|
||||||
|
|
||||||
|
// build the complete barcode
|
||||||
|
QImage ret(line.size(), 1, QImage::Format_ARGB32);
|
||||||
|
memcpy(ret.scanLine(0), line.data(), line.size() * sizeof(QRgb));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2011 Geoffry Song <goffrie@gmail.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_CODE93BARCODE_H
|
||||||
|
#define PRISON_CODE93BARCODE_H
|
||||||
|
|
||||||
|
#include "abstractbarcode_p.h"
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Code 93 Barcode generator
|
||||||
|
*/
|
||||||
|
class Code93Barcode : public Prison::AbstractBarcodePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* creates a Code 93 generator
|
||||||
|
*/
|
||||||
|
Code93Barcode();
|
||||||
|
~Code93Barcode() override;
|
||||||
|
/**
|
||||||
|
* This function generates the barcode
|
||||||
|
* @return QImage containing a barcode, trying to approximate the requested sizes
|
||||||
|
*/
|
||||||
|
QImage paintImage() override;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif // PRISON_CODE39BARCODE_H
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_CONFIG_H
|
||||||
|
#define PRISON_CONFIG_H
|
||||||
|
|
||||||
|
#cmakedefine01 HAVE_DMTX
|
||||||
|
#cmakedefine01 HAVE_ZXING
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2010-2014 Sune Vuorela <sune@vuorela.dk>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "datamatrixbarcode_p.h"
|
||||||
|
#include <dmtx.h>
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
DataMatrixBarcode::DataMatrixBarcode()
|
||||||
|
: AbstractBarcodePrivate(Barcode::TwoDimensions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
DataMatrixBarcode::~DataMatrixBarcode() = default;
|
||||||
|
|
||||||
|
QImage DataMatrixBarcode::paintImage()
|
||||||
|
{
|
||||||
|
const auto data = m_data.toString();
|
||||||
|
if (data.size() > 1200) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
DmtxEncode *enc = dmtxEncodeCreate();
|
||||||
|
dmtxEncodeSetProp(enc, DmtxPropPixelPacking, DmtxPack32bppRGBX);
|
||||||
|
dmtxEncodeSetProp(enc, DmtxPropModuleSize, 1);
|
||||||
|
dmtxEncodeSetProp(enc, DmtxPropMarginSize, 2);
|
||||||
|
|
||||||
|
QByteArray trimmedData(data.trimmed().toUtf8());
|
||||||
|
DmtxPassFail result = dmtxEncodeDataMatrix(enc, trimmedData.length(), reinterpret_cast<unsigned char *>(trimmedData.data()));
|
||||||
|
if (result == DmtxFail) {
|
||||||
|
dmtxEncodeDestroy(&enc);
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
Q_ASSERT(enc->image->width == enc->image->height);
|
||||||
|
|
||||||
|
QImage ret;
|
||||||
|
|
||||||
|
if (m_foreground == Qt::black && m_background == Qt::white) {
|
||||||
|
QImage tmp(enc->image->pxl, enc->image->width, enc->image->height, QImage::Format_ARGB32);
|
||||||
|
// we need to copy, because QImage generated from a char pointer requires the
|
||||||
|
// char pointer to be kept around forever, and manually deleted.
|
||||||
|
ret = tmp.copy();
|
||||||
|
} else {
|
||||||
|
if (enc->image->width > 0) {
|
||||||
|
int size = enc->image->width * enc->image->height * 4;
|
||||||
|
uchar *img = new uchar[size];
|
||||||
|
QByteArray background(4, '\0');
|
||||||
|
background[3] = qAlpha(m_background.rgba());
|
||||||
|
background[2] = qRed(m_background.rgba());
|
||||||
|
background[1] = qGreen(m_background.rgba());
|
||||||
|
background[0] = qBlue(m_background.rgba());
|
||||||
|
QByteArray foreground(4, '\0');
|
||||||
|
foreground[3] = qAlpha(m_foreground.rgba());
|
||||||
|
foreground[2] = qRed(m_foreground.rgba());
|
||||||
|
foreground[1] = qGreen(m_foreground.rgba());
|
||||||
|
foreground[0] = qBlue(m_foreground.rgba());
|
||||||
|
for (int i = 1; i < size; i += 4) {
|
||||||
|
QByteArray color;
|
||||||
|
if (enc->image->pxl[i] == 0x00) {
|
||||||
|
color = foreground;
|
||||||
|
} else {
|
||||||
|
color = background;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
img[i - 1 + j] = color[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QImage tmp(img, enc->image->width, enc->image->height, QImage::Format_ARGB32);
|
||||||
|
// we need to copy, because QImage generated from a char pointer requires the
|
||||||
|
// char pointer to be kept around forever, and manually deleted.
|
||||||
|
ret = tmp.copy();
|
||||||
|
delete[] img;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dmtxEncodeDestroy(&enc);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2010-2016 Sune Vuorela <sune@vuorela.dk>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_DATAMATRIXBARCODE_H
|
||||||
|
#define PRISON_DATAMATRIXBARCODE_H
|
||||||
|
|
||||||
|
#include "abstractbarcode_p.h"
|
||||||
|
#include "prison_export.h"
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This is a Datamatrix barcode generator that uses libdmtx
|
||||||
|
* for the actual generation of barcodes.
|
||||||
|
*/
|
||||||
|
class DataMatrixBarcode : public Prison::AbstractBarcodePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* creates a datamatrixbarcode generator
|
||||||
|
*/
|
||||||
|
DataMatrixBarcode();
|
||||||
|
~DataMatrixBarcode() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* This is the function doing the actual work in generating the barcode
|
||||||
|
* @return QImage containing a DataMatrix, trying to approximate the requested sizes
|
||||||
|
*/
|
||||||
|
QImage paintImage() override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PRISON_DATAMATRIXBARCODE_H
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||||
|
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mecard.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
class Prison::MeCardPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QStringView header;
|
||||||
|
struct Element {
|
||||||
|
QStringView key;
|
||||||
|
QStringList values;
|
||||||
|
bool operator<(QStringView other) const;
|
||||||
|
};
|
||||||
|
std::vector<Element> elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool MeCardPrivate::Element::operator<(QStringView other) const
|
||||||
|
{
|
||||||
|
return key < other;
|
||||||
|
}
|
||||||
|
|
||||||
|
MeCard::MeCard()
|
||||||
|
: d(new MeCardPrivate())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MeCard::MeCard(MeCard &&other) noexcept
|
||||||
|
: d(std::move(other.d))
|
||||||
|
{
|
||||||
|
other.d.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
MeCard &MeCard::operator=(MeCard &&other) noexcept
|
||||||
|
{
|
||||||
|
std::swap(d, other.d);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MeCard::~MeCard() = default;
|
||||||
|
|
||||||
|
std::optional<MeCard> MeCard::parse(const QString &data)
|
||||||
|
{
|
||||||
|
const auto idx = data.indexOf(QLatin1Char(':'));
|
||||||
|
if (idx <= 0 || idx >= data.size() - 1) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
MeCard m;
|
||||||
|
m.d->header = QStringView(data).left(idx);
|
||||||
|
|
||||||
|
auto remaining = QStringView(data).mid(idx + 1);
|
||||||
|
while (remaining.size() > 0) {
|
||||||
|
const auto keyIdx = remaining.indexOf(QLatin1Char(':'));
|
||||||
|
if (keyIdx <= 0 || keyIdx + 2 >= remaining.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString value;
|
||||||
|
auto elemIdx = keyIdx + 1;
|
||||||
|
bool inQuote = false;
|
||||||
|
for (; elemIdx < remaining.size() - 1; ++elemIdx) {
|
||||||
|
auto c = remaining.at(elemIdx);
|
||||||
|
if (elemIdx == (keyIdx + 1) && c == QLatin1Char('"')) { // leading quote
|
||||||
|
inQuote = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inQuote && c == QLatin1Char('"') && remaining.at(elemIdx + 1) == QLatin1Char(';')) { // trailing quote
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == QLatin1Char(';')) { // end of element
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == QLatin1Char('\\')) { // quoted character
|
||||||
|
++elemIdx;
|
||||||
|
c = remaining.at(elemIdx);
|
||||||
|
}
|
||||||
|
value.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = remaining.left(keyIdx);
|
||||||
|
auto it = std::lower_bound(m.d->elements.begin(), m.d->elements.end(), key);
|
||||||
|
if (it == m.d->elements.end()) {
|
||||||
|
m.d->elements.push_back(MeCardPrivate::Element());
|
||||||
|
it = std::prev(m.d->elements.end());
|
||||||
|
} else if ((*it).key != key) {
|
||||||
|
it = m.d->elements.insert(it, MeCardPrivate::Element());
|
||||||
|
}
|
||||||
|
(*it).key = key;
|
||||||
|
(*it).values.push_back(value);
|
||||||
|
|
||||||
|
remaining = remaining.mid(elemIdx + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m.d->elements.empty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MeCard::header() const
|
||||||
|
{
|
||||||
|
return d->header.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringView MeCard::headerView() const
|
||||||
|
{
|
||||||
|
return d->header;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MeCard::value(QStringView key) const
|
||||||
|
{
|
||||||
|
const auto it = std::lower_bound(d->elements.begin(), d->elements.end(), key);
|
||||||
|
if (it != d->elements.end() && (*it).key == key && (*it).values.size() == 1) {
|
||||||
|
return (*it).values.at(0);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MeCard::values(QStringView key) const
|
||||||
|
{
|
||||||
|
const auto it = std::lower_bound(d->elements.begin(), d->elements.end(), key);
|
||||||
|
if (it != d->elements.end() && (*it).key == key) {
|
||||||
|
return (*it).values;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap MeCard::toVariantMap() const
|
||||||
|
{
|
||||||
|
QVariantMap map;
|
||||||
|
for (const auto &element : std::as_const(d->elements)) {
|
||||||
|
if (element.values.size() > 1) {
|
||||||
|
map.insert(element.key.toString(), element.values);
|
||||||
|
} else {
|
||||||
|
map.insert(element.key.toString(), element.values.at(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||||
|
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PRISON_MECARD_H
|
||||||
|
#define PRISON_MECARD_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
#include "prison_export.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace Prison
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Parser for the MeCard format.
|
||||||
|
* This was originally used for a more compact vCard representation, but today
|
||||||
|
* is mostly relevant for Wifi configuration QR codes.
|
||||||
|
* @see https://en.wikipedia.org/wiki/MeCard_(QR_code)
|
||||||
|
* @see https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11
|
||||||
|
* @since 5.101
|
||||||
|
*/
|
||||||
|
class PRISON_EXPORT MeCard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Move constructor
|
||||||
|
*/
|
||||||
|
MeCard(MeCard &&other) noexcept;
|
||||||
|
/**
|
||||||
|
* Move assignment
|
||||||
|
*/
|
||||||
|
MeCard &operator=(MeCard &&other) noexcept;
|
||||||
|
/**
|
||||||
|
* Destructor
|
||||||
|
*/
|
||||||
|
~MeCard();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given string
|
||||||
|
* @param data The string to parse
|
||||||
|
* @return A MeCard, if parsing was successful, a nullopt otherwise.
|
||||||
|
*/
|
||||||
|
static std::optional<MeCard> parse(const QString &data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the MeCard header.
|
||||||
|
*
|
||||||
|
* If you just want to identify the card,
|
||||||
|
* use headerView() instead.
|
||||||
|
*/
|
||||||
|
QString header() const;
|
||||||
|
/**
|
||||||
|
* Get the MeCard header as a string view.
|
||||||
|
*
|
||||||
|
* Useful for identifying the type of card.
|
||||||
|
*/
|
||||||
|
QStringView headerView() const;
|
||||||
|
/**
|
||||||
|
* Get the value for a given key.
|
||||||
|
*
|
||||||
|
* Convenience method for getting the first value
|
||||||
|
* if only one value is expected.
|
||||||
|
*/
|
||||||
|
QString value(QStringView key) const;
|
||||||
|
/**
|
||||||
|
* Get the list of values for a given key.
|
||||||
|
* @return The list of values for the given key
|
||||||
|
*/
|
||||||
|
QStringList values(QStringView key) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the parsed data as QVariantMap.
|
||||||
|
*/
|
||||||
|
QVariantMap toVariantMap() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit MeCard();
|
||||||
|
|
||||||
|
friend class MeCardPrivate;
|
||||||
|
std::unique_ptr<class MeCardPrivate> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Prison
|
||||||
|
|
||||||
|
#endif // PRISON_MECARD_H
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pdf417barcode_p.h"
|
||||||
|
#include "zxingutil_p.h"
|
||||||
|
|
||||||
|
#include <ZXing/BitMatrix.h>
|
||||||
|
#include <ZXing/MultiFormatWriter.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace Prison;
|
||||||
|
|
||||||
|
Pdf417Barcode::Pdf417Barcode()
|
||||||
|
: AbstractBarcodePrivate(Barcode::TwoDimensions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Pdf417Barcode::paintImage()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ZXing::MultiFormatWriter writer(ZXing::BarcodeFormat::PDF417);
|
||||||
|
// ISO/IEC 15438:2006(E) §5.8.3 Quiet Zone
|
||||||
|
writer.setMargin(2);
|
||||||
|
if (m_data.userType() == QMetaType::QByteArray) {
|
||||||
|
writer.setEncoding(ZXing::CharacterSet::BINARY);
|
||||||
|
}
|
||||||
|
// aspect ratio 4 is hard-coded in ZXing
|
||||||
|
const auto matrix = writer.encode(ZXingUtil::toStdWString(m_data), 4, 1);
|
||||||
|
return ZXingUtil::toImage(matrix, m_foreground, m_background);
|
||||||
|
} catch (const std::invalid_argument &e) {
|
||||||
|
}; // input too large
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF Pdf417Barcode::preferredSize(qreal devicePixelRatio) const
|
||||||
|
{
|
||||||
|
return m_cache.size() * (devicePixelRatio < 2 ? 2 : 1);
|
||||||
|
}
|
||||||