2026-04-28 08:26:35 +09:30
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"image/color"
|
|
|
|
|
|
|
|
|
|
"codeberg.org/go-pdf/fpdf"
|
|
|
|
|
"github.com/yeqown/go-qrcode/v2"
|
|
|
|
|
"github.com/yeqown/go-qrcode/writer/standard"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 1
|
|
|
|
|
|
|
|
|
|
var cols = 4
|
2026-04-28 09:45:18 +09:30
|
|
|
var rows = 5
|
|
|
|
|
var qrSize = 40
|
|
|
|
|
var fontSize = 20
|
2026-04-28 08:26:35 +09:30
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
qrc, err := qrcode.New("https://nanocat.net")
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("could not generate QRCode: %v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b := newBWC()
|
|
|
|
|
qrBytes := standard.NewWithWriter(&b,
|
2026-04-28 09:45:18 +09:30
|
|
|
// standard.WithQRWidth(uint8(qrSize)),
|
|
|
|
|
standard.WithBorderWidth(0),
|
2026-04-28 08:26:35 +09:30
|
|
|
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,
|
|
|
|
|
}), standard.WithLogoImageFileJPEG("cat-pet-animal-domestic-104827.jpeg"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// save file
|
|
|
|
|
if err = qrc.Save(qrBytes); err != nil {
|
|
|
|
|
fmt.Printf("could not save image: %v", err)
|
|
|
|
|
}
|
2026-04-28 09:45:18 +09:30
|
|
|
// dim := qrc.Dimension()
|
2026-04-28 08:26:35 +09:30
|
|
|
|
|
|
|
|
pdf := fpdf.New("P", "mm", "A4", "")
|
2026-04-28 09:45:18 +09:30
|
|
|
pdf.SetMargins(0, 0, 0)
|
2026-04-28 08:26:35 +09:30
|
|
|
pageWidth, pageHeight := pdf.GetPageSize()
|
|
|
|
|
|
|
|
|
|
// register the qr
|
|
|
|
|
pdf.RegisterImageOptionsReader("qr", fpdf.ImageOptions{
|
|
|
|
|
ImageType: "png",
|
|
|
|
|
ReadDpi: false,
|
|
|
|
|
AllowNegativePosition: false,
|
|
|
|
|
}, b)
|
|
|
|
|
|
2026-04-28 09:45:18 +09:30
|
|
|
info := pdf.GetImageInfo("qr")
|
|
|
|
|
// panic(info.Height())
|
|
|
|
|
|
2026-04-28 08:26:35 +09:30
|
|
|
pdf.AddPage()
|
2026-04-28 09:45:18 +09:30
|
|
|
pdf.SetFont("Arial", "B", float64(fontSize))
|
2026-04-28 08:26:35 +09:30
|
|
|
|
|
|
|
|
text := "foobar"
|
|
|
|
|
textWidth := pdf.GetStringWidth(text)
|
|
|
|
|
|
|
|
|
|
colWidth := pageWidth / float64(cols)
|
|
|
|
|
rowHeight := pageHeight / float64(rows)
|
|
|
|
|
|
2026-04-28 09:45:18 +09:30
|
|
|
for rowIdx := range rows {
|
|
|
|
|
for colIdx := range cols {
|
|
|
|
|
topLeftX := colWidth * float64(colIdx)
|
|
|
|
|
topLeftY := rowHeight * float64(rowIdx)
|
|
|
|
|
|
|
|
|
|
// if rowHeight*0.8 < float64(qrSize) {
|
|
|
|
|
// panic("row too small for qr")
|
|
|
|
|
// }
|
|
|
|
|
// if colWidth*0.6 < float64(qrSize) {
|
|
|
|
|
// panic("col too small for qr")
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// label at the top
|
|
|
|
|
pdf.Text(
|
|
|
|
|
topLeftX+colWidth/2-textWidth/2,
|
|
|
|
|
topLeftY+float64(fontSize),
|
|
|
|
|
text, // +fmt.Sprintf("%d/%d", rowIdx, colIdx)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
info.Height()
|
|
|
|
|
// pdf at the bottom
|
|
|
|
|
pdf.ImageOptions("qr",
|
|
|
|
|
topLeftX+info.Width()-float64(qrSize), //+float64(dim)/2, // centre
|
|
|
|
|
topLeftY+float64(fontSize)*1.3, // centre
|
2026-04-28 08:26:35 +09:30
|
|
|
float64(qrSize), float64(qrSize),
|
2026-04-28 09:45:18 +09:30
|
|
|
false,
|
|
|
|
|
fpdf.ImageOptions{AllowNegativePosition: true}, 0, "")
|
|
|
|
|
// pdf.Image("qr",
|
|
|
|
|
// topLeftX+5, // centre
|
|
|
|
|
// topLeftY+float64(fontSize)*1.3, // centre
|
|
|
|
|
// float64(qrSize),
|
|
|
|
|
// float64(qrSize),
|
|
|
|
|
// false, "tp", 0, "")
|
2026-04-28 08:26:35 +09:30
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = pdf.OutputFileAndClose("hello.pdf")
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println(pageWidth, pageHeight)
|
|
|
|
|
}
|