🎨 导出 PDF 时支持将资源文件作为附件嵌入 https://github.com/siyuan-note/siyuan/issues/7414

This commit is contained in:
Liang Ding 2023-02-22 21:11:58 +08:00
parent edbe4da57e
commit efc018a27f
No known key found for this signature in database
GPG key ID: 136F30F901A2231D
3 changed files with 196 additions and 29 deletions

View file

@ -7,7 +7,7 @@ require (
github.com/88250/css v0.1.2
github.com/88250/gulu v1.2.3-0.20221117052724-cd06804db798
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.20230222105639-68297f21b5d2
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
github.com/ClarkThan/ahocorasick v0.0.0-20230216061320-bccdb98581a3
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/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/pdfcpu v0.3.13 h1:touMWMZkCGalMIbEg9bxYp7rETM+zwb9hXjwhqi4I7Q=
github.com/88250/pdfcpu v0.3.13/go.mod h1:S5YT38L/GCjVjmB4PB84PymA1qfopjEhfhTNQilLpv4=
github.com/88250/pdfcpu v0.3.14-0.20230222105639-68297f21b5d2 h1:M1JxCmcaLwI7qlQJD5UatAxaIT6tfLvq3GptOsMffn4=
github.com/88250/pdfcpu v0.3.14-0.20230222105639-68297f21b5d2/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/go.mod h1:U3pckKQIgxxkmZjV5yXQjHdGxQK0o/vEZeZ6cQsxfHw=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=

View file

@ -764,36 +764,203 @@ func AddPDFOutline(id, p string, merge bool) (err error) {
}
}
//var assetAbsPaths []string
//for _, dest := range assetDests {
// absPath, _ := GetAssetAbsPath(dest)
// if "" != absPath {
// assetAbsPaths = append(assetAbsPaths, absPath)
// }
//}
//
//if 0 < len(assetAbsPaths) {
// outFile := inFile + ".tmp"
// err = api.AddAttachmentsFile(inFile, outFile, assetAbsPaths, false, nil)
// if nil != err {
// logging.LogErrorf("add attachment failed: %s", err)
// return
// }
//
// err = os.Rename(outFile, inFile)
// if nil != err {
// return
// }
//}
//
//assetLinks, err := api.ListAssetLinks(inFile)
//if nil == err {
// logging.LogInfof("pdf annotation: %+v", assetLinks)
//}
var assetAbsPaths []string
for _, dest := range assetDests {
absPath, _ := GetAssetAbsPath(dest)
if "" != absPath {
assetAbsPaths = append(assetAbsPaths, absPath)
}
}
if 0 < len(assetAbsPaths) {
//outFile := inFile + ".tmp"
//err = api.AddAttachmentsFile(inFile, outFile, assetAbsPaths, false, nil)
//if nil != err {
// logging.LogErrorf("add attachment failed: %s", err)
// return
//}
//
//err = os.Rename(outFile, inFile)
//if nil != err {
// return
//}
assetLinks, listErr := api.ListAssetLinks(inFile)
if nil != listErr {
logging.LogErrorf("list asset links failed: %s", listErr)
return
}
pdfCtx, ctxErr := api.ReadContextFile(inFile)
if nil != ctxErr {
logging.LogErrorf("read pdf context failed: %s", ctxErr)
return
}
linkMap := map[int][]*pdfcpu.IndirectRef{}
//pdfCtx.RemoveAnnotations(nil, nil, nil, false)
for i, link := range assetLinks {
link.URI = strings.ReplaceAll(link.URI, "http://127.0.0.1:6806/export/temp/", "")
//if 1 > len(linkMap[link.Page]) {
// linkMap[link.Page] = []pdfcpu.Annotation{link}
//} else {
// linkMap[link.Page] = append(linkMap[link.Page], link)
//}
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
}
now := pdfcpu.StringLiteral(pdfcpu.DateString(time.Now()))
mediaBox := pdfcpu.RectForFormat("A4")
r := annotRect(i, mediaBox.Width(), mediaBox.Height(), 30, 80)
d := pdfcpu.Dict(
map[string]pdfcpu.Object{
"Type": pdfcpu.Name("Annot"),
"Subtype": pdfcpu.Name("FileAttachment"),
"Contents": pdfcpu.StringLiteral("FileAttachment Annotation"),
"Rect": r.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("SoundFileAttachmentAnnot"),
},
)
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(linkMap[link.Page]) {
linkMap[link.Page] = []*pdfcpu.IndirectRef{ann}
} else {
linkMap[link.Page] = append(linkMap[link.Page], ann)
}
}
for page, anns := range linkMap {
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()
//d.Insert("Annots", array)
}
if writeErr := api.WriteContextFile(pdfCtx, inFile); nil != writeErr {
logging.LogErrorf("write pdf context failed: %s", writeErr)
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 {
tree, err := loadTreeByBlockID(id)
if nil != err {