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 }