From 138695208e06a733d6b27673338fd3ca8de7fe62 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 21 Apr 2026 10:45:38 +0300 Subject: fix o7: keep comic section splitting UTF-8 safe --- internal/comic/artist.go | 5 +++-- internal/comic/comic_test.go | 29 +++++++++++++++++++++++++++++ internal/comic/helpers.go | 7 ++++--- 3 files changed, 36 insertions(+), 5 deletions(-) (limited to 'internal') diff --git a/internal/comic/artist.go b/internal/comic/artist.go index 248e8c4..aa1dd5f 100644 --- a/internal/comic/artist.go +++ b/internal/comic/artist.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "time" + "unicode/utf8" "codeberg.org/snonux/comicforge/internal/provider" ) @@ -377,8 +378,8 @@ func buildPanelLayout(section string, pagePanels []string, panelCount int) strin } excerpt := strings.TrimSpace(section) - if len(excerpt) > comicPromptMaxChars { - excerpt = excerpt[:comicPromptMaxChars] + if utf8.RuneCountInString(excerpt) > comicPromptMaxChars { + excerpt = string([]rune(excerpt)[:comicPromptMaxChars]) if idx := strings.LastIndex(excerpt, " "); idx > 0 { excerpt = excerpt[:idx] } diff --git a/internal/comic/comic_test.go b/internal/comic/comic_test.go index 020fba5..0acccb0 100644 --- a/internal/comic/comic_test.go +++ b/internal/comic/comic_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" "time" + "unicode/utf8" ) func TestSlugify(t *testing.T) { @@ -55,6 +56,34 @@ func TestBuildPanelLayoutUsesFallbackExcerpt(t *testing.T) { } } +func TestSplitIntoSectionsUsesRuneBoundaries(t *testing.T) { + t.Parallel() + + text := strings.Repeat("Български текст за проверка на разделянето. ", 80) + sections := splitIntoSections(text, 4) + if len(sections) != 4 { + t.Fatalf("splitIntoSections() sections = %d, want 4", len(sections)) + } + for i, section := range sections { + if !utf8.ValidString(section) { + t.Fatalf("splitIntoSections() section %d is not valid UTF-8: %q", i, section) + } + } +} + +func TestBuildPanelLayoutTruncatesCyrillicOnRuneBoundaries(t *testing.T) { + t.Parallel() + + text := strings.Repeat("абвгдежзийклмно", 70) + got := buildPanelLayout(text, nil, 2) + if !utf8.ValidString(got) { + t.Fatalf("buildPanelLayout() returned invalid UTF-8: %q", got) + } + if !strings.Contains(got, "…") { + t.Fatalf("buildPanelLayout() = %q, want truncation ellipsis", got) + } +} + func TestParseGenerateResultUsesConfiguredDimensions(t *testing.T) { t.Parallel() diff --git a/internal/comic/helpers.go b/internal/comic/helpers.go index 0458db5..6f5b5a8 100644 --- a/internal/comic/helpers.go +++ b/internal/comic/helpers.go @@ -64,15 +64,16 @@ func distributeParagraphs(paragraphs []string, n int) []string { } func splitByChars(text string, n int) []string { - size := len(text) / n + runes := []rune(text) + size := len(runes) / n sections := make([]string, n) for i := range n { start := i * size end := start + size if i == n-1 { - end = len(text) + end = len(runes) } - sections[i] = strings.TrimSpace(text[start:end]) + sections[i] = strings.TrimSpace(string(runes[start:end])) } return sections } -- cgit v1.2.3