diff --git a/.gitignore b/.gitignore index 11442d71..a6364129 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.png *.dxf *.sh +*.txt +*.pdf diff --git a/cmd/font2mbt/main.go b/cmd/font2mbt/main.go index 9a473dec..ba8c170c 100644 --- a/cmd/font2mbt/main.go +++ b/cmd/font2mbt/main.go @@ -274,6 +274,19 @@ func (g *glyphT) regenerateFace() { g.d = d.String() } +func runeToMbtString(s string) string { + switch s { + case `"`: + return `\"` + case `\`: + return `\\` + case "\t", "\n", "\r": + return fmt.Sprintf(`\x%x`, s) + default: + return s + } +} + func writeFont(fontData *webfont.FontData) { p := &processor{glyphs: map[string]*glyphT{}} if err := webfont.ParseNeededGlyphs(fontData, "", p); err != nil { // DEBUGGING ONLY @@ -290,13 +303,7 @@ func writeFont(fontData *webfont.FontData) { for _, unicode := range keys { glyph := p.glyphs[unicode] - mbtChar := string(unicode) - switch mbtChar { - case `"`: - mbtChar = `\"` - case `\`: - mbtChar = `\\` - } + mbtChar := runeToMbtString(unicode) lines = append(lines, fmt.Sprintf(`"%v": {`, mbtChar)) lines = append(lines, fmt.Sprintf(` char: "%v",`, mbtChar)) diff --git a/cmd/font2mbt/main_test.go b/cmd/font2mbt/main_test.go index ac4090d9..22ede03b 100644 --- a/cmd/font2mbt/main_test.go +++ b/cmd/font2mbt/main_test.go @@ -1,7 +1,40 @@ package main +import "testing" + func String(s string) *string { return &s } +func TestRuneToMbtString(t *testing.T) { + t.Parallel() + tests := []struct { + s string + want string + }{ + { + s: "\t", + want: `\x09`, + }, + { + s: "\r", + want: `\x0d`, + }, + { + s: "\n", + want: `\x0a`, + }, + } + + for _, tt := range tests { + t.Run(tt.s, func(t *testing.T) { + got := runeToMbtString(tt.s) + + if got != tt.want { + t.Errorf("runeToMbtString(%q) = '%v', want '%v'", tt.s, got, tt.want) + } + }) + } +} + /* removed regeneration func TestProcessor_Letter_a(t *testing.T) { g := &webfont.Glyph{ diff --git a/examples/make-address-labels/BalsamiqSans-Regular.json b/examples/make-address-labels/BalsamiqSans-Regular.json new file mode 100644 index 00000000..70f003bb --- /dev/null +++ b/examples/make-address-labels/BalsamiqSans-Regular.json @@ -0,0 +1 @@ +{"Tp":"TrueType","Name":"BalsamiqSans-Regular","Desc":{"Ascent":900,"Descent":-300,"CapHeight":715,"Flags":32,"FontBBox":{"Xmin":-385,"Ymin":-346,"Xmax":1306,"Ymax":1134},"ItalicAngle":0,"StemV":70,"MissingWidth":465},"Up":-75,"Ut":50,"Cw":[465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,465,270,209,373,643,711,944,663,224,360,371,467,669,182,366,195,452,588,387,590,615,610,573,611,491,583,578,202,206,697,715,761,507,922,611,685,711,729,659,596,750,731,310,540,645,575,863,752,762,629,778,711,605,601,732,618,911,566,584,613,375,424,342,527,618,193,566,563,532,573,593,309,577,553,234,238,490,223,827,564,568,580,584,361,495,307,557,480,671,468,452,478,426,205,428,704,465,757,465,217,517,354,772,566,611,291,1052,667,286,1060,465,613,465,465,268,185,423,423,260,659,871,385,854,554,305,984,465,500,636,270,214,624,644,627,622,206,635,294,803,446,528,667,389,782,317,461,652,432,380,193,621,654,112,211,333,423,557,896,954,905,471,613,619,598,601,605,613,946,711,693,637,667,685,288,309,288,288,729,767,730,777,762,789,799,668,786,727,701,687,703,584,643,558,568,566,566,572,566,566,854,532,602,602,602,602,237,237,237,216,610,565,557,570,564,568,563,646,563,549,549,561,563,452,540,432],"Enc":"cp1252","Diff":"","File":"BalsamiqSans-Regular.z","Size1":0,"Size2":0,"OriginalSize":389840,"N":0,"DiffN":0} \ No newline at end of file diff --git a/examples/make-address-labels/BalsamiqSans-Regular.z b/examples/make-address-labels/BalsamiqSans-Regular.z new file mode 100644 index 00000000..3d49b3d0 Binary files /dev/null and b/examples/make-address-labels/BalsamiqSans-Regular.z differ diff --git a/examples/make-address-labels/go.mod b/examples/make-address-labels/go.mod new file mode 100644 index 00000000..a8214b8b --- /dev/null +++ b/examples/make-address-labels/go.mod @@ -0,0 +1,5 @@ +module github.com/gmlewis/go-fonts/examples/make-address-labels + +go 1.23.0 + +require github.com/go-pdf/fpdf v0.9.0 diff --git a/examples/make-address-labels/go.sum b/examples/make-address-labels/go.sum new file mode 100644 index 00000000..23b37055 --- /dev/null +++ b/examples/make-address-labels/go.sum @@ -0,0 +1,2 @@ +github.com/go-pdf/fpdf v0.9.0 h1:PPvSaUuo1iMi9KkaAn90NuKi+P4gwMedWPHhj8YlJQw= +github.com/go-pdf/fpdf v0.9.0/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y= diff --git a/examples/make-address-labels/main.go b/examples/make-address-labels/main.go new file mode 100644 index 00000000..dee6c4a0 --- /dev/null +++ b/examples/make-address-labels/main.go @@ -0,0 +1,106 @@ +// -*- compile-command: "go run main.go postcard-labels.txt"; -*- + +// make-address-labels reads a text file and generates a PDF that can be used +// to print on a standard 8.5x11 inch page of 30 labels arranged in a 3x10 grid. +// Each label is 66mm wide and 25mm high. +// +// The format of the text file is: +// +// Recipient1 Name +// Address Line 1 +// Address Line 2 +// Address Line 3 +// +// Recipient2 Name +// etc... +// +// Usage: +// +// make-address-labels addresses.txt +// +// This will write a file called "addresses.pdf". +package main + +import ( + _ "embed" + "flag" + "log" + "os" + "path/filepath" + "strings" + + "github.com/go-pdf/fpdf" +) + +const ( + pdfMarginMM = 10 + pdfDPI = 300 + mmPerInch = 25.4 + dpmm = pdfDPI / mmPerInch // pixels/mm for 300DPI images + widthInches = 8.5 + heightInches = 11 + widthMM = widthInches * mmPerInch + heightMM = heightInches * mmPerInch + labelWidthMM = 66 + labelHeightMM = 25 + numLabelsX = 3 + numLabelsY = 10 + font1Family = "Balsamiq Sans" +) + +//go:embed BalsamiqSans-Regular.json +var BalsamiqSansRegularJSON []byte + +//go:embed BalsamiqSans-Regular.z +var BalsamiqSansRegularZ []byte + +func main() { + flag.Parse() + + for _, filename := range flag.Args() { + process(filename) + } + + log.Printf("Done.") +} + +func process(filename string) { + buf, err := os.ReadFile(filename) + must(err) + + addresses := strings.Split(string(buf), "\n\n") + log.Printf("Got %v addresses from %v", len(addresses), filename) + + p := fpdf.NewCustom(&fpdf.InitType{ + UnitStr: "mm", + Size: fpdf.SizeType{Wd: widthMM, Ht: heightMM}, + }) + p.AddPage() + p.AddFontFromBytes(font1Family, "", BalsamiqSansRegularJSON, BalsamiqSansRegularZ) + p.SetAutoPageBreak(false, 0) + p.SetTextColor(0, 0, 0) + p.SetFont(font1Family, "", 10) + _, lineHeight1 := p.GetFontSize() + + for i, label := range addresses { + nx, ny := i%numLabelsX, i/numLabelsX + x := pdfMarginMM + float64(nx)*widthMM/numLabelsX + y := float64(ny) * (heightMM - pdfMarginMM) / numLabelsY + lines := strings.Split(label, "\n") + for j, line := range lines { + p.SetXY(x, y+(lineHeight1+1.0)*float64(j)) + // log.Printf("(%v,%v)(%v,%v): %q", nx, ny, x, y, line) + p.CellFormat(1, lineHeight1+10.0, line, "", 2, "AL", false, 0, "") + } + } + + pdfFilename := filepath.Join(filepath.Dir(filename), filepath.Base(filename)+".pdf") + must(p.OutputFileAndClose(pdfFilename)) + log.Printf("Wrote %v", pdfFilename) +} + +func must(err error) { + if err != nil { + log.Fatal(err) + } +}