5
5
using System . Linq ;
6
6
using System . Text ;
7
7
using System . Threading . Tasks ;
8
+ using System . Windows . Media ;
8
9
using System . Windows . Media . Imaging ;
9
10
10
11
namespace PhotoTagger . Imaging {
@@ -35,7 +36,8 @@ private static string fromUniversalNewline(string from) {
35
36
const string XmpDescriptionQuery = "/xmp/dc:description" ;
36
37
const string DateTakenQuery = "/app1/ifd/exif/{ushort=36867}" ;
37
38
const string DateTakenSubsecQuery = "/app1/ifd/exif/{ushort=37521}" ;
38
- const string OrientationQuery = "/app1/ifd/{ushort=274}" ;
39
+ const string JpegOrientationQuery = "/app1/ifd/{ushort=274}" ;
40
+ const string RawOrientationQuery = "/ifd/{ushort=274}" ;
39
41
40
42
const string LatitudeRefQuery = "/app1/ifd/gps/subifd:{ulong=1}" ;
41
43
const string LatitudeQuery = "/app1/ifd/gps/subifd:{ulong=2}" ;
@@ -45,6 +47,32 @@ private static string fromUniversalNewline(string from) {
45
47
const string PaddingQuery = "/app1/ifd/PaddingSchema:Padding" ;
46
48
const string ExifPaddingQuery = "/app1/ifd/exif/PaddingSchema:Padding" ;
47
49
const string XmpPaddingQuery = "/xmp/PaddingSchema:Padding" ;
50
+ const string ColorSpaceQuery = "/app1/{ushort=0}/{ushort=34665}/{ushort=40961}" ;
51
+
52
+ // From the System.Title Photo Metadata Policy
53
+ readonly static string [ ] TitleReadQueries = {
54
+ WinTitleQuery ,
55
+ "/xmp/<xmpalt>dc:title" ,
56
+ XmpTitleQuery ,
57
+ "/app1/ifd/exif/{ushort=37510}" ,
58
+ TitleQuery ,
59
+ "/app13/irb/8bimiptc/iptc/caption" ,
60
+ "/xmp/<xmpalt>dc:description" ,
61
+ XmpDescriptionQuery ,
62
+ "/app13/irb/8bimiptc/iptc/caption" ,
63
+ "/xmp/<xmpalt>exif:UserComment" ,
64
+ } ;
65
+
66
+ // From the System.Title Photo Metadata Policy
67
+ readonly static string [ ] TitleRemoveQueries = {
68
+ WinTitleQuery ,
69
+ XmpTitleQuery ,
70
+ "/app1/ifd/exif/{ushort=37510}" ,
71
+ "/xmp/<xmpalt>exif:UserComment" ,
72
+ TitleQuery ,
73
+ "/app13/irb/8bimiptc/iptc/caption" ,
74
+ XmpDescriptionQuery ,
75
+ } ;
48
76
49
77
#endregion
50
78
@@ -82,22 +110,14 @@ private static string readString(BitmapMetadata metadata, string key) {
82
110
}
83
111
84
112
private static string readTitle ( BitmapMetadata metadata ) {
85
- var xmpTitle = readString ( metadata , XmpDescriptionQuery ) as string ??
86
- readString ( metadata , XmpTitleQuery ) as string ;
87
- if ( ! string . IsNullOrWhiteSpace ( xmpTitle ) ) {
88
- return fromUniversalNewline ( xmpTitle ) . Trim ( ) ;
89
- }
90
- var exifTitle = readString ( metadata , TitleQuery ) ;
91
- if ( ! string . IsNullOrWhiteSpace ( exifTitle ) ) {
92
- return fromUniversalNewline ( exifTitle ) . Trim ( ) ;
93
- }
94
- xmpTitle = readString ( metadata , WinTitleQuery ) ;
95
- if ( ! string . IsNullOrWhiteSpace ( xmpTitle ) ) {
96
- // Remove trailing null.
97
- return fromUniversalNewline ( xmpTitle
98
- . Substring ( 0 , xmpTitle . Length - 1 ) ) . Trim ( ) ;
99
- }
100
- return xmpTitle ;
113
+ foreach ( var query in TitleReadQueries ) {
114
+ var v = readString ( metadata , query ) ;
115
+ if ( ! string . IsNullOrWhiteSpace ( v ) ) {
116
+ return fromUniversalNewline ( v )
117
+ . TrimEnd ( '\0 ' ) . Trim ( ) ;
118
+ }
119
+ }
120
+ return string . Empty ;
101
121
}
102
122
103
123
private static string readAuthor ( BitmapMetadata metadata ) {
@@ -159,7 +179,14 @@ public static Rotation OrienationToRotation(short orienation) {
159
179
160
180
private static short getOrientation ( BitmapMetadata metadata ) {
161
181
try {
162
- var orientationProp = metadata . GetQuery ( OrientationQuery ) as ushort ? ;
182
+ ushort ? orientationProp ;
183
+ if ( metadata . ContainsQuery ( JpegOrientationQuery ) ) {
184
+ orientationProp = metadata . GetQuery ( JpegOrientationQuery ) as ushort ? ;
185
+ } else if ( metadata . ContainsQuery ( RawOrientationQuery ) ) {
186
+ orientationProp = metadata . GetQuery ( RawOrientationQuery ) as ushort ? ;
187
+ } else {
188
+ return 1 ;
189
+ }
163
190
if ( orientationProp . HasValue ) {
164
191
return ( short ) orientationProp . Value ;
165
192
}
@@ -180,39 +207,41 @@ await photo.Dispatcher.InvokeAsync(() => {
180
207
source . Author = photo . Photographer ;
181
208
source . DateTaken = photo . DateTaken ;
182
209
source . Location = photo . Location ;
210
+ source . Orientation = 1 ;
183
211
} ) ;
184
- int pad = 0 ;
185
- if ( source . Title != null ) {
212
+ if ( ! string . IsNullOrWhiteSpace ( source . Title ) ) {
186
213
var title = toUniversalNewline ( source . Title . Trim ( ) ) ;
187
214
var bytes = Encoding . UTF8 . GetBytes ( title ) ;
188
215
dest . SetQuery ( TitleQuery , Encoding . Default . GetString ( bytes ) ) ;
189
216
var utf16bytes = Encoding . Unicode . GetBytes ( title + '\0 ' ) ;
190
217
dest . SetQuery ( WinTitleQuery , utf16bytes ) ;
191
218
dest . Title = title ;
192
- pad += bytes . Length * 4 + utf16bytes . Length + 16 ;
219
+ } else {
220
+ dest . Title = string . Empty ;
221
+ foreach ( var query in TitleRemoveQueries ) {
222
+ dest . RemoveQuery ( query ) ;
223
+ }
193
224
}
194
- if ( source . Author != null ) {
225
+ if ( ! string . IsNullOrWhiteSpace ( source . Author ) ) {
195
226
var bytes = Encoding . UTF8 . GetBytes ( source . Author ) ;
196
227
dest . Author = new ReadOnlyCollection < string > ( new string [ ] {
197
228
Encoding . Default . GetString ( bytes )
198
229
} ) ;
199
- pad += bytes . Length + 16 ;
230
+ } else {
231
+ dest . Author = null ;
200
232
}
201
233
if ( source . DateTaken . HasValue ) {
202
234
var bytes = Encoding . ASCII . GetBytes (
203
235
source . DateTaken . Value . ToString ( ExifDateFormat , CultureInfo . InvariantCulture ) ) ;
204
236
dest . SetQuery ( DateTakenQuery , Encoding . Default . GetString ( bytes ) ) ;
205
- pad += bytes . Length + 16 ;
237
+ } else {
238
+ dest . RemoveQuery ( DateTakenQuery ) ;
239
+ dest . RemoveQuery ( DateTakenSubsecQuery ) ;
206
240
}
207
241
if ( source . Location != null ) {
208
242
setLocation ( dest , source . Location ) ;
209
- pad += 404 ;
210
- }
211
- if ( pad != 0 ) {
212
- uint padding = ( uint ) pad + 256u ;
213
- dest . SetQuery ( PaddingQuery , padding ) ;
214
- dest . SetQuery ( ExifPaddingQuery , padding ) ;
215
- dest . SetQuery ( XmpPaddingQuery , padding ) ;
243
+ } else {
244
+ clearLocation ( dest ) ;
216
245
}
217
246
return source ;
218
247
}
@@ -230,6 +259,21 @@ private static void setLocation(BitmapMetadata dest, GpsLocation loc) {
230
259
dest . SetQuery ( LongitudeQuery , bytesToLongs ( loc . LonBytes ) . ToArray ( ) ) ;
231
260
}
232
261
262
+ private static void clearLocation ( BitmapMetadata dest ) {
263
+ dest . RemoveQuery ( LatitudeRefQuery ) ;
264
+ dest . RemoveQuery ( LatitudeQuery ) ;
265
+ dest . RemoveQuery ( LongitudeRefQuery ) ;
266
+ dest . RemoveQuery ( LongitudeQuery ) ;
267
+ }
268
+
269
+ internal static Rotation SaveRotation ( BitmapMetadata md ) {
270
+ var rotation = OrienationToRotation ( getOrientation ( md ) ) ;
271
+ if ( rotation != Rotation . Rotate0 ) {
272
+ md . SetQuery ( JpegOrientationQuery , ( short ) 1 ) ;
273
+ }
274
+ return rotation ;
275
+ }
276
+
233
277
#endregion
234
278
}
235
279
}
0 commit comments