github.com/moov-io/imagecashletter@v0.10.1/imageViewData.go (about) 1 // Copyright 2020 The Moov Authors 2 // Use of this source code is governed by an Apache License 3 // license that can be found in the LICENSE file. 4 5 package imagecashletter 6 7 import ( 8 "encoding/base64" 9 "encoding/json" 10 "fmt" 11 "strings" 12 "time" 13 ) 14 15 // Errors specific to a ImageViewData Record 16 17 // ImageViewData Record 18 type ImageViewData struct { 19 // ID is a client defined string used as a reference to this record. 20 ID string `json:"id"` 21 // recordType defines the type of record. 22 recordType string 23 // EceInstitutionRoutingNumber contains the routing and transit number of the institution that creates the 24 // bundle header. Format: TTTTAAAAC, where: 25 // TTTT Federal Reserve Prefix 26 // AAAA ABA Institution Identifier 27 // C Check Digit 28 // For a number that identifies a non-financial institution: NNNNNNNNN 29 EceInstitutionRoutingNumber string `json:"eceInstitutionRoutingNumber"` 30 // BundleBusinessDate is the business date of the bundle. 31 // Values: 32 // YYYY 1993 through 9999 33 // MM 01 through 12 34 // DD 01 through 31 35 BundleBusinessDate time.Time `json:"bundleBusinessDate"` 36 // CycleNumber is a code assigned by the institution that creates the bundle. Denotes the cycle under which 37 // the bundle is created. 38 CycleNumber string `json:"cycleNumber"` 39 // EceInstitutionItemSequenceNumber is a number assigned by the institution that creates the CheckDetail Record or 40 // Return. This number is imported from the CheckDetail.ECEInstitutionItemSequenceNumber or 41 // Return.ECEInstitutionItemSequenceNumber associated with the image view conveyed in this Image View Data Record. 42 // The ECE institution must construct the sequence number to guarantee uniqueness for a given routing number, 43 // business day, and cycle number. Must contain a numeric value. 44 EceInstitutionItemSequenceNumber string `json:"eceInstitutionItemSequenceNumber"` 45 // SecurityOriginatorName is a unique name that creates the Digital Signature for data to be exchanged. 46 // Shall be present only under clearing arrangements and when ImageViewDetail.DigitalSignatureIndicator is 1 47 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 48 SecurityOriginatorName string `json:"securityOriginatorName"` 49 // SecurityAuthenticatorName is the unique name that performs authentication on received data. 50 // Shall be present only under clearing arrangements and when ImageViewDetail.DigitalSignatureIndicator is 1 51 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 52 SecurityAuthenticatorName string `json:"securityAuthenticatorName"` 53 // SecurityKeyName is a name or character sequence used by the signer (originator) to communicate a key identifier 54 // to the recipient (authenticator) so the recipient can obtain the key needed to validate the signature. The name 55 // is typically used as an identifier related to the key pair used to sign the image. The name is mutually known to 56 // the security originator and the security authenticator and is unique to this relationship. 57 // Shall be present only under clearing arrangements and when ImageViewDetail.DigitalSignatureIndicator is 1 58 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 59 SecurityKeyName string `json:"securityKeyName"` 60 // ClippingOrigin is a code that defines the corner of the conveyed image view that is taken as the reference point 61 // for the clipping coordinates. Top, bottom, left, and right references apply to a view that presents a visually 62 // correct orientation. When clipping information is present, the nature of the Area of Interest defined by the 63 // clipping rectangle is determined by the value of the ImageViewDetail.ViewDescriptor. Primary front and rear 64 // views shall only have a Defined Value of 0. Can be blank. 65 // Values: 66 // 0: Clipping information is not present–full view present 67 // 1: Clipping origin is top left corner of image view 68 // 2: Clipping origin is top right corner of image view 69 // 3: Clipping origin is bottom right corner of image view 70 // 4: Clipping origin is bottom left corner of image view 71 ClippingOrigin int `json:"clippingOrigin"` 72 // ClippingCoordinateH1 is a number that represents the horizontal offset in pixels from the clipping origin to the 73 // nearest vertical side of the clipping rectangle. The clipping coordinates (h1, h2, v1, v2) convey the clipping 74 // rectangle’s offsets in both horizontal (h) and vertical (v) directions. The offset values collectively establish 75 // the boundary sides of the clipping rectangle. Pixels on the boundary of the clipping rectangle are included in 76 // the selected array of pixels. That is, the first pixel of the selected array is at offset (h1, v1) and the last 77 // pixel of the selected array is at offset (h2, v2). The corner pixel at the origin of the image view is assumed 78 // to have the offset value (0, 0). 79 // Shall be present if Image View Data.ClippingOrigin is present and non-zero. 80 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 81 // Values: 0000–9999 82 ClippingCoordinateH1 string `json:"clippingCoordinateH1"` 83 // ClippingCoordinateH2 is a number that represents the horizontal offset in pixels from the clipping origin to the 84 // furthermost vertical side of the clipping rectangle. 85 // Shall be present if Image View Data.ClippingOrigin is present and non-zero. 86 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 87 // Values: 0000–9999 88 ClippingCoordinateH2 string `json:"clippingCoordinateH2"` 89 // ClippingCoordinateV1 is a number that represents the vertical offset in pixels from the clipping origin to the 90 // nearest horizontal side of the clipping rectangle. 91 // Shall be present if Image View Data.ClippingOrigin is present and non-zero. 92 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 93 // Values: 0000–9999 94 ClippingCoordinateV1 string `json:"clippingCoordinateV1"` 95 // ClippingCoordinateV2 is number that represents the vertical offset in pixels from the clipping origin to the 96 // furthermost horizontal side of the clipping rectangle. 97 // Shall be present if Image View Data.ClippingOrigin is present and non-zero. 98 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 99 // Values: 0000–9999 100 ClippingCoordinateV2 string `json:"clippingCoordinateV2"` 101 // LengthImageReferenceKey is the number of characters in the ImageViewData.ImageReferenceKey. 102 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 103 // Values: 0000 ImageReferenceKey is not present 104 // 0001–9999: Valid when ImageReferenceKey is present 105 LengthImageReferenceKey string `json:"lengthImageReferenceKey"` 106 // ImageReferenceKey is assigned by the ECE institution that creates the CheckDetail or Return, and the related 107 // Image View Records. This designator, when used, shall uniquely identify the item image to the ECE institution. 108 // This designator is a special key with significance to the creating institution. It is intended to be used to 109 // locate within an archive the unique image associated with the item. The designator could be a full access path 110 // and name that would allow direct look up and access to the image, for example a URL. This shall match 111 // CheckDetailAddendumB.ImageReferenceKey, or ReturnAddendumCImageReferenceKey Record, if used. 112 // Size: 0 – 9999 113 ImageReferenceKey string `json:"imageReferenceKey"` 114 // LengthDigitalSignature is the number of bytes in the Image View Data.DigitalSignature. 115 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 116 LengthDigitalSignature string `json:"lengthDigitalSignature"` 117 // DigitalSignature is created by applying the cryptographic algorithm and private/secret key against the data to 118 // be protected. The Digital Signature provides user authentication and data integrity. 119 // Shall be present only under clearing arrangements and when ImageViewDetail.DigitalSignatureIndicator is 1 120 // Shall not be present when ImageViewDetail.ImageIndicator is 0. 121 // Size: 0-99999 122 DigitalSignature []byte `json:"digitalSignature"` 123 // LengthImageData is the number of bytes in the ImageViewData.ImageData. 124 // Shall be present when ImageViewDetail.ImageIndicator is NOT 0 125 // Values: 0000001–99999999 126 LengthImageData string `json:"lengthImageData"` 127 // ImageData contains the image view. The Image Data generally consists of an image header and the image raster 128 // data. The image header provides information that is required to interpret the image raster data. The image 129 // raster data contains the scanned image of the physical item in raster (line by line) format. Each scan line 130 // comprises a set of concatenated pixels. The image comprises a set of scan lines. The image raster data is 131 // typically compressed to reduce the number of bytes needed to transmit and store the image. The header/image 132 // format type is defined by the ImageViewDetail.ImageViewFormatIndicator . The syntax and semantics of the image 133 // header/image format are understood by referring to the appropriate image format specification. The compression 134 // scheme used to compress the image raster data is specified in the 135 // ImageViewDetail.ImageViewCompressionAlgorithmIdentifier and in the image header portion of the Image Data or by 136 // association with the selected image format. 137 // Shall be present when ImageViewDetail.ImageIndicator Record is NOT 0. 138 // Size: 0-9999999 139 ImageData []byte `json:"imageData"` 140 // validator is composed for image cash letter data validation 141 validator 142 // converters is composed for image cash letter to golang Converters 143 converters 144 } 145 146 // NewImageViewData returns a new ImageViewData with default values for non exported fields 147 func NewImageViewData() ImageViewData { 148 ivData := ImageViewData{} 149 ivData.setRecordType() 150 return ivData 151 } 152 153 func (ivData *ImageViewData) setRecordType() { 154 if ivData == nil { 155 return 156 } 157 ivData.recordType = "52" 158 } 159 160 // ParseAndDecode takes the input record string, decodes all except image and parses the ImageViewData values 161 func (ivData *ImageViewData) ParseAndDecode(record string, decode DecodeLineFn) error { 162 // record contains binary data that may not map to UTF-8 charmap 163 recordLength := len(record) 164 if recordLength < 105 { 165 return fmt.Errorf("parse record is short, got %d, expected length is 105", recordLength) // line too short 166 } 167 // Character position 1-2, Always "52" 168 ivData.setRecordType() 169 // 03-11 170 lineOut, err := decode(record[2:11]) 171 if err != nil { 172 return err 173 } 174 ivData.EceInstitutionRoutingNumber = ivData.parseStringField(lineOut) 175 // 12-19 176 lineOut, err = decode(record[11:19]) 177 if err != nil { 178 return err 179 } 180 ivData.BundleBusinessDate = ivData.parseYYYYMMDDDate(lineOut) 181 // 20-21 182 lineOut, err = decode(record[19:21]) 183 if err != nil { 184 return err 185 } 186 ivData.CycleNumber = ivData.parseStringField(lineOut) 187 // 22-36 188 lineOut, err = decode(record[21:36]) 189 if err != nil { 190 return err 191 } 192 ivData.EceInstitutionItemSequenceNumber = ivData.parseStringField(lineOut) 193 // 37-52 194 lineOut, err = decode(record[36:52]) 195 if err != nil { 196 return err 197 } 198 ivData.SecurityOriginatorName = ivData.parseStringField(lineOut) 199 // 53-68 200 lineOut, err = decode(record[52:68]) 201 if err != nil { 202 return err 203 } 204 ivData.SecurityAuthenticatorName = ivData.parseStringField(lineOut) 205 // 69-84 206 lineOut, err = decode(record[68:84]) 207 if err != nil { 208 return err 209 } 210 ivData.SecurityKeyName = ivData.parseStringField(lineOut) 211 // 85-85 212 lineOut, err = decode(record[84:85]) 213 if err != nil { 214 return err 215 } 216 ivData.ClippingOrigin = ivData.parseNumField(lineOut) 217 // 86-89 218 lineOut, err = decode(record[85:89]) 219 if err != nil { 220 return err 221 } 222 ivData.ClippingCoordinateH1 = ivData.parseStringField(lineOut) 223 // 90-93 224 lineOut, err = decode(record[89:93]) 225 if err != nil { 226 return err 227 } 228 ivData.ClippingCoordinateH2 = ivData.parseStringField(lineOut) 229 // 94-97 230 lineOut, err = decode(record[93:97]) 231 if err != nil { 232 return err 233 } 234 ivData.ClippingCoordinateV1 = ivData.parseStringField(lineOut) 235 // 98-101 236 lineOut, err = decode(record[97:101]) 237 if err != nil { 238 return err 239 } 240 ivData.ClippingCoordinateV2 = ivData.parseStringField(lineOut) 241 // 102-105 242 lineOut, err = decode(record[101:105]) 243 if err != nil { 244 return err 245 } 246 ivData.LengthImageReferenceKey = ivData.parseStringField(lineOut) 247 248 lirk := ivData.parseNumField(ivData.LengthImageReferenceKey) 249 if lirk < 0 || recordLength < 110+lirk { 250 return nil // line too short 251 } 252 253 // 106 - (105+X) 254 lineOut, err = decode(record[105 : 105+lirk]) 255 if err != nil { 256 return err 257 } 258 ivData.ImageReferenceKey = ivData.parseStringField(lineOut) 259 // (106 + lirk) – (110 + lirk) 260 lineOut, err = decode(record[105+lirk : 110+lirk]) 261 if err != nil { 262 return err 263 } 264 ivData.LengthDigitalSignature = ivData.parseStringField(lineOut) 265 266 lds := ivData.parseNumField(ivData.LengthDigitalSignature) 267 if lds < 0 || recordLength < 117+lirk+lds { 268 return nil // line too short 269 } 270 // (111 + lirk) – (110 + lirk + lds) 271 lineOut, err = decode(record[110+lirk : 110+lirk+lds]) 272 if err != nil { 273 return err 274 } 275 ivData.DigitalSignature = ivData.stringToBytesField(lineOut) 276 // (111 + lirk + lds) – (117 + lirk + lds) 277 lineOut, err = decode(record[110+lirk+lds : 117+lirk+lds]) 278 if err != nil { 279 return err 280 } 281 ivData.LengthImageData = ivData.parseStringField(lineOut) 282 283 lid := ivData.parseNumField(ivData.LengthImageData) 284 if lid < 0 || recordLength < 117+lirk+lds+lid { 285 return nil // line too short 286 } 287 // (118 + lirk + lds) – (117+lirk + lds + lid) 288 ivData.ImageData = ivData.stringToBytesField(record[117+lirk+lds : 117+lirk+lds+lid]) 289 290 return nil 291 } 292 293 // Parse takes the input record string and parses the ImageViewData values 294 func (ivData *ImageViewData) Parse(record string) error { 295 return ivData.ParseAndDecode(record, Passthrough) 296 } 297 298 func (ivData *ImageViewData) UnmarshalJSON(data []byte) error { 299 type Alias ImageViewData 300 aux := struct { 301 *Alias 302 }{ 303 (*Alias)(ivData), 304 } 305 if err := json.Unmarshal(data, &aux); err != nil { 306 return err 307 } 308 ivData.setRecordType() 309 return nil 310 } 311 312 // String writes the ImageViewData struct to a string. 313 func (ivData *ImageViewData) String() string { 314 if ivData == nil { 315 return "" 316 } 317 return ivData.toString(true) 318 } 319 320 func (ivData *ImageViewData) toString(inclImage bool) string { 321 if ivData == nil { 322 return "" 323 } 324 325 var buf strings.Builder 326 buf.Grow(105) 327 buf.WriteString(ivData.recordType) 328 buf.WriteString(ivData.EceInstitutionRoutingNumberField()) 329 buf.WriteString(ivData.BundleBusinessDateField()) 330 buf.WriteString(ivData.CycleNumberField()) 331 buf.WriteString(ivData.EceInstitutionItemSequenceNumberField()) 332 buf.WriteString(ivData.SecurityOriginatorNameField()) 333 buf.WriteString(ivData.SecurityAuthenticatorNameField()) 334 buf.WriteString(ivData.SecurityKeyNameField()) 335 buf.WriteString(ivData.ClippingOriginField()) 336 buf.WriteString(ivData.ClippingCoordinateH1Field()) 337 buf.WriteString(ivData.ClippingCoordinateH2Field()) 338 buf.WriteString(ivData.ClippingCoordinateV1Field()) 339 buf.WriteString(ivData.ClippingCoordinateV2Field()) 340 buf.WriteString(ivData.LengthImageReferenceKeyField()) 341 if size := ivData.parseNumField(ivData.LengthImageReferenceKey); validSize(size) { 342 buf.Grow(size) 343 } 344 buf.WriteString(ivData.ImageReferenceKeyField()) 345 buf.WriteString(ivData.LengthDigitalSignatureField()) 346 if size := ivData.parseNumField(ivData.LengthDigitalSignature); validSize(size) { 347 buf.Grow(size) 348 } 349 buf.WriteString(ivData.DigitalSignatureField()) 350 buf.WriteString(ivData.LengthImageDataField()) 351 if inclImage { 352 if size := ivData.parseNumField(ivData.LengthImageData); validSize(size) { 353 buf.Grow(size) 354 } 355 buf.WriteString(ivData.ImageDataField()) 356 } 357 return buf.String() 358 } 359 360 // Validate performs image cash letter format rule checks on the record and returns an error if not Validated 361 // The first error encountered is returned and stops the parsing. 362 func (ivData *ImageViewData) Validate() error { 363 if err := ivData.fieldInclusion(); err != nil { 364 return err 365 } 366 // Mandatory 367 if ivData.recordType != "52" { 368 msg := fmt.Sprintf(msgRecordType, 52) 369 return &FieldError{FieldName: "recordType", Value: ivData.recordType, Msg: msg} 370 } 371 if err := ivData.isAlphanumeric(ivData.CycleNumber); err != nil { 372 return &FieldError{FieldName: "CycleNumber", Value: ivData.CycleNumber, Msg: err.Error()} 373 } 374 if err := ivData.isAlphanumericSpecial(ivData.SecurityOriginatorName); err != nil { 375 return &FieldError{FieldName: "SecurityOriginatorName", Value: ivData.SecurityOriginatorName, Msg: err.Error()} 376 } 377 if err := ivData.isAlphanumericSpecial(ivData.SecurityAuthenticatorName); err != nil { 378 return &FieldError{FieldName: "SecurityAuthenticatorName", Value: ivData.SecurityAuthenticatorName, Msg: err.Error()} 379 } 380 if err := ivData.isAlphanumericSpecial(ivData.SecurityKeyName); err != nil { 381 return &FieldError{FieldName: "SecurityKeyName", Value: ivData.SecurityKeyName, Msg: err.Error()} 382 } 383 if err := ivData.isAlphanumericSpecial(ivData.ImageReferenceKey); err != nil { 384 return &FieldError{FieldName: "ImageReferenceKey", Value: ivData.ImageReferenceKey, Msg: err.Error()} 385 } 386 return nil 387 } 388 389 // fieldInclusion validate mandatory fields are not default values. If fields are 390 // invalid the Electronic Exchange will be returned. 391 func (ivData *ImageViewData) fieldInclusion() error { 392 if ivData.recordType == "" { 393 return &FieldError{FieldName: "recordType", 394 Value: ivData.recordType, 395 Msg: msgFieldInclusion + ", did you use ImageViewData()?"} 396 } 397 if ivData.EceInstitutionRoutingNumber == "" { 398 return &FieldError{FieldName: "EceInstitutionRoutingNumber", 399 Value: ivData.EceInstitutionRoutingNumber, 400 Msg: msgFieldInclusion + ", did you use ImageViewData()?"} 401 } 402 if ivData.EceInstitutionRoutingNumberField() == "000000000" { 403 return &FieldError{FieldName: "EceInstitutionRoutingNumber", 404 Value: ivData.EceInstitutionRoutingNumber, 405 Msg: msgFieldInclusion + ", did you use ImageViewData()?"} 406 } 407 if ivData.BundleBusinessDate.IsZero() { 408 return &FieldError{FieldName: "BundleBusinessDate", 409 Value: ivData.BundleBusinessDate.String(), 410 Msg: msgFieldInclusion + ", did you use ImageViewData()?"} 411 } 412 return nil 413 } 414 415 // EceInstitutionRoutingNumberField gets the EceInstitutionRoutingNumber field 416 func (ivData *ImageViewData) EceInstitutionRoutingNumberField() string { 417 return ivData.stringField(ivData.EceInstitutionRoutingNumber, 9) 418 } 419 420 // BundleBusinessDateField gets the BundleBusinessDate field 421 func (ivData *ImageViewData) BundleBusinessDateField() string { 422 return ivData.formatYYYYMMDDDate(ivData.BundleBusinessDate) 423 } 424 425 // CycleNumberField gets the CycleNumber field 426 func (ivData *ImageViewData) CycleNumberField() string { 427 return ivData.alphaField(ivData.CycleNumber, 2) 428 } 429 430 // EceInstitutionItemSequenceNumberField gets a string of the EceInstitutionItemSequenceNumber field 431 func (ivData *ImageViewData) EceInstitutionItemSequenceNumberField() string { 432 return ivData.alphaField(ivData.EceInstitutionItemSequenceNumber, 15) 433 } 434 435 // SecurityOriginatorNameField gets the SecurityOriginatorName field 436 func (ivData *ImageViewData) SecurityOriginatorNameField() string { 437 return ivData.alphaField(ivData.SecurityOriginatorName, 16) 438 } 439 440 // SecurityAuthenticatorNameField gets the SecurityAuthenticatorName field 441 func (ivData *ImageViewData) SecurityAuthenticatorNameField() string { 442 return ivData.alphaField(ivData.SecurityAuthenticatorName, 16) 443 } 444 445 // SecurityKeyNameField gets the SecurityKeyName field 446 func (ivData *ImageViewData) SecurityKeyNameField() string { 447 return ivData.alphaField(ivData.SecurityKeyName, 16) 448 } 449 450 // ClippingOriginField gets the ClippingOrigin field 451 func (ivData *ImageViewData) ClippingOriginField() string { 452 return ivData.numericField(ivData.ClippingOrigin, 1) 453 } 454 455 // ClippingCoordinateH1Field gets the ClippingCoordinateH1 field 456 func (ivData *ImageViewData) ClippingCoordinateH1Field() string { 457 return ivData.alphaField(ivData.ClippingCoordinateH1, 4) 458 } 459 460 // ClippingCoordinateH2Field gets the ClippingCoordinateH2 field 461 func (ivData *ImageViewData) ClippingCoordinateH2Field() string { 462 return ivData.alphaField(ivData.ClippingCoordinateH2, 4) 463 } 464 465 // ClippingCoordinateV1Field gets the ClippingCoordinateV1 field 466 func (ivData *ImageViewData) ClippingCoordinateV1Field() string { 467 return ivData.alphaField(ivData.ClippingCoordinateH1, 4) 468 } 469 470 // ClippingCoordinateV2Field gets the ClippingCoordinateH2 field 471 func (ivData *ImageViewData) ClippingCoordinateV2Field() string { 472 return ivData.alphaField(ivData.ClippingCoordinateV2, 4) 473 } 474 475 // LengthImageReferenceKeyField gets the LengthImageReferenceKey field 476 func (ivData *ImageViewData) LengthImageReferenceKeyField() string { 477 return ivData.stringField(ivData.LengthImageReferenceKey, 4) 478 } 479 480 // ImageReferenceKeyField gets the ImageReferenceKey field 481 func (ivData *ImageViewData) ImageReferenceKeyField() string { 482 return ivData.alphaField(ivData.ImageReferenceKey, uint(ivData.parseNumField(ivData.LengthImageReferenceKey))) 483 } 484 485 // LengthDigitalSignatureField gets the LengthDigitalSignature field 486 func (ivData *ImageViewData) LengthDigitalSignatureField() string { 487 return ivData.alphaField(ivData.LengthDigitalSignature, 5) 488 } 489 490 // DigitalSignatureField gets the DigitalSignature field []byte to string 491 func (ivData *ImageViewData) DigitalSignatureField() string { 492 s := string(ivData.DigitalSignature[:]) 493 return ivData.alphaField(s, uint(ivData.parseNumField(ivData.LengthDigitalSignature))) 494 } 495 496 // LengthImageDataField gets the LengthImageData field 497 func (ivData *ImageViewData) LengthImageDataField() string { 498 return ivData.alphaField(ivData.LengthImageData, 7) 499 } 500 501 // ImageDataField gets the ImageData field []byte to string 502 func (ivData *ImageViewData) ImageDataField() string { 503 // Try and decode our image data, otherwise use the raw bytes. 504 if decoded, err := ivData.DecodeImageData(); len(decoded) > 0 && err == nil { 505 return ivData.alphaField(string(decoded[:]), uint(len(decoded))) 506 } 507 508 // Return the untouched image data padded according to the spec 509 s := string(ivData.ImageData[:]) 510 return ivData.alphaField(s, uint(ivData.parseNumField(ivData.LengthImageData))) 511 } 512 513 // DecodeImageData attempts to read ImageData as a base64 blob. Other formats may be 514 // supported in the future. 515 func (ivData *ImageViewData) DecodeImageData() ([]byte, error) { 516 if ivData == nil || len(ivData.ImageData) == 0 { 517 return nil, nil 518 } 519 520 // Setup output buffer 521 out := make([]byte, base64.StdEncoding.DecodedLen(len(ivData.ImageData))) 522 523 // Decode the image data 524 n, err := base64.StdEncoding.Decode(out, ivData.ImageData) 525 if n == 0 || err != nil { 526 return nil, err 527 } 528 return out, nil 529 }