Files
patrick 9a24ea29e1 FDN-01: repository & projektgerüst
Git-Repository für bestehenden archivdms-Code initialisiert, Branch-/Commit-Konvention (feature/<ticket>-<slug>-Branches, Ticket-Prefix in Commit-Nachricht) etabliert.
2026-08-11 21:27:53 +02:00

65 lines
2.2 KiB
Go

// Package barcode wraps the system zbarimg binary (Debian package
// zbar-tools) as a best-effort barcode decoding sidecar, consistent with
// archivdms's general philosophy of shelling out to small CLI tools via
// os/exec instead of adding CGO/native Go dependencies (see
// internal/ocr/ocr.go package comment for the same rationale applied to
// tesseract/poppler-utils).
//
// Barcode decoding failures (binary missing, non-zero exit because no
// barcode was found, timeout) are never fatal to an upload: DecodeBarcodes
// returns an empty slice and a nil error in the "nothing found / binary
// missing" cases, matching how OCR failures are tolerated by callers.
package barcode
import (
"bytes"
"context"
"fmt"
"os/exec"
"strings"
"time"
)
const defaultTimeout = 20 * time.Second
// DecodeBarcodes runs `zbarimg --raw -q <imagePath>` and returns the
// decoded raw values, one per line of output. If zbarimg is not installed,
// this returns an empty slice and a nil error (tolerant, no hard-fail) —
// callers that want to warn about a missing binary should check
// exec.LookPath("zbarimg") themselves if they need to distinguish
// "not installed" from "installed, nothing found".
func DecodeBarcodes(ctx context.Context, imagePath string) ([]string, error) {
if _, err := exec.LookPath("zbarimg"); err != nil {
// Binary not present: tolerated, same as a missing tesseract binary.
return nil, nil
}
cctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
cmd := exec.CommandContext(cctx, "zbarimg", "--raw", "-q", imagePath)
var out, stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
// zbarimg exits non-zero (typically exit code 4) when no barcode is
// found in the image at all — that is a normal, expected outcome for
// the vast majority of scanned documents, not an error condition.
if exitErr, ok := err.(*exec.ExitError); ok {
_ = exitErr
return nil, nil
}
return nil, fmt.Errorf("barcode: zbarimg failed: %w (%s)", err, strings.TrimSpace(stderr.String()))
}
var values []string
for _, line := range strings.Split(out.String(), "\n") {
line = strings.TrimSpace(line)
if line != "" {
values = append(values, line)
}
}
return values, nil
}