From cfcf695fb410765cbf9206dd831411f44bff7b09 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 09:41:29 +0800 Subject: [PATCH 1/8] :art: Improve export to Word .docx format https://github.com/siyuan-note/siyuan/issues/14970 Signed-off-by: Daniel <845765@qq.com> --- kernel/model/conf.go | 4 +++- kernel/util/pandoc.go | 38 ++++++++++++++++---------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/kernel/model/conf.go b/kernel/model/conf.go index edb5cafa1..0493e333e 100644 --- a/kernel/model/conf.go +++ b/kernel/model/conf.go @@ -1272,7 +1272,9 @@ func subscribeConfEvents() { params += " \"" + util.PandocTemplatePath + "\"" Conf.Export.PandocParams = strings.TrimSpace(params) } - + + logging.LogInfof("pandoc params set to [%s]", Conf.Export.PandocParams) + logging.LogInfof("pandoc template [%s], color filter [%s]", util.PandocTemplatePath, util.PandocColorFilterPath) Conf.Save() }) } diff --git a/kernel/util/pandoc.go b/kernel/util/pandoc.go index c6831bf4a..1cf80595b 100644 --- a/kernel/util/pandoc.go +++ b/kernel/util/pandoc.go @@ -111,7 +111,7 @@ func InitPandoc() { return } - pandocDir := filepath.Join(TempDir, "pandoc") + tempPandocDir := filepath.Join(TempDir, "pandoc") if confPath := filepath.Join(ConfDir, "conf.json"); gulu.File.IsExist(confPath) { // Workspace built-in Pandoc is no longer initialized after customizing Pandoc path https://github.com/siyuan-note/siyuan/issues/8377 @@ -119,7 +119,7 @@ func InitPandoc() { conf := map[string]interface{}{} if err = gulu.JSON.UnmarshalJSON(data, &conf); err == nil && nil != conf["export"] { export := conf["export"].(map[string]interface{}) - if customPandocBinPath := export["pandocBin"].(string); !strings.HasPrefix(customPandocBinPath, pandocDir) { + if customPandocBinPath := export["pandocBin"].(string); !strings.HasPrefix(customPandocBinPath, tempPandocDir) { if pandocVer := getPandocVer(customPandocBinPath); "" != pandocVer { PandocBinPath = customPandocBinPath logging.LogInfof("custom pandoc [ver=%s, bin=%s]", pandocVer, PandocBinPath) @@ -130,24 +130,18 @@ func InitPandoc() { } } - PandocTemplatePath = filepath.Join(pandocDir, "pandoc-resources", "pandoc-template.docx") + PandocTemplatePath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc-template.docx") if !gulu.File.IsExist(PandocTemplatePath) { - PandocTemplatePath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc-template.docx") - if "dev" == Mode || !gulu.File.IsExist(PandocTemplatePath) { - PandocTemplatePath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc-template.docx") - } + PandocTemplatePath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc-template.docx") } if !gulu.File.IsExist(PandocTemplatePath) { PandocTemplatePath = "" logging.LogWarnf("pandoc template file [%s] not found", PandocTemplatePath) } - PandocColorFilterPath = filepath.Join(pandocDir, "pandoc-resources", "pandoc_color_filter.lua") + PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc_color_filter.lua") if !gulu.File.IsExist(PandocColorFilterPath) { - PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc-resources", "pandoc_color_filter.lua") - if "dev" == Mode || !gulu.File.IsExist(PandocColorFilterPath) { - PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc_color_filter.lua") - } + PandocColorFilterPath = filepath.Join(WorkingDir, "pandoc", "pandoc-resources", "pandoc_color_filter.lua") } if !gulu.File.IsExist(PandocColorFilterPath) { PandocColorFilterPath = "" @@ -158,13 +152,13 @@ func InitPandoc() { if gulu.OS.IsWindows() { if "amd64" == runtime.GOARCH { - PandocBinPath = filepath.Join(pandocDir, "bin", "pandoc.exe") + PandocBinPath = filepath.Join(tempPandocDir, "bin", "pandoc.exe") } } else if gulu.OS.IsDarwin() { - PandocBinPath = filepath.Join(pandocDir, "bin", "pandoc") + PandocBinPath = filepath.Join(tempPandocDir, "bin", "pandoc") } else if gulu.OS.IsLinux() { if "amd64" == runtime.GOARCH { - PandocBinPath = filepath.Join(pandocDir, "bin", "pandoc") + PandocBinPath = filepath.Join(tempPandocDir, "bin", "pandoc") } } pandocVer := getPandocVer(PandocBinPath) @@ -174,22 +168,22 @@ func InitPandoc() { } pandocZip := filepath.Join(WorkingDir, "pandoc.zip") - if "dev" == Mode || !gulu.File.IsExist(pandocZip) { + if !gulu.File.IsExist(pandocZip) { if gulu.OS.IsWindows() { if "amd64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-windows-amd64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-windows-amd64.zip") } } else if gulu.OS.IsDarwin() { if "amd64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-darwin-amd64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-darwin-amd64.zip") } else if "arm64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-darwin-arm64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-darwin-arm64.zip") } } else if gulu.OS.IsLinux() { if "amd64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-linux-amd64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-linux-amd64.zip") } else if "arm64" == runtime.GOARCH { - pandocZip = filepath.Join(WorkingDir, "pandoc/pandoc-linux-arm64.zip") + pandocZip = filepath.Join(WorkingDir, "pandoc", "pandoc-linux-arm64.zip") } } } @@ -200,7 +194,7 @@ func InitPandoc() { return } - if err := gulu.Zip.Unzip(pandocZip, pandocDir); err != nil { + if err := gulu.Zip.Unzip(pandocZip, tempPandocDir); err != nil { logging.LogErrorf("unzip pandoc failed: %s", err) return } From f6e9ed5b553758cd10142b1fadec24794dade477 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:03:38 +0800 Subject: [PATCH 2/8] :art: Clean code Signed-off-by: Daniel <845765@qq.com> --- kernel/model/virutalref.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/model/virutalref.go b/kernel/model/virutalref.go index a3923806e..05405a6d3 100644 --- a/kernel/model/virutalref.go +++ b/kernel/model/virutalref.go @@ -45,9 +45,6 @@ var virtualBlockRefCache, _ = ristretto.NewCache(&ristretto.Config{ BufferItems: 64, }) -// newlineRegexp 用于匹配连续或单个换行符的正则表达式 -var newlineRegexp = regexp.MustCompile(`[\r\n]+`) - func getBlockVirtualRefKeywords(root *ast.Node) (ret []string) { val, ok := virtualBlockRefCache.Get(root.ID) if !ok { @@ -256,7 +253,7 @@ func parseKeywords(keywordsStr string) (keywords []string) { // 先处理转义的逗号 keywordsStr = strings.ReplaceAll(keywordsStr, "\\,", "__comma@sep__") // 再将连续或单个换行符替换为一个逗号,避免把 `\\\n` 转换为 `\,` - keywordsStr = newlineRegexp.ReplaceAllString(keywordsStr, ",") + keywordsStr = regexp.MustCompile(`[\r\n]+`).ReplaceAllString(keywordsStr, ",") // 按逗号分隔 for part := range strings.SplitSeq(keywordsStr, ",") { part = strings.TrimSpace(part) // 剔除前后的空白字符 From d5f7bf2c02fcf48817d9a2fc9090ee203441f297 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:05:28 +0800 Subject: [PATCH 3/8] :art: Clean code Signed-off-by: Daniel <845765@qq.com> --- kernel/model/virutalref.go | 2 +- kernel/util/rune.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/model/virutalref.go b/kernel/model/virutalref.go index 05405a6d3..5bb65de18 100644 --- a/kernel/model/virutalref.go +++ b/kernel/model/virutalref.go @@ -253,7 +253,7 @@ func parseKeywords(keywordsStr string) (keywords []string) { // 先处理转义的逗号 keywordsStr = strings.ReplaceAll(keywordsStr, "\\,", "__comma@sep__") // 再将连续或单个换行符替换为一个逗号,避免把 `\\\n` 转换为 `\,` - keywordsStr = regexp.MustCompile(`[\r\n]+`).ReplaceAllString(keywordsStr, ",") + keywordsStr = util.ReplaceNewline(keywordsStr, ",") // 按逗号分隔 for part := range strings.SplitSeq(keywordsStr, ",") { part = strings.TrimSpace(part) // 剔除前后的空白字符 diff --git a/kernel/util/rune.go b/kernel/util/rune.go index 9e58df926..6533508f0 100644 --- a/kernel/util/rune.go +++ b/kernel/util/rune.go @@ -27,6 +27,10 @@ import ( "github.com/siyuan-note/logging" ) +func ReplaceNewline(text, replaceWith string) string { + return regexp.MustCompile(`[\r\n]+`).ReplaceAllString(text, replaceWith) +} + func ContainsCJK(text string) bool { for _, r := range text { ret := unicode.Is(unicode.Han, r) || unicode.Is(unicode.Lm, r) || From e9564ff0ec49ef487c28cfe0d2ef331bca4e9113 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Tue, 20 Jan 2026 10:05:49 +0800 Subject: [PATCH 4/8] :art: Supports setting Pandoc parameters for export docx https://github.com/siyuan-note/siyuan/issues/16845 Signed-off-by: Daniel <845765@qq.com> --- app/src/config/exportConfig.ts | 11 +++++------ kernel/api/setting.go | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/config/exportConfig.ts b/app/src/config/exportConfig.ts index 324c4aa42..f76ce993f 100644 --- a/app/src/config/exportConfig.ts +++ b/app/src/config/exportConfig.ts @@ -165,12 +165,11 @@ export const exportConfig = { -