package main // The label PRINT path: pick the paper-QSL queue, review each address, choose // the routing, and export PDFs whose pages are the exact label size — one PDF // per label kind, because a roll printer holds one stock at a time and a file // mixing 29 mm addresses with 62 mm QSO labels could not be printed at all. // // The pages arrive from the frontend already rasterised: the designer's canvas // renderer draws them at the stock's dpi, so what was previewed is — pixel for // pixel — what lands in the PDF. Go only carries them to disk. import ( "encoding/base64" "fmt" "os" "os/exec" "strings" "hamlog/internal/applog" "hamlog/internal/pdf" "hamlog/internal/qso" wruntime "github.com/wailsapp/wails/v2/pkg/runtime" ) // LabelPaperQueue returns the contacts whose paper QSL is REQUESTED or QUEUED // (ADIF qsl_sent R/Q) — the natural worklist for a labelling session. The // frontend groups them by callsign. func (a *App) LabelPaperQueue() ([]qso.QSO, error) { if a.qso == nil { return nil, fmt.Errorf("db not initialized") } return a.qso.List(a.ctx, qso.ListFilter{ QSLSentIn: []string{"R", "Q"}, Limit: 10_000, }) } // LabelExportPDF writes one PDF of label pages and opens it in the system // viewer, from which the operator prints. pages are base64 PNGs (data-URL // prefix tolerated), all of the same wMm×hMm stock. // // Returns the chosen path ("" if the operator cancelled the dialog — not an // error, they changed their mind). func (a *App) LabelExportPDF(defaultName string, wMm, hMm float64, pages []string) (string, error) { if len(pages) == 0 { return "", fmt.Errorf("nothing to print") } if wMm < 5 || hMm < 5 || wMm > 400 || hMm > 400 { return "", fmt.Errorf("label size out of range") } var doc pdf.Doc for i, p := range pages { if idx := strings.Index(p, ","); idx >= 0 && strings.Contains(p[:idx], "base64") { p = p[idx+1:] } raw, err := base64.StdEncoding.DecodeString(p) if err != nil { return "", fmt.Errorf("page %d: %w", i+1, err) } if err := doc.AddImagePage(raw, wMm, hMm); err != nil { return "", fmt.Errorf("page %d: %w", i+1, err) } } out, err := doc.Bytes() if err != nil { return "", err } name := strings.TrimSpace(defaultName) if name == "" { name = "labels.pdf" } if !strings.HasSuffix(strings.ToLower(name), ".pdf") { name += ".pdf" } path, err := wruntime.SaveFileDialog(a.ctx, wruntime.SaveDialogOptions{ DefaultFilename: name, Title: "Save label PDF", Filters: []wruntime.FileFilter{{DisplayName: "PDF", Pattern: "*.pdf"}}, }) if err != nil { return "", err } if path == "" { return "", nil // cancelled } if err := os.WriteFile(path, out, 0o644); err != nil { return "", err } applog.Printf("labels: wrote %d page(s) (%.0f×%.0f mm) to %s", len(pages), wMm, hMm, path) // Opened in the default PDF viewer — printing happens there, by design: the // operator asked for a file they can check and print with their own tool. if err := exec.Command("rundll32", "url.dll,FileProtocolHandler", path).Start(); err != nil { applog.Printf("labels: could not open the PDF viewer: %v", err) } return path, nil }