mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-26 13:48:55 +08:00
Form fill field rotation (#385)
* Refactor text field rotation * Add rotation support for checkbox fields * Add rotation support for combobox fields * Add rotation support for text combobox fields * Add documentation for the applyRotation of the AppearanceStyle
This commit is contained in:
parent
54e965785b
commit
ca7f479b7c
@ -219,15 +219,15 @@ func genFieldTextAppearance(wa *model.PdfAnnotationWidget, ftxt *model.PdfFieldT
|
||||
return nil, err
|
||||
}
|
||||
width, height := rect.Width(), rect.Height()
|
||||
bboxWidth, bboxHeight := width, height
|
||||
|
||||
var rotation float64
|
||||
if mkDict, has := core.GetDict(wa.MK); has {
|
||||
mkDict, has := core.GetDict(wa.MK)
|
||||
if has {
|
||||
bsDict, _ := core.GetDict(wa.BS)
|
||||
err := style.applyAppearanceCharacteristics(mkDict, bsDict, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rotation, _ = core.GetNumberAsFloat(mkDict.Get("R"))
|
||||
}
|
||||
|
||||
// Get and process the default appearance string (DA) operands.
|
||||
@ -252,26 +252,10 @@ func genFieldTextAppearance(wa *model.PdfAnnotationWidget, ftxt *model.PdfFieldT
|
||||
cc.Add_BMC("Tx")
|
||||
cc.Add_q()
|
||||
|
||||
bboxWidth, bboxHeight := width, height
|
||||
if rotation != 0 {
|
||||
// Calculate bounding box before rotation.
|
||||
revRotation := -rotation
|
||||
bbox := draw.Path{Points: []draw.Point{
|
||||
draw.NewPoint(0, 0).Rotate(revRotation),
|
||||
draw.NewPoint(width, 0).Rotate(revRotation),
|
||||
draw.NewPoint(0, height).Rotate(revRotation),
|
||||
draw.NewPoint(width, height).Rotate(revRotation),
|
||||
}}.GetBoundingBox()
|
||||
|
||||
// Apply rotation if present.
|
||||
// Update width and height, as the appearance is generated based on
|
||||
// the bounding of the annotation with no rotation.
|
||||
width = bbox.Width
|
||||
height = bbox.Height
|
||||
|
||||
// Apply rotation.
|
||||
cc.RotateDeg(rotation)
|
||||
cc.Translate(bbox.X, bbox.Y)
|
||||
}
|
||||
width, height = style.applyRotation(mkDict, width, height, cc)
|
||||
|
||||
// Graphic state changes.
|
||||
cc.Add_BT()
|
||||
@ -513,8 +497,10 @@ func genFieldTextCombAppearance(wa *model.PdfAnnotationWidget, ftxt *model.PdfFi
|
||||
return nil, err
|
||||
}
|
||||
width, height := rect.Width(), rect.Height()
|
||||
bboxWidth, bboxHeight := width, height
|
||||
|
||||
if mkDict, has := core.GetDict(wa.MK); has {
|
||||
mkDict, has := core.GetDict(wa.MK)
|
||||
if has {
|
||||
bsDict, _ := core.GetDict(wa.BS)
|
||||
err := style.applyAppearanceCharacteristics(mkDict, bsDict, nil)
|
||||
if err != nil {
|
||||
@ -551,6 +537,11 @@ func genFieldTextCombAppearance(wa *model.PdfAnnotationWidget, ftxt *model.PdfFi
|
||||
cc.Add_BMC("Tx")
|
||||
cc.Add_q()
|
||||
|
||||
// Apply rotation if present.
|
||||
// Update width and height, as the appearance is generated based on
|
||||
// the bounding of the annotation with no rotation.
|
||||
width, height = style.applyRotation(mkDict, width, height, cc)
|
||||
|
||||
// Graphic state changes.
|
||||
cc.Add_BT()
|
||||
|
||||
@ -680,7 +671,7 @@ func genFieldTextCombAppearance(wa *model.PdfAnnotationWidget, ftxt *model.PdfFi
|
||||
|
||||
xform := model.NewXObjectForm()
|
||||
xform.Resources = resources
|
||||
xform.BBox = core.MakeArrayFromFloats([]float64{0, 0, width, height})
|
||||
xform.BBox = core.MakeArrayFromFloats([]float64{0, 0, bboxWidth, bboxHeight})
|
||||
xform.SetContentStream(cc.Bytes(), defStreamEncoder())
|
||||
|
||||
apDict := core.MakeDict()
|
||||
@ -702,6 +693,7 @@ func genFieldCheckboxAppearance(wa *model.PdfAnnotationWidget, fbtn *model.PdfFi
|
||||
return nil, err
|
||||
}
|
||||
width, height := rect.Width(), rect.Height()
|
||||
bboxWidth, bboxHeight := width, height
|
||||
|
||||
common.Log.Debug("Checkbox, wa BS: %v", wa.BS)
|
||||
|
||||
@ -710,7 +702,8 @@ func genFieldCheckboxAppearance(wa *model.PdfAnnotationWidget, fbtn *model.PdfFi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if mkDict, has := core.GetDict(wa.MK); has {
|
||||
mkDict, has := core.GetDict(wa.MK)
|
||||
if has {
|
||||
bsDict, _ := core.GetDict(wa.BS)
|
||||
err := style.applyAppearanceCharacteristics(mkDict, bsDict, zapfdb)
|
||||
if err != nil {
|
||||
@ -732,6 +725,11 @@ func genFieldCheckboxAppearance(wa *model.PdfAnnotationWidget, fbtn *model.PdfFi
|
||||
drawAlignmentReticle(cc, style2, width, height)
|
||||
}
|
||||
|
||||
// Apply rotation if present.
|
||||
// Update width and height, as the appearance is generated based on
|
||||
// the bounding of the annotation with no rotation.
|
||||
width, height = style.applyRotation(mkDict, width, height, cc)
|
||||
|
||||
fontsize := style.AutoFontSizeFraction * height
|
||||
|
||||
checkmetrics, ok := zapfdb.GetRuneMetrics(style.CheckmarkRune)
|
||||
@ -767,7 +765,7 @@ func genFieldCheckboxAppearance(wa *model.PdfAnnotationWidget, fbtn *model.PdfFi
|
||||
|
||||
xformOn.Resources = model.NewPdfPageResources()
|
||||
xformOn.Resources.SetFontByName("ZaDb", zapfdb.ToPdfObject())
|
||||
xformOn.BBox = core.MakeArrayFromFloats([]float64{0, 0, width, height})
|
||||
xformOn.BBox = core.MakeArrayFromFloats([]float64{0, 0, bboxWidth, bboxHeight})
|
||||
xformOn.SetContentStream(cc.Bytes(), defStreamEncoder())
|
||||
}
|
||||
|
||||
@ -777,7 +775,7 @@ func genFieldCheckboxAppearance(wa *model.PdfAnnotationWidget, fbtn *model.PdfFi
|
||||
if style.BorderSize > 0 {
|
||||
drawRect(cc, style, width, height)
|
||||
}
|
||||
xformOff.BBox = core.MakeArrayFromFloats([]float64{0, 0, width, height})
|
||||
xformOff.BBox = core.MakeArrayFromFloats([]float64{0, 0, bboxWidth, bboxHeight})
|
||||
xformOff.SetContentStream(cc.Bytes(), defStreamEncoder())
|
||||
}
|
||||
|
||||
@ -813,7 +811,8 @@ func genFieldComboboxAppearance(form *model.PdfAcroForm, wa *model.PdfAnnotation
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if mkDict, has := core.GetDict(wa.MK); has {
|
||||
mkDict, has := core.GetDict(wa.MK)
|
||||
if has {
|
||||
bsDict, _ := core.GetDict(wa.BS)
|
||||
err := style.applyAppearanceCharacteristics(mkDict, bsDict, nil)
|
||||
if err != nil {
|
||||
@ -839,7 +838,7 @@ func genFieldComboboxAppearance(form *model.PdfAcroForm, wa *model.PdfAnnotation
|
||||
}
|
||||
|
||||
if len(optstr) > 0 {
|
||||
xform, err := makeComboboxTextXObjForm(fch.PdfField, width, height, optstr, style, daOps, form.DR)
|
||||
xform, err := makeComboboxTextXObjForm(fch.PdfField, width, height, optstr, style, daOps, form.DR, mkDict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -857,8 +856,9 @@ func genFieldComboboxAppearance(form *model.PdfAcroForm, wa *model.PdfAnnotation
|
||||
// Make a text-based XObj Form.
|
||||
func makeComboboxTextXObjForm(field *model.PdfField, width, height float64,
|
||||
text string, style AppearanceStyle, daOps *contentstream.ContentStreamOperations,
|
||||
dr *model.PdfPageResources) (*model.XObjectForm, error) {
|
||||
dr *model.PdfPageResources, mkDict *core.PdfObjectDictionary) (*model.XObjectForm, error) {
|
||||
resources := model.NewPdfPageResources()
|
||||
bboxWidth, bboxHeight := width, height
|
||||
|
||||
cc := contentstream.NewContentCreator()
|
||||
if style.BorderSize > 0 {
|
||||
@ -875,6 +875,11 @@ func makeComboboxTextXObjForm(field *model.PdfField, width, height float64,
|
||||
// Graphic state changes.
|
||||
cc.Add_BT()
|
||||
|
||||
// Apply rotation if present.
|
||||
// Update width and height, as the appearance is generated based on
|
||||
// the bounding of the annotation with no rotation.
|
||||
width, height = style.applyRotation(mkDict, width, height, cc)
|
||||
|
||||
// Process DA operands.
|
||||
apFont, hasTf, err := style.processDA(field, daOps, dr, resources, cc)
|
||||
if err != nil {
|
||||
@ -950,7 +955,7 @@ func makeComboboxTextXObjForm(field *model.PdfField, width, height float64,
|
||||
|
||||
xform := model.NewXObjectForm()
|
||||
xform.Resources = resources
|
||||
xform.BBox = core.MakeArrayFromFloats([]float64{0, 0, width, height})
|
||||
xform.BBox = core.MakeArrayFromFloats([]float64{0, 0, bboxWidth, bboxHeight})
|
||||
xform.SetContentStream(cc.Bytes(), defStreamEncoder())
|
||||
|
||||
return xform, nil
|
||||
@ -1067,6 +1072,40 @@ func (style *AppearanceStyle) applyAppearanceCharacteristics(mkDict *core.PdfObj
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyRotation applies the rotation specified by the MK dictionary,
|
||||
// if present. The method returns the width and height of the annotation
|
||||
// rectangle with no rotation.
|
||||
func (style *AppearanceStyle) applyRotation(mkDict *core.PdfObjectDictionary,
|
||||
width, height float64, cc *contentstream.ContentCreator) (float64, float64) {
|
||||
if !style.AllowMK {
|
||||
return width, height
|
||||
}
|
||||
if mkDict == nil {
|
||||
return width, height
|
||||
}
|
||||
|
||||
// Extract rotation from the MK dictionary.
|
||||
rotation, _ := core.GetNumberAsFloat(mkDict.Get("R"))
|
||||
if rotation == 0 {
|
||||
return width, height
|
||||
}
|
||||
|
||||
// Calculate bounding box before rotation.
|
||||
revRotation := -rotation
|
||||
bbox := draw.Path{Points: []draw.Point{
|
||||
draw.NewPoint(0, 0).Rotate(revRotation),
|
||||
draw.NewPoint(width, 0).Rotate(revRotation),
|
||||
draw.NewPoint(0, height).Rotate(revRotation),
|
||||
draw.NewPoint(width, height).Rotate(revRotation),
|
||||
}}.GetBoundingBox()
|
||||
|
||||
// Apply rotation.
|
||||
cc.RotateDeg(rotation)
|
||||
cc.Translate(bbox.X, bbox.Y)
|
||||
|
||||
return bbox.Width, bbox.Height
|
||||
}
|
||||
|
||||
// processDA adds the operands found in the field default appearance stream to
|
||||
// the provided content stream creator. It also provides a fallback font, based
|
||||
// on the configuration of the AppearanceStyle, if no valid font is specified
|
||||
|
Loading…
x
Reference in New Issue
Block a user