diff --git a/internal/modules/team_qr_pdf/generator.go b/internal/modules/team_qr_pdf/generator.go index 8dbc7b6..80b7ecb 100644 --- a/internal/modules/team_qr_pdf/generator.go +++ b/internal/modules/team_qr_pdf/generator.go @@ -84,15 +84,6 @@ func Generate(cards []Card) ([]byte, error) { } x := margin + float64(i%columns)*width y := margin + float64(i%perPage/columns)*height - bitmap := qr.Bitmap() - pixel := qrSize / float64(len(bitmap)) - for row, cells := range bitmap { - for col, black := range cells { - if black { - pdf.Rect(x+(width-qrSize)/2+float64(col)*pixel, y+3+float64(row)*pixel, pixel, pixel, "F") - } - } - } name := strings.Join(strings.Fields(card.Name), " ") for _, r := range name { if r > 0xffff { @@ -105,6 +96,15 @@ func Generate(cards []Card) ([]byte, error) { instructions = append(instructions, pdf.SplitText(paragraph, width-6)...) } instructionHeight := float64(len(instructions)) * 3.3 + bitmap := qr.Bitmap() + pixel := qrSize / float64(len(bitmap)) + for row, cells := range bitmap { + for col, black := range cells { + if black { + pdf.Rect(x+(width-qrSize)/2+float64(col)*pixel, y+3+instructionHeight+float64(row)*pixel, pixel, pixel, "F") + } + } + } var lines []string var lineHeight float64 for size := 12.0; size >= 6; size-- { @@ -119,12 +119,12 @@ func Generate(cards []Card) ([]byte, error) { return nil, fmt.Errorf("название команды %d не помещается на карточке (%d строк, %f мм)", i+1, len(lines), lineHeight) } for line, text := range lines { - pdf.SetXY(x+3, y+qrSize+5+float64(line)*lineHeight) + pdf.SetXY(x+3, y+instructionHeight+qrSize+5+float64(line)*lineHeight) pdf.CellFormat(width-6, lineHeight, text, "", 0, "C", false, 0, "") } pdf.SetFont("teams", "", 8) for line, text := range instructions { - pdf.SetXY(x+3, y+qrSize+5+float64(len(lines))*lineHeight+float64(line)*3.3) + pdf.SetXY(x+3, y+3+float64(line)*3.3) pdf.CellFormat(width-6, 3.3, text, "", 0, "L", false, 0, "") } } diff --git a/internal/modules/team_qr_pdf/wifi.go b/internal/modules/team_qr_pdf/wifi.go index e2644bb..3c884c0 100644 --- a/internal/modules/team_qr_pdf/wifi.go +++ b/internal/modules/team_qr_pdf/wifi.go @@ -51,9 +51,9 @@ func WiFiInstructions(name, password string) []string { access = "Пароль: «" + password + "»" } return []string{ - "1. Подключитесь к Wi-Fi «" + name + "».", + "Подключитесь к Wi-Fi «" + name + "».", access, - "2. Отсканируйте QR-код команды.", - "Если телефон сообщает, что сеть без интернета, оставайтесь подключены.", + "Оставайтесь в сети без интернета.", + "Затем отсканируйте QR-код.", } } diff --git a/internal/modules/team_qr_pdf/wifi_test.go b/internal/modules/team_qr_pdf/wifi_test.go index b13a010..cec1f8b 100644 --- a/internal/modules/team_qr_pdf/wifi_test.go +++ b/internal/modules/team_qr_pdf/wifi_test.go @@ -43,10 +43,10 @@ func TestWiFiInstructions(t *testing.T) { } got := WiFiInstructions(" Venue ", " secret ") want := []string{ - "1. Подключитесь к Wi-Fi « Venue ».", + "Подключитесь к Wi-Fi « Venue ».", "Пароль: « secret »", - "2. Отсканируйте QR-код команды.", - "Если телефон сообщает, что сеть без интернета, оставайтесь подключены.", + "Оставайтесь в сети без интернета.", + "Затем отсканируйте QR-код.", } if !reflect.DeepEqual(got, want) { t.Fatal("instructions changed credentials") diff --git a/internal/services/team_service/receipt.go b/internal/services/team_service/receipt.go index 9a4033f..52a307f 100644 --- a/internal/services/team_service/receipt.go +++ b/internal/services/team_service/receipt.go @@ -152,8 +152,8 @@ func renderReceipt(card team_qr_pdf.Card) (*image.Gray, error) { return nil, ErrInvalidReceipt } qrSize := len(bitmap) * scale - qrY := 16 + len(lines)*nameLineHeight + 16 - height := qrY + qrSize + 24 + len(instructions)*26 + qrY := 16 + len(instructions)*26 + 16 + height := qrY + qrSize + 24 + len(lines)*nameLineHeight for card.WiFiName != "" && height > receiptMaxHeight && nameSize > 16 { _ = face.Close() nameSize -= 2 @@ -163,8 +163,7 @@ func renderReceipt(card team_qr_pdf.Card) (*image.Gray, error) { 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 + height = qrY + qrSize + 24 + len(lines)*nameLineHeight } if height > receiptMaxHeight { return nil, ErrInvalidReceipt @@ -174,7 +173,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*nameLineHeight) + drawer.Dot = fixed.P((receiptWidth-width)/2, qrY+qrSize+face.Metrics().Ascent.Ceil()+i*nameLineHeight) drawer.DrawString(line) } x := (receiptWidth - qrSize) / 2 @@ -187,7 +186,7 @@ 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.Dot = fixed.P(16, 16+instructionFace.Metrics().Ascent.Ceil()+i*26) drawer.DrawString(line) } for i, pixel := range img.Pix { diff --git a/internal/services/team_service/receipt_test.go b/internal/services/team_service/receipt_test.go index aa8bb53..135f69c 100644 --- a/internal/services/team_service/receipt_test.go +++ b/internal/services/team_service/receipt_test.go @@ -14,6 +14,7 @@ import ( "evening_detective_server/internal/repos" "evening_detective_server/internal/repos/games_repo" "evening_detective_server/internal/repos/teams_repo" + "github.com/skip2/go-qrcode" "golang.org/x/image/font" "golang.org/x/image/font/opentype" ) @@ -198,3 +199,26 @@ func TestWrapReceiptTextPreservesCredentials(t *testing.T) { } } } + +func TestReceiptQRCodeBeforeTeamName(t *testing.T) { + card := team_qr_pdf.Card{Name: "Вечерние сыщики", URL: "https://example.org/team"} + img, err := renderReceipt(card) + if err != nil { + t.Fatal(err) + } + qr, err := qrcode.New(card.URL, qrcode.Medium) + if err != nil { + t.Fatal(err) + } + bitmap := qr.Bitmap() + scale := (receiptWidth - 32) / len(bitmap) + x := (receiptWidth - len(bitmap)*scale) / 2 + // With no instructions the QR starts at the top, before the team name. + for row, cells := range bitmap { + for col, black := range cells { + if got := img.GrayAt(x+col*scale, 32+row*scale).Y == 0; got != black { + t.Fatal("QR must precede the team name") + } + } + } +}