Files
OpsLog/app_labels_print.go
T
rouggy 1d7633484b fix(labels): the manager's address, not the DX's — and one PDF, just opened
Three corrections from the first real session. Routed via a manager, the box
was prefilled with the DX's own address — the one thing that must not go on
that envelope. It now starts empty and the QRZ fetch fills in the MANAGER's;
switching the routing recomputes the box unless the operator has typed in it,
because a hand-checked address is not the app's to replace.

A fetch that came back with no street was overwriting a reviewed address with a
bare country — the cty.dat fallback dressed as an answer. It now refuses to
touch the box and says nothing was found. And the prefill no longer stacks the
same town three times (address + QTH + country all carrying it).

The output is ONE PDF for the whole session, each page at its own label size,
written to the temp dir and opened straight in the viewer — no save dialog: the
file is a print run, not a document to keep.
2026-08-28 20:07:36 +02:00

91 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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"
"path/filepath"
"strings"
"time"
"hamlog/internal/applog"
"hamlog/internal/pdf"
"hamlog/internal/qso"
)
// 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,
})
}
// LabelPDFPage is one page of the session's output: a rasterised label and its
// physical size. Sizes vary WITHIN one document — the operator asked for a
// single PDF holding QSO labels, addresses and return labels together, and PDF
// pages each carry their own MediaBox, so a 90×29 page can follow a 100×62 one.
type LabelPDFPage struct {
PNG string `json:"png"` // base64, data-URL prefix tolerated
WMm float64 `json:"w_mm"`
HMm float64 `json:"h_mm"`
}
// LabelOpenPDF writes the session's labels to ONE temporary PDF and opens it in
// the system viewer, from which the operator prints. No save dialog by choice:
// the file is a print run, not a document to keep — anyone who wants to keep it
// saves from the viewer.
func (a *App) LabelOpenPDF(pages []LabelPDFPage) (string, error) {
if len(pages) == 0 {
return "", fmt.Errorf("nothing to print")
}
var doc pdf.Doc
for i, pg := range pages {
if pg.WMm < 5 || pg.HMm < 5 || pg.WMm > 400 || pg.HMm > 400 {
return "", fmt.Errorf("page %d: label size out of range", i+1)
}
p := pg.PNG
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, pg.WMm, pg.HMm); err != nil {
return "", fmt.Errorf("page %d: %w", i+1, err)
}
}
out, err := doc.Bytes()
if err != nil {
return "", err
}
// A timestamped name in the temp dir: two sessions in one evening must not
// fight over the file, least of all while a viewer holds the first one open.
path := filepath.Join(os.TempDir(), fmt.Sprintf("opslog-labels-%s.pdf", time.Now().Format("20060102-150405")))
if err := os.WriteFile(path, out, 0o644); err != nil {
return "", err
}
applog.Printf("labels: wrote %d page(s) to %s", len(pages), path)
if err := exec.Command("rundll32", "url.dll,FileProtocolHandler", path).Start(); err != nil {
applog.Printf("labels: could not open the PDF viewer: %v", err)
return "", fmt.Errorf("the PDF was written to %s but no viewer opened: %w", path, err)
}
return path, nil
}