From 4bb5ece67797d6564a518d62d57077482027bceb Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Fri, 1 May 2026 18:40:57 -0600 Subject: [PATCH] Added a new font and increased the size and resolution of the result image --- pkg/reports/BitCount.ttf | Bin 0 -> 110072 bytes pkg/reports/image_export.go | 209 +++++++++++++++++++----------------- 2 files changed, 108 insertions(+), 101 deletions(-) create mode 100644 pkg/reports/BitCount.ttf diff --git a/pkg/reports/BitCount.ttf b/pkg/reports/BitCount.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e149e2b63d6e0d5d332f71d6705cca197d7fadc1 GIT binary patch literal 110072 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 literal 0 HcmV?d00001 diff --git a/pkg/reports/image_export.go b/pkg/reports/image_export.go index 7dba7cc..3d6803e 100644 --- a/pkg/reports/image_export.go +++ b/pkg/reports/image_export.go @@ -1,6 +1,7 @@ package reports import ( + _ "embed" "fmt" "image/color" "image/jpeg" @@ -15,14 +16,29 @@ import ( "github.com/fogleman/gg" ) +//go:embed BitCount.ttf +var fontData []byte + +var fontPath string + +func init() { + tmpDir := os.TempDir() + fontPath = filepath.Join(tmpDir, "BitCount.ttf") + if _, err := os.Stat(fontPath); os.IsNotExist(err) { + if err := os.WriteFile(fontPath, fontData, 0644); err != nil { + fmt.Fprintf(os.Stderr, "Warning: failed to write embedded font: %v\n", err) + } + } +} + const ( - canvasW = 1400 - canvasH = 1160 - gridGap = 20 - radius = 24 - unitCol = 325 - twoCol = 670 - fourCol = 1360 + canvasW = 2100 + canvasH = 1740 + gridGap = 30 + radius = 36 + unitCol = 487 + twoCol = 1005 + fourCol = 2040 ) var ( @@ -30,16 +46,16 @@ var ( cardBg = color.RGBA{24, 24, 27, 255} cardBgPurple = color.RGBA{30, 20, 36, 255} cardBgBlue = color.RGBA{15, 23, 42, 255} - accentMagenta = color.RGBA{192, 132, 252, 255} - accentCyan = color.RGBA{45, 212, 191, 255} - accentOrange = color.RGBA{251, 146, 60, 255} - accentGreen = color.RGBA{74, 222, 128, 255} - accentBlue = color.RGBA{96, 165, 250, 255} - textPrimary = color.RGBA{244, 244, 245, 255} - textSecondary = color.RGBA{161, 161, 170, 255} - textMuted = color.RGBA{113, 113, 122, 255} - borderColor = color.RGBA{39, 39, 42, 255} - barTrack = color.RGBA{39, 39, 42, 255} + accentMagenta = color.RGBA{220, 90, 255, 255} + accentCyan = color.RGBA{0, 244, 255, 255} + accentOrange = color.RGBA{255, 150, 0, 255} + accentGreen = color.RGBA{34, 255, 100, 255} + accentBlue = color.RGBA{100, 180, 255, 255} + textPrimary = color.RGBA{255, 255, 255, 255} + textSecondary = color.RGBA{200, 200, 215, 255} + textMuted = color.RGBA{150, 150, 170, 255} + borderColor = color.RGBA{60, 60, 70, 255} + barTrack = color.RGBA{50, 50, 65, 255} ) type bentoCard struct { @@ -54,13 +70,13 @@ func col(n int) float64 { func cardLayout() []bentoCard { r1y := float64(gridGap) - r1h := float64(240) + r1h := float64(360) r2y := r1y + r1h + gridGap - r2h := float64(240) + r2h := float64(360) r3y := r2y + r2h + gridGap - r3h := float64(300) + r3h := float64(450) r4y := r3y + r3h + gridGap - r4h := float64(280) + r4h := float64(420) return []bentoCard{ {col(0), r1y, twoCol, r1h, cardBgPurple, accentMagenta}, @@ -75,17 +91,8 @@ func cardLayout() []bentoCard { } func loadFont(dc *gg.Context, size float64) { - paths := []string{ - "/System/Library/Fonts/Helvetica.ttc", - "/System/Library/Fonts/SFNS.ttf", - "/System/Library/Fonts/Supplemental/Arial.ttf", - "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", - "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", - } - for _, p := range paths { - if err := dc.LoadFontFace(p, size); err == nil { - return - } + if err := dc.LoadFontFace(fontPath, size); err != nil { + fmt.Fprintf(os.Stderr, "Warning: failed to load embedded font: %v\n", err) } } @@ -100,27 +107,27 @@ func drawCard(dc *gg.Context, c bentoCard) { } func label(dc *gg.Context, c bentoCard, text string) { - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textSecondary) - dc.DrawString(text, c.x+24, c.y+32) + dc.DrawString(text, c.x+36, c.y+48) } func bigNum(dc *gg.Context, c bentoCard, value, sub string) { - loadFont(dc, 64) + loadFont(dc, 96) dc.SetColor(c.accent) - dc.DrawString(value, c.x+24, c.y+110) + dc.DrawString(value, c.x+36, c.y+165) if sub != "" { - loadFont(dc, 14) + loadFont(dc, 21) dc.SetColor(textSecondary) - dc.DrawString(sub, c.x+24, c.y+136) + dc.DrawString(sub, c.x+36, c.y+204) } } func bigText(dc *gg.Context, c bentoCard, value string, size float64) { - loadFont(dc, size) + loadFont(dc, size*1.5) dc.SetColor(c.accent) - dc.DrawString(value, c.x+24, c.y+110) + dc.DrawString(value, c.x+36, c.y+165) } func filledBar(dc *gg.Context, x, y, totalW, h, pct float64, fill color.RGBA) { @@ -142,10 +149,10 @@ func renderCommitsCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { bigNum(dc, c, formatNumber(stats.TotalCommits), fmt.Sprintf("%.1f commits per day on average", stats.AverageCommitsPerDay)) - dc.DrawRoundedRectangle(c.x+24, c.y+c.h-40, c.w-48, 10, 5) - dc.SetColor(color.RGBA{50, 40, 60, 255}) + dc.DrawRoundedRectangle(c.x+36, c.y+c.h-60, c.w-72, 15, 7) + dc.SetColor(color.RGBA{70, 50, 90, 255}) dc.Fill() - dc.DrawRoundedRectangle(c.x+24, c.y+c.h-40, (c.w-48)*0.72, 10, 5) + dc.DrawRoundedRectangle(c.x+36, c.y+c.h-60, (c.w-72)*0.72, 15, 7) dc.SetColor(c.accent) dc.Fill() } @@ -154,29 +161,29 @@ func renderReposCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { label(dc, c, "REPOSITORIES") bigNum(dc, c, formatNumber(stats.TotalRepositories), "worked on this year") - y := c.y + 160 - maxW := c.w - 48 + y := c.y + 240 + maxW := c.w - 72 for i, repo := range stats.TopRepositories { if i >= 3 { break } - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textMuted) - dc.DrawString(truncate(repo.Name, maxW, dc), c.x+24, y) - y += 24 + dc.DrawString(truncate(repo.Name, maxW, dc), c.x+36, y) + y += 36 } } func renderActiveDayCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { label(dc, c, "MOST ACTIVE DAY") - bigText(dc, c, stats.MostActiveDay, 48) + bigText(dc, c, stats.MostActiveDay, 72) if len(stats.CommitsByWeekday) > 0 { - loadFont(dc, 14) + loadFont(dc, 21) dc.SetColor(textSecondary) dc.DrawString( fmt.Sprintf("%d commits that day", stats.CommitsByWeekday[0].Count), - c.x+24, c.y+136, + c.x+36, c.y+204, ) } } @@ -188,7 +195,7 @@ func renderAvgCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { func renderActiveMonthCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { label(dc, c, "MOST ACTIVE MONTH") - bigText(dc, c, stats.MostActiveMonth, 44) + bigText(dc, c, stats.MostActiveMonth, 66) } func renderTopRepoCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { @@ -199,25 +206,25 @@ func renderTopRepoCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { } top := stats.TopRepositories[0] - maxW := c.w - 48 + maxW := c.w - 72 - loadFont(dc, 32) + loadFont(dc, 48) dc.SetColor(c.accent) - dc.DrawString(truncate(top.Name, maxW, dc), c.x+24, c.y+100) + dc.DrawString(truncate(top.Name, maxW, dc), c.x+36, c.y+150) - loadFont(dc, 13) + loadFont(dc, 19) dc.SetColor(textSecondary) - dc.DrawString("your #1 repository this year", c.x+24, c.y+126) + dc.DrawString("your #1 repository this year", c.x+36, c.y+189) - y := c.y + 164 + y := c.y + 246 for i := 1; i < len(stats.TopRepositories) && i < 4; i++ { - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textMuted) dc.DrawString( - fmt.Sprintf("#%d %s", i+1, truncate(stats.TopRepositories[i].Name, maxW-30, dc)), - c.x+24, y, + fmt.Sprintf("#%d %s", i+1, truncate(stats.TopRepositories[i].Name, maxW-45, dc)), + c.x+36, y, ) - y += 24 + y += 36 } } @@ -225,24 +232,24 @@ func renderLanguagesCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { label(dc, c, "LANGUAGES") if len(stats.Languages) == 0 { - loadFont(dc, 15) + loadFont(dc, 22) dc.SetColor(textSecondary) - dc.DrawString("No language data available.", c.x+24, c.y+100) + dc.DrawString("No language data available.", c.x+36, c.y+150) return } const ( numCols = 2 rowsPerCol = 5 - rowH = 48.0 - startY = 60.0 - labelW = 110.0 - countW = 90.0 - barH = 14.0 + rowH = 72.0 + startY = 90.0 + labelW = 165.0 + countW = 135.0 + barH = 21.0 ) maxCount := stats.Languages[0].Count - halfW := (c.w - 48 - gridGap) / 2 + halfW := (c.w - 72 - gridGap) / 2 for i, lang := range stats.Languages { if i >= numCols*rowsPerCol { @@ -252,23 +259,23 @@ func renderLanguagesCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { colIdx := i / rowsPerCol rowIdx := i % rowsPerCol - baseX := c.x + 24 + float64(colIdx)*(halfW+float64(gridGap)) + baseX := c.x + 36 + float64(colIdx)*(halfW+float64(gridGap)) baseY := c.y + startY + float64(rowIdx)*rowH pct := float64(lang.Count) / float64(maxCount) - barW := halfW - labelW - countW - 10 + barW := halfW - labelW - countW - 15 - loadFont(dc, 13) + loadFont(dc, 19) dc.SetColor(textPrimary) - dc.DrawString(truncatePx(lang.Language, labelW-8, dc), baseX, baseY+12) + dc.DrawString(truncatePx(lang.Language, labelW-12, dc), baseX, baseY+18) - filledBar(dc, baseX+labelW, baseY+2, barW, barH, pct, c.accent) + filledBar(dc, baseX+labelW, baseY+3, barW, barH, pct, c.accent) - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textSecondary) dc.DrawString( fmt.Sprintf("%d %.1f%%", lang.Count, lang.Percent), - baseX+labelW+barW+10, baseY+13, + baseX+labelW+barW+15, baseY+19, ) } } @@ -277,69 +284,69 @@ func renderActivityCard(dc *gg.Context, c bentoCard, stats *models.UserStats) { label(dc, c, "ACTIVITY PATTERNS") const ( - startY = 54.0 - rowH = 28.0 - labelW = 44.0 - countW = 50.0 - barH = 14.0 + startY = 81.0 + rowH = 42.0 + labelW = 66.0 + countW = 75.0 + barH = 21.0 ) - halfW := (c.w - 48 - float64(gridGap)) / 2 - lx := c.x + 24 + halfW := (c.w - 72 - float64(gridGap)) / 2 + lx := c.x + 36 - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textSecondary) dc.DrawString("By Weekday", lx, c.y+startY) if len(stats.CommitsByWeekday) > 0 { maxC := stats.CommitsByWeekday[0].Count - barW := halfW - labelW - countW - 10 + barW := halfW - labelW - countW - 15 for i, day := range stats.CommitsByWeekday { if i >= 7 { break } - by := c.y + startY + 24 + float64(i)*rowH + by := c.y + startY + 36 + float64(i)*rowH - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textPrimary) - dc.DrawString(day.Day[:3], lx, by+12) + dc.DrawString(day.Day[:3], lx, by+18) pct := float64(day.Count) / float64(maxC) - filledBar(dc, lx+labelW, by+2, barW, barH, pct, accentCyan) + filledBar(dc, lx+labelW, by+3, barW, barH, pct, accentCyan) - loadFont(dc, 11) + loadFont(dc, 16) dc.SetColor(textMuted) - dc.DrawString(formatNumber(day.Count), lx+labelW+barW+8, by+12) + dc.DrawString(formatNumber(day.Count), lx+labelW+barW+12, by+18) } } - rx := c.x + 24 + halfW + float64(gridGap) + rx := c.x + 36 + halfW + float64(gridGap) - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textSecondary) dc.DrawString("By Month", rx, c.y+startY) if len(stats.CommitsByMonth) > 0 { maxC := stats.CommitsByMonth[0].Count - barW := halfW - labelW - countW - 10 + barW := halfW - labelW - countW - 15 for i, month := range stats.CommitsByMonth { if i >= 7 { break } - by := c.y + startY + 24 + float64(i)*rowH + by := c.y + startY + 36 + float64(i)*rowH - loadFont(dc, 12) + loadFont(dc, 18) dc.SetColor(textPrimary) - dc.DrawString(month.Date.Format("Jan"), rx, by+12) + dc.DrawString(month.Date.Format("Jan"), rx, by+18) pct := float64(month.Count) / float64(maxC) - filledBar(dc, rx+labelW, by+2, barW, barH, pct, accentOrange) + filledBar(dc, rx+labelW, by+3, barW, barH, pct, accentOrange) - loadFont(dc, 11) + loadFont(dc, 16) dc.SetColor(textMuted) - dc.DrawString(formatNumber(month.Count), rx+labelW+barW+8, by+12) + dc.DrawString(formatNumber(month.Count), rx+labelW+barW+12, by+18) } } }