Improved outline handling (in progress)

Structure element currently scrapped.  Later to add support for that.
Then will fix the SE of the outline item.
Still a bit of testing and validation required.
This commit is contained in:
Gunnsteinn Hall 2016-12-05 00:46:27 +00:00
parent a9621e8c0f
commit af5ea0bd00
4 changed files with 122 additions and 56 deletions

View File

@ -21,7 +21,8 @@ type PdfOutlineTreeNode struct {
// PDF outline dictionary (Table 152 - p. 376). // PDF outline dictionary (Table 152 - p. 376).
type PdfOutline struct { type PdfOutline struct {
PdfOutlineTreeNode PdfOutlineTreeNode
Count *int64 Parent *PdfOutlineTreeNode
Count *int64
primitive *PdfIndirectObject primitive *PdfIndirectObject
} }
@ -85,8 +86,14 @@ func NewOutlineBookmark(title string, page *PdfIndirectObject) *PdfOutlineItem {
} }
// Does not traverse the tree. // Does not traverse the tree.
func newPdfOutlineFromDict(dict *PdfObjectDictionary) (*PdfOutline, error) { func newPdfOutlineFromIndirectObject(container *PdfIndirectObject) (*PdfOutline, error) {
dict, isDict := container.PdfObject.(*PdfObjectDictionary)
if !isDict {
return nil, fmt.Errorf("Outline object not a dictionary")
}
outline := PdfOutline{} outline := PdfOutline{}
outline.primitive = container
outline.context = &outline outline.context = &outline
if obj, hasType := (*dict)["Type"]; hasType { if obj, hasType := (*dict)["Type"]; hasType {
@ -114,8 +121,14 @@ func newPdfOutlineFromDict(dict *PdfObjectDictionary) (*PdfOutline, error) {
} }
// Does not traverse the tree. // Does not traverse the tree.
func (this *PdfReader) newPdfOutlineItemFromDict(dict *PdfObjectDictionary) (*PdfOutlineItem, error) { func (this *PdfReader) newPdfOutlineItemFromIndirectObject(container *PdfIndirectObject) (*PdfOutlineItem, error) {
dict, isDict := container.PdfObject.(*PdfObjectDictionary)
if !isDict {
return nil, fmt.Errorf("Outline object not a dictionary")
}
item := PdfOutlineItem{} item := PdfOutlineItem{}
item.primitive = container
item.context = &item item.context = &item
// Title (required). // Title (required).
@ -164,11 +177,16 @@ func (this *PdfReader) newPdfOutlineItemFromDict(dict *PdfObjectDictionary) (*Pd
return nil, err return nil, err
} }
} }
if obj, hasKey := (*dict)["SE"]; hasKey { if _, hasKey := (*dict)["SE"]; hasKey {
item.SE, err = this.traceToObject(obj) // XXX: To add structure element support.
if err != nil { // Currently not supporting structure elements.
return nil, err item.SE = nil
} /*
item.SE, err = this.traceToObject(obj)
if err != nil {
return nil, err
}
*/
} }
if obj, hasKey := (*dict)["C"]; hasKey { if obj, hasKey := (*dict)["C"]; hasKey {
item.C, err = this.traceToObject(obj) item.C, err = this.traceToObject(obj)
@ -213,6 +231,7 @@ func (this *PdfOutline) GetContainingPdfObject() PdfObject {
// Recursively build the Outline tree PDF object. // Recursively build the Outline tree PDF object.
func (this *PdfOutline) ToPdfObject() PdfObject { func (this *PdfOutline) ToPdfObject() PdfObject {
fmt.Printf("Outline primitive: %+v\n", this.primitive)
container := this.primitive container := this.primitive
dict := container.PdfObject.(*PdfObjectDictionary) dict := container.PdfObject.(*PdfObjectDictionary)
@ -227,6 +246,10 @@ func (this *PdfOutline) ToPdfObject() PdfObject {
//PdfObjectConverterCache[this.Last.getOuter()] //PdfObjectConverterCache[this.Last.getOuter()]
} }
if this.Parent != nil {
dict.Set("Parent", this.Parent.getOuter().GetContainingPdfObject())
}
return container return container
} }
@ -244,6 +267,16 @@ func (this *PdfOutlineItem) ToPdfObject() PdfObject {
if this.A != nil { if this.A != nil {
(*dict)["A"] = this.A (*dict)["A"] = this.A
} }
if _, hasSE := (*dict)["SE"]; hasSE {
// XXX: Currently not supporting structure element hierarchy.
// Remove it.
delete(*dict, "SE")
}
/*
if this.SE != nil {
(*dict)["SE"] = this.SE
}
*/
if this.C != nil { if this.C != nil {
(*dict)["C"] = this.C (*dict)["C"] = this.C
} }

View File

@ -69,8 +69,14 @@ func NewPdfPage() *PdfPage {
return &page return &page
} }
func (this *PdfPage) setContainer(container *PdfIndirectObject) {
container.PdfObject = this.pageDict
this.primitive = container
}
// Build a PdfPage based on the underlying dictionary. // Build a PdfPage based on the underlying dictionary.
// Used in loading existing PDF files. // Used in loading existing PDF files.
// Note that a new container is created (indirect object).
func (reader *PdfReader) newPdfPageFromDict(p *PdfObjectDictionary) (*PdfPage, error) { func (reader *PdfReader) newPdfPageFromDict(p *PdfObjectDictionary) (*PdfPage, error) {
page := NewPdfPage() page := NewPdfPage()

View File

@ -255,7 +255,7 @@ func (this *PdfReader) loadOutlines() (*PdfOutlineTreeNode, error) {
common.Log.Debug("Outline root dict: %v", dict) common.Log.Debug("Outline root dict: %v", dict)
outlineTree, err := this.buildOutlineTree(dict) outlineTree, _, err := this.buildOutlineTree(outlineRoot, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -265,80 +265,105 @@ func (this *PdfReader) loadOutlines() (*PdfOutlineTreeNode, error) {
} }
// Recursive build outline tree. // Recursive build outline tree.
func (this *PdfReader) buildOutlineTree(obj PdfObject) (*PdfOutlineTreeNode, error) { // prev PdfObject,
dict, ok := TraceToDirectObject(obj).(*PdfObjectDictionary) // Input: The indirect object containing an Outlines or Outline item dictionary.
if !ok { // Parent, Prev are the parent or previous node in the hierarchy.
return nil, errors.New("Not a dictionary object") // The function returns the corresponding tree node and the last node which is used
// for setting the Last pointer of the tree node structures.
func (this *PdfReader) buildOutlineTree(obj PdfObject, parent *PdfOutlineTreeNode, prev *PdfOutlineTreeNode) (*PdfOutlineTreeNode, *PdfOutlineTreeNode, error) {
container, isInd := obj.(*PdfIndirectObject)
if !isInd {
return nil, nil, fmt.Errorf("Outline container not an indirect object %T", obj)
} }
common.Log.Debug("build outline tree: dict: %v", dict) dict, ok := container.PdfObject.(*PdfObjectDictionary)
if !ok {
return nil, nil, errors.New("Not a dictionary object")
}
common.Log.Debug("build outline tree: dict: %v (%v) p: %p", dict, container, container)
if _, hasTitle := (*dict)["Title"]; hasTitle { if _, hasTitle := (*dict)["Title"]; hasTitle {
// Outline item has a title. // Outline item has a title. (required)
outlineItem, err := this.newPdfOutlineItemFromDict(dict) outlineItem, err := this.newPdfOutlineItemFromIndirectObject(container)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
outlineItem.Parent = parent
outlineItem.Prev = prev
if firstObj, hasChildren := (*dict)["First"]; hasChildren {
firstObj, err = this.traceToObject(firstObj)
if err != nil {
return nil, nil, err
}
if _, isNull := firstObj.(*PdfObjectNull); !isNull {
first, last, err := this.buildOutlineTree(firstObj, &outlineItem.PdfOutlineTreeNode, nil)
if err != nil {
return nil, nil, err
}
outlineItem.First = first
outlineItem.Last = last
}
}
// Resolve the reference to next // Resolve the reference to next
if nextObj, hasNext := (*dict)["Next"]; hasNext { if nextObj, hasNext := (*dict)["Next"]; hasNext {
nextObj, err = this.traceToObject(nextObj) nextObj, err = this.traceToObject(nextObj)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
nextObj = TraceToDirectObject(nextObj)
if _, isNull := nextObj.(*PdfObjectNull); !isNull { if _, isNull := nextObj.(*PdfObjectNull); !isNull {
nextDict, ok := nextObj.(*PdfObjectDictionary) next, last, err := this.buildOutlineTree(nextObj, parent, &outlineItem.PdfOutlineTreeNode)
if !ok {
return nil, fmt.Errorf("Next not a dictionary object (%T)", nextObj)
}
outlineItem.Next, err = this.buildOutlineTree(nextDict)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
outlineItem.Next = next
return &outlineItem.PdfOutlineTreeNode, last, nil
} }
} }
if firstObj, hasChildren := (*dict)["First"]; hasChildren {
firstObj, err = this.traceToObject(firstObj) return &outlineItem.PdfOutlineTreeNode, &outlineItem.PdfOutlineTreeNode, nil
if err != nil {
return nil, err
}
firstObj = TraceToDirectObject(firstObj)
if _, isNull := firstObj.(*PdfObjectNull); !isNull {
firstDict, ok := firstObj.(*PdfObjectDictionary)
if !ok {
return nil, fmt.Errorf("First not a dictionary object (%T)", firstObj)
}
outlineItem.First, err = this.buildOutlineTree(firstDict)
if err != nil {
return nil, err
}
}
}
return &outlineItem.PdfOutlineTreeNode, nil
} else { } else {
// Outline dictionary (structure element). // Outline dictionary (structure element).
outline, err := newPdfOutlineFromDict(dict)
outline, err := newPdfOutlineFromIndirectObject(container)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
outline.Parent = parent
//outline.Prev = parent
if firstObj, hasChildren := (*dict)["First"]; hasChildren { if firstObj, hasChildren := (*dict)["First"]; hasChildren {
firstObj, err = this.traceToObject(firstObj) firstObj, err = this.traceToObject(firstObj)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
firstObj = TraceToDirectObject(firstObj)
if _, isNull := firstObj.(*PdfObjectNull); !isNull { if _, isNull := firstObj.(*PdfObjectNull); !isNull {
firstDict, ok := firstObj.(*PdfObjectDictionary) first, last, err := this.buildOutlineTree(firstObj, &outline.PdfOutlineTreeNode, nil)
if !ok {
return nil, fmt.Errorf("First not a dictionary object (%T)", firstObj)
}
outline.First, err = this.buildOutlineTree(firstDict)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
outline.First = first
outline.Last = last
} }
} }
return &outline.PdfOutlineTreeNode, nil
/*
if nextObj, hasNext := (*dict)["Next"]; hasNext {
nextObj, err = this.traceToObject(nextObj)
if err != nil {
return nil, nil, err
}
if _, isNull := nextObj.(*PdfObjectNull); !isNull {
next, last, err := this.buildOutlineTree(nextObj, parent, &outline.PdfOutlineTreeNode)
if err != nil {
return nil, nil, err
}
outline.Next = next
return &outline.PdfOutlineTreeNode, last, nil
}
}*/
return &outline.PdfOutlineTreeNode, &outline.PdfOutlineTreeNode, nil
} }
} }
@ -494,6 +519,7 @@ func (this *PdfReader) buildPageList(node *PdfIndirectObject, parent *PdfIndirec
if err != nil { if err != nil {
return err return err
} }
p.setContainer(node)
if parent != nil { if parent != nil {
// Set the parent (in case missing or incorrect). // Set the parent (in case missing or incorrect).

View File

@ -154,7 +154,7 @@ func (this *PdfWriter) addObjects(obj PdfObject) error {
if io, isIndirectObj := obj.(*PdfIndirectObject); isIndirectObj { if io, isIndirectObj := obj.(*PdfIndirectObject); isIndirectObj {
common.Log.Debug("Indirect") common.Log.Debug("Indirect")
common.Log.Debug("- %s", obj) common.Log.Debug("- %s (%p)", obj, io)
common.Log.Debug("- %s", io.PdfObject) common.Log.Debug("- %s", io.PdfObject)
if this.addObject(io) { if this.addObject(io) {
err := this.addObjects(io.PdfObject) err := this.addObjects(io.PdfObject)
@ -561,8 +561,9 @@ func (this *PdfWriter) Write(ws io.WriteSeeker) error {
common.Log.Debug("Write()") common.Log.Debug("Write()")
// Outlines. // Outlines.
if this.outlineTree != nil { if this.outlineTree != nil {
common.Log.Debug("OutlineTree: %v", this.outlineTree) common.Log.Debug("OutlineTree: %+v", this.outlineTree)
outlines := this.outlineTree.ToPdfObject() outlines := this.outlineTree.ToPdfObject()
common.Log.Debug("Outlines: %+v (%T, p:%p)", outlines, outlines, outlines)
(*this.catalog)["Outlines"] = outlines (*this.catalog)["Outlines"] = outlines
err := this.addObjects(outlines) err := this.addObjects(outlines)
if err != nil { if err != nil {