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  }