diff --git a/kernel/api/export.go b/kernel/api/export.go index aa7ed6f6a..a5d0c7820 100644 --- a/kernel/api/export.go +++ b/kernel/api/export.go @@ -160,7 +160,11 @@ func exportDocx(c *gin.Context) { id := arg["id"].(string) savePath := arg["savePath"].(string) removeAssets := arg["removeAssets"].(bool) - err := model.ExportDocx(id, savePath, removeAssets) + merge := false + if nil != arg["merge"] { + merge = arg["merge"].(bool) + } + err := model.ExportDocx(id, savePath, removeAssets, merge) if nil != err { ret.Code = 1 ret.Msg = err.Error() @@ -180,7 +184,7 @@ func exportMdHTML(c *gin.Context) { id := arg["id"].(string) savePath := arg["savePath"].(string) - name, content := model.ExportMarkdownHTML(id, savePath, false) + name, content := model.ExportMarkdownHTML(id, savePath, false, false) ret.Data = map[string]interface{}{ "id": id, "name": name, @@ -229,10 +233,14 @@ func exportPreviewHTML(c *gin.Context) { id := arg["id"].(string) keepFold := false - if arg["keepFold"] != nil { + if nil != arg["keepFold"] { keepFold = arg["keepFold"].(bool) } - name, content := model.ExportHTML(id, "", true, keepFold) + merge := false + if nil != arg["merge"] { + merge = arg["merge"].(bool) + } + name, content := model.ExportHTML(id, "", true, keepFold, merge) // 导出 PDF 预览时点击块引转换后的脚注跳转不正确 https://github.com/siyuan-note/siyuan/issues/5894 content = strings.ReplaceAll(content, "http://"+util.LocalHost+":"+util.ServerPort+"/#", "#") @@ -256,10 +264,14 @@ func exportHTML(c *gin.Context) { pdf := arg["pdf"].(bool) savePath := arg["savePath"].(string) keepFold := false - if arg["keepFold"] != nil { + if nil != arg["keepFold"] { keepFold = arg["keepFold"].(bool) } - name, content := model.ExportHTML(id, savePath, pdf, keepFold) + merge := false + if nil != arg["merge"] { + merge = arg["merge"].(bool) + } + name, content := model.ExportHTML(id, savePath, pdf, keepFold, merge) ret.Data = map[string]interface{}{ "id": id, "name": name, diff --git a/kernel/model/box.go b/kernel/model/box.go index 1020549a3..37a543411 100644 --- a/kernel/model/box.go +++ b/kernel/model/box.go @@ -442,6 +442,7 @@ func moveTree(tree *parse.Tree) { func parseStdMd(markdown []byte) (ret *parse.Tree) { luteEngine := lute.New() + luteEngine.SetProtyleWYSIWYG(true) luteEngine.SetFootnotes(false) luteEngine.SetToC(false) luteEngine.SetIndentCodeBlock(false) diff --git a/kernel/model/export.go b/kernel/model/export.go index 353b894a6..dc37da427 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -194,7 +194,7 @@ func Preview(id string) string { return luteEngine.ProtylePreview(tree, luteEngine.RenderOptions) } -func ExportDocx(id, savePath string, removeAssets bool) (err error) { +func ExportDocx(id, savePath string, removeAssets, merge bool) (err error) { if !util.IsValidPandocBin(Conf.Export.PandocBin) { return errors.New(Conf.Language(115)) } @@ -204,7 +204,7 @@ func ExportDocx(id, savePath string, removeAssets bool) (err error) { return } defer os.Remove(tmpDir) - name, content := ExportMarkdownHTML(id, tmpDir, true) + name, content := ExportMarkdownHTML(id, tmpDir, true, merge) tmpDocxPath := filepath.Join(tmpDir, name+".docx") args := []string{ // pandoc -f html --resource-path=请从这里开始 请从这里开始\index.html -o test.docx @@ -237,9 +237,18 @@ func ExportDocx(id, savePath string, removeAssets bool) (err error) { return } -func ExportMarkdownHTML(id, savePath string, docx bool) (name, dom string) { +func ExportMarkdownHTML(id, savePath string, docx, merge bool) (name, dom string) { tree, _ := loadTreeByBlockID(id) + if merge { + var mergeErr error + tree, mergeErr = mergeSubDocs(tree) + if nil != mergeErr { + logging.LogErrorf("merge sub docs failed: %s", mergeErr) + return + } + } + tree = exportTree(tree, true, true, false) name = path.Base(tree.HPath) name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614 @@ -329,7 +338,7 @@ func ExportMarkdownHTML(id, savePath string, docx bool) (name, dom string) { return } -func ExportHTML(id, savePath string, pdf, keepFold bool) (name, dom string) { +func ExportHTML(id, savePath string, pdf, keepFold, merge bool) (name, dom string) { savePath = strings.TrimSpace(savePath) tree, _ := loadTreeByBlockID(id) var headings []*ast.Node @@ -354,6 +363,14 @@ func ExportHTML(id, savePath string, pdf, keepFold bool) (name, dom string) { } } + if merge { + var mergeErr error + tree, mergeErr = mergeSubDocs(tree) + if nil != mergeErr { + logging.LogErrorf("merge sub docs failed: %s", mergeErr) + return + } + } tree = exportTree(tree, true, true, keepFold) name = path.Base(tree.HPath) name = util.FilterFileName(name) // 导出 PDF、HTML 和 Word 时未移除不支持的文件名符号 https://github.com/siyuan-note/siyuan/issues/5614 diff --git a/kernel/model/export_merge.go b/kernel/model/export_merge.go new file mode 100644 index 000000000..140e804ea --- /dev/null +++ b/kernel/model/export_merge.go @@ -0,0 +1,104 @@ +// SiYuan - Build Your Eternal Digital Garden +// Copyright (c) 2020-present, b3log.org +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package model + +import ( + "github.com/88250/lute/ast" + "github.com/88250/lute/parse" +) + +func mergeSubDocs(rootTree *parse.Tree) (ret *parse.Tree, err error) { + ret = rootTree + rootBlock := &Block{Box: rootTree.Box, ID: rootTree.ID, Path: rootTree.Path} + if err = buildBlockChildren(rootBlock); nil != err { + return + } + + insertPoint := rootTree.Root.LastChild + for { + i := 0 + if err = walkBlock(insertPoint, rootBlock, i); nil != err { + return + } + + if nil == rootBlock.Children { + break + } + } + return +} + +func walkBlock(insertPoint *ast.Node, block *Block, level int) (err error) { + level++ + for _, c := range block.Children { + if err = walkBlock(insertPoint, c, level); nil != err { + return + } + } + + for _, c := range block.Children { + nodes, loadErr := loadTreeNodes(c.Box, c.Path, level) + if nil != loadErr { + return + } + + for j := len(nodes) - 1; -1 < j; j-- { + insertPoint.InsertAfter(nodes[j]) + } + } + block.Children = nil + return +} + +func loadTreeNodes(box string, p string, level int) (ret []*ast.Node, err error) { + tree, err := LoadTree(box, p) + if nil != err { + return + } + + hLevel := level + if 6 < level { + hLevel = 6 + } + + heading := &ast.Node{Type: ast.NodeHeading, HeadingLevel: hLevel} + heading.AppendChild(&ast.Node{Type: ast.NodeText, Tokens: []byte(tree.Root.IALAttr("title"))}) + tree.Root.PrependChild(heading) + for c := tree.Root.FirstChild; nil != c; c = c.Next { + ret = append(ret, c) + } + return +} + +func buildBlockChildren(block *Block) (err error) { + files, _, err := ListDocTree(block.Box, block.Path, Conf.FileTree.Sort) + if nil != err { + return + } + + for _, f := range files { + childBlock := &Block{Box: block.Box, ID: f.ID, Path: f.Path} + block.Children = append(block.Children, childBlock) + } + + for _, c := range block.Children { + if err = buildBlockChildren(c); nil != err { + return + } + } + return +} diff --git a/kernel/model/file.go b/kernel/model/file.go index 520dea408..f8cb21b6b 100644 --- a/kernel/model/file.go +++ b/kernel/model/file.go @@ -50,9 +50,9 @@ import ( type File struct { Path string `json:"path"` - Name string `json:"name"` + Name string `json:"name"` // 标题,即 ial["title"] Icon string `json:"icon"` - Name1 string `json:"name1"` + Name1 string `json:"name1"` // 命名,即 ial["name"] Alias string `json:"alias"` Memo string `json:"memo"` Bookmark string `json:"bookmark"`