package main import ( "fmt" "log/slog" "codeberg.org/go-pdf/fpdf" "github.com/yeqown/go-qrcode/v2" "github.com/yeqown/go-qrcode/writer/standard" ) var cols = 4 var rows = 4 var qrSize = 48.0 var fontSize = 25.0 // pts var fontSizeMM = fontSize / 72 * 25.4 var text = "Á[Hjqy]|" var font = "Courier" var textTopPadding = 0.0 // typically none is required var qrTopPadding = 5.0 // below the baseline of the text, remember the descenders like 'y', 'q' var requiredCellPadding = 3.0 // half on each side, effectively // var probably some padding instead of offsets for qr/font placement func main() { qrc, err := qrcode.New("https://nanocat.net/dom/sdfih/dsifj/sidhof/sdifho/sdfiuoh/safiuho") if err != nil { fmt.Printf("could not generate QRCode: %v", err) return } b := newBWC() qrBytes := standard.NewWithWriter(&b, // standard.WithQRWidth(uint8(qrSize)), standard.WithBorderWidth(0), standard.WithBuiltinImageEncoder(standard.PNG_FORMAT), // standard.WithCircleShape(), // standard.WithFgGradient(&standard.LinearGradient{ // Stops: []standard.ColorStop{{ // T: 0.0, // Color: color.RGBA{ // R: 255, // G: 0, // B: 0, // A: 0, // }, // }, { // T: 1.0, // Color: color.RGBA{ // R: 0, // G: 255, // B: 0, // A: 0, // }, // }}, // Angle: 45, // }), ) // save file if err = qrc.Save(qrBytes); err != nil { fmt.Printf("could not save image: %v", err) } // dim := qrc.Dimension() pdf := fpdf.New("P", "mm", "A4", "") pdf.SetMargins(0, 0, 0) pageWidth, pageHeight := pdf.GetPageSize() // register the qr pdf.RegisterImageOptionsReader("qr", fpdf.ImageOptions{ ImageType: "png", ReadDpi: false, AllowNegativePosition: false, }, b) // info := pdf.GetImageInfo("qr") // panic(info.Height()) pdf.AddPage() pdf.SetFont(font, "B", float64(fontSize)) textWidth := pdf.GetStringWidth(text) colWidth := pageWidth / float64(cols) rowHeight := pageHeight / float64(rows) cellHeight := textTopPadding + fontSizeMM + qrTopPadding + qrSize cellWidth := qrSize if cellHeight > rowHeight-requiredCellPadding { slog.Warn("qr is too big for row", "cell_height", cellHeight, "row_height", rowHeight) } slog.Info("check width", "cell_width", cellWidth, "col_width", colWidth) if cellWidth > colWidth-requiredCellPadding { slog.Warn("qr is too big for column", "cell_width", cellWidth, "col_width", colWidth) } if textWidth > colWidth { slog.Warn("text probably too wide") } for rowIdx := range rows { for colIdx := range cols { topLeftX := colWidth * float64(colIdx) topLeftY := rowHeight * float64(rowIdx) // draw box pdf.SetDashPattern([]float64{1, 1}, 0) pdf.Rect(topLeftX, topLeftY, colWidth, rowHeight, "D") pdf.SetDashPattern([]float64{}, 0) // label at the top pdf.Text( topLeftX+colWidth/2-textWidth/2, topLeftY+fontSizeMM+textTopPadding, text, // +fmt.Sprintf("%d/%d", rowIdx, colIdx) ) // qr at the bottom pdf.ImageOptions("qr", topLeftX+colWidth/2-qrSize/2, topLeftY+textTopPadding+fontSizeMM+qrTopPadding, qrSize, qrSize, false, fpdf.ImageOptions{AllowNegativePosition: true}, 0, "") } } err = pdf.OutputFileAndClose("hello.pdf") if err != nil { panic(err) } fmt.Println(pageWidth, pageHeight) }