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 }