2026-05-26 17:11:09 +02:00

51 lines
1.5 KiB
Go

package platepdf
import (
"bytes"
"encoding/base64"
"fmt"
"printer.backend/internal/model"
)
const renderDPI = 300
// BuildCompositeSVG assembles a plate-sized SVG by embedding each item as a PNG
// rendered by rsvg-convert (identical to the standalone item file).
func BuildCompositeSVG(plate model.Plate, spec model.ItemSpec, itemSVG []byte, preview model.LayoutPreview) ([]byte, error) {
itemW, itemH, err := svgViewportMM(itemSVG)
if err != nil {
return nil, err
}
if itemW <= 0 || itemH <= 0 {
itemW, itemH = spec.SizeMM, spec.SizeMM
}
itemPNG, err := svgToPNGViaRsvg(itemSVG, renderDPI)
if err != nil {
return nil, fmt.Errorf("render item: %w", err)
}
itemB64 := base64.StdEncoding.EncodeToString(itemPNG)
var b bytes.Buffer
fmt.Fprintf(&b, `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 %.4f %.4f" width="%.4fmm" height="%.4fmm">
<rect width="%.4f" height="%.4f" fill="#ffffff"/>
<rect x="%.4f" y="%.4f" width="%.4f" height="%.4f" fill="none" stroke="#cccccc" stroke-width="0.2"/>
`,
plate.WidthMM, plate.HeightMM, plate.WidthMM, plate.HeightMM,
plate.WidthMM, plate.HeightMM,
preview.PrintableXMM, preview.PrintableYMM, preview.PrintableWMM, preview.PrintableHMM,
)
for _, pos := range preview.Positions {
fmt.Fprintf(&b,
`<image x="%.4f" y="%.4f" width="%.4f" height="%.4f" href="data:image/png;base64,%s"/>`,
pos.XMM, pos.YMM, itemW, itemH, itemB64,
)
}
b.WriteString("\n</svg>")
return b.Bytes(), nil
}