mirror of
https://github.com/unidoc/unipdf.git
synced 2025-05-01 22:17:29 +08:00
Combine forms package into model. Move fdf to internal.
This commit is contained in:
parent
38be8ce6ee
commit
a0c0e76353
@ -1,4 +1,9 @@
|
|||||||
package forms
|
/*
|
||||||
|
* This file is subject to the terms and conditions defined in
|
||||||
|
* file 'LICENSE.md', which is part of this source code package.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -7,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/unidoc/unidoc/common"
|
"github.com/unidoc/unidoc/common"
|
||||||
"github.com/unidoc/unidoc/pdf/core"
|
"github.com/unidoc/unidoc/pdf/core"
|
||||||
"github.com/unidoc/unidoc/pdf/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FieldFlag represents form field flags. Some of the flags can apply to all types of fields whereas other
|
// FieldFlag represents form field flags. Some of the flags can apply to all types of fields whereas other
|
||||||
@ -74,13 +78,16 @@ func (flag FieldFlag) Has(fl FieldFlag) bool {
|
|||||||
// The PdfField is typically not used directly, but is encapsulated by the more specific field types such as
|
// The PdfField is typically not used directly, but is encapsulated by the more specific field types such as
|
||||||
// PdfFieldButton etc (i.e. the context attribute).
|
// PdfFieldButton etc (i.e. the context attribute).
|
||||||
type PdfField struct {
|
type PdfField struct {
|
||||||
context model.PdfModel // Field data
|
context PdfModel // Field data
|
||||||
container *core.PdfIndirectObject // Dictionary information stored inside an indirect object.
|
container *core.PdfIndirectObject // Dictionary information stored inside an indirect object.
|
||||||
isTerminal *bool // If set: indicates whether is a terminal field (if null, may not be determined yet).
|
isTerminal *bool // If set: indicates whether is a terminal field (if null, may not be determined yet).
|
||||||
|
|
||||||
FT *core.PdfObjectName
|
|
||||||
Parent *PdfField
|
Parent *PdfField
|
||||||
Kids *core.PdfObjectArray
|
Annotations []*PdfAnnotation
|
||||||
|
Kids []*PdfField
|
||||||
|
|
||||||
|
FT *core.PdfObjectName
|
||||||
|
//Kids *core.PdfObjectArray
|
||||||
T *core.PdfObjectString
|
T *core.PdfObjectString
|
||||||
TU *core.PdfObjectString
|
TU *core.PdfObjectString
|
||||||
TM *core.PdfObjectString
|
TM *core.PdfObjectString
|
||||||
@ -144,12 +151,12 @@ func (f *PdfField) PartialName() string {
|
|||||||
|
|
||||||
// GetContext returns the PdfField context which is the more specific field data type, e.g. PdfFieldButton
|
// GetContext returns the PdfField context which is the more specific field data type, e.g. PdfFieldButton
|
||||||
// for a button field.
|
// for a button field.
|
||||||
func (f *PdfField) GetContext() model.PdfModel {
|
func (f *PdfField) GetContext() PdfModel {
|
||||||
return f.context
|
return f.context
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetContext sets the specific fielddata type, e.g. would be PdfFieldButton for a button field.
|
// SetContext sets the specific fielddata type, e.g. would be PdfFieldButton for a button field.
|
||||||
func (f *PdfField) SetContext(ctx model.PdfModel) {
|
func (f *PdfField) SetContext(ctx PdfModel) {
|
||||||
f.context = ctx
|
f.context = ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +184,28 @@ func (f *PdfField) ToPdfObject() core.PdfObject {
|
|||||||
if f.Parent != nil {
|
if f.Parent != nil {
|
||||||
d.Set("Parent", f.Parent.GetContainingPdfObject())
|
d.Set("Parent", f.Parent.GetContainingPdfObject())
|
||||||
}
|
}
|
||||||
d.SetIfNotNil("Kids", f.Kids)
|
|
||||||
|
if f.Kids != nil {
|
||||||
|
// Create an array of the kids (fields or widgets).
|
||||||
|
kids := core.MakeArray()
|
||||||
|
for _, child := range f.Kids {
|
||||||
|
kids.Append(child.ToPdfObject())
|
||||||
|
}
|
||||||
|
d.Set("Kids", kids)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Annotations != nil {
|
||||||
|
_, hasKids := d.Get("Kids").(*core.PdfObjectArray)
|
||||||
|
if !hasKids {
|
||||||
|
d.Set("Kids", &core.PdfObjectArray{})
|
||||||
|
}
|
||||||
|
// TODO: If only 1 widget annotation, it can be merged in.
|
||||||
|
kids := d.Get("Kids").(*core.PdfObjectArray)
|
||||||
|
for _, annot := range f.Annotations {
|
||||||
|
kids.Append(annot.GetContext().ToPdfObject())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
d.SetIfNotNil("T", f.T)
|
d.SetIfNotNil("T", f.T)
|
||||||
d.SetIfNotNil("TU", f.TU)
|
d.SetIfNotNil("TU", f.TU)
|
||||||
d.SetIfNotNil("TM", f.TM)
|
d.SetIfNotNil("TM", f.TM)
|
||||||
@ -353,7 +381,8 @@ type PdfFieldSignature struct {
|
|||||||
SV *core.PdfIndirectObject
|
SV *core.PdfIndirectObject
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig *PdfFieldSignature) ToPdfObject() core.PdfObject {
|
// ToPdfObject returns an indirect object containing the signature field dictionary.
|
||||||
|
func (sig *PdfFieldSignature) ToPdfObject() *core.PdfIndirectObject {
|
||||||
// Set general field attributes
|
// Set general field attributes
|
||||||
sig.PdfField.ToPdfObject()
|
sig.PdfField.ToPdfObject()
|
||||||
container := sig.container
|
container := sig.container
|
||||||
@ -414,15 +443,10 @@ func (f *PdfField) inherit(eval func(*PdfField) bool) (bool, error) {
|
|||||||
// IsTerminal returns true for terminal fields, false otherwise.
|
// IsTerminal returns true for terminal fields, false otherwise.
|
||||||
// Terminal fields are fields whose descendants are only widget annotations.
|
// Terminal fields are fields whose descendants are only widget annotations.
|
||||||
func (f *PdfField) IsTerminal() bool {
|
func (f *PdfField) IsTerminal() bool {
|
||||||
terminal := true
|
if len(f.Kids) == 0 {
|
||||||
|
return true
|
||||||
if f.Kids != nil {
|
|
||||||
for _, k := range *f.Kids {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
return false
|
||||||
|
|
||||||
return terminal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags returns the field flags for the field accounting for any inherited flags.
|
// Flags returns the field flags for the field accounting for any inherited flags.
|
||||||
@ -446,8 +470,7 @@ func (f *PdfField) Flags() FieldFlag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newPdfFieldFromIndirectObject load a field from an indirect object containing the field dictionary.
|
// newPdfFieldFromIndirectObject load a field from an indirect object containing the field dictionary.
|
||||||
//func (r *model.PdfReader) newPdfFieldFromIndirectObject(r *model.PdfReader, container *core.PdfIndirectObject, parent *PdfField) (*PdfField, error) {
|
func (r *PdfReader) newPdfFieldFromIndirectObject(container *core.PdfIndirectObject, parent *PdfField) (*PdfField, error) {
|
||||||
func newPdfFieldFromIndirectObject(r *model.PdfReader, container *core.PdfIndirectObject, parent *PdfField) (*PdfField, error) {
|
|
||||||
d, isDict := container.PdfObject.(*core.PdfObjectDictionary)
|
d, isDict := container.PdfObject.(*core.PdfObjectDictionary)
|
||||||
if !isDict {
|
if !isDict {
|
||||||
return nil, fmt.Errorf("PdfField indirect object not containing a dictionary")
|
return nil, fmt.Errorf("PdfField indirect object not containing a dictionary")
|
||||||
@ -458,18 +481,18 @@ func newPdfFieldFromIndirectObject(r *model.PdfReader, container *core.PdfIndire
|
|||||||
// Field type (required in terminal fields).
|
// Field type (required in terminal fields).
|
||||||
// Can be /Btn /Tx /Ch /Sig
|
// Can be /Btn /Tx /Ch /Sig
|
||||||
// Required for a terminal field (inheritable).
|
// Required for a terminal field (inheritable).
|
||||||
var err error
|
if obj := d.GetDirect("FT"); obj != nil {
|
||||||
if obj := d.Get("FT"); obj != nil {
|
|
||||||
obj, err = r.traceToObject(obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
name, ok := obj.(*core.PdfObjectName)
|
name, ok := obj.(*core.PdfObjectName)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Invalid type of FT field (%T)", obj)
|
return nil, fmt.Errorf("Invalid type of FT field (%T)", obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
field.FT = name
|
field.FT = name
|
||||||
|
isTerminal := true
|
||||||
|
field.isTerminal = &isTerminal
|
||||||
|
} else {
|
||||||
|
isTerminal := false
|
||||||
|
field.isTerminal = &isTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-terminal field: One whose descendants are fields. Not an actual field, just a container for inheritable
|
// Non-terminal field: One whose descendants are fields. Not an actual field, just a container for inheritable
|
||||||
@ -481,29 +504,24 @@ func newPdfFieldFromIndirectObject(r *model.PdfReader, container *core.PdfIndire
|
|||||||
field.T = s
|
field.T = s
|
||||||
} else {
|
} else {
|
||||||
common.Log.Debug("Invalid - T field missing (required)")
|
common.Log.Debug("Invalid - T field missing (required)")
|
||||||
return nil, model.ErrTypeCheck
|
return nil, ErrTypeCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alternate description (Optional)
|
// Alternate description (Optional)
|
||||||
if s, has := d.Get("TU").(*core.PdfObjectString); has {
|
field.TU, _ = d.Get("TU").(*core.PdfObjectString)
|
||||||
field.TU = s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapping name (Optional)
|
// Mapping name (Optional)
|
||||||
if s, has := d.Get("TM").(*core.PdfObjectString); has {
|
field.TM, _ = d.Get("TM").(*core.PdfObjectString)
|
||||||
field.TM = s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field flag. (Optional; inheritable)
|
// Field flag. (Optional; inheritable)
|
||||||
if i, has := d.Get("Ff").(*core.PdfObjectInteger); has {
|
field.Ff, _ = d.Get("Ff").(*core.PdfObjectInteger)
|
||||||
field.Ff = i
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value (Optional; inheritable) - Various types depending on the field type.
|
// Value (Optional; inheritable) - Various types depending on the field type.
|
||||||
field.V = d.Get("V")
|
field.V = d.Get("V")
|
||||||
|
|
||||||
// Default value for reset (Optional; inheritable)
|
// Default value for reset (Optional; inheritable)
|
||||||
field.DV = d.Get("DV")
|
field.DV = d.Get("DV")
|
||||||
|
|
||||||
// Additional actions dictionary (Optional)
|
// Additional actions dictionary (Optional)
|
||||||
field.AA = d.Get("AA")
|
field.AA = d.Get("AA")
|
||||||
|
|
||||||
@ -511,18 +529,33 @@ func newPdfFieldFromIndirectObject(r *model.PdfReader, container *core.PdfIndire
|
|||||||
if field.FT != nil {
|
if field.FT != nil {
|
||||||
switch *field.FT {
|
switch *field.FT {
|
||||||
case "Tx":
|
case "Tx":
|
||||||
|
ctx, err := newPdfFieldTextFromDict(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.PdfField = field
|
||||||
|
field.context = ctx
|
||||||
case "Ch":
|
case "Ch":
|
||||||
|
ctx, err := newPdfFieldChoiceFromDict(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.PdfField = field
|
||||||
|
field.context = ctx
|
||||||
|
case "Btn":
|
||||||
|
ctx, err := newPdfFieldButtonFromDict(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.PdfField = field
|
||||||
|
field.context = ctx
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unsupported field type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type specific:
|
// Type specific:
|
||||||
|
|
||||||
// Variable text:
|
|
||||||
field.DA = d.Get("DA")
|
|
||||||
field.Q = d.Get("Q")
|
|
||||||
field.DS = d.Get("DS")
|
|
||||||
field.RV = d.Get("RV")
|
|
||||||
|
|
||||||
// In a non-terminal field, the Kids array shall refer to field dictionaries that are immediate descendants of this field.
|
// In a non-terminal field, the Kids array shall refer to field dictionaries that are immediate descendants of this field.
|
||||||
// In a terminal field, the Kids array ordinarily shall refer to one or more separate widget annotations that are associated
|
// In a terminal field, the Kids array ordinarily shall refer to one or more separate widget annotations that are associated
|
||||||
// with this field. However, if there is only one associated widget annotation, and its contents have been merged into the field
|
// with this field. However, if there is only one associated widget annotation, and its contents have been merged into the field
|
||||||
@ -533,51 +566,36 @@ func newPdfFieldFromIndirectObject(r *model.PdfReader, container *core.PdfIndire
|
|||||||
field.Parent = parent
|
field.Parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
field.Annotations = []*PdfAnnotation{}
|
||||||
|
|
||||||
// Has a merged-in widget annotation?
|
// Has a merged-in widget annotation?
|
||||||
if obj := d.Get("Subtype"); obj != nil {
|
if name := d.GetDirect("Subtype").(*core.PdfObjectName); name != nil {
|
||||||
obj, err = r.traceToObject(obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
common.Log.Trace("Merged in annotation (%T)", obj)
|
|
||||||
name, ok := obj.(*core.PdfObjectName)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Invalid type of Subtype (%T)", obj)
|
|
||||||
}
|
|
||||||
if *name == "Widget" {
|
if *name == "Widget" {
|
||||||
// Is a merged field / widget dict.
|
// Is a merged field / widget dict.
|
||||||
|
|
||||||
// Check if the annotation has already been loaded?
|
// Note that r.newPdfAnnotationFromIndirectObject acts as a caching mechanism if the annotation
|
||||||
// Most likely referenced to by a page... Could be in either direction.
|
// has been loaded elsewhere already.
|
||||||
// r.newPdfAnnotationFromIndirectObject acts as a caching mechanism.
|
|
||||||
annot, err := r.newPdfAnnotationFromIndirectObject(container)
|
annot, err := r.newPdfAnnotationFromIndirectObject(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
widget, ok := annot.GetContext().(*PdfAnnotationWidget)
|
widget, ok := annot.GetContext().(*PdfAnnotationWidget)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Invalid widget")
|
return nil, fmt.Errorf("Invalid widget annotation")
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.Parent = field.GetContainingPdfObject()
|
widget.Parent = field.GetContainingPdfObject()
|
||||||
field.KidsA = append(field.KidsA, annot)
|
|
||||||
|
field.Annotations = append(field.Annotations, annot)
|
||||||
|
|
||||||
return field, nil
|
return field, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has kids (can be field and/or widget annotations).
|
// Kids can be field and/or widget annotations.
|
||||||
if obj := d.Get("Kids"); obj != nil {
|
if kids, has := d.GetDirect("Kids").(*core.PdfObjectArray); has {
|
||||||
obj, err := r.traceToObject(obj)
|
field.Kids = []*PdfField{}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fieldArray, ok := core.TraceToDirectObject(obj).(*core.PdfObjectArray)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Kids not an array (%T)", obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
field.KidsF = []PdfModel{}
|
for _, obj := range *kids {
|
||||||
for _, obj := range *fieldArray {
|
|
||||||
obj, err := r.traceToObject(obj)
|
obj, err := r.traceToObject(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -588,14 +606,60 @@ func newPdfFieldFromIndirectObject(r *model.PdfReader, container *core.PdfIndire
|
|||||||
return nil, fmt.Errorf("Not an indirect object (form field)")
|
return nil, fmt.Errorf("Not an indirect object (form field)")
|
||||||
}
|
}
|
||||||
|
|
||||||
childField, err := r.newPdfFieldFromIndirectObject(container, field)
|
dict, ok := container.Direct().(*core.PdfObjectDictionary)
|
||||||
if err != nil {
|
if !ok {
|
||||||
return nil, err
|
return nil, ErrTypeCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
field.KidsF = append(field.KidsF, childField)
|
// Widget annotations contain key Subtype with value equal to /Widget. Otherwise are assumed to be fields.
|
||||||
|
if name, has := dict.GetDirect("Subtype").(*core.PdfObjectName); has && *name == "Widget" {
|
||||||
|
widg, err := r.newPdfAnnotationFromIndirectObject(container)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Debug("Error loading widget annotation for field: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
field.Annotations = append(field.Annotations, widg)
|
||||||
|
} else {
|
||||||
|
childf, err := r.newPdfFieldFromIndirectObject(container, field)
|
||||||
|
if err != nil {
|
||||||
|
common.Log.Debug("Error loading child field: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
field.Kids = append(field.Kids, childf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return field, nil
|
return field, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newPdfFieldTextFromDict returns a new PdfFieldText (representing a variable text field) loaded from a dictionary.
|
||||||
|
// This function loads only text-field specific fields (called by a more generic field loader).
|
||||||
|
func newPdfFieldTextFromDict(d *core.PdfObjectDictionary) (*PdfFieldText, error) {
|
||||||
|
textf := &PdfFieldText{}
|
||||||
|
textf.DA, _ = d.GetDirect("DA").(*core.PdfObjectString)
|
||||||
|
textf.Q, _ = d.GetDirect("Q").(*core.PdfObjectInteger)
|
||||||
|
textf.DS, _ = d.GetDirect("DS").(*core.PdfObjectString)
|
||||||
|
textf.RV = d.Get("RV")
|
||||||
|
// TODO: MaxLen should be loaded for other fields too?
|
||||||
|
textf.MaxLen = d.Get("MaxLen").(*core.PdfObjectInteger)
|
||||||
|
return textf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPdfFieldChoiceFromDict returns a new PdfFieldChoice (representing a choice field) loaded from a dictionary.
|
||||||
|
// This function loads only choice-field specific fields (called by a more generic field loader).
|
||||||
|
func newPdfFieldChoiceFromDict(d *core.PdfObjectDictionary) (*PdfFieldChoice, error) {
|
||||||
|
choicef := &PdfFieldChoice{}
|
||||||
|
choicef.Opt, _ = d.GetDirect("Opt").(*core.PdfObjectArray)
|
||||||
|
choicef.TI, _ = d.GetDirect("TI").(*core.PdfObjectInteger)
|
||||||
|
choicef.I, _ = d.GetDirect("I").(*core.PdfObjectArray)
|
||||||
|
return choicef, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPdfFieldButtonFromDict returns a new PdfFieldButton (representing a button field) loaded from a dictionary.
|
||||||
|
// This function loads only button-field specific fields (called by a more generic field loader).
|
||||||
|
func newPdfFieldButtonFromDict(d *core.PdfObjectDictionary) (*PdfFieldButton, error) {
|
||||||
|
buttonf := &PdfFieldButton{}
|
||||||
|
buttonf.Opt, _ = d.GetDirect("Opt").(*core.PdfObjectArray)
|
||||||
|
return buttonf, nil
|
||||||
|
}
|
@ -1,245 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is subject to the terms and conditions defined in
|
|
||||||
* file 'LICENSE.md', which is part of this source code package.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/unidoc/unidoc/common"
|
|
||||||
"github.com/unidoc/unidoc/pdf/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PdfField represents a field of an interactive form.
|
|
||||||
// Implements PdfModel interface.
|
|
||||||
type PdfField struct {
|
|
||||||
FT *core.PdfObjectName // field type
|
|
||||||
Parent *PdfField
|
|
||||||
// In a non-terminal field, the Kids array shall refer to field dictionaries that are immediate descendants of this field.
|
|
||||||
// In a terminal field, the Kids array ordinarily shall refer to one or more separate widget annotations that are associated
|
|
||||||
// with this field. However, if there is only one associated widget annotation, and its contents have been merged into the field
|
|
||||||
// dictionary, Kids shall be omitted.
|
|
||||||
KidsF []PdfModel // Kids can be array of other fields or widgets (PdfModel).
|
|
||||||
KidsA []*PdfAnnotation
|
|
||||||
T core.PdfObject
|
|
||||||
TU core.PdfObject
|
|
||||||
TM core.PdfObject
|
|
||||||
Ff core.PdfObject // field flag
|
|
||||||
V core.PdfObject //value
|
|
||||||
DV core.PdfObject
|
|
||||||
AA core.PdfObject
|
|
||||||
|
|
||||||
// Variable Text:
|
|
||||||
DA core.PdfObject
|
|
||||||
Q core.PdfObject
|
|
||||||
DS core.PdfObject
|
|
||||||
RV core.PdfObject
|
|
||||||
|
|
||||||
primitive *core.PdfIndirectObject
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPdfField() *PdfField {
|
|
||||||
field := &PdfField{}
|
|
||||||
container := core.MakeIndirectObject(core.MakeDict())
|
|
||||||
field.primitive = container
|
|
||||||
return field
|
|
||||||
}
|
|
||||||
|
|
||||||
// newPdfFieldFromIndirectObject is used to load fields from PDF files.
|
|
||||||
func (r *PdfReader) newPdfFieldFromIndirectObject(container *core.PdfIndirectObject, parent *PdfField) (*PdfField, error) {
|
|
||||||
d, isDict := container.PdfObject.(*core.PdfObjectDictionary)
|
|
||||||
if !isDict {
|
|
||||||
return nil, fmt.Errorf("Pdf Field indirect object not containing a dictionary")
|
|
||||||
}
|
|
||||||
|
|
||||||
field := NewPdfField()
|
|
||||||
|
|
||||||
// Field type (required in terminal fields).
|
|
||||||
// Can be /Btn /Tx /Ch /Sig
|
|
||||||
// Required for a terminal field (inheritable).
|
|
||||||
var err error
|
|
||||||
if obj := d.Get("FT"); obj != nil {
|
|
||||||
obj, err = r.traceToObject(obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
name, ok := obj.(*core.PdfObjectName)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Invalid type of FT field (%T)", obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
field.FT = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Partial field name (Optional)
|
|
||||||
field.T = d.Get("T")
|
|
||||||
// Alternate description (Optional)
|
|
||||||
field.TU = d.Get("TU")
|
|
||||||
// Mapping name (Optional)
|
|
||||||
field.TM = d.Get("TM")
|
|
||||||
// Field flag. (Optional; inheritable)
|
|
||||||
field.Ff = d.Get("Ff")
|
|
||||||
// Value (Optional; inheritable) - Various types depending on the field type.
|
|
||||||
field.V = d.Get("V")
|
|
||||||
// Default value for reset (Optional; inheritable)
|
|
||||||
field.DV = d.Get("DV")
|
|
||||||
// Additional actions dictionary (Optional)
|
|
||||||
field.AA = d.Get("AA")
|
|
||||||
|
|
||||||
// Variable text:
|
|
||||||
field.DA = d.Get("DA")
|
|
||||||
field.Q = d.Get("Q")
|
|
||||||
field.DS = d.Get("DS")
|
|
||||||
field.RV = d.Get("RV")
|
|
||||||
|
|
||||||
// In a non-terminal field, the Kids array shall refer to field dictionaries that are immediate descendants of this field.
|
|
||||||
// In a terminal field, the Kids array ordinarily shall refer to one or more separate widget annotations that are associated
|
|
||||||
// with this field. However, if there is only one associated widget annotation, and its contents have been merged into the field
|
|
||||||
// dictionary, Kids shall be omitted.
|
|
||||||
|
|
||||||
// Set ourself?
|
|
||||||
if parent != nil {
|
|
||||||
field.Parent = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has a merged-in widget annotation?
|
|
||||||
if obj := d.Get("Subtype"); obj != nil {
|
|
||||||
obj, err = r.traceToObject(obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
common.Log.Trace("Merged in annotation (%T)", obj)
|
|
||||||
name, ok := obj.(*core.PdfObjectName)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Invalid type of Subtype (%T)", obj)
|
|
||||||
}
|
|
||||||
if *name == "Widget" {
|
|
||||||
// Is a merged field / widget dict.
|
|
||||||
|
|
||||||
// Check if the annotation has already been loaded?
|
|
||||||
// Most likely referenced to by a page... Could be in either direction.
|
|
||||||
// r.newPdfAnnotationFromIndirectObject acts as a caching mechanism.
|
|
||||||
annot, err := r.newPdfAnnotationFromIndirectObject(container)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
widget, ok := annot.GetContext().(*PdfAnnotationWidget)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Invalid widget")
|
|
||||||
}
|
|
||||||
|
|
||||||
widget.Parent = field.GetContainingPdfObject()
|
|
||||||
field.KidsA = append(field.KidsA, annot)
|
|
||||||
return field, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has kids (can be field and/or widget annotations).
|
|
||||||
if obj := d.Get("Kids"); obj != nil {
|
|
||||||
obj, err := r.traceToObject(obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fieldArray, ok := core.TraceToDirectObject(obj).(*core.PdfObjectArray)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Kids not an array (%T)", obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
field.KidsF = []PdfModel{}
|
|
||||||
for _, obj := range *fieldArray {
|
|
||||||
obj, err := r.traceToObject(obj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
container, isIndirect := obj.(*core.PdfIndirectObject)
|
|
||||||
if !isIndirect {
|
|
||||||
return nil, fmt.Errorf("Not an indirect object (form field)")
|
|
||||||
}
|
|
||||||
|
|
||||||
childField, err := r.newPdfFieldFromIndirectObject(container, field)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
field.KidsF = append(field.KidsF, childField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return field, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContainingPdfObject returns the containing object for the PdfField, i.e. an indirect object
|
|
||||||
// containing the field dictionary.
|
|
||||||
func (this *PdfField) GetContainingPdfObject() core.PdfObject {
|
|
||||||
return this.primitive
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToPdfObject returns the Field as a field dictionary within an indirect object as container.
|
|
||||||
// If Kids refer only to a single pdf widget annotation widget, then can merge it in.
|
|
||||||
// Currently not merging it in.
|
|
||||||
func (this *PdfField) ToPdfObject() core.PdfObject {
|
|
||||||
container := this.primitive
|
|
||||||
dict := container.PdfObject.(*core.PdfObjectDictionary)
|
|
||||||
|
|
||||||
if this.Parent != nil {
|
|
||||||
dict.Set("Parent", this.Parent.GetContainingPdfObject())
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.KidsF != nil {
|
|
||||||
// Create an array of the kids (fields or widgets).
|
|
||||||
common.Log.Trace("KidsF: %+v", this.KidsF)
|
|
||||||
arr := core.PdfObjectArray{}
|
|
||||||
for _, child := range this.KidsF {
|
|
||||||
arr = append(arr, child.ToPdfObject())
|
|
||||||
}
|
|
||||||
dict.Set("Kids", &arr)
|
|
||||||
}
|
|
||||||
if this.KidsA != nil {
|
|
||||||
common.Log.Trace("KidsA: %+v", this.KidsA)
|
|
||||||
_, hasKids := dict.Get("Kids").(*core.PdfObjectArray)
|
|
||||||
if !hasKids {
|
|
||||||
dict.Set("Kids", &core.PdfObjectArray{})
|
|
||||||
}
|
|
||||||
arr := dict.Get("Kids").(*core.PdfObjectArray)
|
|
||||||
for _, child := range this.KidsA {
|
|
||||||
*arr = append(*arr, child.GetContext().ToPdfObject())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.FT != nil {
|
|
||||||
dict.Set("FT", this.FT)
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.T != nil {
|
|
||||||
dict.Set("T", this.T)
|
|
||||||
}
|
|
||||||
if this.TU != nil {
|
|
||||||
dict.Set("TU", this.TU)
|
|
||||||
}
|
|
||||||
if this.TM != nil {
|
|
||||||
dict.Set("TM", this.TM)
|
|
||||||
}
|
|
||||||
if this.Ff != nil {
|
|
||||||
dict.Set("Ff", this.Ff)
|
|
||||||
}
|
|
||||||
if this.V != nil {
|
|
||||||
dict.Set("V", this.V)
|
|
||||||
}
|
|
||||||
if this.DV != nil {
|
|
||||||
dict.Set("DV", this.DV)
|
|
||||||
}
|
|
||||||
if this.AA != nil {
|
|
||||||
dict.Set("AA", this.AA)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable text:
|
|
||||||
dict.SetIfNotNil("DA", this.DA)
|
|
||||||
dict.SetIfNotNil("Q", this.Q)
|
|
||||||
dict.SetIfNotNil("DS", this.DS)
|
|
||||||
dict.SetIfNotNil("RV", this.RV)
|
|
||||||
|
|
||||||
return container
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user