package services import ( "bytes" "encoding/base64" "errors" "fmt" "html/template" "math" "strings" "go.uber.org/zap" ) type Templater interface { Execute(template *template.Template, data interface{}) (string, error) StringToTemplate(templateString string) (*template.Template, error) } type DefaultTemplater struct { BarcodeService BarcodeService StaticService StaticService Logger *zap.SugaredLogger } func idToEan13(id string, prefix string) (string, error) { if len(id) > 12 { return "", errors.New("id too long") } for len(id) < 11 { id = "0" + id } id = prefix + id return id, nil } func (t *DefaultTemplater) GenerateEPC(iban string, bic string, name string, title string, amount int, currency string) (string, error) { var err error code := fmt.Sprintf(`BCD 002 1 INST %s %s %s %s%.2f %s `, bic, name, iban, currency, float64(amount)/100, title, ) buf, err := t.BarcodeService.GenerateBarcode("qr", code, 500, 500, 0) return base64.StdEncoding.EncodeToString(buf.Bytes()), err } func (t *DefaultTemplater) GenerateBarcode(code string, format string, prefix string) (string, error) { var err error if format == "ean13" { code, err = idToEan13(code, prefix) if err != nil { return "", err } } buf, err := t.BarcodeService.GenerateBarcode(format, code, 1000, 500, 0) return base64.StdEncoding.EncodeToString(buf.Bytes()), err } func (t *DefaultTemplater) SelectSponsorImage(id int) (string, error) { logger := t.Logger.Named("SelectSponsorImage") sponsors, err := t.StaticService.ListFilesInStaticSubFolder("images/sponsors") if err != nil { logger.Errorw("Failed to list sponsors", "error", err) return "", err } logger.Debugw("Selected sponsor", "sponsors", sponsors, "id", id, "selected", sponsors[id%len(sponsors)]) return t.StaticService.GetImage("sponsors/" + strings.TrimSuffix(sponsors[id%len(sponsors)], ".base64")), nil } func (t *DefaultTemplater) LoadImage(name string) (string, error) { return t.StaticService.GetImage(name), nil } func (t *DefaultTemplater) FormatUnit(unit string, locale string, amount int) (string, error) { var formatted string var seperator string switch locale { case "de": seperator = " " default: seperator = "" } switch unit { case "kilometer": if amount < 1000 { formatted = fmt.Sprintf("%d%sm", amount, seperator) } else if (amount % 1000) == 0 { formatted = fmt.Sprintf("%d%skm", amount/1000, seperator) } else { kilometers := math.Floor(float64(amount) / 1000) meters := amount - int(kilometers)*1000 formatted = fmt.Sprintf("%d%skm %d%sm", int(kilometers), seperator, meters, seperator) } case "euro": formatted = fmt.Sprintf("%.2f", float64(amount)/100) default: return "", errors.New("unknown unit") } if locale == "de" { return strings.Replace(formatted, ".", ",", -1), nil } return formatted, nil } func (t *DefaultTemplater) StringToTemplate(templateString string) (*template.Template, error) { return template.New("template").Funcs(template.FuncMap{ "barcode": t.GenerateBarcode, "sponsorLogo": t.SelectSponsorImage, "formatUnit": t.FormatUnit, "loadImage": t.LoadImage, "epcCode": t.GenerateEPC, }).Parse(templateString) } func (t *DefaultTemplater) Execute(baseTemplate *template.Template, data interface{}) (string, error) { resultBuffer := new(bytes.Buffer) err := baseTemplate.Execute(resultBuffer, data) if err != nil { return "", err } return resultBuffer.String(), nil }