package bookfile import ( "io" "strings" ) // txt.go: the plain-text book. No audit banners, no flags in brackets, no operator vocabulary — the file // is prose plus what the model carries: the title first, the notice (when there is one), then every // chapter as its title line followed by its paragraphs. Blocks are separated by ONE blank line, chapters // by TWO, so a chapter boundary reads differently from a paragraph boundary even in a viewer that shows // nothing but text. The file ends with a newline. // // The same block list the EPUB renders (Blocks) is what lands here, so the two formats say the same thing — // cleaned the same way too: every string the EPUB passes through xmlText goes through CleanText here, so // a control character in a title or a notice cannot reach the text reader when the EPUB reader is spared it. // WriteTXT writes b as plain UTF-8 text to w. Deterministic. func WriteTXT(w io.Writer, b *Book) error { if err := b.validate(); err != nil { return err } var sb strings.Builder sb.WriteString(strings.TrimSpace(CleanText(b.Title))) for _, p := range b.Notice { sb.WriteString("\n\n" + CleanText(p)) } for _, ch := range b.Chapters { sb.WriteString("\n\n\n" + CleanText(ch.Title)) for _, p := range ch.Paragraphs { sb.WriteString("\n\n" + CleanText(p)) } } sb.WriteString("\n") _, err := io.WriteString(w, sb.String()) return err }