mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-26 13:48:55 +08:00
288 lines
8.1 KiB
Go
288 lines
8.1 KiB
Go
/*
|
|
* This file is subject to the terms and conditions defined in
|
|
* file 'LICENSE.md', which is part of this source code package.
|
|
*/
|
|
|
|
package annotator
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
|
|
"github.com/unidoc/unipdf/v3/contentstream"
|
|
"github.com/unidoc/unipdf/v3/core"
|
|
"github.com/unidoc/unipdf/v3/model"
|
|
)
|
|
|
|
// TextFieldOptions defines optional parameter for a text field in a form.
|
|
type TextFieldOptions struct {
|
|
MaxLen int // Ignored if <= 0.
|
|
Value string // Ignored if empty ("").
|
|
}
|
|
|
|
// NewTextField generates a new text field with partial name `name` at location
|
|
// specified by `rect` on given `page` and with field specific options `opt`.
|
|
func NewTextField(page *model.PdfPage, name string, rect []float64, opt TextFieldOptions) (*model.PdfFieldText, error) {
|
|
if page == nil {
|
|
return nil, errors.New("page not specified")
|
|
}
|
|
if len(name) <= 0 {
|
|
return nil, errors.New("required attribute not specified")
|
|
}
|
|
if len(rect) != 4 {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
|
|
field := model.NewPdfField()
|
|
textfield := &model.PdfFieldText{}
|
|
field.SetContext(textfield)
|
|
textfield.PdfField = field
|
|
|
|
textfield.T = core.MakeString(name)
|
|
|
|
if opt.MaxLen > 0 {
|
|
textfield.MaxLen = core.MakeInteger(int64(opt.MaxLen))
|
|
}
|
|
if len(opt.Value) > 0 {
|
|
textfield.V = core.MakeString(opt.Value)
|
|
}
|
|
|
|
widget := model.NewPdfAnnotationWidget()
|
|
widget.Rect = core.MakeArrayFromFloats(rect) //[]float64{144.0, 595.89, 294.0, 617.9})
|
|
widget.P = page.ToPdfObject()
|
|
widget.F = core.MakeInteger(4) // 4 (100 -> Print/show annotations).
|
|
widget.Parent = textfield.ToPdfObject()
|
|
|
|
textfield.Annotations = append(textfield.Annotations, widget)
|
|
|
|
return textfield, nil
|
|
}
|
|
|
|
// CheckboxFieldOptions defines optional parameters for a checkbox field a form.
|
|
type CheckboxFieldOptions struct {
|
|
Checked bool
|
|
}
|
|
|
|
// NewCheckboxField generates a new checkbox field with partial name `name` at location `rect`
|
|
// on specified `page` and with field specific options `opt`.
|
|
func NewCheckboxField(page *model.PdfPage, name string, rect []float64, opt CheckboxFieldOptions) (*model.PdfFieldButton, error) {
|
|
if page == nil {
|
|
return nil, errors.New("page not specified")
|
|
}
|
|
if len(name) <= 0 {
|
|
return nil, errors.New("required attribute not specified")
|
|
}
|
|
if len(rect) != 4 {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
|
|
zapfdb, err := model.NewStandard14Font(model.ZapfDingbatsName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
field := model.NewPdfField()
|
|
buttonfield := &model.PdfFieldButton{}
|
|
field.SetContext(buttonfield)
|
|
buttonfield.PdfField = field
|
|
|
|
buttonfield.T = core.MakeString(name)
|
|
buttonfield.SetType(model.ButtonTypeCheckbox)
|
|
|
|
state := "Off"
|
|
if opt.Checked {
|
|
state = "Yes"
|
|
}
|
|
|
|
buttonfield.V = core.MakeName(state)
|
|
|
|
widget := model.NewPdfAnnotationWidget()
|
|
widget.Rect = core.MakeArrayFromFloats(rect)
|
|
widget.P = page.ToPdfObject()
|
|
widget.F = core.MakeInteger(4)
|
|
widget.Parent = buttonfield.ToPdfObject()
|
|
|
|
w := rect[2] - rect[0]
|
|
h := rect[3] - rect[1]
|
|
|
|
// Off state.
|
|
var cs bytes.Buffer
|
|
cs.WriteString("q\n")
|
|
cs.WriteString("0 0 1 rg\n")
|
|
cs.WriteString("BT\n")
|
|
cs.WriteString("/ZaDb 12 Tf\n")
|
|
cs.WriteString("ET\n")
|
|
cs.WriteString("Q\n")
|
|
|
|
cc := contentstream.NewContentCreator()
|
|
cc.Add_q()
|
|
cc.Add_rg(0, 0, 1)
|
|
cc.Add_BT()
|
|
cc.Add_Tf(*core.MakeName("ZaDb"), 12)
|
|
cc.Add_Td(0, 0)
|
|
cc.Add_ET()
|
|
cc.Add_Q()
|
|
|
|
xformOff := model.NewXObjectForm()
|
|
xformOff.SetContentStream(cc.Bytes(), core.NewRawEncoder())
|
|
xformOff.BBox = core.MakeArrayFromFloats([]float64{0, 0, w, h})
|
|
xformOff.Resources = model.NewPdfPageResources()
|
|
xformOff.Resources.SetFontByName("ZaDb", zapfdb.ToPdfObject())
|
|
|
|
// On state (Yes).
|
|
cc = contentstream.NewContentCreator()
|
|
cc.Add_q()
|
|
cc.Add_re(0, 0, w, h)
|
|
cc.Add_W().Add_n()
|
|
cc.Add_rg(0, 0, 1)
|
|
cc.Translate(0, 3.0)
|
|
cc.Add_BT()
|
|
cc.Add_Tf(*core.MakeName("ZaDb"), 12)
|
|
cc.Add_Td(0, 0)
|
|
cc.Add_Tj(*core.MakeString("\064"))
|
|
cc.Add_ET()
|
|
cc.Add_Q()
|
|
|
|
xformOn := model.NewXObjectForm()
|
|
xformOn.SetContentStream(cc.Bytes(), core.NewRawEncoder())
|
|
xformOn.BBox = core.MakeArrayFromFloats([]float64{0, 0, w, h})
|
|
xformOn.Resources = model.NewPdfPageResources()
|
|
xformOn.Resources.SetFontByName("ZaDb", zapfdb.ToPdfObject())
|
|
|
|
dchoiceapp := core.MakeDict()
|
|
dchoiceapp.Set("Off", xformOff.ToPdfObject())
|
|
dchoiceapp.Set("Yes", xformOn.ToPdfObject())
|
|
|
|
appearance := core.MakeDict()
|
|
appearance.Set("N", dchoiceapp)
|
|
|
|
widget.AP = appearance
|
|
widget.AS = core.MakeName(state)
|
|
|
|
buttonfield.Annotations = append(buttonfield.Annotations, widget)
|
|
|
|
return buttonfield, nil
|
|
}
|
|
|
|
// ComboboxFieldOptions defines optional parameters for a combobox form field.
|
|
type ComboboxFieldOptions struct {
|
|
// Choices is the list of string values that can be selected.
|
|
Choices []string
|
|
}
|
|
|
|
// NewComboboxField generates a new combobox form field with partial name `name` at location `rect`
|
|
// on specified `page` and with field specific options `opt`.
|
|
func NewComboboxField(page *model.PdfPage, name string, rect []float64, opt ComboboxFieldOptions) (*model.PdfFieldChoice, error) {
|
|
if page == nil {
|
|
return nil, errors.New("page not specified")
|
|
}
|
|
if len(name) <= 0 {
|
|
return nil, errors.New("required attribute not specified")
|
|
}
|
|
if len(rect) != 4 {
|
|
return nil, errors.New("invalid range")
|
|
}
|
|
|
|
field := model.NewPdfField()
|
|
chfield := &model.PdfFieldChoice{}
|
|
field.SetContext(chfield)
|
|
chfield.PdfField = field
|
|
|
|
chfield.T = core.MakeString(name)
|
|
chfield.Opt = core.MakeArray()
|
|
for _, choicestr := range opt.Choices {
|
|
chfield.Opt.Append(core.MakeString(choicestr))
|
|
}
|
|
chfield.SetFlag(model.FieldFlagCombo)
|
|
|
|
widget := model.NewPdfAnnotationWidget()
|
|
widget.Rect = core.MakeArrayFromFloats(rect)
|
|
widget.P = page.ToPdfObject()
|
|
widget.F = core.MakeInteger(4) // TODO: Make flags for these values and a way to set.
|
|
widget.Parent = chfield.ToPdfObject()
|
|
|
|
chfield.Annotations = append(chfield.Annotations, widget)
|
|
|
|
return chfield, nil
|
|
}
|
|
|
|
// SignatureLine represents a line of information in the signature field appearance.
|
|
type SignatureLine struct {
|
|
Desc string
|
|
Text string
|
|
}
|
|
|
|
// NewSignatureLine returns a new signature line displayed as a part of the
|
|
// signature field appearance.
|
|
func NewSignatureLine(desc, text string) *SignatureLine {
|
|
return &SignatureLine{
|
|
Desc: desc,
|
|
Text: text,
|
|
}
|
|
}
|
|
|
|
// SignatureFieldOpts represents a set of options used to configure
|
|
// an appearance widget dictionary.
|
|
type SignatureFieldOpts struct {
|
|
// Rect represents the area the signature annotation is displayed on.
|
|
Rect []float64
|
|
|
|
// AutoSize specifies if the content of the appearance should be
|
|
// scaled to fit in the annotation rectangle.
|
|
AutoSize bool
|
|
|
|
// Font specifies the font of the text content.
|
|
Font *model.PdfFont
|
|
|
|
// FontSize specifies the size of the text content.
|
|
FontSize float64
|
|
|
|
// LineHeight specifies the height of a line of text in the appearance annotation.
|
|
LineHeight float64
|
|
|
|
// TextColor represents the color of the text content displayed.
|
|
TextColor model.PdfColor
|
|
|
|
// FillColor represents the background color of the appearance annotation area.
|
|
FillColor model.PdfColor
|
|
|
|
// BorderSize represents border size of the appearance annotation area.
|
|
BorderSize float64
|
|
|
|
// BorderColor represents the border color of the appearance annotation area.
|
|
BorderColor model.PdfColor
|
|
}
|
|
|
|
// NewSignatureFieldOpts returns a new initialized instance of options
|
|
// used to generate a signature appearance.
|
|
func NewSignatureFieldOpts() *SignatureFieldOpts {
|
|
return &SignatureFieldOpts{
|
|
Font: model.DefaultFont(),
|
|
FontSize: 10,
|
|
LineHeight: 1,
|
|
AutoSize: true,
|
|
TextColor: model.NewPdfColorDeviceGray(0),
|
|
BorderColor: model.NewPdfColorDeviceGray(0),
|
|
FillColor: model.NewPdfColorDeviceGray(1),
|
|
}
|
|
}
|
|
|
|
// NewSignatureField returns a new signature field with a visible appearance
|
|
// containing the specified signature lines and styled according to the
|
|
// specified options.
|
|
func NewSignatureField(signature *model.PdfSignature, lines []*SignatureLine, opts *SignatureFieldOpts) (*model.PdfFieldSignature, error) {
|
|
if signature == nil {
|
|
return nil, errors.New("signature cannot be nil")
|
|
}
|
|
|
|
apDict, err := genFieldSignatureAppearance(lines, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
field := model.NewPdfFieldSignature(signature)
|
|
field.Rect = core.MakeArrayFromFloats(opts.Rect)
|
|
field.AP = apDict
|
|
return field, nil
|
|
}
|