Files
nexarch/dms/internal/testcorpus/scanstack.go
patrickandClaude Sonnet 5 26bb03aade
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
INT-10: rasterung mit 200 dpi, einteilige stapel im lieferregister
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 20:09:20 +02:00

136 lines
3.7 KiB
Go

package testcorpus
import (
"bytes"
"compress/zlib"
"fmt"
"math/rand"
"github.com/makiuchi-d/gozxing"
"github.com/makiuchi-d/gozxing/oned"
"github.com/makiuchi-d/gozxing/qrcode"
)
// ScanKind bestimmt den Inhalt einer synthetischen Scanseite.
type ScanKind int
const (
ScanText ScanKind = iota
ScanBlank
ScanBlankNoisy
ScanCode128
ScanQR
)
// ScanPage ist eine Seite eines synthetischen Bildstapels; Value ist der
// Barcode-Inhalt bei ScanCode128 und ScanQR, bei ScanText die Kennung.
type ScanPage struct {
Kind ScanKind
Value string
}
const (
scanW = 600
scanH = 800
)
// GenerateScanStack baut ein Mehrseiten-PDF aus Graustufenbildern ohne Textebene
// (wie ein Netzwerkscanner); gerastert wird sie mit 200 dpi.
func GenerateScanStack(pages []ScanPage) ([]byte, error) {
var buf bytes.Buffer
var offsets []int
obj := func(s string) {
offsets = append(offsets, buf.Len())
buf.WriteString(s)
}
buf.WriteString("%PDF-1.4\n")
obj("1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n")
kids := ""
for i := range pages {
kids += fmt.Sprintf("%d 0 R ", 3+3*i)
}
obj(fmt.Sprintf("2 0 obj<</Type/Pages/Kids[%s]/Count %d>>endobj\n", kids, len(pages)))
for i, p := range pages {
pix, err := renderScanPage(p, int64(i))
if err != nil {
return nil, err
}
var z bytes.Buffer
zw := zlib.NewWriter(&z)
_, _ = zw.Write(pix)
_ = zw.Close()
page, contents, img := 3+3*i, 4+3*i, 5+3*i
obj(fmt.Sprintf("%d 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 288 384]/Resources<</XObject<</Im0 %d 0 R>>>>/Contents %d 0 R>>endobj\n", page, img, contents))
content := "q 288 0 0 384 0 0 cm /Im0 Do Q"
obj(fmt.Sprintf("%d 0 obj<</Length %d>>stream\n%s\nendstream endobj\n", contents, len(content), content))
offsets = append(offsets, buf.Len())
fmt.Fprintf(&buf, "%d 0 obj<</Type/XObject/Subtype/Image/Width %d/Height %d/ColorSpace/DeviceGray/BitsPerComponent 8/Filter/FlateDecode/Length %d>>stream\n", img, scanW, scanH, z.Len())
buf.Write(z.Bytes())
buf.WriteString("\nendstream endobj\n")
}
xref := buf.Len()
fmt.Fprintf(&buf, "xref\n0 %d\n0000000000 65535 f \n", len(offsets)+1)
for _, o := range offsets {
fmt.Fprintf(&buf, "%010d 00000 n \n", o)
}
fmt.Fprintf(&buf, "trailer<</Size %d/Root 1 0 R>>\nstartxref\n%d\n%%%%EOF\n", len(offsets)+1, xref)
return buf.Bytes(), nil
}
func renderScanPage(p ScanPage, seed int64) ([]byte, error) {
pix := bytes.Repeat([]byte{255}, scanW*scanH)
switch p.Kind {
case ScanBlank:
case ScanBlankNoisy:
rng := rand.New(rand.NewSource(seed + 1))
for i := 0; i < scanW*scanH/5000; i++ {
pix[rng.Intn(scanW*scanH)] = 40
}
for y := 0; y < scanH; y++ {
for x := 0; x < 12; x++ {
pix[y*scanW+x] = 90
}
}
case ScanText:
for line := 0; line < 18; line++ {
y := 80 + line*36
w := 300 + (line*53)%240
for dy := 0; dy < 10; dy++ {
for x := 60; x < 60+w && x < scanW-40; x++ {
pix[(y+dy)*scanW+x] = 20
}
}
}
case ScanCode128:
m, err := oned.NewCode128Writer().Encode(p.Value, gozxing.BarcodeFormat_CODE_128, 480, 120, nil)
if err != nil {
return nil, fmt.Errorf("code128 erzeugen: %w", err)
}
drawMatrix(pix, m, 60, 300, 1, 1)
case ScanQR:
m, err := qrcode.NewQRCodeWriter().Encode(p.Value, gozxing.BarcodeFormat_QR_CODE, 300, 300, nil)
if err != nil {
return nil, fmt.Errorf("qr erzeugen: %w", err)
}
drawMatrix(pix, m, 150, 250, 1, 1)
}
return pix, nil
}
func drawMatrix(pix []byte, m *gozxing.BitMatrix, x0, y0, sx, sy int) {
for y := 0; y < m.GetHeight(); y++ {
for x := 0; x < m.GetWidth(); x++ {
if m.Get(x, y) {
for dy := 0; dy < sy; dy++ {
for dx := 0; dx < sx; dx++ {
px, py := x0+x*sx+dx, y0+y*sy+dy
if px < scanW && py < scanH {
pix[py*scanW+px] = 0
}
}
}
}
}
}
}