diff --git a/kernel/go.mod b/kernel/go.mod index cdb28a6f5..1f107641b 100644 --- a/kernel/go.mod +++ b/kernel/go.mod @@ -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 diff --git a/kernel/go.sum b/kernel/go.sum index cb44b26c3..0fd87b5cc 100644 --- a/kernel/go.sum +++ b/kernel/go.sum @@ -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= diff --git a/kernel/model/export.go b/kernel/model/export.go index e7642445c..2637c1c61 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -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 {