Skip to content

Commit 05f5618

Browse files
committed
Ctl add generate PDF
1 parent 9354e61 commit 05f5618

File tree

8 files changed

+300
-22
lines changed

8 files changed

+300
-22
lines changed

ctl/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ Username="test"
1313
Password="test"
1414
Cookie="csrftoken=XXXXXXXXX; LEETCODE_SESSION=YYYYYYYY;"
1515
CSRFtoken="ZZZZZZZZ"
16-
```
16+
```
17+
18+
## PDF 生成
19+
20+
`leetcode-go pdf` 命令先生成书籍内容的合并版本 pdf.md,再用 vscode 或者其他支持 toc 目录生成的工具,生成 toc。最后用 Typora 把 md 文件转换成 pdf。就可以发布 release 新版本了。

ctl/label.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,14 @@ package main
22

33
import (
44
"bufio"
5-
// "encoding/json"
5+
"errors"
66
"fmt"
7-
// m "github.com/halfrost/LeetCode-Go/ctl/models"
8-
// "github.com/halfrost/LeetCode-Go/ctl/util"
97
"github.com/halfrost/LeetCode-Go/ctl/util"
108
"github.com/spf13/cobra"
119
"io"
10+
"io/ioutil"
1211
"os"
1312
"regexp"
14-
// "sort"
15-
// "strconv"
16-
"errors"
17-
"io/ioutil"
1813
"strings"
1914
)
2015

@@ -248,22 +243,22 @@ func eofDel(filePath string) ([]byte, error) {
248243
}
249244
if ok, _ := regexp.Match(delLine, line); ok {
250245
reg := regexp.MustCompile(delLine)
251-
newByte := reg.ReplaceAll(line, []byte(" "))
246+
newByte := reg.ReplaceAll(line, []byte(""))
252247
output = append(output, newByte...)
253248
output = append(output, []byte("\n")...)
254249
} else if ok, _ := regexp.Match(delHeader, line); ok {
255250
reg := regexp.MustCompile(delHeader)
256-
newByte := reg.ReplaceAll(line, []byte(" "))
251+
newByte := reg.ReplaceAll(line, []byte(""))
257252
output = append(output, newByte...)
258253
output = append(output, []byte("\n")...)
259254
} else if ok, _ := regexp.Match(delLabel, line); ok {
260255
reg := regexp.MustCompile(delLabel)
261-
newByte := reg.ReplaceAll(line, []byte(" "))
256+
newByte := reg.ReplaceAll(line, []byte(""))
262257
output = append(output, newByte...)
263258
output = append(output, []byte("\n")...)
264259
} else if ok, _ := regexp.Match(delFooter, line); ok {
265260
reg := regexp.MustCompile(delFooter)
266-
newByte := reg.ReplaceAll(line, []byte(" "))
261+
newByte := reg.ReplaceAll(line, []byte(""))
267262
output = append(output, newByte...)
268263
output = append(output, []byte("\n")...)
269264
} else {

ctl/meta/PDFPreface

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<img src="https://books.halfrost.com/leetcode/logo.png" alt="logo" heigth="1300px" align="center"/>
2+
3+
4+
# 说明
5+
6+
此版本是 https://books.halfrost.com/leetcode 网页的离线版,由于网页版实时会更新,所以此 PDF 版难免会有一些排版或者错别字。如果读者遇到了,可以到网页版相应页面,点击页面 edit 按钮,提交 pr 进行更改。此 PDF 版本号是 V1.5.20。PDF 永久更新地址是 https://github.com/halfrost/LeetCode-Go/releases/,以版本号区分不同版本。笔者还是强烈推荐看在线版,有任何错误都会立即更新。如果觉得此书对刷题有一点点帮助,可以给此书点一个 star,鼓励一下笔者早点更新更多题解。
7+
8+
> 版本号说明,V1.5.20,1 是大版本号,5 代表当前题解中有几百题,目前是 520 题,所以第二个版本号是 5,20 代表当前题解中有几十题,目前是 520 题,所以第三个版本号是 20 。
9+
10+
# 目录
11+
12+
[toc]

ctl/models/tagproblem.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (t TagList) tableLine() string {
115115
}
116116

117117
// GenerateTagMdRows define
118-
func GenerateTagMdRows(solutionIds []int, metaMap map[int]TagList, mdrows []Mdrow) []TagList {
118+
func GenerateTagMdRows(solutionIds []int, metaMap map[int]TagList, mdrows []Mdrow, internal bool) []TagList {
119119
tl := []TagList{}
120120
for _, row := range mdrows {
121121
if util.BinarySearch(solutionIds, int(row.FrontendQuestionID)) != -1 {
@@ -129,7 +129,11 @@ func GenerateTagMdRows(solutionIds []int, metaMap map[int]TagList, mdrows []Mdro
129129
s5 := strings.Replace(s4, ")", "", -1)
130130
s6 := strings.Replace(s5, ",", "", -1)
131131
s7 := strings.Replace(s6, "?", "", -1)
132-
tmp.SolutionPath = fmt.Sprintf("[Go]({{< relref \"/ChapterFour/%v.md\" >}})", fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
132+
if internal {
133+
tmp.SolutionPath = fmt.Sprintf("[Go]({{< relref \"/ChapterFour/%v.md\" >}})", fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
134+
} else {
135+
tmp.SolutionPath = fmt.Sprintf("[Go](https://books.halfrost.com/leetcode/ChapterFour/%v)", fmt.Sprintf("%04d.%v", int(row.FrontendQuestionID), s7))
136+
}
133137
tmp.Acceptance = row.Acceptance
134138
tmp.Difficulty = row.Difficulty
135139
tmp.TimeComplexity = metaMap[int(row.FrontendQuestionID)].TimeComplexity

ctl/pdf.go

Lines changed: 198 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,212 @@
11
package main
22

33
import (
4+
"bufio"
5+
"fmt"
6+
"github.com/halfrost/LeetCode-Go/ctl/util"
47
"github.com/spf13/cobra"
8+
"io"
9+
"io/ioutil"
10+
"os"
11+
"regexp"
12+
"strings"
13+
)
14+
15+
var (
16+
cleanString1 = "{{< columns >}}"
17+
cleanString2 = "<--->"
18+
cleanString3 = "{{< /columns >}}"
19+
cleanString4 = "<img src=\"https://books.halfrost.com/leetcode/logo.png\" alt=\"logo\" height=\"600\" align=\"right\" style=\"padding-left: 30px;\"/>"
20+
pdfPreface = `<img src="https://books.halfrost.com/leetcode/logo.png" alt="logo" heigth="1300px" align="center"/>
21+
22+
23+
# 说明
24+
25+
此版本是 https://books.halfrost.com/leetcode 网页的离线版,由于网页版实时会更新,所以此 PDF 版难免会有一些排版或者错别字。如果读者遇到了,可以到网页版相应页面,点击页面 edit 按钮,提交 pr 进行更改。此 PDF 版本号是 V%v.%v.%v。PDF 永久更新地址是 https://github.com/halfrost/LeetCode-Go/releases/,以版本号区分不同版本。笔者还是强烈推荐看在线版,有任何错误都会立即更新。如果觉得此书对刷题有一点点帮助,可以给此书点一个 star,鼓励一下笔者早点更新更多题解。
26+
27+
> 版本号说明,V%v.%v.%v,%v 是大版本号,%v 代表当前题解中有几百题,目前是 %v 题,所以第二个版本号是 %v,%v 代表当前题解中有几十题,目前是 %v 题,所以第三个版本号是 %v 。
28+
29+
# 目录
30+
31+
[toc]
32+
33+
`
34+
35+
majorVersion = 1
36+
midVersion = 0
37+
lastVersion = 0
38+
totalSolutions = 0
539
)
640

741
func newPDFCommand() *cobra.Command {
842
cmd := &cobra.Command{
9-
Use: "pdf <subcommand>",
43+
Use: "pdf",
1044
Short: "PDF related commands",
1145
Run: func(cmd *cobra.Command, args []string) {
12-
46+
generatePDF()
1347
},
1448
}
15-
//mc.PersistentFlags().StringVar(&logicEndpoint, "endpoint", "localhost:5880", "endpoint of logic service")
49+
// cmd.Flags().StringVar(&alias, "alias", "", "alias")
50+
// cmd.Flags().StringVar(&appId, "appid", "", "appid")
1651
return cmd
1752
}
53+
54+
func generatePDF() {
55+
var (
56+
pdf, tmp []byte
57+
err error
58+
)
59+
// 先删除 pre-next
60+
delPreNext()
61+
62+
chapterFourFileOrder := util.LoadChapterFourDir()
63+
totalSolutions = len(chapterFourFileOrder)
64+
midVersion = totalSolutions / 100
65+
lastVersion = totalSolutions % 100
66+
fmt.Printf("[当前的版本号是 V%v.%v.%v]\n", majorVersion, midVersion, lastVersion)
67+
// 删除原始文档中的头部,并创建临时文件夹
68+
prepare(fmt.Sprintf("../PDF v%v.%v.%v.md", majorVersion, midVersion, lastVersion))
69+
// PDF 前言
70+
pdf = append(pdf, []byte(fmt.Sprintf(pdfPreface, majorVersion, midVersion, lastVersion, majorVersion, midVersion, lastVersion, majorVersion, midVersion, totalSolutions, midVersion, lastVersion, totalSolutions, lastVersion))...)
71+
// PDF 第一章
72+
tmp, err = loadChapter(chapterOneFileOrder, "./pdftemp", "ChapterOne")
73+
pdf = append(pdf, tmp...)
74+
// PDF 第二章
75+
tmp, err = loadChapter(chapterTwoFileOrder, "./pdftemp", "ChapterTwo")
76+
pdf = append(pdf, tmp...)
77+
// PDF 第三章
78+
tmp, err = loadChapter(chapterThreeFileOrder, "./pdftemp", "ChapterThree")
79+
pdf = append(pdf, tmp...)
80+
// PDF 第四章
81+
tmp, err = util.LoadFile("./pdftemp/ChapterFour/_index.md")
82+
pdf = append(pdf, tmp...)
83+
tmp, err = loadChapter(chapterFourFileOrder, "../website/content", "ChapterFour")
84+
pdf = append(pdf, tmp...)
85+
if err != nil {
86+
fmt.Println(err)
87+
}
88+
// 生成 PDF
89+
util.WriteFile(fmt.Sprintf("../PDF v%v.%v.%v.md", majorVersion, midVersion, lastVersion), pdf)
90+
// 还原现场
91+
addPreNext()
92+
util.DestoryDir("./pdftemp")
93+
}
94+
95+
func loadChapter(order []string, path, chapter string) ([]byte, error) {
96+
var (
97+
res, tmp []byte
98+
err error
99+
)
100+
for index, v := range order {
101+
if chapter == "ChapterOne" && index == 0 {
102+
// 清理不支持的特殊 MarkDown 语法
103+
tmp, err = clean(fmt.Sprintf("%v/%v/%v.md", path, chapter, v))
104+
} else {
105+
tmp, err = util.LoadFile(fmt.Sprintf("%v/%v/%v.md", path, chapter, v))
106+
}
107+
if err != nil {
108+
fmt.Println(err)
109+
return []byte{}, err
110+
}
111+
res = append(res, tmp...)
112+
}
113+
return res, err
114+
}
115+
116+
func prepare(path string) {
117+
err := os.Remove(path)
118+
if err != nil {
119+
fmt.Println("pdf 还没有创建")
120+
fmt.Println(err)
121+
}
122+
fmt.Println("pdf 删除成功,开始构建全新版本")
123+
124+
err = os.MkdirAll("./pdftemp/ChapterOne", os.ModePerm)
125+
if err != nil {
126+
fmt.Println(err)
127+
}
128+
for _, v := range chapterOneFileOrder {
129+
removeHeader(fmt.Sprintf("../website/content/ChapterOne/%v.md", v), fmt.Sprintf("./pdftemp/ChapterOne/%v.md", v), 4)
130+
}
131+
132+
err = os.MkdirAll("./pdftemp/ChapterTwo", os.ModePerm)
133+
if err != nil {
134+
fmt.Println(err)
135+
}
136+
// 生成外部链接的 ChapterTwo
137+
buildChapterTwo(false)
138+
util.CopyFile("./pdftemp/ChapterTwo/_index.md", "../website/content/ChapterTwo/_index.md")
139+
140+
for _, v := range chapterTwoFileOrder {
141+
removeHeader(fmt.Sprintf("./pdftemp/ChapterTwo/%v.md", v), fmt.Sprintf("./pdftemp/ChapterTwo/%v.md", v), 4)
142+
}
143+
144+
err = os.MkdirAll("./pdftemp/ChapterThree", os.ModePerm)
145+
if err != nil {
146+
fmt.Println(err)
147+
}
148+
for _, v := range chapterThreeFileOrder {
149+
removeHeader(fmt.Sprintf("../website/content/ChapterThree/%v.md", v), fmt.Sprintf("./pdftemp/ChapterThree/%v.md", v), 4)
150+
}
151+
152+
err = os.MkdirAll("./pdftemp/ChapterFour", os.ModePerm)
153+
if err != nil {
154+
fmt.Println(err)
155+
}
156+
removeHeader(fmt.Sprintf("../website/content/ChapterFour/_index.md"), fmt.Sprintf("./pdftemp/ChapterFour/_index.md"), 4)
157+
}
158+
159+
func clean(filePath string) ([]byte, error) {
160+
f, err := os.OpenFile(filePath, os.O_RDONLY, 0644)
161+
if err != nil {
162+
return nil, err
163+
}
164+
defer f.Close()
165+
reader, output := bufio.NewReader(f), []byte{}
166+
for {
167+
line, _, err := reader.ReadLine()
168+
if err != nil {
169+
if err == io.EOF {
170+
return output, nil
171+
}
172+
return nil, err
173+
}
174+
if ok, _ := regexp.Match(cleanString1, line); ok {
175+
reg := regexp.MustCompile(cleanString1)
176+
newByte := reg.ReplaceAll(line, []byte(""))
177+
output = append(output, newByte...)
178+
output = append(output, []byte("\n")...)
179+
} else if ok, _ := regexp.Match(cleanString2, line); ok {
180+
reg := regexp.MustCompile(cleanString2)
181+
newByte := reg.ReplaceAll(line, []byte(""))
182+
output = append(output, newByte...)
183+
output = append(output, []byte("\n")...)
184+
} else if ok, _ := regexp.Match(cleanString3, line); ok {
185+
reg := regexp.MustCompile(cleanString3)
186+
newByte := reg.ReplaceAll(line, []byte(""))
187+
output = append(output, newByte...)
188+
output = append(output, []byte("\n")...)
189+
} else if ok, _ := regexp.Match(cleanString4, line); ok {
190+
reg := regexp.MustCompile(cleanString4)
191+
newByte := reg.ReplaceAll(line, []byte(""))
192+
output = append(output, newByte...)
193+
output = append(output, []byte("\n")...)
194+
} else {
195+
output = append(output, line...)
196+
output = append(output, []byte("\n")...)
197+
}
198+
}
199+
}
200+
201+
func removeHeader(path, newPath string, lineNumber int) {
202+
file, err := ioutil.ReadFile(path)
203+
if err != nil {
204+
panic(err)
205+
}
206+
info, _ := os.Stat(path)
207+
mode := info.Mode()
208+
array := strings.Split(string(file), "\n")
209+
array = array[lineNumber:]
210+
ioutil.WriteFile(newPath, []byte(strings.Join(array, "\n")), mode)
211+
//fmt.Println("remove line successful")
212+
}

ctl/render.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func newBuildChapterTwo() *cobra.Command {
5555
Use: "chapter-two",
5656
Short: "Build Chapter Two commands",
5757
Run: func(cmd *cobra.Command, args []string) {
58-
buildChapterTwo()
58+
buildChapterTwo(true)
5959
},
6060
}
6161
// cmd.Flags().StringVar(&alias, "alias", "", "alias")
@@ -147,7 +147,9 @@ func renderReadme(filePath string, total, try int, mdrows, omdrows m.Mdrows, use
147147
}
148148
}
149149

150-
func buildChapterTwo() {
150+
// internal: true 渲染的链接都是 hugo 内部链接,用户生成 hugo web
151+
// false 渲染的链接是外部 HTTPS 链接,用于生成 PDF
152+
func buildChapterTwo(internal bool) {
151153
var (
152154
gr m.GraphQLResp
153155
questions []m.Question
@@ -169,15 +171,20 @@ func buildChapterTwo() {
169171
if err != nil {
170172
fmt.Printf("err = %v\n", err)
171173
}
172-
tls := m.GenerateTagMdRows(solutionIds, tl, mdrows)
174+
tls := m.GenerateTagMdRows(solutionIds, tl, mdrows, internal)
173175
//fmt.Printf("tls = %v\n", tls)
174176
// 按照模板渲染 README
175177
res, err := renderChapterTwo(fmt.Sprintf("./template/%v.md", chapterTwoFileName[index]), m.TagLists{TagLists: tls})
176178
if err != nil {
177179
fmt.Println(err)
178180
return
179181
}
180-
util.WriteFile(fmt.Sprintf("../website/content/ChapterTwo/%v.md", chapterTwoFileName[index]), res)
182+
if internal {
183+
util.WriteFile(fmt.Sprintf("../website/content/ChapterTwo/%v.md", chapterTwoFileName[index]), res)
184+
} else {
185+
util.WriteFile(fmt.Sprintf("./pdftemp/ChapterTwo/%v.md", chapterTwoFileName[index]), res)
186+
}
187+
181188
count++
182189
}
183190
fmt.Printf("write %v files successful", count)

0 commit comments

Comments
 (0)