Outputting/writing outline tree in the works

This commit is contained in:
Gunnsteinn Hall 2016-08-18 09:43:44 +00:00
parent 83e4b0946c
commit 49b5529a92
4 changed files with 154 additions and 13 deletions

View File

@ -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
}

View File

@ -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{}

View File

@ -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
}

View File

@ -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