github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/model/structures.go (about)

     1  /*
     2   * This file is subject to the terms and conditions defined in
     3   * file 'LICENSE.md', which is part of this source code package.
     4   */
     5  
     6  package model
     7  
     8  // Common basic data structures: PdfRectangle, PdfDate, etc.
     9  // These kinds of data structures can be copied, do not need a unique copy of each object.
    10  
    11  import (
    12  	"errors"
    13  	"fmt"
    14  	"regexp"
    15  	"strconv"
    16  
    17  	. "github.com/unidoc/unidoc/pdf/core"
    18  )
    19  
    20  // Definition of a rectangle.
    21  type PdfRectangle struct {
    22  	Llx float64 // Lower left corner (ll).
    23  	Lly float64
    24  	Urx float64 // Upper right corner (ur).
    25  	Ury float64
    26  }
    27  
    28  // Create a PDF rectangle object based on an input array of 4 integers.
    29  // Defining the lower left (LL) and upper right (UR) corners with
    30  // floating point numbers.
    31  func NewPdfRectangle(arr PdfObjectArray) (*PdfRectangle, error) {
    32  	rect := PdfRectangle{}
    33  	if len(arr) != 4 {
    34  		return nil, errors.New("Invalid rectangle array, len != 4")
    35  	}
    36  
    37  	var err error
    38  	rect.Llx, err = getNumberAsFloat(arr[0])
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	rect.Lly, err = getNumberAsFloat(arr[1])
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	rect.Urx, err = getNumberAsFloat(arr[2])
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	rect.Ury, err = getNumberAsFloat(arr[3])
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	return &rect, nil
    59  }
    60  
    61  // Convert to a PDF object.
    62  func (rect *PdfRectangle) ToPdfObject() PdfObject {
    63  	arr := PdfObjectArray{}
    64  	arr = append(arr, MakeFloat(rect.Llx))
    65  	arr = append(arr, MakeFloat(rect.Lly))
    66  	arr = append(arr, MakeFloat(rect.Urx))
    67  	arr = append(arr, MakeFloat(rect.Ury))
    68  	return &arr
    69  }
    70  
    71  // A date is a PDF string of the form:
    72  // (D:YYYYMMDDHHmmSSOHH'mm)
    73  type PdfDate struct {
    74  	year          int64 // YYYY
    75  	month         int64 // MM (01-12)
    76  	day           int64 // DD (01-31)
    77  	hour          int64 // HH (00-23)
    78  	minute        int64 // mm (00-59)
    79  	second        int64 // SS (00-59)
    80  	utOffsetSign  byte  // O ('+' / '-' / 'Z')
    81  	utOffsetHours int64 // HH' (00-23 followed by ')
    82  	utOffsetMins  int64 // mm (00-59)
    83  }
    84  
    85  var reDate = regexp.MustCompile(`\s*D\s*:\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})([+-Z])?(\d{2})?'?(\d{2})?`)
    86  
    87  // Make a new PdfDate object from a PDF date string (see 7.9.4 Dates).
    88  // format: "D: YYYYMMDDHHmmSSOHH'mm"
    89  func NewPdfDate(dateStr string) (PdfDate, error) {
    90  	d := PdfDate{}
    91  
    92  	matches := reDate.FindAllStringSubmatch(dateStr, 1)
    93  	if len(matches) < 1 {
    94  		return d, fmt.Errorf("Invalid date string (%s)", dateStr)
    95  	}
    96  	if len(matches[0]) != 10 {
    97  		return d, errors.New("Invalid regexp group match length != 10")
    98  	}
    99  
   100  	// No need to handle err from ParseInt, as pre-validated via regexp.
   101  	d.year, _ = strconv.ParseInt(matches[0][1], 10, 32)
   102  	d.month, _ = strconv.ParseInt(matches[0][2], 10, 32)
   103  	d.day, _ = strconv.ParseInt(matches[0][3], 10, 32)
   104  	d.hour, _ = strconv.ParseInt(matches[0][4], 10, 32)
   105  	d.minute, _ = strconv.ParseInt(matches[0][5], 10, 32)
   106  	d.second, _ = strconv.ParseInt(matches[0][6], 10, 32)
   107  	// Some poor implementations do not include the offset.
   108  	if len(matches[0][7]) > 0 {
   109  		d.utOffsetSign = matches[0][7][0]
   110  	} else {
   111  		d.utOffsetSign = '+'
   112  	}
   113  	if len(matches[0][8]) > 0 {
   114  		d.utOffsetHours, _ = strconv.ParseInt(matches[0][8], 10, 32)
   115  	} else {
   116  		d.utOffsetHours = 0
   117  	}
   118  	if len(matches[0][9]) > 0 {
   119  		d.utOffsetMins, _ = strconv.ParseInt(matches[0][9], 10, 32)
   120  	} else {
   121  		d.utOffsetMins = 0
   122  	}
   123  
   124  	return d, nil
   125  }
   126  
   127  // Convert to a PDF string object.
   128  func (date *PdfDate) ToPdfObject() PdfObject {
   129  	str := fmt.Sprintf("D:%.4d%.2d%.2d%.2d%.2d%.2d%c%.2d'%.2d'",
   130  		date.year, date.month, date.day, date.hour, date.minute, date.second,
   131  		date.utOffsetSign, date.utOffsetHours, date.utOffsetMins)
   132  	pdfStr := PdfObjectString(str)
   133  	return &pdfStr
   134  }