github.com/boki/go-xmp@v1.0.1/models/pm/model.go (about) 1 // Copyright (c) 2017-2018 Alexander Eichhorn 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 // http://forums.camerabits.com/index.php?topic=903.0 16 17 // Package pm implements metadata written by Photomechanic software found in many professional images. 18 package pm 19 20 import ( 21 "bytes" 22 "fmt" 23 "strconv" 24 "strings" 25 "time" 26 27 "trimmer.io/go-xmp/xmp" 28 ) 29 30 var ( 31 NsPm = xmp.NewNamespace("photomechanic", "http://ns.camerabits.com/photomechanic/1.0/", NewModel) 32 ) 33 34 func init() { 35 xmp.Register(NsPm, xmp.ImageMetadata) 36 } 37 38 func NewModel(name string) xmp.Model { 39 return &Photomechanic{} 40 } 41 42 func MakeModel(d *xmp.Document) (*Photomechanic, error) { 43 m, err := d.MakeModel(NsPm) 44 if err != nil { 45 return nil, err 46 } 47 x, _ := m.(*Photomechanic) 48 return x, nil 49 } 50 51 func FindModel(d *xmp.Document) *Photomechanic { 52 if m := d.FindModel(NsPm); m != nil { 53 return m.(*Photomechanic) 54 } 55 return nil 56 } 57 58 func (x Photomechanic) Can(nsName string) bool { 59 return nsName == "photomechanic" 60 } 61 62 func (x *Photomechanic) Namespaces() xmp.NamespaceList { 63 return xmp.NamespaceList{NsPm} 64 } 65 66 func (x *Photomechanic) SyncModel(d *xmp.Document) error { 67 return nil 68 } 69 70 func (x *Photomechanic) SyncFromXMP(d *xmp.Document) error { 71 return nil 72 } 73 74 func (x Photomechanic) SyncToXMP(d *xmp.Document) error { 75 return nil 76 } 77 78 func (x *Photomechanic) CanTag(tag string) bool { 79 _, err := xmp.GetNativeField(x, tag) 80 return err == nil 81 } 82 83 func (x *Photomechanic) GetTag(tag string) (string, error) { 84 if v, err := xmp.GetNativeField(x, tag); err != nil { 85 return "", fmt.Errorf("%s: %v", NsPm.GetName(), err) 86 } else { 87 return v, nil 88 } 89 } 90 91 func (x *Photomechanic) SetTag(tag, value string) error { 92 if err := xmp.SetNativeField(x, tag, value); err != nil { 93 return fmt.Errorf("%s: %v", NsPm.GetName(), err) 94 } 95 return nil 96 } 97 98 type Photomechanic struct { 99 EditStatus string `xmp:"photomechanic:EditStatus"` // edit status field from IPTC record 100 ColorClass int `xmp:"photomechanic:ColorClass"` // USA 101 CountryCode string `xmp:"photomechanic:CountryCode"` // USA 102 TimeCreated Time `xmp:"photomechanic:TimeCreated"` // HHMMSS+dHdS 103 Prefs Preferences `xmp:"photomechanic:Prefs"` // T:C:R:F 104 Tagged xmp.Bool `xmp:"photomechanic:Tagged"` // "True" 105 Version string `xmp:"photomechanic:PMVersion"` // PM5 106 ColorClassEval int `xmp:"photomechanic:ColorClassEval"` // ="2" 107 ColorClassApply xmp.Bool `xmp:"photomechanic:ColorClassApply"` // ="True" 108 RatingEval int `xmp:"photomechanic:RatingEval"` // ="4" 109 RatingApply xmp.Bool `xmp:"photomechanic:RatingApply"` // ="True" 110 TagEval int `xmp:"photomechanic:TagEval"` // ="0" 111 TagApply xmp.Bool `xmp:"photomechanic:TagApply"` // ="False" 112 CaptionMergeStyle int `xmp:"photomechanic:CaptionMergeStyle"` // ="1" 113 ApplyAPCustom int `xmp:"photomechanic:ApplyAPCustom"` // ="0" 114 MergeAPCustom int `xmp:"photomechanic:MergeAPCustom"` // ="0" 115 ApplyDateType int `xmp:"photomechanic:ApplyDateType"` // ="2" 116 FieldsToApply xmp.StringArray `xmp:"photomechanic:FieldsToApply"` 117 } 118 119 type Time time.Time 120 121 const pmTimeFormat = "150405-0700" 122 123 // HH = 24hr hour value (0-23) 124 // MM = minute value (0-59) 125 // SS = second value (0-59) 126 // dH = GMT hour delta (+/- 12) 127 // dS = GMT minute delta (0-59) 128 129 func (x Time) IsZero() bool { 130 return time.Time(x).IsZero() 131 } 132 133 func (x Time) MarshalText() ([]byte, error) { 134 if x.IsZero() { 135 return nil, nil 136 } 137 return []byte(time.Time(x).Format(pmTimeFormat)), nil 138 } 139 140 func (x *Time) UnmarshalText(data []byte) error { 141 if len(data) == 0 { 142 return nil 143 } 144 if t, err := time.Parse(pmTimeFormat, string(data)); err != nil { 145 return fmt.Errorf("photomechanic: invalid time value '%s'", string(data)) 146 } else { 147 *x = Time(t) 148 } 149 return nil 150 } 151 152 // T:C:R:F 153 type Preferences struct { 154 TagStatus int // T = Tag status (0 or 1) 155 ColorClass int // C = Color Class value (0-8) 156 Rating int // R = Rating (0-5) 157 Frame int // F = Frame number of image or -1 if undetermined 158 } 159 160 func (x Preferences) IsZero() bool { 161 return x.TagStatus == 0 && x.ColorClass == 0 && x.Rating == 0 && x.Frame == 0 162 } 163 164 func (x Preferences) MarshalText() ([]byte, error) { 165 buf := bytes.Buffer{} 166 buf.WriteString(strconv.FormatInt(int64(x.TagStatus), 10)) 167 buf.WriteByte(':') 168 buf.WriteString(strconv.FormatInt(int64(x.ColorClass), 10)) 169 buf.WriteByte(':') 170 buf.WriteString(strconv.FormatInt(int64(x.Rating), 10)) 171 buf.WriteByte(':') 172 buf.WriteString(strconv.FormatInt(int64(x.Frame), 10)) 173 return buf.Bytes(), nil 174 } 175 176 func (x *Preferences) UnmarshalText(data []byte) error { 177 p := Preferences{} 178 var err error 179 fields := strings.Split(string(data), ":") 180 if len(fields) != 4 { 181 return fmt.Errorf("photomechanic: invalid prefs value '%s'", string(data)) 182 } 183 if p.TagStatus, err = strconv.Atoi(fields[0]); err != nil { 184 return fmt.Errorf("photomechanic: invalid tag status value '%s': %v", string(data), err) 185 } 186 if p.ColorClass, err = strconv.Atoi(fields[1]); err != nil { 187 return fmt.Errorf("photomechanic: invalid color class value '%s': %v", string(data), err) 188 } 189 if p.Rating, err = strconv.Atoi(fields[2]); err != nil { 190 return fmt.Errorf("photomechanic: invalid rating value '%s': %v", string(data), err) 191 } 192 if p.Frame, err = strconv.Atoi(fields[3]); err != nil { 193 return fmt.Errorf("photomechanic: invalid frame number value '%s': %v", string(data), err) 194 } 195 *x = p 196 return nil 197 }