generated from VLADIMIR/template
up
This commit is contained in:
@@ -103,6 +103,9 @@ func (s *TeamService) teamReceipt(ctx context.Context, gameID int, teamID string
|
||||
}
|
||||
|
||||
func renderReceipt(card team_qr_pdf.Card) (*image.Gray, error) {
|
||||
if err := team_qr_pdf.ValidateWiFi(card.WiFiName, card.WiFiPassword); err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrInvalidReceipt, err)
|
||||
}
|
||||
name := strings.Join(strings.Fields(card.Name), " ")
|
||||
if name == "" || len([]rune(name)) > 255 {
|
||||
return nil, ErrInvalidReceipt
|
||||
@@ -111,17 +114,34 @@ func renderReceipt(card team_qr_pdf.Card) (*image.Gray, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse receipt font: %w", err)
|
||||
}
|
||||
face, err := opentype.NewFace(ttf, &opentype.FaceOptions{Size: 26, DPI: 72, Hinting: font.HintingFull})
|
||||
nameSize, nameLineHeight := 26.0, 34
|
||||
if card.WiFiName != "" {
|
||||
nameSize, nameLineHeight = 20, 26
|
||||
}
|
||||
face, err := opentype.NewFace(ttf, &opentype.FaceOptions{Size: nameSize, DPI: 72, Hinting: font.HintingFull})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open receipt font: %w", err)
|
||||
}
|
||||
defer face.Close()
|
||||
defer func() {
|
||||
if face != nil {
|
||||
_ = face.Close()
|
||||
}
|
||||
}()
|
||||
for _, r := range name {
|
||||
if _, ok := face.GlyphAdvance(r); !ok {
|
||||
return nil, ErrInvalidReceipt
|
||||
}
|
||||
}
|
||||
lines := wrapReceiptName(name, face)
|
||||
instructionFace, err := opentype.NewFace(ttf, &opentype.FaceOptions{Size: 20, DPI: 72, Hinting: font.HintingFull})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open Wi-Fi receipt font: %w", err)
|
||||
}
|
||||
defer instructionFace.Close()
|
||||
var instructions []string
|
||||
for _, paragraph := range team_qr_pdf.WiFiInstructions(card.WiFiName, card.WiFiPassword) {
|
||||
instructions = append(instructions, wrapReceiptText(paragraph, instructionFace)...)
|
||||
}
|
||||
qr, err := qrcode.New(card.URL, qrcode.Medium)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidReceipt
|
||||
@@ -132,8 +152,20 @@ func renderReceipt(card team_qr_pdf.Card) (*image.Gray, error) {
|
||||
return nil, ErrInvalidReceipt
|
||||
}
|
||||
qrSize := len(bitmap) * scale
|
||||
qrY := 16 + len(lines)*34 + 16
|
||||
height := qrY + qrSize + 24
|
||||
qrY := 16 + len(lines)*nameLineHeight + 16
|
||||
height := qrY + qrSize + 24 + len(instructions)*26
|
||||
for card.WiFiName != "" && height > receiptMaxHeight && nameSize > 16 {
|
||||
_ = face.Close()
|
||||
nameSize -= 2
|
||||
nameLineHeight = int(nameSize) + 6
|
||||
face, err = opentype.NewFace(ttf, &opentype.FaceOptions{Size: nameSize, DPI: 72, Hinting: font.HintingFull})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resize receipt font: %w", err)
|
||||
}
|
||||
lines = wrapReceiptName(name, face)
|
||||
qrY = 16 + len(lines)*nameLineHeight + 16
|
||||
height = qrY + qrSize + 24 + len(instructions)*26
|
||||
}
|
||||
if height > receiptMaxHeight {
|
||||
return nil, ErrInvalidReceipt
|
||||
}
|
||||
@@ -142,7 +174,7 @@ func renderReceipt(card team_qr_pdf.Card) (*image.Gray, error) {
|
||||
drawer := font.Drawer{Dst: img, Src: image.Black, Face: face}
|
||||
for i, line := range lines {
|
||||
width := drawer.MeasureString(line).Ceil()
|
||||
drawer.Dot = fixed.P((receiptWidth-width)/2, 16+face.Metrics().Ascent.Ceil()+i*34)
|
||||
drawer.Dot = fixed.P((receiptWidth-width)/2, 16+face.Metrics().Ascent.Ceil()+i*nameLineHeight)
|
||||
drawer.DrawString(line)
|
||||
}
|
||||
x := (receiptWidth - qrSize) / 2
|
||||
@@ -153,6 +185,11 @@ func renderReceipt(card team_qr_pdf.Card) (*image.Gray, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
drawer.Face = instructionFace
|
||||
for i, line := range instructions {
|
||||
drawer.Dot = fixed.P(16, qrY+qrSize+instructionFace.Metrics().Ascent.Ceil()+i*26)
|
||||
drawer.DrawString(line)
|
||||
}
|
||||
for i, pixel := range img.Pix {
|
||||
if pixel < 128 {
|
||||
img.Pix[i] = 0
|
||||
@@ -211,3 +248,25 @@ func validateReceiptRaster(data []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Preserve spaces in credentials when wrapping the printed instructions.
|
||||
func wrapReceiptText(text string, face font.Face) []string {
|
||||
var lines []string
|
||||
line := ""
|
||||
for _, r := range text {
|
||||
for line != "" && font.MeasureString(face, line+string(r)).Ceil() > receiptWidth-32 {
|
||||
if space := strings.LastIndexByte(line, ' '); space >= 0 {
|
||||
lines = append(lines, line[:space+1])
|
||||
line = line[space+1:]
|
||||
} else {
|
||||
lines = append(lines, line)
|
||||
line = ""
|
||||
}
|
||||
}
|
||||
line += string(r)
|
||||
}
|
||||
if line != "" {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user