Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vanessa 2023-02-23 13:20:28 +08:00
commit d59c131170
16 changed files with 264 additions and 69 deletions

20
API.md
View File

@ -457,13 +457,12 @@ View API token in <kbd>Settings - About</kbd>, request header: `Authorization: T
* `/api/asset/upload` * `/api/asset/upload`
* The parameter is an HTTP Multipart form * The parameter is an HTTP Multipart form
* `assetsDirPath`: The folder path where the assets are stored. The arguments have the following three cases * `assetsDirPath`: The folder path where assets are stored, with the data folder as the root path, for example:
* `"/assets/"`: workspace/data/assets/ folder
* `"/assets/sub/"`: workspace/data/assets/sub/ folder
1. `"/assets/"`: Workspace/data/assets folder Under normal circumstances, it is recommended to use the first method, which is stored in the assets folder
2. `"/Test Notebook/assets/"`: Assets folder under `Test Notebook` of the workspace.
3. `"/Test Notebook/foo/assets/"`: Assets folder under foo folder under `Test notebook`
It is recommended to use the first one, which is stored in the workspace assets folder uniformly.
* `file[]`: Uploaded file list * `file[]`: Uploaded file list
* Return value * Return value
@ -705,7 +704,7 @@ View API token in <kbd>Settings - About</kbd>, request header: `Authorization: T
} }
``` ```
* `id`: ID of the block to be got * `id`: ID of the block to be got
* Return value * Return value
```json ```json
@ -718,7 +717,7 @@ View API token in <kbd>Settings - About</kbd>, request header: `Authorization: T
} }
} }
``` ```
## Attributes ## Attributes
### Set block attributes ### Set block attributes
@ -814,8 +813,9 @@ View API token in <kbd>Settings - About</kbd>, request header: `Authorization: T
"path": "F:\\SiYuan\\data\\templates\\foo.md" "path": "F:\\SiYuan\\data\\templates\\foo.md"
} }
``` ```
* `id`: The ID of the document where the rendering is called
* `path`: Template file absolute path * `id`: The ID of the document where the rendering is called
* `path`: Template file absolute path
* Return value * Return value
```json ```json

View File

@ -454,13 +454,11 @@
* `/api/asset/upload` * `/api/asset/upload`
* 参数为 HTTP Multipart 表单 * 参数为 HTTP Multipart 表单
* `assetsDirPath`:资源文件存放的文件夹路径,实参有以下三种情况 * `assetsDirPath`:资源文件存放的文件夹路径,以 data 文件夹作为根路径,比如:
* `"/assets/"`:工作空间/data/assets/ 文件夹
* `"/assets/sub/"`:工作空间/data/assets/sub/ 文件夹
1. `"/assets/"`:工作空间/data/assets 文件夹 常规情况下建议用第一种,统一存放到工作空间资源文件夹下。
2. `"/测试笔记本/assets/"``测试笔记本`下的 assets 文件夹
3. `"/测试笔记本/foo/assets/"``测试笔记本`下 foo 文件夹下的 assets 文件夹
建议用第一种,统一存放到工作空间资源文件夹下。
* `file[]`:上传的文件列表 * `file[]`:上传的文件列表
* 返回值 * 返回值

View File

@ -199,9 +199,10 @@
"exportPDF1": "Landscape page", "exportPDF1": "Landscape page",
"exportPDF2": "Page margins", "exportPDF2": "Page margins",
"exportPDF3": "Page Scale", "exportPDF3": "Page Scale",
"exportPDF4": "Remove assets directory", "exportPDF4": "Embed assets",
"exportPDF5": "Keep folded", "exportPDF5": "Keep folded",
"exportPDF6": "Merge subdocuments", "mergeSubdocs": "Merge subdocuments",
"removeAssetsFolder": "Remove assets directory",
"upload": "Upload", "upload": "Upload",
"reminderTip": "The reminder time cannot be less than the current time", "reminderTip": "The reminder time cannot be less than the current time",
"wechatTip": "The content block will be sent to the cloud in clear text, and pushed through the WeChat MP template message when it expires", "wechatTip": "The content block will be sent to the cloud in clear text, and pushed through the WeChat MP template message when it expires",

View File

@ -199,9 +199,10 @@
"exportPDF1": "Página apaisada", "exportPDF1": "Página apaisada",
"exportPDF2": "Márgenes de la página", "exportPDF2": "Márgenes de la página",
"exportPDF3": "Escala de la página", "exportPDF3": "Escala de la página",
"exportPDF4": "Eliminar directorio de activos", "exportPDF4": "Activos incrustados",
"exportPDF5": "Mantener doblado", "exportPDF5": "Mantener doblado",
"exportPDF6": "Fusionar subdocumentos", "mergeSubdocs": "Fusionar subdocumentos",
"removeAssetsFolder": "Eliminar directorio de activos",
"upload": "Subir", "upload": "Subir",
"reminderTip": "La hora del recordatorio no puede ser inferior a la hora actual", "reminderTip": "La hora del recordatorio no puede ser inferior a la hora actual",
"wechatTip": "El bloque de contenido se enviará a la nube en texto claro, y se empujará a través del mensaje de plantilla de WeChat MP cuando caduque", "wechatTip": "El bloque de contenido se enviará a la nube en texto claro, y se empujará a través del mensaje de plantilla de WeChat MP cuando caduque",

View File

@ -199,9 +199,10 @@
"exportPDF1": "Page paysage", "exportPDF1": "Page paysage",
"exportPDF2": "Marges de page", "exportPDF2": "Marges de page",
"exportPDF3": "Échelle de page", "exportPDF3": "Échelle de page",
"exportPDF4": "Supprimer le répertoire des actifs", "exportPDF4": "Incorporer des ressources",
"exportPDF5": "Garder plié", "exportPDF5": "Garder plié",
"exportPDF6": "Fusionner les sous-documents", "mergeSubdocs": "Fusionner les sous-documents",
"removeAssetsFolder": "Supprimer le répertoire des actifs",
"upload": "Télécharger", "upload": "Télécharger",
"reminderTip": "The reminder time cannot be less than the current time", "reminderTip": "The reminder time cannot be less than the current time",
"wechatTip": "Le bloc de contenu sera envoyé au cloud en texte clair et transmis au message du modèle de compte officiel WeChat à son expiration.", "wechatTip": "Le bloc de contenu sera envoyé au cloud en texte clair et transmis au message du modèle de compte officiel WeChat à son expiration.",

View File

@ -199,9 +199,10 @@
"exportPDF1": "橫向頁面", "exportPDF1": "橫向頁面",
"exportPDF2": "頁面邊距", "exportPDF2": "頁面邊距",
"exportPDF3": "頁面縮放", "exportPDF3": "頁面縮放",
"exportPDF4": "移除 assets 目錄", "exportPDF4": "嵌入資源文件",
"exportPDF5": "保持折疊狀態", "exportPDF5": "保持折疊狀態",
"exportPDF6": "合併子文檔", "mergeSubdocs": "合併子文檔",
"removeAssetsFolder": "移除 assets 目錄",
"upload": "上傳", "upload": "上傳",
"reminderTip": "提醒時間不能小於當前時間", "reminderTip": "提醒時間不能小於當前時間",
"wechatTip": "該內容塊將以明文形式發送到雲端,到期時通過微信公眾號模板消息進行推送", "wechatTip": "該內容塊將以明文形式發送到雲端,到期時通過微信公眾號模板消息進行推送",

View File

@ -199,9 +199,10 @@
"exportPDF1": "横向页面", "exportPDF1": "横向页面",
"exportPDF2": "页面边距", "exportPDF2": "页面边距",
"exportPDF3": "页面缩放", "exportPDF3": "页面缩放",
"exportPDF4": "移除 assets 目录", "exportPDF4": "嵌入资源文件",
"exportPDF5": "保持折叠状态", "exportPDF5": "保持折叠状态",
"exportPDF6": "合并子文档", "mergeSubdocs": "合并子文档",
"removeAssetsFolder": "移除 assets 目录",
"upload": "上传", "upload": "上传",
"reminderTip": "提醒时间不能小于当前时间", "reminderTip": "提醒时间不能小于当前时间",
"wechatTip": "该内容块将以明文形式发送到云端,到期时通过微信公众号模板消息进行推送", "wechatTip": "该内容块将以明文形式发送到云端,到期时通过微信公众号模板消息进行推送",

View File

@ -1,7 +1,7 @@
import {hideMessage, showMessage} from "../../dialog/message"; import {hideMessage, showMessage} from "../../dialog/message";
import {Constants} from "../../constants"; import {Constants} from "../../constants";
/// #if !BROWSER /// #if !BROWSER
import {OpenDialogReturnValue, ipcRenderer} from "electron"; import {ipcRenderer, OpenDialogReturnValue} from "electron";
import {app, BrowserWindow, dialog, getCurrentWindow} from "@electron/remote"; import {app, BrowserWindow, dialog, getCurrentWindow} from "@electron/remote";
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
@ -33,14 +33,14 @@ export const saveExport = (option: { type: string, id: string }) => {
content: `<div class="b3-dialog__content"> content: `<div class="b3-dialog__content">
<label class="fn__flex b3-label"> <label class="fn__flex b3-label">
<div class="fn__flex-1"> <div class="fn__flex-1">
${window.siyuan.languages.exportPDF4} ${window.siyuan.languages.removeAssetsFolder}
</div> </div>
<span class="fn__space"></span> <span class="fn__space"></span>
<input id="removeAssets" class="b3-switch" type="checkbox" ${localData.removeAssets ? "checked" : ""}> <input id="removeAssets" class="b3-switch" type="checkbox" ${localData.removeAssets ? "checked" : ""}>
</label> </label>
<label class="fn__flex b3-label"> <label class="fn__flex b3-label">
<div class="fn__flex-1"> <div class="fn__flex-1">
${window.siyuan.languages.exportPDF6} ${window.siyuan.languages.mergeSubdocs}
</div> </div>
<span class="fn__space"></span> <span class="fn__space"></span>
<input id="mergeSubdocs" class="b3-switch" type="checkbox" ${localData.mergeSubdocs ? "checked" : ""}> <input id="mergeSubdocs" class="b3-switch" type="checkbox" ${localData.mergeSubdocs ? "checked" : ""}>
@ -215,7 +215,7 @@ const renderPDF = (id: string) => {
</label> </label>
<label class="b3-label"> <label class="b3-label">
<div> <div>
${window.siyuan.languages.exportPDF6} ${window.siyuan.languages.mergeSubdocs}
</div> </div>
<span class="fn__hr"></span> <span class="fn__hr"></span>
<input id="mergeSubdocs" class="b3-switch" type="checkbox" ${localData.mergeSubdocs ? "checked" : ""}> <input id="mergeSubdocs" class="b3-switch" type="checkbox" ${localData.mergeSubdocs ? "checked" : ""}>

View File

@ -3,7 +3,7 @@ import {exportLayout, getInstanceById, JSONToLayout, resetLayout, resizeDrag, re
import {hotKey2Electron, setStorageVal, updateHotkeyTip} from "../protyle/util/compatibility"; import {hotKey2Electron, setStorageVal, updateHotkeyTip} from "../protyle/util/compatibility";
/// #if !BROWSER /// #if !BROWSER
import {dialog, getCurrentWindow} from "@electron/remote"; import {dialog, getCurrentWindow} from "@electron/remote";
import {webFrame, ipcRenderer, OpenDialogReturnValue} from "electron"; import {ipcRenderer, OpenDialogReturnValue, webFrame} from "electron";
import * as fs from "fs"; import * as fs from "fs";
import * as path from "path"; import * as path from "path";
import {afterExport} from "../protyle/export/util"; import {afterExport} from "../protyle/export/util";
@ -418,10 +418,11 @@ export const initWindow = () => {
const pdfFilePath = path.join(result.filePaths[0], replaceLocalPath(ipcData.rootTitle) + ".pdf"); const pdfFilePath = path.join(result.filePaths[0], replaceLocalPath(ipcData.rootTitle) + ".pdf");
fs.writeFileSync(pdfFilePath, pdfData); fs.writeFileSync(pdfFilePath, pdfData);
window.siyuan.printWin.destroy(); window.siyuan.printWin.destroy();
fetchPost("/api/export/addPDFOutline", { fetchPost("/api/export/processPDF", {
id: ipcData.rootId, id: ipcData.rootId,
merge: ipcData.mergeSubdocs, merge: ipcData.mergeSubdocs,
path: pdfFilePath path: pdfFilePath,
removeAssets: ipcData.removeAssets,
}, () => { }, () => {
afterExport(pdfFilePath, msgId); afterExport(pdfFilePath, msgId);
if (ipcData.removeAssets) { if (ipcData.removeAssets) {

View File

@ -311,7 +311,7 @@ func exportHTML(c *gin.Context) {
} }
} }
func addPDFOutline(c *gin.Context) { func processPDF(c *gin.Context) {
ret := gulu.Ret.NewResult() ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret) defer c.JSON(http.StatusOK, ret)
@ -326,7 +326,8 @@ func addPDFOutline(c *gin.Context) {
if nil != arg["merge"] { if nil != arg["merge"] {
merge = arg["merge"].(bool) merge = arg["merge"].(bool)
} }
err := model.AddPDFOutline(id, path, merge) removeAssets := arg["removeAssets"].(bool)
err := model.ProcessPDF(id, path, merge, removeAssets)
if nil != err { if nil != err {
ret.Code = -1 ret.Code = -1
ret.Msg = err.Error() ret.Msg = err.Error()

View File

@ -233,7 +233,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/export/exportPreviewHTML", model.CheckAuth, exportPreviewHTML) ginServer.Handle("POST", "/api/export/exportPreviewHTML", model.CheckAuth, exportPreviewHTML)
ginServer.Handle("POST", "/api/export/exportMdHTML", model.CheckAuth, exportMdHTML) ginServer.Handle("POST", "/api/export/exportMdHTML", model.CheckAuth, exportMdHTML)
ginServer.Handle("POST", "/api/export/exportDocx", model.CheckAuth, exportDocx) ginServer.Handle("POST", "/api/export/exportDocx", model.CheckAuth, exportDocx)
ginServer.Handle("POST", "/api/export/addPDFOutline", model.CheckAuth, addPDFOutline) ginServer.Handle("POST", "/api/export/processPDF", model.CheckAuth, processPDF)
ginServer.Handle("POST", "/api/export/preview", model.CheckAuth, exportPreview) ginServer.Handle("POST", "/api/export/preview", model.CheckAuth, exportPreview)
ginServer.Handle("POST", "/api/export/exportAsFile", model.CheckAuth, exportAsFile) ginServer.Handle("POST", "/api/export/exportAsFile", model.CheckAuth, exportAsFile)
ginServer.Handle("POST", "/api/export/exportData", model.CheckAuth, exportData) ginServer.Handle("POST", "/api/export/exportData", model.CheckAuth, exportData)

View File

@ -7,7 +7,7 @@ require (
github.com/88250/css v0.1.2 github.com/88250/css v0.1.2
github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798 github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798
github.com/88250/lute v1.7.6-0.20230220030205-b0f64d7ba66e github.com/88250/lute v1.7.6-0.20230220030205-b0f64d7ba66e
github.com/88250/pdfcpu v0.3.13 github.com/88250/pdfcpu v0.3.14-0.20230223031826-d2ae187e1c38
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
github.com/ClarkThan/ahocorasick v0.0.0-20230216061320-bccdb98581a3 github.com/ClarkThan/ahocorasick v0.0.0-20230216061320-bccdb98581a3
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732 github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732

View File

@ -10,8 +10,8 @@ github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798 h1:sR/s/Y9wyl79ZRCUER
github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798/go.mod h1:I1qBzsksFL2ciGSuqDE7R3XW4BUMrfDgOvSXEk7FsAI= github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798/go.mod h1:I1qBzsksFL2ciGSuqDE7R3XW4BUMrfDgOvSXEk7FsAI=
github.com/88250/lute v1.7.6-0.20230220030205-b0f64d7ba66e h1:7UgFzsksh+z6IX2z+BKG3tt1TU7LJNb0zOHDbhLEaUc= github.com/88250/lute v1.7.6-0.20230220030205-b0f64d7ba66e h1:7UgFzsksh+z6IX2z+BKG3tt1TU7LJNb0zOHDbhLEaUc=
github.com/88250/lute v1.7.6-0.20230220030205-b0f64d7ba66e/go.mod h1:cEoBGi0zArPqAsp0MdG9SKinvH/xxZZWXU7sRx8vHSA= github.com/88250/lute v1.7.6-0.20230220030205-b0f64d7ba66e/go.mod h1:cEoBGi0zArPqAsp0MdG9SKinvH/xxZZWXU7sRx8vHSA=
github.com/88250/pdfcpu v0.3.13 h1:touMWMZkCGalMIbEg9bxYp7rETM+zwb9hXjwhqi4I7Q= github.com/88250/pdfcpu v0.3.14-0.20230223031826-d2ae187e1c38 h1:MaFRabDTXOpLBrdP4qkZnjFBIUTu/rk8S6fu7hC6jCY=
github.com/88250/pdfcpu v0.3.13/go.mod h1:S5YT38L/GCjVjmB4PB84PymA1qfopjEhfhTNQilLpv4= github.com/88250/pdfcpu v0.3.14-0.20230223031826-d2ae187e1c38/go.mod h1:S5YT38L/GCjVjmB4PB84PymA1qfopjEhfhTNQilLpv4=
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY= github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1/go.mod h1:U3pckKQIgxxkmZjV5yXQjHdGxQK0o/vEZeZ6cQsxfHw= github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1/go.mod h1:U3pckKQIgxxkmZjV5yXQjHdGxQK0o/vEZeZ6cQsxfHw=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=

View File

@ -654,7 +654,7 @@ func processIFrame(tree *parse.Tree) {
} }
} }
func AddPDFOutline(id, p string, merge bool) (err error) { func ProcessPDF(id, p string, merge, removeAssets bool) (err error) {
inFile := p inFile := p
links, err := api.ListToCLinks(inFile) links, err := api.ListToCLinks(inFile)
if nil != err { if nil != err {
@ -764,36 +764,220 @@ func AddPDFOutline(id, p string, merge bool) (err error) {
} }
} }
//var assetAbsPaths []string var assetAbsPaths []string
//for _, dest := range assetDests { for _, dest := range assetDests {
// absPath, _ := GetAssetAbsPath(dest) absPath, _ := GetAssetAbsPath(dest)
// if "" != absPath { if "" != absPath {
// assetAbsPaths = append(assetAbsPaths, absPath) assetAbsPaths = append(assetAbsPaths, absPath)
// } }
//} }
//
//if 0 < len(assetAbsPaths) { pdfCtx, ctxErr := api.ReadContextFile(inFile)
// outFile := inFile + ".tmp" if nil != ctxErr {
// err = api.AddAttachmentsFile(inFile, outFile, assetAbsPaths, false, nil) logging.LogErrorf("read pdf context failed: %s", ctxErr)
// if nil != err { return
// logging.LogErrorf("add attachment failed: %s", err) }
// return
// } if 0 < len(assetAbsPaths) {
// assetLinks, otherLinks, listErr := api.ListLinks(inFile)
// err = os.Rename(outFile, inFile) if nil != listErr {
// if nil != err { logging.LogErrorf("list asset links failed: %s", listErr)
// return return
// } }
//}
// if _, removeErr := pdfCtx.RemoveAnnotations(nil, nil, nil, false); nil != removeErr {
//assetLinks, err := api.ListAssetLinks(inFile) logging.LogWarnf("remove annotations failed: %s", removeErr)
//if nil == err { }
// logging.LogInfof("pdf annotation: %+v", assetLinks)
//} linkMap := map[int][]pdfcpu.AnnotationRenderer{}
for _, link := range otherLinks {
link.URI, _ = url.PathUnescape(link.URI)
if 1 > len(linkMap[link.Page]) {
linkMap[link.Page] = []pdfcpu.AnnotationRenderer{link}
} else {
linkMap[link.Page] = append(linkMap[link.Page], link)
}
}
attachmentMap := map[int][]*pdfcpu.IndirectRef{}
now := pdfcpu.StringLiteral(pdfcpu.DateString(time.Now()))
for _, link := range assetLinks {
link.URI = strings.ReplaceAll(link.URI, "http://127.0.0.1:6806/export/temp/", "")
link.URI, _ = url.PathUnescape(link.URI)
if !removeAssets {
// 不移除资源文件夹的话将超链接指向资源文件夹
if 1 > len(linkMap[link.Page]) {
linkMap[link.Page] = []pdfcpu.AnnotationRenderer{link}
} else {
linkMap[link.Page] = append(linkMap[link.Page], link)
}
continue
}
// 移除资源文件夹的话使用内嵌附件
absPath, getErr := GetAssetAbsPath(link.URI)
if nil != getErr {
continue
}
ir, newErr := pdfCtx.XRefTable.NewEmbeddedFileStreamDict(absPath)
if nil != newErr {
logging.LogWarnf("new embedded file stream dict failed: %s", newErr)
continue
}
fn := filepath.Base(absPath)
fileSpecDict, newErr := pdfCtx.XRefTable.NewFileSpecDict(fn, pdfcpu.EncodeUTF16String(fn), "attached by SiYuan", *ir)
if nil != newErr {
logging.LogWarnf("new file spec dict failed: %s", newErr)
continue
}
ir, indErr := pdfCtx.XRefTable.IndRefForNewObject(fileSpecDict)
if nil != indErr {
logging.LogWarnf("ind ref for new object failed: %s", indErr)
continue
}
lx := link.Rect.LL.X + link.Rect.Width()
ly := link.Rect.LL.Y + link.Rect.Height()/2
ux := lx + link.Rect.Height()/2
uy := ly + link.Rect.Height()/2
d := pdfcpu.Dict(
map[string]pdfcpu.Object{
"Type": pdfcpu.Name("Annot"),
"Subtype": pdfcpu.Name("FileAttachment"),
"Contents": pdfcpu.StringLiteral(""),
"Rect": pdfcpu.Rect(lx, ly, ux, uy).Array(),
"P": link.P,
"M": now,
"F": pdfcpu.Integer(0),
"Border": pdfcpu.NewIntegerArray(0, 0, 1),
"C": pdfcpu.NewNumberArray(0.5, 0.0, 0.5),
"CA": pdfcpu.Float(0.95),
"CreationDate": now,
"Name": pdfcpu.Name("FileAttachment"),
"FS": *ir,
"NM": pdfcpu.StringLiteral(""),
},
)
ann, indErr := pdfCtx.XRefTable.IndRefForNewObject(d)
if nil != indErr {
logging.LogWarnf("ind ref for new object failed: %s", indErr)
continue
}
pageDictIndRef, pageErr := pdfCtx.PageDictIndRef(link.Page)
if nil != pageErr {
logging.LogWarnf("page dict ind ref failed: %s", pageErr)
continue
}
d, defErr := pdfCtx.DereferenceDict(*pageDictIndRef)
if nil != defErr {
logging.LogWarnf("dereference dict failed: %s", defErr)
continue
}
if 1 > len(attachmentMap[link.Page]) {
attachmentMap[link.Page] = []*pdfcpu.IndirectRef{ann}
} else {
attachmentMap[link.Page] = append(attachmentMap[link.Page], ann)
}
}
if 0 < len(linkMap) {
if _, addErr := pdfCtx.AddAnnotationsMap(linkMap, false); nil != addErr {
logging.LogErrorf("add annotations map failed: %s", addErr)
}
}
// 添加附件注解指向内嵌的附件
for page, anns := range attachmentMap {
pageDictIndRef, pageErr := pdfCtx.PageDictIndRef(page)
if nil != pageErr {
logging.LogWarnf("page dict ind ref failed: %s", pageErr)
continue
}
pageDict, defErr := pdfCtx.DereferenceDict(*pageDictIndRef)
if nil != defErr {
logging.LogWarnf("dereference dict failed: %s", defErr)
continue
}
array := pdfcpu.Array{}
for _, ann := range anns {
array = append(array, *ann)
}
obj, found := pageDict.Find("Annots")
if !found {
pageDict.Insert("Annots", array)
pdfCtx.EnsureVersionForWriting()
continue
}
ir, ok := obj.(pdfcpu.IndirectRef)
if !ok {
pageDict.Update("Annots", append(obj.(pdfcpu.Array), array...))
pdfCtx.EnsureVersionForWriting()
continue
}
// Annots array is an IndirectReference.
o, err := pdfCtx.Dereference(ir)
if err != nil || o == nil {
continue
}
annots, _ := o.(pdfcpu.Array)
entry, ok := pdfCtx.FindTableEntryForIndRef(&ir)
if !ok {
continue
}
entry.Object = append(annots, array...)
pdfCtx.EnsureVersionForWriting()
}
}
pdfcpu.VersionStr = "SiYuan v" + util.Ver
if writeErr := api.WriteContextFile(pdfCtx, inFile); nil != writeErr {
logging.LogErrorf("write pdf context failed: %s", writeErr)
return
}
return return
} }
func annotRect(i int, w, h, d, l float64) *pdfcpu.Rectangle {
// d..distance between annotation rectangles
// l..side length of rectangle
// max number of rectangles fitting into w
xmax := int((w - d) / (l + d))
// max number of rectangles fitting into h
ymax := int((h - d) / (l + d))
col := float64(i % xmax)
row := float64(i / xmax % ymax)
llx := d + col*(l+d)
lly := d + row*(l+d)
urx := llx + l
ury := lly + l
return pdfcpu.Rect(llx, lly, urx, ury)
}
func ExportStdMarkdown(id string) string { func ExportStdMarkdown(id string) string {
tree, err := loadTreeByBlockID(id) tree, err := loadTreeByBlockID(id)
if nil != err { if nil != err {

View File

@ -674,6 +674,10 @@ func GetDecks() (decks []*riff.Deck) {
if 1 > len(decks) { if 1 > len(decks) {
decks = []*riff.Deck{} decks = []*riff.Deck{}
} }
sort.Slice(decks, func(i, j int) bool {
return decks[i].Updated > decks[j].Updated
})
return return
} }

View File

@ -130,9 +130,11 @@ func Upload(c *gin.Context) {
docDirLocalPath := filepath.Join(util.DataDir, bt.BoxID, path.Dir(bt.Path)) docDirLocalPath := filepath.Join(util.DataDir, bt.BoxID, path.Dir(bt.Path))
assetsDirPath = getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), docDirLocalPath) assetsDirPath = getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), docDirLocalPath)
} }
relAssetsDirPath := "assets"
if nil != form.Value["assetsDirPath"] { if nil != form.Value["assetsDirPath"] {
assetsDirPath = form.Value["assetsDirPath"][0] relAssetsDirPath = form.Value["assetsDirPath"][0]
assetsDirPath = filepath.Join(util.DataDir, assetsDirPath) assetsDirPath = filepath.Join(util.DataDir, relAssetsDirPath)
} }
if !gulu.File.IsExist(assetsDirPath) { if !gulu.File.IsExist(assetsDirPath) {
if err = os.MkdirAll(assetsDirPath, 0755); nil != err { if err = os.MkdirAll(assetsDirPath, 0755); nil != err {
@ -187,7 +189,7 @@ func Upload(c *gin.Context) {
break break
} }
f.Close() f.Close()
succMap[baseName] = "assets/" + fName succMap[baseName] = path.Join(relAssetsDirPath, fName)
} }
} }