1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gexiv2/gexiv2.h>
21 #include <glib.h>
22 #include <gtk/gtk.h>
23 
24 #include <libgimp/gimp.h>
25 #include "libgimp/stdplugins-intl.h"
26 
27 #include "metadata-tags.h"
28 
29 
30 /* The meaning of "single" and "multi" here denotes whether it is used in a
31  * single line or a multi line edit field.
32  * Depending on it's xmp type multi line can be saved as either:
33  * - one tag of type text, possibly including newlines
34  * - an array of tags of the same type for seq and bag, where each line in
35  *   the multi line edit will be one item in the array
36  */
37 const metadata_tag default_metadata_tags[] =
38 {
39   /* Description */
40   { "Xmp.dc.title",                              "single", 16,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, //  0
41   { "Xmp.dc.creator",                            "single", 13,  TAG_TYPE_XMP, GIMP_XMP_SEQ   }, //  1
42   { "Xmp.dc.description",                        "multi",  14,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, //  2
43   { "Xmp.dc.subject",                            "multi",  15,  TAG_TYPE_XMP, GIMP_XMP_BAG   }, //  3
44   { "Xmp.dc.rights",                             "single", 17,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, //  4
45   { "Xmp.photoshop.AuthorsPosition",             "single", 19,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, //  5
46   { "Xmp.photoshop.CaptionWriter",               "single", 21,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, //  6
47   { "Xmp.xmp.Rating",                            "combo",  -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, //  7
48   { "Xmp.xmpRights.Marked",                      "combo",  -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, //  8
49   { "Xmp.xmpRights.WebStatement",                "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, //  9
50 
51   /* IPTC */
52   { "Xmp.photoshop.DateCreated",                 "single",  0,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 10
53   { "Xmp.photoshop.Headline",                    "multi",   3,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 11
54   { "Xmp.photoshop.TransmissionReference",       "single",  1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 12
55   { "Xmp.photoshop.Instructions",                "multi",   2,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 13
56   { "Xmp.iptc.IntellectualGenre",                "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 14
57   { "Xmp.iptc.Scene",                            "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_BAG   }, // 15
58   { "Xmp.iptc.Location",                         "single", 18,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 16
59   { "Xmp.iptc.CountryCode",                      "single", 20,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 17
60   { "Xmp.iptc.SubjectCode",                      "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_BAG   }, // 18
61   { "Xmp.xmpRights.UsageTerms",                  "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 19
62   { "Xmp.photoshop.City",                        "single",  5,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 20
63   { "Xmp.photoshop.State",                       "single",  6,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 21
64   { "Xmp.photoshop.Country",                     "single",  7,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 22
65   /* Xmp.photoshop.CaptionWriter here is a duplicate of #6 above. We keep it here to not have
66    * to renumber the tag references. It seems it is not used on the IPTC tab. */
67   { "Xmp.photoshop.CaptionWriter",               "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 23
68   { "Xmp.photoshop.Credit",                      "single",  8,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 24
69   { "Xmp.photoshop.Source",                      "single",  9,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 25
70   { "Xmp.photoshop.Urgency",                     "combo",  11,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 26
71 
72   /* IPTC Extension */
73   { "Xmp.iptcExt.PersonInImage",                 "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_BAG   }, // 27
74   { "Xmp.iptcExt.Sublocation",                   "single", 12,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 28
75   { "Xmp.iptcExt.City",                          "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 29
76   { "Xmp.iptcExt.ProvinceState",                 "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 30
77   { "Xmp.iptcExt.CountryName",                   "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 31
78   { "Xmp.iptcExt.CountryCode",                   "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 32
79   { "Xmp.iptcExt.WorldRegion",                   "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 33
80   { "Xmp.iptcExt.LocationShown",                 "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 34
81   { "Xmp.iptcExt.OrganisationInImageName",       "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 35
82   { "Xmp.iptcExt.OrganisationInImageCode",       "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 36
83   { "Xmp.iptcExt.Event",                         "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 37
84   { "Xmp.iptcExt.RegistryId",                    "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 38
85   { "Xmp.iptcExt.ArtworkOrObject",               "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 39
86   { "Xmp.iptcExt.AddlModelInfo",                 "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 40
87   { "Xmp.iptcExt.ModelAge",                      "single", -1,  TAG_TYPE_XMP, GIMP_XMP_BAG   }, // 41
88   { "Xmp.iptcExt.MaxAvailWidth",                 "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 42
89   { "Xmp.iptcExt.MaxAvailHeight",                "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 43
90   { "Xmp.iptcExt.DigitalSourceType",             "combo",  -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 44
91   { "Xmp.plus.MinorModelAgeDisclosure",          "combo",  -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 45
92   { "Xmp.plus.ModelReleaseStatus",               "combo",  -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 46
93   { "Xmp.plus.ModelReleaseID",                   "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 47
94   { "Xmp.plus.ImageSupplierName",                "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 48
95   { "Xmp.plus.ImageSupplierID",                  "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 49
96   { "Xmp.plus.ImageSupplierImageID",             "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 50
97   { "Xmp.plus.ImageCreator",                     "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 51
98   { "Xmp.plus.CopyrightOwner",                   "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 52
99   { "Xmp.plus.Licensor",                         "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 53
100   { "Xmp.plus.PropertyReleaseStatus",            "combo",  -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 54
101   { "Xmp.plus.PropertyReleaseID",                "list",   -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 55
102 
103   /* Categories */
104   { "Xmp.photoshop.Category",                    "single",  4,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 56
105   { "Xmp.photoshop.SupplementalCategories",      "multi",  10,  TAG_TYPE_XMP, GIMP_XMP_BAG   }, // 57
106 
107   /* GPS */
108   { "Exif.GPSInfo.GPSLongitude",                 "single", -1,  TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 58
109   { "Exif.GPSInfo.GPSLongitudeRef",              "combo",  -1,  TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 59
110   { "Exif.GPSInfo.GPSLatitude",                  "single", -1,  TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 60
111   { "Exif.GPSInfo.GPSLatitudeRef",               "combo",  -1,  TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 61
112   { "Exif.GPSInfo.GPSAltitude",                  "single", -1,  TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 62
113   { "Exif.GPSInfo.GPSAltitudeRef",               "combo",  -1,  TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 63
114 
115   /* DICOM */
116   { "Xmp.DICOM.PatientName",                     "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 64
117   { "Xmp.DICOM.PatientID",                       "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 65
118   { "Xmp.DICOM.PatientDOB",                      "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 66
119   { "Xmp.DICOM.PatientSex",                      "combo",  -1,  TAG_TYPE_XMP, GIMP_XMP_NONE  }, // 67
120   { "Xmp.DICOM.StudyID",                         "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 68
121   { "Xmp.DICOM.StudyPhysician",                  "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 69
122   { "Xmp.DICOM.StudyDateTime",                   "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 70
123   { "Xmp.DICOM.StudyDescription",                "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 71
124   { "Xmp.DICOM.SeriesNumber",                    "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 72
125   { "Xmp.DICOM.SeriesModality",                  "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 73
126   { "Xmp.DICOM.SeriesDateTime",                  "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 74
127   { "Xmp.DICOM.SeriesDescription",               "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 75
128   { "Xmp.DICOM.EquipmentInstitution",            "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 76
129   { "Xmp.DICOM.EquipmentManufacturer",           "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 77
130 
131   /* IPTC */
132   { "Xmp.iptc.CiAdrExtadr",                      "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 78
133   { "Xmp.iptc.CiAdrCity",                        "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 79
134   { "Xmp.iptc.CiAdrRegion",                      "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 80
135   { "Xmp.iptc.CiAdrPcode",                       "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 81
136   { "Xmp.iptc.CiAdrCtry",                        "single", -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 82
137   { "Xmp.iptc.CiTelWork",                        "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 83
138   { "Xmp.iptc.CiEmailWork",                      "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }, // 84
139   { "Xmp.iptc.CiUrlWork",                        "multi",  -1,  TAG_TYPE_XMP, GIMP_XMP_TEXT  }  // 85
140 
141 };
142 const gint n_default_metadata_tags = G_N_ELEMENTS (default_metadata_tags);
143 
144 /* Then meaning of "single" and "multi" below is a little different than above.
145  * "single" - for iptc tags that can appear only once,
146  * "multi"  - for iptc tags that are repeatable, i.e. can appear multiple times.
147  */
148 const metadata_tag equivalent_metadata_tags[] =
149 {
150   { "Iptc.Application2.DateCreated",             "single", 10,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  0
151   { "Iptc.Application2.TransmissionReference",   "single", 12,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  1
152   { "Iptc.Application2.SpecialInstructions",     "single", 13,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  2
153   { "Iptc.Application2.Headline",                "single", 11,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  3
154   { "Iptc.Application2.Category",                "single", 56,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  4
155   { "Iptc.Application2.City",                    "single", 20,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  5
156   { "Iptc.Application2.ProvinceState",           "single", 21,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  6
157   { "Iptc.Application2.CountryName",             "single", 22,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  7
158   { "Iptc.Application2.Credit",                  "single", 24,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  8
159   { "Iptc.Application2.Source",                  "single", 25,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, //  9
160   { "Iptc.Application2.SuppCategory",            "multi",  57,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 10
161   { "Iptc.Application2.Urgency",                 "combo",  26,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 11
162   { "Iptc.Application2.SubLocation",             "single", 28,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 12
163   { "Iptc.Application2.Byline",                  "single",  1,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 13
164   { "Iptc.Application2.Caption",                 "single",  2,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 14
165   { "Iptc.Application2.Keywords",                "multi",   3,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 15
166   { "Iptc.Application2.ObjectName",              "single",  0,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 16
167   { "Iptc.Application2.Copyright",               "single",  4,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 17
168   { "Iptc.Application2.LocationName",            "multi",  16,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 18
169   { "Iptc.Application2.BylineTitle",             "multi",   5,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 19
170   { "Iptc.Application2.CountryCode",             "single", 17,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 20
171   { "Iptc.Application2.Writer",                  "multi",   6,  TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 21
172 };
173 const gint n_equivalent_metadata_tags = G_N_ELEMENTS (equivalent_metadata_tags);
174 
175 /* Digital Source Type Combobox Items
176  * http://cv.iptc.org/newscodes/digitalsourcetype/
177  */
178 const combobox_str_tag digitalsourcetype[] =
179 {
180   { "",                                                              N_("Select a value")                                   },
181   { "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture", N_("Original digital capture of a real life scene")    },
182   { "http://cv.iptc.org/newscodes/digitalsourcetype/negativeFilm",   N_("Digitized from a negative on film")                },
183   { "http://cv.iptc.org/newscodes/digitalsourcetype/positiveFilm",   N_("Digitized from a positive on film")                },
184   { "http://cv.iptc.org/newscodes/digitalsourcetype/print",          N_("Digitized from a print on non-transparent medium") },
185   { "http://cv.iptc.org/newscodes/digitalsourcetype/softwareImage",  N_("Created by software")                              }
186 };
187 const gint n_digitalsourcetype = G_N_ELEMENTS (digitalsourcetype);
188 
189 /* Model Release Status Combobox Items
190  * http://ns.useplus.org/LDF/ldf-XMPSpecification#ModelReleaseStatus
191  */
192 const combobox_str_tag modelreleasestatus[] =
193 {
194   { "",                                       N_("Select a value")                       },
195   { "http://ns.useplus.org/ldf/vocab/MR-NON", N_("None")                                 },
196   { "http://ns.useplus.org/ldf/vocab/MR-NAP", N_("Not Applicable")                       },
197   { "http://ns.useplus.org/ldf/vocab/MR-UMR", N_("Unlimited Model Releases")             },
198   { "http://ns.useplus.org/ldf/vocab/MR-LMR", N_("Limited or Incomplete Model Releases") }
199 };
200 const gint n_modelreleasestatus = G_N_ELEMENTS (modelreleasestatus);
201 
202 /* Property Release Status Combobox Items
203  * http://ns.useplus.org/LDF/ldf-XMPSpecification#PropertyReleaseStatus
204  */
205 const combobox_str_tag propertyreleasestatus[] =
206 {
207   { "http://ns.useplus.org/ldf/vocab/PR-NON", N_("None")                                    },
208   { "http://ns.useplus.org/ldf/vocab/PR-NAP", N_("Not Applicable")                          },
209   { "http://ns.useplus.org/ldf/vocab/PR-UPR", N_("Unlimited Property Releases")             },
210   { "http://ns.useplus.org/ldf/vocab/PR-LPR", N_("Limited or Incomplete Property Releases") }
211 };
212 const gint n_propertyreleasestatus = G_N_ELEMENTS (propertyreleasestatus);
213 
214 /* Minor Model Age Disclosure Combobox Items
215  * http://ns.useplus.org/LDF/ldf-XMPSpecification#MinorModelAgeDisclosure
216  */
217 const combobox_str_tag minormodelagedisclosure[] =
218 {
219   { "http://ns.useplus.org/ldf/vocab/AG-UNK", N_("Age Unknown")     },
220   { "http://ns.useplus.org/ldf/vocab/AG-A25", N_("Age 25 or Over")  },
221   { "http://ns.useplus.org/ldf/vocab/AG-A24", N_("Age 24")          },
222   { "http://ns.useplus.org/ldf/vocab/AG-A23", N_("Age 23")          },
223   { "http://ns.useplus.org/ldf/vocab/AG-A22", N_("Age 22")          },
224   { "http://ns.useplus.org/ldf/vocab/AG-A21", N_("Age 21")          },
225   { "http://ns.useplus.org/ldf/vocab/AG-A20", N_("Age 20")          },
226   { "http://ns.useplus.org/ldf/vocab/AG-A19", N_("Age 19")          },
227   { "http://ns.useplus.org/ldf/vocab/AG-A18", N_("Age 18")          },
228   { "http://ns.useplus.org/ldf/vocab/AG-A17", N_("Age 17")          },
229   { "http://ns.useplus.org/ldf/vocab/AG-A16", N_("Age 16")          },
230   { "http://ns.useplus.org/ldf/vocab/AG-A15", N_("Age 15")          },
231   { "http://ns.useplus.org/ldf/vocab/AG-U14", N_("Age 14 or Under") }
232 };
233 const gint n_minormodelagedisclosure = G_N_ELEMENTS (minormodelagedisclosure);
234 
235 /* Urgency */
236 const gchar *urgency[] =
237 {
238   N_("None"), N_("High"), N_("2"), N_("3"), N_("4"), N_("Normal"), N_("6"), N_("7"), N_("Low")
239 };
240 const gint n_urgency = G_N_ELEMENTS (urgency);
241 
242 /* Marked */
243 const combobox_int_tag marked[] =
244 {
245   { -1,     N_("Unknown")       }, // DO NOT SAVE
246   {  TRUE,  N_("Copyrighted")   }, // TRUE
247   {  FALSE, N_("Public Domain") }, // FALSE
248 };
249 const gint n_marked = G_N_ELEMENTS (marked);
250 
251 /* Phone Types */
252 const combobox_str_tag phone_types[] =
253 {
254   { "",                                      N_("Select a value") },
255   { "http://ns.useplus.org/ldf/vocab/work",  N_("Work")  },
256   { "http://ns.useplus.org/ldf/vocab/cell",  N_("Cell")  },
257   { "http://ns.useplus.org/ldf/vocab/fax",   N_("Fax")   },
258   { "http://ns.useplus.org/ldf/vocab/home",  N_("Home")  },
259   { "http://ns.useplus.org/ldf/vocab/pager", N_("Pager") }
260 };
261 const gint n_phone_types = G_N_ELEMENTS (phone_types);
262 
263 /* DICOM Patient Sex
264  * http://dicomlookup.com/lookup.asp?sw=Ttable&q=C.7-1
265  * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/XMP.html#DICOM
266  * https://dicomiseasy.blogspot.ca/2011/11/introduction-to-dicom-chapter-iii-dicom.html
267  * http://dicom.nema.org/standard.html
268  */
269 const combobox_str_tag dicom[] =
270 {
271   { "",        N_("Select a value") },
272   { "male",    N_("Male")    },
273   { "female",  N_("Female")  },
274   { "other",   N_("Other")   },
275 };
276 const gint n_dicom = G_N_ELEMENTS (dicom);
277 
278 /* GPS Altitude Ref */
279 const gchar *gpsaltref[] =
280 {
281   N_("Unknown"), N_("Above sea level"), N_("Below sea level")
282 };
283 const gint n_gpsaltref = G_N_ELEMENTS (gpsaltref);
284 
285 /* GPS Latitude Ref */
286 const gchar *gpslatref[] =
287 {
288   N_("Unknown"), N_("North"), N_("South")
289 };
290 const gint n_gpslatref = G_N_ELEMENTS (gpslatref);
291 
292 /* GPS Longitude Ref */
293 const gchar *gpslngref[] =
294 {
295   N_("Unknown"), N_("East"), N_("West")
296 };
297 const gint n_gpslngref = G_N_ELEMENTS (gpslngref);
298 
299 /* GPS Measurement System */
300 const gchar *gpsaltsys[] =
301 {
302   "m", "ft"
303 };
304 const gint n_gpsaltsys = G_N_ELEMENTS (gpsaltsys);
305 
306 const TranslateHeaderTag creatorContactInfoHeader =
307 {
308   "Xmp.iptc.CreatorContactInfo", "type=\"Struct\"", 8
309 };
310 
311 const TranslateTag creatorContactInfoTags[] =
312 {
313   { "Xmp.iptc.CiAdrExtadr", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrExtadr", "multi",  -1,  TAG_TYPE_XMP },
314   { "Xmp.iptc.CiAdrCity",   "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCity",   "single", -1,  TAG_TYPE_XMP },
315   { "Xmp.iptc.CiAdrRegion", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrRegion", "single", -1,  TAG_TYPE_XMP },
316   { "Xmp.iptc.CiAdrPcode",  "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrPcode",  "single", -1,  TAG_TYPE_XMP },
317   { "Xmp.iptc.CiAdrCtry",   "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCtry",   "single", -1,  TAG_TYPE_XMP },
318   { "Xmp.iptc.CiTelWork",   "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiTelWork",   "multi",  -1,  TAG_TYPE_XMP },
319   { "Xmp.iptc.CiEmailWork", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiEmailWork", "multi",  -1,  TAG_TYPE_XMP },
320   { "Xmp.iptc.CiUrlWork",   "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiUrlWork",   "multi",  -1,  TAG_TYPE_XMP }
321 };
322 
323 const TranslateHeaderTag locationCreationInfoHeader =
324 {
325   "Xmp.iptcExt.LocationCreated", "type=\"Bag\"", 6
326 };
327 
328 const TranslateTag locationCreationInfoTags[] =
329 {
330   { "Xmp.iptcExt.Sublocation",   "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:Sublocation",   "single", -1,  TAG_TYPE_XMP },
331   { "Xmp.iptcExt.City",          "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:City",          "single", -1,  TAG_TYPE_XMP },
332   { "Xmp.iptcExt.ProvinceState", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:ProvinceState", "single", -1,  TAG_TYPE_XMP },
333   { "Xmp.iptcExt.CountryName",   "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:CountryName",   "single", -1,  TAG_TYPE_XMP },
334   { "Xmp.iptcExt.CountryCode",   "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:CountryCode",   "single", -1,  TAG_TYPE_XMP },
335   { "Xmp.iptcExt.WorldRegion",   "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:WorldRegion",   "single", -1,  TAG_TYPE_XMP }
336 };
337 
338 const TranslateHeaderTag imageSupplierInfoHeader =
339 {
340   "Xmp.plus.ImageSupplier", "type=\"Seq\"", 2
341 };
342 
343 const TranslateTag imageSupplierInfoTags[] =
344 {
345   { "Xmp.plus.ImageSupplierName",   "Xmp.plus.ImageSupplier[1]/plus:ImageSupplierName", "multi",  -1,  TAG_TYPE_XMP },
346   { "Xmp.plus.ImageSupplierID",     "Xmp.plus.ImageSupplier[1]/plus:ImageSupplierID",   "single", -1,  TAG_TYPE_XMP }
347 };
348 
349 /* Plus and IPTC extension tags */
350 
351 const gchar *licensor[] =
352 {
353   "/plus:LicensorName",
354   "/plus:LicensorID",
355   "/plus:LicensorTelephone1",
356   "/plus:LicensorTelephoneType1",
357   "/plus:LicensorTelephone2",
358   "/plus:LicensorTelephoneType2",
359   "/plus:LicensorEmail",
360   "/plus:LicensorURL"
361 };
362 const gint n_licensor = G_N_ELEMENTS (licensor);
363 
364 const gint licensor_special_handling[] =
365 {
366   METADATA_NONE,
367   METADATA_NONE,
368   METADATA_NONE,
369   METADATA_PHONETYPE,
370   METADATA_NONE,
371   METADATA_PHONETYPE,
372   METADATA_NONE,
373   METADATA_NONE
374 };
375 
376 #ifdef USE_TAGS
377 const gchar *imagesupplier[] =
378 {
379   "/plus:ImageSupplierName",
380   "/plus:ImageSupplierID"
381 };
382 const gint n_imagesupplier = G_N_ELEMENTS (imagesupplier);
383 #endif
384 
385 const gchar *imagecreator[] =
386 {
387   "/plus:ImageCreatorName",
388   "/plus:ImageCreatorID"
389 };
390 const gint n_imagecreator = G_N_ELEMENTS (imagecreator);
391 
392 const gchar *copyrightowner[] =
393 {
394   "/plus:CopyrightOwnerName",
395   "/plus:CopyrightOwnerID"
396 };
397 const gint n_copyrightowner = G_N_ELEMENTS (copyrightowner);
398 
399 const gchar *registryid[] =
400 {
401   "/Iptc4xmpExt:RegOrgId",
402   "/Iptc4xmpExt:RegItemId"
403 };
404 const gint n_registryid = G_N_ELEMENTS (registryid);
405 
406 const gchar *registryid_alternative[] =
407 {
408   "/iptcExt:RegOrgId",
409   "/iptcExt:RegItemId"
410 };
411 
412 const gchar *artworkorobject[] =
413 {
414   "/Iptc4xmpExt:AOTitle",
415   "/Iptc4xmpExt:AODateCreated",
416   "/Iptc4xmpExt:AOCreator",
417   "/Iptc4xmpExt:AOSource",
418   "/Iptc4xmpExt:AOSourceInvNo",
419   "/Iptc4xmpExt:AOCopyrightNotice",
420 };
421 const gint n_artworkorobject = G_N_ELEMENTS (artworkorobject);
422 
423 const gchar *artworkorobject_alternative[] =
424 {
425   "/iptcExt:AOTitle",
426   "/iptcExt:AODateCreated",
427   "/iptcExt:AOCreator",
428   "/iptcExt:AOSource",
429   "/iptcExt:AOSourceInvNo",
430   "/iptcExt:AOCopyrightNotice",
431 };
432 
433 const gchar *locationshown[] =
434 {
435   "/Iptc4xmpExt:Sublocation",
436   "/Iptc4xmpExt:City",
437   "/Iptc4xmpExt:ProvinceState",
438   "/Iptc4xmpExt:CountryName",
439   "/Iptc4xmpExt:CountryCode",
440   "/Iptc4xmpExt:WorldRegion"
441 };
442 const gint n_locationshown = G_N_ELEMENTS (locationshown);
443 
444 const gchar *locationshown_alternative[] =
445 {
446   "/iptcExt:Sublocation",
447   "/iptcExt:City",
448   "/iptcExt:ProvinceState",
449   "/iptcExt:CountryName",
450   "/iptcExt:CountryCode",
451   "/iptcExt:WorldRegion"
452 };
453 
454 
455 #ifdef USE_TAGS
456 const gchar *locationcreated[] =
457 {
458   "/Iptc4xmpExt:Sublocation",
459   "/Iptc4xmpExt:City",
460   "/Iptc4xmpExt:ProvinceState",
461   "/Iptc4xmpExt:CountryName",
462   "/Iptc4xmpExt:CountryCode",
463   "/Iptc4xmpExt:WorldRegion"
464 };
465 const gint n_locationcreated = G_N_ELEMENTS (locationcreated);
466 #endif
467 
468 
469 gchar *
metadata_format_gps_longitude_latitude(const gdouble value)470 metadata_format_gps_longitude_latitude (const gdouble value)
471 {
472   gint     deg, min;
473   gdouble  sec;
474   gdouble  gps_value = value;
475 
476   if (gps_value < 0.f)
477     gps_value *= -1.f;
478 
479   deg = (gint) gps_value;
480   min = (gint) ((gps_value - (gdouble) deg) * 60.f);
481   sec = ((gps_value - (gdouble) deg - (gdouble) (min / 60.f)) * 60.f * 60.f);
482 
483   return g_strdup_printf ("%ddeg %d' %.3f\"", deg, min, sec);
484 }
485 
486 /*
487  * use_meter: True return meters, False return feet
488  * measurement_symbol: Should be "m", "ft", or empty string (not NULL)
489  */
490 gchar *
metadata_format_gps_altitude(const gdouble value,gboolean use_meter,gchar * measurement_symbol)491 metadata_format_gps_altitude (const gdouble  value,
492                               gboolean       use_meter,
493                               gchar         *measurement_symbol)
494 {
495   gdouble  gps_value = value;
496 
497   if (gps_value < 0.f)
498     gps_value *= -1.f;
499 
500   if (! use_meter)
501     {
502       gps_value *= 3.28;
503     }
504 
505   return g_strdup_printf ("%.2f%s", gps_value, measurement_symbol);
506 }
507