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
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
136 lines
3.7 KiB
Go
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|