diff --git a/model/outline.go b/model/outline.go index 5f7054fb..1851641f 100644 --- a/model/outline.go +++ b/model/outline.go @@ -6,9 +6,6 @@ package model import ( - "errors" - - "github.com/unidoc/unipdf/v3/common" "github.com/unidoc/unipdf/v3/core" ) @@ -99,56 +96,6 @@ func NewOutline() *Outline { return &Outline{} } -// NewOutlineFromReaderOutline returns a new outline from the outline tree of -// the passed in reader. -func NewOutlineFromReaderOutline(r *PdfReader) (*Outline, error) { - if r == nil { - return nil, errors.New("cannot create outline from nil reader") - } - - outlineTree := r.GetOutlineTree() - if outlineTree == nil { - return nil, errors.New("the specified reader does not have an outline tree") - } - - var traverseFunc func(node *PdfOutlineTreeNode, entries *[]*OutlineItem) - traverseFunc = func(node *PdfOutlineTreeNode, entries *[]*OutlineItem) { - if node == nil { - return - } - if node.context == nil { - common.Log.Debug("ERROR: missing outline entry context") - return - } - - // Check if node is an outline item. - var entry *OutlineItem - if item, ok := node.context.(*PdfOutlineItem); ok { - entry = NewOutlineItem(item.Title.Decoded(), newOutlineDestFromPdfObject(item.Dest, r)) - *entries = append(*entries, entry) - - // Traverse next node. - if item.Next != nil { - traverseFunc(item.Next, entries) - } - } - - // Check if node has children. - if node.First != nil { - if entry != nil { - entries = &entry.Entries - } - - // Traverse node children. - traverseFunc(node.First, entries) - } - } - - outline := NewOutline() - traverseFunc(outlineTree, &outline.Entries) - return outline, nil -} - // Add appends a top level outline item to the outline. func (o *Outline) Add(item *OutlineItem) { o.Entries = append(o.Entries, item) diff --git a/model/reader.go b/model/reader.go index 5c570c06..d20c4c3f 100644 --- a/model/reader.go +++ b/model/reader.go @@ -399,6 +399,9 @@ func (r *PdfReader) GetOutlineTree() *PdfOutlineTreeNode { } // GetOutlinesFlattened returns a flattened list of tree nodes and titles. +// NOTE: for most use cases, it is recommended to use the high-level GetOutlines +// method instead, which also provides information regarding the destination +// of the outline items. func (r *PdfReader) GetOutlinesFlattened() ([]*PdfOutlineTreeNode, []string, error) { var outlineNodeList []*PdfOutlineTreeNode var flattenedTitleList []string @@ -435,6 +438,57 @@ func (r *PdfReader) GetOutlinesFlattened() ([]*PdfOutlineTreeNode, []string, err return outlineNodeList, flattenedTitleList, nil } +// GetOutlines returns a high-level Outline object, based on the outline tree +// of the reader. +func (r *PdfReader) GetOutlines() (*Outline, error) { + if r == nil { + return nil, errors.New("cannot create outline from nil reader") + } + + outlineTree := r.GetOutlineTree() + if outlineTree == nil { + return nil, errors.New("the specified reader does not have an outline tree") + } + + var traverseFunc func(node *PdfOutlineTreeNode, entries *[]*OutlineItem) + traverseFunc = func(node *PdfOutlineTreeNode, entries *[]*OutlineItem) { + if node == nil { + return + } + if node.context == nil { + common.Log.Debug("ERROR: missing outline entry context") + return + } + + // Check if node is an outline item. + var entry *OutlineItem + if item, ok := node.context.(*PdfOutlineItem); ok { + entry = NewOutlineItem(item.Title.Decoded(), + newOutlineDestFromPdfObject(item.Dest, r)) + *entries = append(*entries, entry) + + // Traverse next node. + if item.Next != nil { + traverseFunc(item.Next, entries) + } + } + + // Check if node has children. + if node.First != nil { + if entry != nil { + entries = &entry.Entries + } + + // Traverse node children. + traverseFunc(node.First, entries) + } + } + + outline := NewOutline() + traverseFunc(outlineTree, &outline.Entries) + return outline, nil +} + // loadForms loads the AcroForm. func (r *PdfReader) loadForms() (*PdfAcroForm, error) { if r.parser.GetCrypter() != nil && !r.parser.IsAuthenticated() {