mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00
Outputting/writing outline tree in the works
This commit is contained in:
parent
83e4b0946c
commit
49b5529a92
112
pdf/outlines.go
112
pdf/outlines.go
@ -2,8 +2,16 @@ package pdf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
)
|
||||
|
||||
type PdfObjectConverter interface {
|
||||
ToPdfObject() PdfObject
|
||||
}
|
||||
|
||||
var PdfObjectConverterCache map[PdfObjectConverter]PdfObject = map[PdfObjectConverter]PdfObject{}
|
||||
|
||||
type PdfOutlineTreeNode struct {
|
||||
context interface{} // Allow accessing outer structure.
|
||||
First *PdfOutlineTreeNode
|
||||
@ -30,6 +38,26 @@ type PdfOutlineItem struct {
|
||||
F PdfObject
|
||||
}
|
||||
|
||||
func NewPdfOutlineTree() *PdfOutline {
|
||||
outlineTree := PdfOutline{}
|
||||
outlineTree.context = &outlineTree
|
||||
return &outlineTree
|
||||
}
|
||||
|
||||
func NewOutlineBookmark(title string, page *PdfIndirectObject) *PdfOutlineItem {
|
||||
bookmark := PdfOutlineItem{}
|
||||
bookmark.context = &bookmark
|
||||
|
||||
bookmark.Title = MakeString(title)
|
||||
|
||||
destArray := PdfObjectArray{}
|
||||
destArray = append(destArray, page)
|
||||
destArray = append(destArray, MakeName("Fit"))
|
||||
bookmark.Dest = &destArray
|
||||
|
||||
return &bookmark
|
||||
}
|
||||
|
||||
// Does not traverse the tree.
|
||||
func newPdfOutlineFromDict(dict *PdfObjectDictionary) (*PdfOutline, error) {
|
||||
outline := PdfOutline{}
|
||||
@ -101,3 +129,87 @@ func newPdfOutlineItemFromDict(dict *PdfObjectDictionary) (*PdfOutlineItem, erro
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func (this *PdfOutlineTreeNode) ToPdfObject() PdfObject {
|
||||
if outline, isOutline := this.context.(*PdfOutline); isOutline {
|
||||
return outline.ToPdfObject()
|
||||
}
|
||||
if outlineItem, isOutlineItem := this.context.(*PdfOutlineItem); isOutlineItem {
|
||||
return outlineItem.ToPdfObject()
|
||||
}
|
||||
|
||||
common.Log.Error("Invalid outline tree node item") // Should never happen.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recursively build the Outline tree PDF object.
|
||||
func (this *PdfOutline) ToPdfObject() PdfObject {
|
||||
if cachedObj, isCached := PdfObjectConverterCache[this]; isCached {
|
||||
return cachedObj
|
||||
}
|
||||
|
||||
outlines := PdfIndirectObject{}
|
||||
|
||||
outlinesDict := PdfObjectDictionary{}
|
||||
outlinesDict[PdfObjectName("Type")] = MakeName("Outlines")
|
||||
|
||||
if this.First != nil {
|
||||
outlinesDict[PdfObjectName("First")] = this.First.ToPdfObject()
|
||||
}
|
||||
|
||||
if this.Last != nil {
|
||||
outlinesDict[PdfObjectName("Last")] = this.Last.ToPdfObject()
|
||||
}
|
||||
|
||||
outlines.PdfObject = &outlinesDict
|
||||
|
||||
PdfObjectConverterCache[this] = &outlines
|
||||
|
||||
return &outlines
|
||||
}
|
||||
|
||||
// Outline item.
|
||||
// Recursively build the Outline tree PDF object.
|
||||
func (this *PdfOutlineItem) ToPdfObject() PdfObject {
|
||||
if cachedObj, isCached := PdfObjectConverterCache[this]; isCached {
|
||||
return cachedObj
|
||||
}
|
||||
|
||||
container := PdfIndirectObject{}
|
||||
|
||||
dict := PdfObjectDictionary{}
|
||||
dict["Title"] = this.Title
|
||||
if this.A != nil {
|
||||
dict["A"] = this.A
|
||||
}
|
||||
if this.C != nil {
|
||||
dict["C"] = this.C
|
||||
}
|
||||
if this.Dest != nil {
|
||||
dict["Dest"] = this.Dest
|
||||
}
|
||||
if this.F != nil {
|
||||
dict["F"] = this.F
|
||||
}
|
||||
if this.Count != nil {
|
||||
dict["Count"] = MakeInteger(*this.Count)
|
||||
}
|
||||
|
||||
if this.Next != nil {
|
||||
dict["Next"] = this.Next.ToPdfObject()
|
||||
}
|
||||
if this.First != nil {
|
||||
dict["First"] = this.First.ToPdfObject()
|
||||
}
|
||||
if this.Prev != nil {
|
||||
dict["Prev"] = this.Prev.ToPdfObject()
|
||||
}
|
||||
if this.Last != nil {
|
||||
dict["Last"] = this.Last.ToPdfObject()
|
||||
}
|
||||
|
||||
container.PdfObject = &dict
|
||||
PdfObjectConverterCache[this] = &container
|
||||
|
||||
return &container
|
||||
}
|
||||
|
@ -275,6 +275,7 @@ func NewPdfPagesFromDict(dict PdfObjectDictionary) (*PdfPages, error) {
|
||||
}
|
||||
|
||||
// Build a PdfPage based on the underlying dictionary.
|
||||
// Used in loading existing PDF files.
|
||||
func (reader *PdfReader) newPdfPageFromDict(p *PdfObjectDictionary) (*PdfPage, error) {
|
||||
page := PdfPage{}
|
||||
page.pageDict = &PdfObjectDictionary{}
|
||||
|
@ -648,6 +648,7 @@ func (this *PdfReader) GetOutlinesForPage(page PdfObject) ([]*PdfIndirectObject,
|
||||
|
||||
// Get a page by the page number.
|
||||
// Indirect object with type /Page.
|
||||
// GetPageAsIndirectObject
|
||||
func (this *PdfReader) GetPage(pageNumber int) (PdfObject, error) {
|
||||
if this.parser.crypter != nil && !this.parser.crypter.authenticated {
|
||||
return nil, fmt.Errorf("File need to be decrypted first")
|
||||
@ -667,3 +668,18 @@ func (this *PdfReader) GetPage(pageNumber int) (PdfObject, error) {
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
||||
// Get a page by the page number.
|
||||
// Indirect object with type /Page.
|
||||
// GetPageAsIndirectObject
|
||||
func (this *PdfReader) GetPageAsPdfPage(pageNumber int) (*PdfPage, error) {
|
||||
if this.parser.crypter != nil && !this.parser.crypter.authenticated {
|
||||
return nil, fmt.Errorf("File need to be decrypted first")
|
||||
}
|
||||
if len(this.pageList) < pageNumber {
|
||||
return nil, errors.New("Invalid page number (page count too short)")
|
||||
}
|
||||
page := this.PageList[pageNumber-1]
|
||||
|
||||
return page, nil
|
||||
}
|
||||
|
@ -60,15 +60,16 @@ func SetPdfCreator(creator string) {
|
||||
}
|
||||
|
||||
type PdfWriter struct {
|
||||
root *PdfIndirectObject
|
||||
pages *PdfIndirectObject
|
||||
objects []PdfObject
|
||||
objectsMap map[PdfObject]bool // Quick lookup table.
|
||||
writer *bufio.Writer
|
||||
outlines []*PdfIndirectObject
|
||||
catalog *PdfObjectDictionary
|
||||
fields []PdfObject
|
||||
infoObj *PdfIndirectObject
|
||||
root *PdfIndirectObject
|
||||
pages *PdfIndirectObject
|
||||
objects []PdfObject
|
||||
objectsMap map[PdfObject]bool // Quick lookup table.
|
||||
writer *bufio.Writer
|
||||
outlines []*PdfIndirectObject
|
||||
outlineTree *PdfOutlineTreeNode
|
||||
catalog *PdfObjectDictionary
|
||||
fields []PdfObject
|
||||
infoObj *PdfIndirectObject
|
||||
// Encryption
|
||||
crypter *PdfCrypt
|
||||
encryptDict *PdfObjectDictionary
|
||||
@ -317,6 +318,11 @@ func (this *PdfWriter) AddOutlines(outlinesList []*PdfIndirectObject) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add outlines to a PDF file.
|
||||
func (this *PdfWriter) AddOutlineTree(outlineTree *PdfOutlineTreeNode) {
|
||||
this.outlineTree = outlineTree
|
||||
}
|
||||
|
||||
// Look for a specific key. Returns a list of entries.
|
||||
// What if something appears on many pages?
|
||||
func (this *PdfWriter) seekByName(obj PdfObject, followKeys []string, key string) ([]PdfObject, error) {
|
||||
@ -353,10 +359,6 @@ func (this *PdfWriter) seekByName(obj PdfObject, followKeys []string, key string
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Ignore arrays.
|
||||
//if arr, isArray := obj.(*PdfObjectArray); isArray {
|
||||
//}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
@ -553,6 +555,16 @@ func (this *PdfWriter) Encrypt(userPass, ownerPass []byte, options *EncryptOptio
|
||||
// Write the pdf out.
|
||||
func (this *PdfWriter) Write(ws io.WriteSeeker) error {
|
||||
common.Log.Debug("Write()")
|
||||
// Outlines.
|
||||
if this.outlineTree != nil {
|
||||
outlines := this.outlineTree.ToPdfObject()
|
||||
(*this.catalog)["Outlines"] = outlines
|
||||
err := this.addObjects(outlines)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Phase this one out.
|
||||
if len(this.outlines) > 0 {
|
||||
// Add the outlines dictionary if some outlines added.
|
||||
// Assume they are correct, not referencing anything not added
|
||||
|
Loading…
x
Reference in New Issue
Block a user