github.com/pdfcpu/pdfcpu@v0.11.1/pkg/pdfcpu/primitives/fieldGroup.go (about)

     1  /*
     2  	Copyright 2022 The pdfcpu Authors.
     3  
     4  	Licensed under the Apache License, Version 2.0 (the "License");
     5  	you may not use this file except in compliance with the License.
     6  	You may obtain a copy of the License at
     7  
     8  		http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  	Unless required by applicable law or agreed to in writing, software
    11  	distributed under the License is distributed on an "AS IS" BASIS,
    12  	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  	See the License for the specific language governing permissions and
    14  	limitations under the License.
    15  */
    16  
    17  package primitives
    18  
    19  import (
    20  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/color"
    21  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
    22  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
    23  )
    24  
    25  // FieldGroup is a container for fields.
    26  type FieldGroup struct {
    27  	pdf               *PDF
    28  	content           *Content
    29  	Name              string
    30  	Value             string
    31  	Border            *Border
    32  	Padding           *Padding
    33  	BackgroundColor   string `json:"bgCol"`
    34  	bgCol             *color.SimpleColor
    35  	TextFields        []*TextField        `json:"textfield"`        // text fields with optional label
    36  	DateFields        []*DateField        `json:"datefield"`        // date fields with optional label
    37  	CheckBoxes        []*CheckBox         `json:"checkbox"`         // checkboxes with optional label
    38  	RadioButtonGroups []*RadioButtonGroup `json:"radiobuttongroup"` // radiobutton groups with optional label
    39  	ComboBoxes        []*ComboBox         `json:"combobox"`         // comboboxes with optional label
    40  	ListBoxes         []*ListBox          `json:"listbox"`          // listboxes with optional label
    41  	Hide              bool
    42  }
    43  
    44  func (fg *FieldGroup) validateBorder() error {
    45  	if fg.Border != nil {
    46  		fg.Border.pdf = fg.pdf
    47  		if err := fg.Border.validate(); err != nil {
    48  			return err
    49  		}
    50  	}
    51  	return nil
    52  }
    53  
    54  func (fg *FieldGroup) validatePadding() error {
    55  	if fg.Padding != nil {
    56  		if err := fg.Padding.validate(); err != nil {
    57  			return err
    58  		}
    59  	}
    60  	return nil
    61  }
    62  
    63  func (fg *FieldGroup) validateBackgroundColor() error {
    64  	if fg.BackgroundColor != "" {
    65  		sc, err := fg.pdf.parseColor(fg.BackgroundColor)
    66  		if err != nil {
    67  			return err
    68  		}
    69  		fg.bgCol = sc
    70  	}
    71  	return nil
    72  }
    73  
    74  func (fg *FieldGroup) validateBorderPaddingBgCol() error {
    75  	if err := fg.validateBorder(); err != nil {
    76  		return err
    77  	}
    78  
    79  	if err := fg.validatePadding(); err != nil {
    80  		return err
    81  	}
    82  
    83  	return fg.validateBackgroundColor()
    84  }
    85  
    86  func (fg *FieldGroup) validate() error {
    87  
    88  	if err := fg.validateBorderPaddingBgCol(); err != nil {
    89  		return err
    90  	}
    91  
    92  	for _, tf := range fg.TextFields {
    93  		tf.pdf = fg.pdf
    94  		tf.content = fg.content
    95  		if err := tf.validate(); err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	for _, df := range fg.DateFields {
   101  		df.pdf = fg.pdf
   102  		df.content = fg.content
   103  		if err := df.validate(); err != nil {
   104  			return err
   105  		}
   106  	}
   107  
   108  	for _, cb := range fg.CheckBoxes {
   109  		cb.pdf = fg.pdf
   110  		cb.content = fg.content
   111  		if err := cb.validate(); err != nil {
   112  			return err
   113  		}
   114  	}
   115  
   116  	for _, rbg := range fg.RadioButtonGroups {
   117  		rbg.pdf = fg.pdf
   118  		rbg.content = fg.content
   119  		if err := rbg.validate(); err != nil {
   120  			return err
   121  		}
   122  	}
   123  
   124  	for _, cb := range fg.ComboBoxes {
   125  		cb.pdf = fg.pdf
   126  		cb.content = fg.content
   127  		if err := cb.validate(); err != nil {
   128  			return err
   129  		}
   130  	}
   131  
   132  	for _, lb := range fg.ListBoxes {
   133  		lb.pdf = fg.pdf
   134  		lb.content = fg.content
   135  		if err := lb.validate(); err != nil {
   136  			return err
   137  		}
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  func (fg *FieldGroup) mergeIn(fg0 *FieldGroup) {
   144  	if fg.Border == nil {
   145  		fg.Border = fg0.Border
   146  	}
   147  
   148  	if fg.Padding == nil {
   149  		fg.Padding = fg0.Padding
   150  	}
   151  
   152  	if fg.bgCol == nil {
   153  		fg.bgCol = fg0.bgCol
   154  	}
   155  
   156  	if !fg.Hide {
   157  		fg.Hide = fg0.Hide
   158  	}
   159  }
   160  
   161  func (fg *FieldGroup) calcBBoxFromTextFields(bbox **types.Rectangle, p *model.Page, pageNr int, fonts model.FontMap) error {
   162  	for _, tf := range fg.TextFields {
   163  		if err := tf.prepForRender(p, pageNr, fonts); err != nil {
   164  			return err
   165  		}
   166  		*bbox = model.CalcBoundingBoxForRects(*bbox, tf.bbox())
   167  	}
   168  	return nil
   169  }
   170  
   171  func (fg *FieldGroup) calcBBoxFromDateFields(bbox **types.Rectangle, p *model.Page, pageNr int, fonts model.FontMap) error {
   172  	for _, df := range fg.DateFields {
   173  		if err := df.prepForRender(p, pageNr, fonts); err != nil {
   174  			return err
   175  		}
   176  		*bbox = model.CalcBoundingBoxForRects(*bbox, df.bbox())
   177  	}
   178  	return nil
   179  }
   180  
   181  func (fg *FieldGroup) calcBBoxFromCheckBoxes(bbox **types.Rectangle, p *model.Page, pageNr int, fonts model.FontMap) error {
   182  	for _, cb := range fg.CheckBoxes {
   183  		if err := cb.prepForRender(p, pageNr, fonts); err != nil {
   184  			return err
   185  		}
   186  		*bbox = model.CalcBoundingBoxForRects(*bbox, cb.bbox())
   187  	}
   188  	return nil
   189  }
   190  
   191  func (fg *FieldGroup) calcBBoxFromRadioButtonGroups(bbox **types.Rectangle, p *model.Page, pageNr int, fonts model.FontMap) error {
   192  	for _, rbg := range fg.RadioButtonGroups {
   193  		if err := rbg.prepForRender(p, pageNr, fonts); err != nil {
   194  			return err
   195  		}
   196  		*bbox = model.CalcBoundingBoxForRects(*bbox, rbg.bbox())
   197  	}
   198  	return nil
   199  }
   200  
   201  func (fg *FieldGroup) calcBBoxFromComboBoxes(bbox **types.Rectangle, p *model.Page, pageNr int, fonts model.FontMap) error {
   202  	for _, cb := range fg.ComboBoxes {
   203  		if err := cb.prepForRender(p, pageNr, fonts); err != nil {
   204  			return err
   205  		}
   206  		*bbox = model.CalcBoundingBoxForRects(*bbox, cb.bbox())
   207  	}
   208  	return nil
   209  }
   210  
   211  func (fg *FieldGroup) calcBBoxFromListBoxes(bbox **types.Rectangle, p *model.Page, pageNr int, fonts model.FontMap) error {
   212  	for _, lb := range fg.ListBoxes {
   213  		if err := lb.prepForRender(p, pageNr, fonts); err != nil {
   214  			return err
   215  		}
   216  		*bbox = model.CalcBoundingBoxForRects(*bbox, lb.bbox())
   217  	}
   218  	return nil
   219  }
   220  
   221  func (fg *FieldGroup) calcBBox(p *model.Page, pageNr int, fonts model.FontMap) (*types.Rectangle, error) {
   222  	var bbox *types.Rectangle
   223  
   224  	if err := fg.calcBBoxFromTextFields(&bbox, p, pageNr, fonts); err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	if err := fg.calcBBoxFromDateFields(&bbox, p, pageNr, fonts); err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	if err := fg.calcBBoxFromCheckBoxes(&bbox, p, pageNr, fonts); err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	if err := fg.calcBBoxFromRadioButtonGroups(&bbox, p, pageNr, fonts); err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	if err := fg.calcBBoxFromComboBoxes(&bbox, p, pageNr, fonts); err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	if err := fg.calcBBoxFromListBoxes(&bbox, p, pageNr, fonts); err != nil {
   245  		return nil, err
   246  	}
   247  
   248  	return bbox, nil
   249  }
   250  
   251  func (fg *FieldGroup) renderBBox(bbox *types.Rectangle, p *model.Page) error {
   252  	r := fg.content.Box()
   253  	var bw float64
   254  	if fg.Border != nil {
   255  		bw = float64(fg.Border.Width)
   256  	}
   257  
   258  	var pTop, pRight, pBottom, pLeft float64
   259  	if fg.Padding != nil {
   260  		p := fg.Padding
   261  		pTop, pRight, pBottom, pLeft = p.Top, p.Right, p.Bottom, p.Left
   262  	}
   263  
   264  	x := bbox.LL.X - r.LL.X - bw - pLeft
   265  	w := bbox.Width() + 2*bw + pLeft + pRight
   266  	if x < 0 {
   267  		w += x
   268  		x = 0
   269  	}
   270  
   271  	y := bbox.LL.Y - r.LL.Y - bw - pBottom
   272  	h := bbox.Height() + 2*bw + pBottom + pTop
   273  	if y < 0 {
   274  		h += y
   275  		y = 0
   276  	}
   277  
   278  	sb := SimpleBox{
   279  		x:       x,
   280  		y:       y,
   281  		Width:   w,
   282  		Height:  h,
   283  		fillCol: fg.bgCol,
   284  		Border:  fg.Border,
   285  	}
   286  
   287  	sb.pdf = fg.pdf
   288  	sb.content = fg.content
   289  
   290  	return sb.render(p)
   291  }
   292  
   293  func (fg *FieldGroup) renderTextFields(p *model.Page, fonts model.FontMap) error {
   294  	for _, tf := range fg.TextFields {
   295  		if tf.Hide {
   296  			continue
   297  		}
   298  		if err := tf.doRender(p, fonts); err != nil {
   299  			return err
   300  		}
   301  	}
   302  	return nil
   303  }
   304  
   305  func (fg *FieldGroup) renderDateFields(p *model.Page, fonts model.FontMap) error {
   306  	for _, df := range fg.DateFields {
   307  		if df.Hide {
   308  			continue
   309  		}
   310  		if err := df.doRender(p, fonts); err != nil {
   311  			return err
   312  		}
   313  	}
   314  	return nil
   315  }
   316  
   317  func (fg *FieldGroup) renderCheckBoxes(p *model.Page, fonts model.FontMap) error {
   318  	for _, cb := range fg.CheckBoxes {
   319  		if cb.Hide {
   320  			continue
   321  		}
   322  		if err := cb.doRender(p, fonts); err != nil {
   323  			return err
   324  		}
   325  	}
   326  	return nil
   327  }
   328  
   329  func (fg *FieldGroup) renderRadioButtonGroups(p *model.Page, pageNr int, fonts model.FontMap) error {
   330  	for _, rbg := range fg.RadioButtonGroups {
   331  		if rbg.Hide {
   332  			continue
   333  		}
   334  		if err := rbg.doRender(p, pageNr, fonts); err != nil {
   335  			return err
   336  		}
   337  	}
   338  	return nil
   339  }
   340  
   341  func (fg *FieldGroup) renderComboBoxes(p *model.Page, fonts model.FontMap) error {
   342  	for _, cb := range fg.ComboBoxes {
   343  		if cb.Hide {
   344  			continue
   345  		}
   346  		if err := cb.doRender(p, fonts); err != nil {
   347  			return err
   348  		}
   349  	}
   350  	return nil
   351  }
   352  
   353  func (fg *FieldGroup) renderListBoxes(p *model.Page, fonts model.FontMap) error {
   354  	for _, cb := range fg.ListBoxes {
   355  		if cb.Hide {
   356  			continue
   357  		}
   358  		if err := cb.doRender(p, fonts); err != nil {
   359  			return err
   360  		}
   361  	}
   362  	return nil
   363  }
   364  
   365  func (fg *FieldGroup) renderFields(p *model.Page, pageNr int, fonts model.FontMap) error {
   366  	if err := fg.renderTextFields(p, fonts); err != nil {
   367  		return err
   368  	}
   369  	if err := fg.renderDateFields(p, fonts); err != nil {
   370  		return err
   371  	}
   372  	if err := fg.renderCheckBoxes(p, fonts); err != nil {
   373  		return err
   374  	}
   375  	if err := fg.renderRadioButtonGroups(p, pageNr, fonts); err != nil {
   376  		return err
   377  	}
   378  	if err := fg.renderComboBoxes(p, fonts); err != nil {
   379  		return err
   380  	}
   381  	return fg.renderListBoxes(p, fonts)
   382  }
   383  
   384  func (fg *FieldGroup) render(p *model.Page, pageNr int, fonts model.FontMap) error {
   385  	bbox, err := fg.calcBBox(p, pageNr, fonts)
   386  	if err != nil {
   387  		return err
   388  	}
   389  
   390  	// Render simpleBox containing all fields of this group.
   391  	if err := fg.renderBBox(bbox, p); err != nil {
   392  		return err
   393  	}
   394  
   395  	if err := fg.renderFields(p, pageNr, fonts); err != nil {
   396  		return err
   397  	}
   398  
   399  	if fg.pdf.Debug {
   400  		fg.pdf.highlightPos(p.Buf, bbox.LL.X, bbox.LL.Y, fg.content.Box())
   401  	}
   402  
   403  	return nil
   404  }