From a1723b5114038092b8186f23a61b7b63b5897d78 Mon Sep 17 00:00:00 2001 From: ystyle Date: Tue, 27 Jun 2023 17:41:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E6=94=AF=E6=8C=81,=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E8=A1=8C=E9=97=B4=E8=B7=9D,=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=87=AA=E5=AE=9A=E4=B9=89=E6=9C=AA=E7=9F=A5=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- azw3.go | 15 +++++++++- convert.go | 84 ++++++++++++++++++++++++++++++++++++++++++++---------- epub.go | 26 +++++++++++++++-- go.mod | 2 +- go.sum | 4 +-- mobil.go | 5 ++++ 6 files changed, 115 insertions(+), 21 deletions(-) diff --git a/azw3.go b/azw3.go index d2d9c5d..076f96a 100644 --- a/azw3.go +++ b/azw3.go @@ -34,13 +34,26 @@ func (convert Azw3Converter) Build(book Book) error { Language: language.MustParse(book.Lang), UniqueID: rand.Uint32(), } - css := fmt.Sprintf(cssContent, book.Align, book.Bottom, book.Indent) + var excss string + if book.LineHeight != "" { + excss = fmt.Sprintf("line-height: %s;", book.LineHeight) + } + css := fmt.Sprintf(cssContent, book.Align, book.Bottom, book.Indent, excss) for _, section := range chunk { ch := mobi.Chapter{ Title: section.Title, Chunks: mobi.Chunks(convert.wrapTitle(section.Title, section.Content, book.Align)), } mb.Chapters = append(mb.Chapters, ch) + if len(section.Sections) > 0 { + for _, subsection := range section.Sections { + ch := mobi.Chapter{ + Title: subsection.Title, + Chunks: mobi.Chunks(convert.wrapTitle(subsection.Title, subsection.Content, book.Align)), + } + mb.Chapters = append(mb.Chapters, ch) + } + } } mb.CSSFlows = []string{css} diff --git a/convert.go b/convert.go index dadaac7..6aaa677 100644 --- a/convert.go +++ b/convert.go @@ -27,12 +27,15 @@ type Book struct { Filename string // 目录 Bookname string // 书名 Match string // 正则 + VolumeMatch string // 卷匹配规则 Author string // 作者 Max uint // 标题最大字数 Indent uint // 段落缩进字段 Align string // 标题对齐方式 + UnknowTitle string // 未知章节名称 Cover string // 封面图片 Bottom string // 段阿落间距 + LineHeight string // 行高 Tips bool // 是否添加教程文本 Lang string // 设置语言 Out string // 输出文件名 @@ -41,13 +44,14 @@ type Book struct { Decoder *encoding.Decoder PageStylesFile string Reg *regexp.Regexp - Reg2 *regexp.Regexp + VolumeReg *regexp.Regexp version string } type Section struct { - Title string - Content string + Title string + Content string + Sections []Section } type Converter interface { @@ -60,7 +64,7 @@ const ( htmlTitleStart = `

` mobiTtmlTitleStart = `

` htmlTitleEnd = "

" - Match = "^第[0-9一二三四五六七八九十零〇百千两 ]+卷" + VolumeMatch = "^第[0-9一二三四五六七八九十零〇百千两 ]+卷" DefaultMatchTips = "^第[0-9一二三四五六七八九十零〇百千两 ]+[章回节集卷]|^[Ss]ection.{1,20}$|^[Cc]hapter.{1,20}$|^[Pp]age.{1,20}$|^\\d{1,4}$|^引子$|^楔子$|^章节目录|^章节|^序章" cssContent = ` .title {text-align:%s} @@ -68,6 +72,7 @@ const ( margin-bottom: %s; margin-top: 0; text-indent: %dem; + %s } ` Tutorial = `本书由kaf-cli生成:
@@ -80,7 +85,9 @@ func NewBookSimple(filename string) (*Book, error) { Filename: filename, Bookname: "", Match: DefaultMatchTips, + VolumeMatch: VolumeMatch, Author: "YSTYLE", + UnknowTitle: "章节正文", Max: 35, Indent: 2, Align: GetEnv("KAF_CLI_ALIGN", "center"), @@ -108,10 +115,13 @@ func NewBookArgs() *Book { flag.StringVar(&book.Bookname, "bookname", "", "书名: 默认为txt文件名") flag.UintVar(&book.Max, "max", 35, "标题最大字数") flag.StringVar(&book.Match, "match", "", "匹配标题的正则表达式, 不写可以自动识别, 如果没生成章节就参考教程。例: -match 第.{1,8}章 表示第和章字之间可以有1-8个任意文字") + flag.StringVar(&book.VolumeMatch, "volumematch", VolumeMatch, "卷匹配规则") + flag.StringVar(&book.UnknowTitle, "unknowtitle", "章节正文", "未知章节默认名称") flag.UintVar(&book.Indent, "indent", 2, "段落缩进字数") flag.StringVar(&book.Align, "align", GetEnv("KAF_CLI_ALIGN", "center"), "标题对齐方式: left、center、righ。环境变量KAF_CLI_ALIGN可修改默认值") flag.StringVar(&book.Cover, "cover", "cover.png", "封面图片") flag.StringVar(&book.Bottom, "bottom", "1em", "段落间距(单位可以为em、px)") + flag.StringVar(&book.LineHeight, "lineheight", "", "行高(用于设置行间距, 默认为1.5rem)") flag.StringVar(&book.Format, "format", GetEnv("KAF_CLI_FORMAT", "all"), "书籍格式: all、epub、mobi、azw3。环境变量KAF_CLI_FORMAT可修改默认值") flag.StringVar(&book.Lang, "lang", GetEnv("KAF_CLI_LANG", "zh"), "设置语言: en,de,fr,it,es,zh,ja,pt,ru,nl。环境变量KAF_CLI_LANG可修改默认值") flag.BoolVar(&book.Tips, "tips", true, "添加本软件教程") @@ -169,8 +179,11 @@ func (book *Book) Check(version string) error { return fmt.Errorf("生成匹配规则出错: %s\n%s\n", book.Match, err.Error()) } book.Reg = reg - reg2, _ := regexp.Compile(Match) - book.Reg2 = reg2 + reg2, err := regexp.Compile(book.VolumeMatch) + if err != nil { + return fmt.Errorf("生成匹配规则出错: %s\n%s\n", book.VolumeMatch, err.Error()) + } + book.VolumeReg = reg2 return nil } @@ -220,6 +233,7 @@ func (book Book) ToString() { } else { fmt.Println("匹配条件:", book.Match) } + fmt.Println("卷匹配条件:", book.VolumeMatch) fmt.Println("转换格式:", book.Format) fmt.Println() } @@ -230,10 +244,11 @@ func (book *Book) Parse() error { start := time.Now() buf := book.readBuffer(book.Filename) var title string + var volume *Section var content bytes.Buffer if book.Tips { contentList = append(contentList, Section{ - Title: "说明", + Title: "制作说明", Content: Tutorial, }) } @@ -246,10 +261,17 @@ func (book *Book) Parse() error { addPart(&content, line) } } - contentList = append(contentList, Section{ + section := Section{ Title: title, Content: content.String(), - }) + } + if volume == nil { + contentList = append(contentList, section) + } else { + volume.Sections = append(volume.Sections, section) + contentList = append(contentList, *volume) + volume = nil + } content.Reset() break } @@ -262,16 +284,30 @@ func (book *Book) Parse() error { if len(line) == 0 { continue } + if book.VolumeReg.MatchString(line) { + if volume != nil { + contentList = append(contentList, *volume) + } + volume = &Section{ + Title: line, + } + continue + } // 处理标题 if utf8.RuneCountInString(line) <= int(book.Max) && book.Reg.MatchString(line) { if title == "" { - title = "章节正文" + title = book.UnknowTitle } if content.Len() > 0 { - contentList = append(contentList, Section{ + section := Section{ Title: title, Content: content.String(), - }) + } + if volume == nil || section.Title == book.UnknowTitle { + contentList = append(contentList, section) + } else { + volume.Sections = append(volume.Sections, section) + } } title = line content.Reset() @@ -284,14 +320,24 @@ func (book *Book) Parse() error { if title == "" { title = "章节正文" } - contentList = append(contentList, Section{ + section := Section{ Title: title, Content: content.String(), - }) + } + if volume == nil { + contentList = append(contentList, section) + } else { + volume.Sections = append(volume.Sections, section) + contentList = append(contentList, *volume) + volume = nil + } + } + if volume != nil { + contentList = append(contentList, *volume) } end := time.Now().Sub(start) fmt.Println("读取文件耗时:", end) - fmt.Println("匹配章节:", len(contentList)) + fmt.Println("匹配章节:", sectionCount(contentList)) // 添加提示 if book.Tips { contentList = append(contentList, Section{ @@ -303,6 +349,14 @@ func (book *Book) Parse() error { return nil } +func sectionCount(sections []Section) int { + var count int + for _, section := range sections { + count += 1 + len(section.Sections) + } + return count +} + func (book *Book) Convert() { start := time.Now() // 解析文本 diff --git a/epub.go b/epub.go index 20c98be..9a3ca28 100644 --- a/epub.go +++ b/epub.go @@ -32,7 +32,11 @@ func (convert EpubConverter) Build(book Book) error { }() pageStylesFile := filepath.Join(tempDir, "page_styles.css") - err = os.WriteFile(pageStylesFile, []byte(fmt.Sprintf(cssContent, book.Align, book.Bottom, book.Indent)), 0666) + var excss string + if book.LineHeight != "" { + excss = fmt.Sprintf("line-height: %s;", book.LineHeight) + } + err = os.WriteFile(pageStylesFile, []byte(fmt.Sprintf(cssContent, book.Align, book.Bottom, book.Indent, excss)), 0666) if err != nil { return fmt.Errorf("无法写入样式文件: %w", err) } @@ -55,7 +59,25 @@ func (convert EpubConverter) Build(book Book) error { } for _, section := range book.SectionList { - e.AddSection(convert.wrapTitle(section.Title, section.Content), section.Title, "", css) + if len(section.Sections) > 0 { + internalFilename, _ := e.AddSection( + convert.wrapTitle(section.Title, section.Content), + section.Title, + "", + css, + ) + for _, subsecton := range section.Sections { + e.AddSubSection( + internalFilename, + convert.wrapTitle(subsecton.Title, subsecton.Content), + subsecton.Title, + "", + css, + ) + } + } else { + e.AddSection(convert.wrapTitle(section.Title, section.Content), section.Title, "", css) + } } // Write the EPUB diff --git a/go.mod b/go.mod index df1e198..aa51da2 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/ystyle/kaf-cli require ( github.com/766b/mobi v0.0.0-20200528201125-c87aa9e3c890 - github.com/bmaupin/go-epub v1.0.1 + github.com/bmaupin/go-epub v1.1.0 github.com/leotaku/mobi v0.0.0-20220405163106-82e29bde7964 github.com/ystyle/google-analytics v0.0.0-20210425064301-a7f754dd0649 golang.org/x/net v0.7.0 diff --git a/go.sum b/go.sum index 494bdc2..70d74a9 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/766b/mobi v0.0.0-20200528201125-c87aa9e3c890 h1:5EBpQM2h7OdIRR1jw1IypiKzbYo/8Hx5/iRxgqPSKFI= github.com/766b/mobi v0.0.0-20200528201125-c87aa9e3c890/go.mod h1:ut/OVrYa64AAfis3e0GO4uEb8mwXSJoqAqnIMQd24HQ= -github.com/bmaupin/go-epub v1.0.1 h1:LLbczYCXO/1sGpFd4/QRaDiEhevo4PYQxBQClZPRoco= -github.com/bmaupin/go-epub v1.0.1/go.mod h1:mBan+0WgVv5JbPNw1xfnfQoTRN9iPMKBshZwPOL0SY0= +github.com/bmaupin/go-epub v1.1.0 h1:XJyvvjchtUlbZ2P7eaEeB8EFw2NgVY5ycREFpmd6MKM= +github.com/bmaupin/go-epub v1.1.0/go.mod h1:mBan+0WgVv5JbPNw1xfnfQoTRN9iPMKBshZwPOL0SY0= github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gofrs/uuid v3.1.0+incompatible h1:q2rtkjaKT4YEr6E1kamy0Ha4RtepWlQBedyHx0uzKwA= diff --git a/mobil.go b/mobil.go index 9ecf213..b742088 100644 --- a/mobil.go +++ b/mobil.go @@ -25,6 +25,11 @@ func (convert MobiConverter) Build(book Book) error { m.NewExthRecord(mobi.EXTH_AUTHOR, book.Author) for _, section := range book.SectionList { m.NewChapter(section.Title, []byte(section.Content)) + if len(section.Sections) > 0 { + for _, subsection := range section.Sections { + m.NewChapter(subsection.Title, []byte(subsection.Content)) + } + } } m.Write() fmt.Println("生成mobi电子书耗时:", time.Now().Sub(start))