2016-07-19 23:16:25 +00:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms and conditions defined in
|
2016-07-29 17:23:39 +00:00
|
|
|
* file 'LICENSE.md', which is part of this source code package.
|
2016-07-19 23:16:25 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
//
|
|
|
|
// Allow higher level manipulation of PDF files and pages.
|
2017-04-19 12:05:20 +00:00
|
|
|
// This can be continuously expanded to support more and more features.
|
2016-07-19 23:16:25 +00:00
|
|
|
// Generic handling can be done by defining elements as PdfObject which
|
|
|
|
// can later be replaced and fully defined.
|
|
|
|
//
|
|
|
|
|
2016-09-08 17:53:45 +00:00
|
|
|
package model
|
2016-07-19 23:16:25 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2016-12-13 21:42:27 +00:00
|
|
|
"fmt"
|
2017-02-28 08:59:17 +00:00
|
|
|
"strings"
|
2016-08-19 11:34:55 +00:00
|
|
|
|
2017-06-04 12:35:34 +00:00
|
|
|
"github.com/unidoc/unidoc/common"
|
2016-09-08 17:53:45 +00:00
|
|
|
. "github.com/unidoc/unidoc/pdf/core"
|
2016-07-19 23:16:25 +00:00
|
|
|
)
|
|
|
|
|
2018-07-14 02:25:29 +00:00
|
|
|
// PdfPage represents a page in a PDF document. (7.7.3.3 - Table 30).
|
2016-07-19 23:16:25 +00:00
|
|
|
type PdfPage struct {
|
2017-04-21 12:08:58 +00:00
|
|
|
Parent PdfObject
|
|
|
|
LastModified *PdfDate
|
|
|
|
Resources *PdfPageResources
|
|
|
|
CropBox *PdfRectangle
|
|
|
|
MediaBox *PdfRectangle
|
|
|
|
BleedBox *PdfRectangle
|
|
|
|
TrimBox *PdfRectangle
|
|
|
|
ArtBox *PdfRectangle
|
|
|
|
BoxColorInfo PdfObject
|
|
|
|
Contents PdfObject
|
|
|
|
Rotate *int64
|
|
|
|
Group PdfObject
|
|
|
|
Thumb PdfObject
|
|
|
|
B PdfObject
|
|
|
|
Dur PdfObject
|
|
|
|
Trans PdfObject
|
2016-07-19 23:16:25 +00:00
|
|
|
AA PdfObject
|
|
|
|
Metadata PdfObject
|
|
|
|
PieceInfo PdfObject
|
|
|
|
StructParents PdfObject
|
|
|
|
ID PdfObject
|
|
|
|
PZ PdfObject
|
|
|
|
SeparationInfo PdfObject
|
|
|
|
Tabs PdfObject
|
|
|
|
TemplateInstantiated PdfObject
|
|
|
|
PresSteps PdfObject
|
|
|
|
UserUnit PdfObject
|
|
|
|
VP PdfObject
|
2017-04-21 12:08:58 +00:00
|
|
|
|
2016-09-07 17:56:45 +00:00
|
|
|
Annotations []*PdfAnnotation
|
2016-09-11 23:17:38 +00:00
|
|
|
|
|
|
|
// Primitive container.
|
|
|
|
pageDict *PdfObjectDictionary
|
|
|
|
primitive *PdfIndirectObject
|
2016-08-15 01:31:35 +00:00
|
|
|
}
|
|
|
|
|
2016-08-20 10:15:48 +00:00
|
|
|
func NewPdfPage() *PdfPage {
|
|
|
|
page := PdfPage{}
|
2017-07-08 21:04:13 +00:00
|
|
|
page.pageDict = MakeDict()
|
2016-09-11 23:17:38 +00:00
|
|
|
|
|
|
|
container := PdfIndirectObject{}
|
|
|
|
container.PdfObject = page.pageDict
|
|
|
|
page.primitive = &container
|
|
|
|
|
2016-08-20 10:15:48 +00:00
|
|
|
return &page
|
|
|
|
}
|
|
|
|
|
2016-12-05 00:46:27 +00:00
|
|
|
func (this *PdfPage) setContainer(container *PdfIndirectObject) {
|
|
|
|
container.PdfObject = this.pageDict
|
|
|
|
this.primitive = container
|
|
|
|
}
|
|
|
|
|
2017-06-28 15:15:44 +00:00
|
|
|
func (this *PdfPage) Duplicate() *PdfPage {
|
|
|
|
var dup PdfPage
|
|
|
|
dup = *this
|
2017-07-08 21:04:13 +00:00
|
|
|
dup.pageDict = MakeDict()
|
2017-06-28 15:15:44 +00:00
|
|
|
dup.primitive = MakeIndirectObject(dup.pageDict)
|
|
|
|
|
|
|
|
return &dup
|
|
|
|
}
|
|
|
|
|
2016-07-19 23:16:25 +00:00
|
|
|
// Build a PdfPage based on the underlying dictionary.
|
2016-08-18 09:43:44 +00:00
|
|
|
// Used in loading existing PDF files.
|
2016-12-05 00:46:27 +00:00
|
|
|
// Note that a new container is created (indirect object).
|
2016-08-16 09:36:24 +00:00
|
|
|
func (reader *PdfReader) newPdfPageFromDict(p *PdfObjectDictionary) (*PdfPage, error) {
|
2016-08-20 10:15:48 +00:00
|
|
|
page := NewPdfPage()
|
2017-03-02 12:50:18 +00:00
|
|
|
page.pageDict = p //XXX?
|
2016-08-15 01:31:35 +00:00
|
|
|
|
|
|
|
d := *p
|
2016-07-19 23:16:25 +00:00
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
pType, ok := d.Get("Type").(*PdfObjectName)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Missing/Invalid Page dictionary Type")
|
|
|
|
}
|
|
|
|
if *pType != "Page" {
|
|
|
|
return nil, errors.New("Page dictionary Type != Page")
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Parent"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Parent = obj
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("LastModified"); obj != nil {
|
2016-08-26 21:20:50 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
strObj, ok := TraceToDirectObject(obj).(*PdfObjectString)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Page dictionary LastModified != string")
|
|
|
|
}
|
2018-07-14 02:25:29 +00:00
|
|
|
lastmod, err := NewPdfDate(strObj.Str())
|
2016-07-19 23:16:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
page.LastModified = &lastmod
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Resources"); obj != nil {
|
2016-08-26 21:20:50 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
2016-08-16 09:36:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dict, ok := TraceToDirectObject(obj).(*PdfObjectDictionary)
|
2016-07-25 14:06:37 +00:00
|
|
|
if !ok {
|
2016-08-16 09:36:24 +00:00
|
|
|
return nil, fmt.Errorf("Invalid resource dictionary (%T)", obj)
|
2016-07-25 14:06:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
page.Resources, err = NewPdfPageResourcesFromDict(dict)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-07-01 21:56:59 +00:00
|
|
|
} else {
|
2017-07-06 16:26:22 +00:00
|
|
|
// If Resources not explicitly defined, look up the tree (Parent objects) using
|
|
|
|
// the getResources() function. Resources should always be accessible.
|
|
|
|
resources, err := page.getResources()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if resources == nil {
|
|
|
|
resources = NewPdfPageResources()
|
|
|
|
}
|
|
|
|
page.Resources = resources
|
2016-07-19 23:16:25 +00:00
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("MediaBox"); obj != nil {
|
2016-08-25 17:26:38 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
boxArr, ok := TraceToDirectObject(obj).(*PdfObjectArray)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Page MediaBox not an array")
|
|
|
|
}
|
|
|
|
page.MediaBox, err = NewPdfRectangle(*boxArr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("CropBox"); obj != nil {
|
2016-08-25 17:26:38 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
boxArr, ok := TraceToDirectObject(obj).(*PdfObjectArray)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Page CropBox not an array")
|
|
|
|
}
|
|
|
|
page.CropBox, err = NewPdfRectangle(*boxArr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("BleedBox"); obj != nil {
|
2016-08-25 17:26:38 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
boxArr, ok := TraceToDirectObject(obj).(*PdfObjectArray)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Page BleedBox not an array")
|
|
|
|
}
|
|
|
|
page.BleedBox, err = NewPdfRectangle(*boxArr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("TrimBox"); obj != nil {
|
2016-08-25 17:26:38 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
boxArr, ok := TraceToDirectObject(obj).(*PdfObjectArray)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Page TrimBox not an array")
|
|
|
|
}
|
|
|
|
page.TrimBox, err = NewPdfRectangle(*boxArr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("ArtBox"); obj != nil {
|
2016-08-25 17:26:38 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
boxArr, ok := TraceToDirectObject(obj).(*PdfObjectArray)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Page ArtBox not an array")
|
|
|
|
}
|
|
|
|
page.ArtBox, err = NewPdfRectangle(*boxArr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("BoxColorInfo"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.BoxColorInfo = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Contents"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Contents = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Rotate"); obj != nil {
|
2016-08-26 21:20:50 +00:00
|
|
|
var err error
|
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
iObj, ok := TraceToDirectObject(obj).(*PdfObjectInteger)
|
2016-07-19 23:16:25 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Invalid Page Rotate object")
|
|
|
|
}
|
|
|
|
iVal := int64(*iObj)
|
|
|
|
page.Rotate = &iVal
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Group"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Group = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Thumb"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Thumb = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("B"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.B = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Dur"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Dur = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Trans"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Trans = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
//if obj := d.Get("Annots"); obj != nil {
|
2016-09-12 17:59:45 +00:00
|
|
|
// page.Annots = obj
|
|
|
|
//}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("AA"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.AA = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Metadata"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Metadata = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("PieceInfo"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.PieceInfo = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("StructParents"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.StructParents = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("ID"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.ID = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("PZ"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.PZ = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("SeparationInfo"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.SeparationInfo = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("Tabs"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.Tabs = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("TemplateInstantiated"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.TemplateInstantiated = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("PresSteps"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.PresSteps = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("UserUnit"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.UserUnit = obj
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := d.Get("VP"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
page.VP = obj
|
|
|
|
}
|
|
|
|
|
2016-09-07 17:56:45 +00:00
|
|
|
var err error
|
|
|
|
page.Annotations, err = reader.LoadAnnotations(&d)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-08-20 10:15:48 +00:00
|
|
|
return page, nil
|
2016-07-19 23:16:25 +00:00
|
|
|
}
|
|
|
|
|
2016-09-07 17:56:45 +00:00
|
|
|
func (reader *PdfReader) LoadAnnotations(d *PdfObjectDictionary) ([]*PdfAnnotation, error) {
|
2017-07-08 21:04:13 +00:00
|
|
|
annotsObj := d.Get("Annots")
|
|
|
|
if annotsObj == nil {
|
2016-09-07 17:56:45 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
annotsObj, err = reader.traceToObject(annotsObj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
annotsArr, ok := TraceToDirectObject(annotsObj).(*PdfObjectArray)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("Annots not an array")
|
|
|
|
}
|
|
|
|
|
|
|
|
annotations := []*PdfAnnotation{}
|
2018-07-15 17:52:53 +00:00
|
|
|
for _, obj := range annotsArr.Elements() {
|
2016-09-07 17:56:45 +00:00
|
|
|
obj, err = reader.traceToObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-12-05 16:21:23 +00:00
|
|
|
|
|
|
|
// Technically all annotation dictionaries should be inside indirect objects.
|
|
|
|
// In reality, sometimes the annotation dictionary is inline within the Annots array.
|
|
|
|
if _, isNull := obj.(*PdfObjectNull); isNull {
|
|
|
|
// Can safely ignore.
|
|
|
|
continue
|
2016-09-07 17:56:45 +00:00
|
|
|
}
|
2016-12-05 16:21:23 +00:00
|
|
|
|
|
|
|
annotDict, isDict := obj.(*PdfObjectDictionary)
|
|
|
|
indirectObj, isIndirect := obj.(*PdfIndirectObject)
|
|
|
|
if isDict {
|
|
|
|
// Create a container; indirect object; around the dictionary.
|
|
|
|
indirectObj = &PdfIndirectObject{}
|
|
|
|
indirectObj.PdfObject = annotDict
|
|
|
|
} else {
|
|
|
|
if !isIndirect {
|
|
|
|
return nil, fmt.Errorf("Annotation not in an indirect object")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
annot, err := reader.newPdfAnnotationFromIndirectObject(indirectObj)
|
2016-09-07 17:56:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
annotations = append(annotations, annot)
|
|
|
|
}
|
|
|
|
|
|
|
|
return annotations, nil
|
|
|
|
}
|
|
|
|
|
2016-07-19 23:16:25 +00:00
|
|
|
// Get the inheritable media box value, either from the page
|
|
|
|
// or a higher up page/pages struct.
|
|
|
|
func (this *PdfPage) GetMediaBox() (*PdfRectangle, error) {
|
|
|
|
if this.MediaBox != nil {
|
|
|
|
return this.MediaBox, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
node := this.Parent
|
|
|
|
for node != nil {
|
|
|
|
dictObj, ok := node.(*PdfIndirectObject)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Invalid parent object")
|
|
|
|
}
|
|
|
|
|
|
|
|
dict, ok := dictObj.PdfObject.(*PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Invalid parent objects dictionary")
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := dict.Get("MediaBox"); obj != nil {
|
2016-07-19 23:16:25 +00:00
|
|
|
arr, ok := obj.(*PdfObjectArray)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Invalid media box")
|
|
|
|
}
|
|
|
|
rect, err := NewPdfRectangle(*arr)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rect, nil
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
node = dict.Get("Parent")
|
2016-07-19 23:16:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.New("Media box not defined")
|
|
|
|
}
|
|
|
|
|
2017-07-06 16:26:22 +00:00
|
|
|
// Get the inheritable resources, either from the page or or a higher up page/pages struct.
|
|
|
|
func (this *PdfPage) getResources() (*PdfPageResources, error) {
|
2016-12-13 19:40:33 +00:00
|
|
|
if this.Resources != nil {
|
|
|
|
return this.Resources, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
node := this.Parent
|
|
|
|
for node != nil {
|
|
|
|
dictObj, ok := node.(*PdfIndirectObject)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Invalid parent object")
|
|
|
|
}
|
|
|
|
|
|
|
|
dict, ok := dictObj.PdfObject.(*PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Invalid parent objects dictionary")
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := dict.Get("Resources"); obj != nil {
|
2018-04-18 09:09:29 +03:00
|
|
|
prDict, ok := TraceToDirectObject(obj).(*PdfObjectDictionary)
|
2016-12-13 19:40:33 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Invalid resource dict!")
|
|
|
|
}
|
|
|
|
resources, err := NewPdfPageResourcesFromDict(prDict)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return resources, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep moving up the tree...
|
2017-07-08 21:04:13 +00:00
|
|
|
node = dict.Get("Parent")
|
2016-12-13 19:40:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-08 16:54:40 +00:00
|
|
|
// No resources defined...
|
|
|
|
return nil, nil
|
2016-12-13 19:40:33 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 23:16:25 +00:00
|
|
|
// Convert the Page to a PDF object dictionary.
|
2016-09-11 23:17:38 +00:00
|
|
|
func (this *PdfPage) GetPageDict() *PdfObjectDictionary {
|
2016-08-15 01:31:35 +00:00
|
|
|
p := this.pageDict
|
2018-08-07 20:51:15 +00:00
|
|
|
p.Clear()
|
2017-07-08 21:04:13 +00:00
|
|
|
p.Set("Type", MakeName("Page"))
|
|
|
|
p.Set("Parent", this.Parent)
|
2016-07-19 23:16:25 +00:00
|
|
|
|
|
|
|
if this.LastModified != nil {
|
|
|
|
p.Set("LastModified", this.LastModified.ToPdfObject())
|
|
|
|
}
|
2016-07-25 14:06:37 +00:00
|
|
|
if this.Resources != nil {
|
|
|
|
p.Set("Resources", this.Resources.ToPdfObject())
|
|
|
|
}
|
2016-07-19 23:16:25 +00:00
|
|
|
if this.CropBox != nil {
|
|
|
|
p.Set("CropBox", this.CropBox.ToPdfObject())
|
|
|
|
}
|
|
|
|
if this.MediaBox != nil {
|
|
|
|
p.Set("MediaBox", this.MediaBox.ToPdfObject())
|
|
|
|
}
|
|
|
|
if this.BleedBox != nil {
|
|
|
|
p.Set("BleedBox", this.BleedBox.ToPdfObject())
|
|
|
|
}
|
|
|
|
if this.TrimBox != nil {
|
|
|
|
p.Set("TrimBox", this.TrimBox.ToPdfObject())
|
|
|
|
}
|
|
|
|
if this.ArtBox != nil {
|
|
|
|
p.Set("ArtBox", this.ArtBox.ToPdfObject())
|
|
|
|
}
|
|
|
|
p.SetIfNotNil("BoxColorInfo", this.BoxColorInfo)
|
|
|
|
p.SetIfNotNil("Contents", this.Contents)
|
|
|
|
|
|
|
|
if this.Rotate != nil {
|
2016-07-25 14:06:37 +00:00
|
|
|
p.Set("Rotate", MakeInteger(*this.Rotate))
|
2016-07-19 23:16:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
p.SetIfNotNil("Group", this.Group)
|
|
|
|
p.SetIfNotNil("Thumb", this.Thumb)
|
|
|
|
p.SetIfNotNil("B", this.B)
|
|
|
|
p.SetIfNotNil("Dur", this.Dur)
|
|
|
|
p.SetIfNotNil("Trans", this.Trans)
|
|
|
|
p.SetIfNotNil("AA", this.AA)
|
|
|
|
p.SetIfNotNil("Metadata", this.Metadata)
|
|
|
|
p.SetIfNotNil("PieceInfo", this.PieceInfo)
|
|
|
|
p.SetIfNotNil("StructParents", this.StructParents)
|
|
|
|
p.SetIfNotNil("ID", this.ID)
|
|
|
|
p.SetIfNotNil("PZ", this.PZ)
|
|
|
|
p.SetIfNotNil("SeparationInfo", this.SeparationInfo)
|
|
|
|
p.SetIfNotNil("Tabs", this.Tabs)
|
|
|
|
p.SetIfNotNil("TemplateInstantiated", this.TemplateInstantiated)
|
|
|
|
p.SetIfNotNil("PresSteps", this.PresSteps)
|
|
|
|
p.SetIfNotNil("UserUnit", this.UserUnit)
|
|
|
|
p.SetIfNotNil("VP", this.VP)
|
|
|
|
|
2016-09-07 17:56:45 +00:00
|
|
|
if this.Annotations != nil {
|
2018-07-15 17:52:53 +00:00
|
|
|
arr := MakeArray()
|
2016-09-13 09:51:57 +00:00
|
|
|
for _, annot := range this.Annotations {
|
2017-04-24 18:18:46 +00:00
|
|
|
if subannot := annot.GetContext(); subannot != nil {
|
2018-07-15 17:52:53 +00:00
|
|
|
arr.Append(subannot.ToPdfObject())
|
2017-04-24 18:18:46 +00:00
|
|
|
} else {
|
|
|
|
// Generic annotation dict (without subtype).
|
2018-07-15 17:52:53 +00:00
|
|
|
arr.Append(annot.ToPdfObject())
|
2017-04-24 18:18:46 +00:00
|
|
|
}
|
2016-09-07 17:56:45 +00:00
|
|
|
}
|
2018-07-15 17:52:53 +00:00
|
|
|
p.Set("Annots", arr)
|
2016-09-07 17:56:45 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 23:16:25 +00:00
|
|
|
return p
|
|
|
|
}
|
2016-07-25 14:06:37 +00:00
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// GetPageAsIndirectObject returns the page as a dictionary within an PdfIndirectObject.
|
2016-09-11 23:17:38 +00:00
|
|
|
func (this *PdfPage) GetPageAsIndirectObject() *PdfIndirectObject {
|
|
|
|
return this.primitive
|
2016-07-25 14:06:37 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// GetContainingPdfObject returns the page as a dictionary within an PdfIndirectObject.
|
2016-09-11 23:17:38 +00:00
|
|
|
func (this *PdfPage) GetContainingPdfObject() PdfObject {
|
|
|
|
return this.primitive
|
2016-09-09 15:02:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// ToPdfObject converts the PdfPage to a dictionary within an indirect object container.
|
2016-09-11 23:17:38 +00:00
|
|
|
func (this *PdfPage) ToPdfObject() PdfObject {
|
|
|
|
container := this.primitive
|
|
|
|
this.GetPageDict() // update.
|
|
|
|
return container
|
2016-09-09 15:02:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// AddImageResource adds an image to the XObject resources.
|
2016-07-25 14:06:37 +00:00
|
|
|
func (this *PdfPage) AddImageResource(name PdfObjectName, ximg *XObjectImage) error {
|
|
|
|
var xresDict *PdfObjectDictionary
|
|
|
|
if this.Resources.XObject == nil {
|
2017-07-08 21:04:13 +00:00
|
|
|
xresDict = MakeDict()
|
2016-07-25 14:06:37 +00:00
|
|
|
this.Resources.XObject = xresDict
|
|
|
|
} else {
|
|
|
|
var ok bool
|
|
|
|
xresDict, ok = (this.Resources.XObject).(*PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
return errors.New("Invalid xres dict type")
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
// Make a stream object container.
|
2017-07-08 21:04:13 +00:00
|
|
|
xresDict.Set(name, ximg.ToPdfObject())
|
2016-07-25 14:06:37 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-06-28 15:15:44 +00:00
|
|
|
// Check if has XObject resource by name.
|
|
|
|
func (this *PdfPage) HasXObjectByName(name PdfObjectName) bool {
|
2017-07-06 16:26:22 +00:00
|
|
|
xresDict, has := this.Resources.XObject.(*PdfObjectDictionary)
|
2017-01-03 10:51:39 +00:00
|
|
|
if !has {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := xresDict.Get(name); obj != nil {
|
2017-01-03 10:51:39 +00:00
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
2017-06-28 15:15:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get XObject by name.
|
|
|
|
func (this *PdfPage) GetXObjectByName(name PdfObjectName) (PdfObject, bool) {
|
2017-07-06 16:26:22 +00:00
|
|
|
xresDict, has := this.Resources.XObject.(*PdfObjectDictionary)
|
2017-06-28 15:15:44 +00:00
|
|
|
if !has {
|
|
|
|
return nil, false
|
|
|
|
}
|
2017-05-24 21:43:32 +00:00
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := xresDict.Get(name); obj != nil {
|
2017-06-28 15:15:44 +00:00
|
|
|
return obj, true
|
|
|
|
} else {
|
|
|
|
return nil, false
|
|
|
|
}
|
2017-05-24 21:43:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if has font resource by name.
|
|
|
|
func (this *PdfPage) HasFontByName(name PdfObjectName) bool {
|
2017-07-06 16:26:22 +00:00
|
|
|
fontDict, has := this.Resources.Font.(*PdfObjectDictionary)
|
2017-05-24 21:43:32 +00:00
|
|
|
if !has {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
if obj := fontDict.Get(name); obj != nil {
|
2017-05-24 21:43:32 +00:00
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
2017-01-03 10:51:39 +00:00
|
|
|
}
|
|
|
|
|
2017-05-28 14:45:02 +00:00
|
|
|
// Check if ExtGState name is available.
|
|
|
|
func (this *PdfPage) HasExtGState(name PdfObjectName) bool {
|
|
|
|
if this.Resources == nil {
|
2017-05-28 15:14:32 +00:00
|
|
|
return false
|
2017-05-28 14:45:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if this.Resources.ExtGState == nil {
|
2017-05-28 15:14:32 +00:00
|
|
|
return false
|
2017-05-28 14:45:02 +00:00
|
|
|
}
|
|
|
|
|
2017-06-13 11:34:53 +00:00
|
|
|
egsDict, ok := TraceToDirectObject(this.Resources.ExtGState).(*PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
common.Log.Debug("Expected ExtGState dictionary is not a dictionary: %v", TraceToDirectObject(this.Resources.ExtGState))
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the dictionary.
|
2017-07-08 21:04:13 +00:00
|
|
|
obj := egsDict.Get(name)
|
|
|
|
has := obj != nil
|
2017-05-28 14:45:02 +00:00
|
|
|
|
|
|
|
return has
|
|
|
|
}
|
|
|
|
|
2016-07-25 14:06:37 +00:00
|
|
|
// Add a graphics state to the XObject resources.
|
2017-06-13 11:34:53 +00:00
|
|
|
func (this *PdfPage) AddExtGState(name PdfObjectName, egs *PdfObjectDictionary) error {
|
2016-07-25 14:06:37 +00:00
|
|
|
if this.Resources == nil {
|
2016-12-13 19:40:33 +00:00
|
|
|
//this.Resources = &PdfPageResources{}
|
|
|
|
this.Resources = NewPdfPageResources()
|
2016-07-25 14:06:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if this.Resources.ExtGState == nil {
|
2017-07-08 21:04:13 +00:00
|
|
|
this.Resources.ExtGState = MakeDict()
|
2016-07-25 14:06:37 +00:00
|
|
|
}
|
|
|
|
|
2017-06-13 11:34:53 +00:00
|
|
|
egsDict, ok := TraceToDirectObject(this.Resources.ExtGState).(*PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
common.Log.Debug("Expected ExtGState dictionary is not a dictionary: %v", TraceToDirectObject(this.Resources.ExtGState))
|
|
|
|
return errors.New("Type check error")
|
|
|
|
}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
egsDict.Set(name, egs)
|
2017-06-13 11:34:53 +00:00
|
|
|
return nil
|
2016-07-25 14:06:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-30 00:27:21 +00:00
|
|
|
// Add a font dictionary to the Font resources.
|
2017-06-28 15:15:44 +00:00
|
|
|
func (this *PdfPage) AddFont(name PdfObjectName, font PdfObject) error {
|
2016-07-30 00:27:21 +00:00
|
|
|
if this.Resources == nil {
|
2016-12-13 19:40:33 +00:00
|
|
|
this.Resources = NewPdfPageResources()
|
2016-07-30 00:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if this.Resources.Font == nil {
|
2017-07-08 21:04:13 +00:00
|
|
|
this.Resources.Font = MakeDict()
|
2016-07-30 00:27:21 +00:00
|
|
|
}
|
|
|
|
|
2017-06-04 12:35:34 +00:00
|
|
|
fontDict, ok := TraceToDirectObject(this.Resources.Font).(*PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
common.Log.Debug("Expected font dictionary is not a dictionary: %v", TraceToDirectObject(this.Resources.Font))
|
|
|
|
return errors.New("Type check error")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the dictionary.
|
2017-07-08 21:04:13 +00:00
|
|
|
fontDict.Set(name, font)
|
2017-06-04 12:35:34 +00:00
|
|
|
|
|
|
|
return nil
|
2016-07-30 00:27:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type WatermarkImageOptions struct {
|
|
|
|
Alpha float64
|
2016-07-30 00:34:35 +00:00
|
|
|
FitToWidth bool
|
2016-07-30 00:27:21 +00:00
|
|
|
PreserveAspectRatio bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a watermark to the page.
|
|
|
|
func (this *PdfPage) AddWatermarkImage(ximg *XObjectImage, opt WatermarkImageOptions) error {
|
2017-06-19 16:43:17 +00:00
|
|
|
// Page dimensions.
|
2016-07-30 00:27:21 +00:00
|
|
|
bbox, err := this.GetMediaBox()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pWidth := bbox.Urx - bbox.Llx
|
|
|
|
pHeight := bbox.Ury - bbox.Lly
|
|
|
|
|
2016-07-30 00:34:35 +00:00
|
|
|
wWidth := float64(*ximg.Width)
|
|
|
|
xOffset := (float64(pWidth) - float64(wWidth)) / 2
|
|
|
|
if opt.FitToWidth {
|
|
|
|
wWidth = pWidth
|
|
|
|
xOffset = 0
|
|
|
|
}
|
2016-07-30 00:27:21 +00:00
|
|
|
wHeight := pHeight
|
|
|
|
yOffset := float64(0)
|
|
|
|
if opt.PreserveAspectRatio {
|
|
|
|
wHeight = wWidth * float64(*ximg.Height) / float64(*ximg.Width)
|
|
|
|
yOffset = (pHeight - wHeight) / 2
|
|
|
|
}
|
|
|
|
|
2017-06-28 15:15:44 +00:00
|
|
|
if this.Resources == nil {
|
|
|
|
this.Resources = NewPdfPageResources()
|
|
|
|
}
|
|
|
|
|
2017-06-19 16:43:17 +00:00
|
|
|
// Find available image name for this page.
|
|
|
|
i := 0
|
|
|
|
imgName := PdfObjectName(fmt.Sprintf("Imw%d", i))
|
2017-06-28 15:15:44 +00:00
|
|
|
for this.Resources.HasXObjectByName(imgName) {
|
2017-06-19 16:43:17 +00:00
|
|
|
i++
|
|
|
|
imgName = PdfObjectName(fmt.Sprintf("Imw%d", i))
|
|
|
|
}
|
|
|
|
|
|
|
|
err = this.AddImageResource(imgName, ximg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-07-30 00:27:21 +00:00
|
|
|
|
2017-06-19 16:43:17 +00:00
|
|
|
i = 0
|
|
|
|
gsName := PdfObjectName(fmt.Sprintf("GS%d", i))
|
|
|
|
for this.HasExtGState(gsName) {
|
|
|
|
i++
|
|
|
|
gsName = PdfObjectName(fmt.Sprintf("GS%d", i))
|
|
|
|
}
|
2017-07-08 21:04:13 +00:00
|
|
|
gs0 := MakeDict()
|
|
|
|
gs0.Set("BM", MakeName("Normal"))
|
|
|
|
gs0.Set("CA", MakeFloat(opt.Alpha))
|
|
|
|
gs0.Set("ca", MakeFloat(opt.Alpha))
|
|
|
|
err = this.AddExtGState(gsName, gs0)
|
2017-06-19 16:43:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-07-30 00:27:21 +00:00
|
|
|
|
|
|
|
contentStr := fmt.Sprintf("q\n"+
|
2017-06-19 16:43:17 +00:00
|
|
|
"/%s gs\n"+
|
2016-07-30 00:34:35 +00:00
|
|
|
"%.0f 0 0 %.0f %.4f %.4f cm\n"+
|
2016-07-30 00:27:21 +00:00
|
|
|
"/%s Do\n"+
|
2017-06-19 16:43:17 +00:00
|
|
|
"Q", gsName, wWidth, wHeight, xOffset, yOffset, imgName)
|
2016-07-30 00:27:21 +00:00
|
|
|
this.AddContentStreamByString(contentStr)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// AddContentStreamByString adds content stream by string. Puts the content string into a stream
|
2016-07-25 14:06:37 +00:00
|
|
|
// object and points the content stream towards it.
|
2018-08-08 13:13:26 +00:00
|
|
|
func (this *PdfPage) AddContentStreamByString(contentStr string) error {
|
|
|
|
stream, err := MakeStream([]byte(contentStr), NewFlateEncoder())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-07-25 14:06:37 +00:00
|
|
|
|
2016-07-30 00:27:21 +00:00
|
|
|
if this.Contents == nil {
|
|
|
|
// If not set, place it directly.
|
2018-08-08 13:13:26 +00:00
|
|
|
this.Contents = stream
|
2017-07-01 21:56:59 +00:00
|
|
|
} else if contArray, isArray := TraceToDirectObject(this.Contents).(*PdfObjectArray); isArray {
|
2016-07-30 00:27:21 +00:00
|
|
|
// If an array of content streams, append it.
|
2018-08-08 13:13:26 +00:00
|
|
|
contArray.Append(stream)
|
2016-07-30 00:27:21 +00:00
|
|
|
} else {
|
|
|
|
// Only 1 element in place. Wrap inside a new array and add the new one.
|
2018-08-08 13:13:26 +00:00
|
|
|
contArray := MakeArray(this.Contents, stream)
|
2018-07-15 17:52:53 +00:00
|
|
|
this.Contents = contArray
|
2016-07-30 00:27:21 +00:00
|
|
|
}
|
2018-08-08 13:13:26 +00:00
|
|
|
|
|
|
|
return nil
|
2017-02-13 15:30:36 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 04:27:58 +00:00
|
|
|
// AppendContentStream adds content stream by string. Appends to the last
|
|
|
|
// contentstream instance if many.
|
|
|
|
func (this *PdfPage) AppendContentStream(contentStr string) error {
|
|
|
|
cstreams, err := this.GetContentStreams()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(cstreams) == 0 {
|
|
|
|
cstreams = []string{contentStr}
|
|
|
|
return this.SetContentStreams(cstreams, NewFlateEncoder())
|
|
|
|
}
|
|
|
|
|
|
|
|
var b strings.Builder
|
|
|
|
b.WriteString(cstreams[len(cstreams)-1])
|
|
|
|
b.WriteString("\n")
|
|
|
|
b.WriteString(contentStr)
|
|
|
|
cstreams[len(cstreams)-1] = b.String()
|
|
|
|
|
|
|
|
return this.SetContentStreams(cstreams, NewFlateEncoder())
|
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// SetContentStreams sets the content streams based on a string array. Will make 1 object stream
|
2017-02-13 15:30:36 +00:00
|
|
|
// for each string and reference from the page Contents. Each stream will be
|
|
|
|
// encoded using the encoding specified by the StreamEncoder, if empty, will
|
|
|
|
// use identity encoding (raw data).
|
|
|
|
func (this *PdfPage) SetContentStreams(cStreams []string, encoder StreamEncoder) error {
|
|
|
|
if len(cStreams) == 0 {
|
|
|
|
this.Contents = nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If encoding is not set, use default raw encoder.
|
|
|
|
if encoder == nil {
|
|
|
|
encoder = NewRawEncoder()
|
|
|
|
}
|
|
|
|
|
|
|
|
streamObjs := []*PdfObjectStream{}
|
|
|
|
for _, cStream := range cStreams {
|
|
|
|
stream := &PdfObjectStream{}
|
|
|
|
|
|
|
|
// Make a new stream dict based on the encoding parameters.
|
2017-02-13 20:35:42 +00:00
|
|
|
sDict := encoder.MakeStreamDict()
|
2017-02-13 15:30:36 +00:00
|
|
|
|
|
|
|
encoded, err := encoder.EncodeBytes([]byte(cStream))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-07-30 00:27:21 +00:00
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
sDict.Set("Length", MakeInteger(int64(len(encoded))))
|
2017-02-13 15:30:36 +00:00
|
|
|
|
|
|
|
stream.PdfObjectDictionary = sDict
|
|
|
|
stream.Stream = []byte(encoded)
|
|
|
|
|
|
|
|
streamObjs = append(streamObjs, stream)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the page contents.
|
|
|
|
// Point directly to the object stream if only one, or embed in an array.
|
|
|
|
if len(streamObjs) == 1 {
|
|
|
|
this.Contents = streamObjs[0]
|
|
|
|
} else {
|
2018-07-15 17:52:53 +00:00
|
|
|
contArray := MakeArray()
|
2017-02-13 15:30:36 +00:00
|
|
|
for _, streamObj := range streamObjs {
|
2018-07-15 17:52:53 +00:00
|
|
|
contArray.Append(streamObj)
|
2017-02-13 15:30:36 +00:00
|
|
|
}
|
2018-07-15 17:52:53 +00:00
|
|
|
this.Contents = contArray
|
2017-02-13 15:30:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2016-07-25 14:06:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-22 08:46:18 +00:00
|
|
|
func getContentStreamAsString(cstreamObj PdfObject) (string, error) {
|
|
|
|
if cstream, ok := TraceToDirectObject(cstreamObj).(*PdfObjectString); ok {
|
2018-07-14 02:25:29 +00:00
|
|
|
return cstream.Str(), nil
|
2016-08-22 08:46:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if cstream, ok := TraceToDirectObject(cstreamObj).(*PdfObjectStream); ok {
|
2016-09-08 17:53:45 +00:00
|
|
|
buf, err := DecodeStream(cstream)
|
2016-08-22 08:46:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(buf), nil
|
|
|
|
}
|
2017-07-01 21:56:59 +00:00
|
|
|
return "", fmt.Errorf("Invalid content stream object holder (%T)", TraceToDirectObject(cstreamObj))
|
2016-08-22 08:46:18 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// GetContentStreams returns the content stream as an array of strings.
|
2016-08-22 08:46:18 +00:00
|
|
|
func (this *PdfPage) GetContentStreams() ([]string, error) {
|
|
|
|
if this.Contents == nil {
|
|
|
|
return nil, nil
|
2017-02-12 21:37:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
contents := TraceToDirectObject(this.Contents)
|
|
|
|
if contArray, isArray := contents.(*PdfObjectArray); isArray {
|
2016-08-22 08:46:18 +00:00
|
|
|
// If an array of content streams, append it.
|
|
|
|
cstreams := []string{}
|
2018-07-15 17:52:53 +00:00
|
|
|
for _, cstreamObj := range contArray.Elements() {
|
2016-08-22 08:46:18 +00:00
|
|
|
cstreamStr, err := getContentStreamAsString(cstreamObj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cstreams = append(cstreams, cstreamStr)
|
|
|
|
}
|
|
|
|
return cstreams, nil
|
|
|
|
} else {
|
|
|
|
// Only 1 element in place. Wrap inside a new array and add the new one.
|
2017-02-12 21:37:07 +00:00
|
|
|
cstreamStr, err := getContentStreamAsString(contents)
|
2016-08-22 08:46:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cstreams := []string{cstreamStr}
|
|
|
|
return cstreams, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-28 08:59:17 +00:00
|
|
|
// Get all the content streams for a page as one string.
|
|
|
|
func (this *PdfPage) GetAllContentStreams() (string, error) {
|
|
|
|
cstreams, err := this.GetContentStreams()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return strings.Join(cstreams, " "), nil
|
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// PdfPageResourcesColorspaces contains the colorspace in the PdfPageResources.
|
2017-04-19 12:05:20 +00:00
|
|
|
// Needs to have matching name and colorspace map entry. The Names define the order.
|
2017-02-22 21:10:57 +00:00
|
|
|
type PdfPageResourcesColorspaces struct {
|
|
|
|
Names []string
|
|
|
|
Colorspaces map[string]PdfColorspace
|
|
|
|
|
|
|
|
container *PdfIndirectObject
|
|
|
|
}
|
|
|
|
|
2017-06-28 15:15:44 +00:00
|
|
|
func NewPdfPageResourcesColorspaces() *PdfPageResourcesColorspaces {
|
|
|
|
colorspaces := &PdfPageResourcesColorspaces{}
|
|
|
|
colorspaces.Names = []string{}
|
|
|
|
colorspaces.Colorspaces = map[string]PdfColorspace{}
|
|
|
|
colorspaces.container = &PdfIndirectObject{}
|
|
|
|
return colorspaces
|
|
|
|
}
|
|
|
|
|
2018-07-15 17:52:53 +00:00
|
|
|
// Set sets the colorspace corresponding to key. Add to Names if not set.
|
2017-06-28 15:15:44 +00:00
|
|
|
func (this *PdfPageResourcesColorspaces) Set(key PdfObjectName, val PdfColorspace) {
|
|
|
|
if _, has := this.Colorspaces[string(key)]; !has {
|
|
|
|
this.Names = append(this.Names, string(key))
|
|
|
|
}
|
|
|
|
this.Colorspaces[string(key)] = val
|
|
|
|
}
|
|
|
|
|
2017-02-22 21:10:57 +00:00
|
|
|
func newPdfPageResourcesColorspacesFromPdfObject(obj PdfObject) (*PdfPageResourcesColorspaces, error) {
|
|
|
|
colorspaces := &PdfPageResourcesColorspaces{}
|
|
|
|
|
|
|
|
if indObj, isIndirect := obj.(*PdfIndirectObject); isIndirect {
|
|
|
|
colorspaces.container = indObj
|
|
|
|
obj = indObj.PdfObject
|
|
|
|
}
|
|
|
|
|
|
|
|
dict, ok := obj.(*PdfObjectDictionary)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("CS attribute type error")
|
|
|
|
}
|
|
|
|
|
|
|
|
colorspaces.Names = []string{}
|
|
|
|
colorspaces.Colorspaces = map[string]PdfColorspace{}
|
|
|
|
|
2017-07-08 21:04:13 +00:00
|
|
|
for _, csName := range dict.Keys() {
|
|
|
|
csObj := dict.Get(csName)
|
2017-02-22 21:10:57 +00:00
|
|
|
colorspaces.Names = append(colorspaces.Names, string(csName))
|
2017-08-10 07:23:42 +00:00
|
|
|
cs, err := NewPdfColorspaceFromPdfObject(csObj)
|
2017-02-22 21:10:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
colorspaces.Colorspaces[string(csName)] = cs
|
|
|
|
}
|
|
|
|
|
|
|
|
return colorspaces, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *PdfPageResourcesColorspaces) ToPdfObject() PdfObject {
|
2017-07-08 21:04:13 +00:00
|
|
|
dict := MakeDict()
|
2017-02-22 21:10:57 +00:00
|
|
|
for _, csName := range this.Names {
|
2017-07-08 21:04:13 +00:00
|
|
|
dict.Set(PdfObjectName(csName), this.Colorspaces[csName].ToPdfObject())
|
2017-02-22 21:10:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if this.container != nil {
|
|
|
|
this.container.PdfObject = dict
|
|
|
|
return this.container
|
|
|
|
}
|
|
|
|
|
|
|
|
return dict
|
|
|
|
}
|