package main import ( "bytes" "flag" "fmt" "os" "strings" "text/template" "github.com/jung-kurt/gofpdf" ) var ( flagInputCsv = flag.String("csv", "", "CSV file (downloaded from Patreon)") flagOutputPdf = flag.String("o", "out.pdf", "filename to output PDF to") flagPortrait = flag.Bool("portrait", true, "portrait mode?") flagUnitStr = flag.String("unitStr", "mm", "units to use for other measurements") flagPaperType = flag.String("paper", "Letter", "paper size") flagCols = flag.Int("cols", 3, "number of columns") flagRows = flag.Int("row", 10, "number of rows") flagSkipLabels = flag.Int("skip", 0, "skip labels (useful for printing on partially-printed label sheets") flagOffsetX = flag.Float64("x", 6.5, "left margin") flagOffsetY = flag.Float64("y", 12.7, "top margin") flagLabelWidth = flag.Float64("w", 66.8, "label width") flagLabelHeight = flag.Float64("h", 25.4, "label height") flagGapX = flag.Float64("gapx", 3.4, "gap between columns") flagGapY = flag.Float64("gapy", 0., "gap between rows") flagDrawLines = flag.Bool("lines", false, "draw lines?") flagFontSize = flag.Float64("fontsize", 10, "font size") flagFontFamily = flag.String("fontfamily", "Arial", "font family") flagFontStyle = flag.String("fontstyle", "", "font style (B for bold)") flagTemplate = flag.String("template", "{{.Addressee}}\n{{.Street}}\n{{.City}}, {{.State}}\n{{.Zip}}, {{.Country}}\n", "mailing label template") lineHeight = 4.0 ) func main() { flag.Parse() if *flagInputCsv == "" { fmt.Println("Must specify input csv with -csv") flag.Usage() os.Exit(1) } pL := "P" if !*flagPortrait { pL = "L" } pdf := gofpdf.New(pL, *flagUnitStr, *flagPaperType, "") pdf.SetFont(*flagFontFamily, *flagFontStyle, *flagFontSize) data := LoadCSVFromFile(*flagInputCsv) DoTheThing(pdf, data) err := pdf.OutputFileAndClose(*flagOutputPdf) if err != nil { panic(err) } } func DoTheThing(pdf *gofpdf.Fpdf, data CSV) { tpl, err := template.New("mailing labels").Parse(*flagTemplate) if err != nil { panic(err) } tr := pdf.UnicodeTranslatorFromDescriptor("") for i := 0; i < data.NumRecords(); { AddGriddedPage(pdf) for x := 0; x < *flagCols; x++ { for y := 0; y < *flagRows; y++ { if *flagSkipLabels > 0 { *flagSkipLabels-- continue } for ; i < data.NumRecords() && data.PatreonEmptyAddress(i); i++ { fmt.Printf("Warning: skipping empty addressee: %s\n", data.GetField(i, "Name")) fmt.Printf("%v\n", data.Record(i)) } if i >= data.NumRecords() { return } lx := *flagOffsetX + (*flagLabelWidth + *flagGapX) * float64(x) + 4. ly := *flagOffsetY + (*flagLabelHeight + *flagGapY) * float64(y) + 8. var buf bytes.Buffer err := tpl.Execute(&buf, data.Record(i)) if err != nil { panic(err) } DrawText(pdf, tr(buf.String()), lx, ly) i++ } } } } func AddGriddedPage(pdf *gofpdf.Fpdf) { pdf.AddPage() if !*flagDrawLines { return } for x := 0; x < *flagCols; x++ { for y := 0; y < *flagRows; y++ { lx := *flagOffsetX + (*flagLabelWidth + *flagGapX) * float64(x) ly := *flagOffsetY + (*flagLabelHeight + *flagGapY) * float64(y) pdf.Rect(lx, ly, *flagLabelWidth, *flagLabelHeight, "D") } } } func DrawText(pdf *gofpdf.Fpdf, str string, x, y float64) { lines := strings.Split(str, "\n") for _, s := range lines { pdf.Text(x, y, s) y += lineHeight } }