Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9707

.NET で Exif 情報の日時を変更する

$
0
0
はじめに 僕は以前 Delphi でファイルの日時を変更する Windows アプリを作りました。 簡単エクスプローラ拡張 EzExpEx これは残念なことにユニコードの名称のファイルを扱えないんですよね。 最新の Delphi でビルドし直せばいいのですが、僕が持っている Delphi ではだめなんです。 これを VB.NET で作り直したいとずっと思っていました。 僕のアプリが扱えるファイルの日時は ・ファイル自体の作成日時、更新日時 ・Exif 情報の撮影日時、更新日時 ・PDF ファイルの作成日時、更新日時 ・概要情報の作成日時、更新日時 ファイル自体の日時を変更するのは .NET の標準機能で対応できますが、Exif 情報の日時などはどうでしょう。 Exif 情報の日時を変更する Exif 情報は、撮影日時や撮影条件など画像ファイルに埋込された情報です。 ファイルの Exif 情報は .NET の Image オブジェクトで変更できます。 ・画像のExif情報を取得する、設定する - DOBON.NET ・.NET TIPS デジカメ画像のExif情報を取得するには? - C# - @IT これは楽でいいのですが、この方法で Exif 情報を変更すると画像そのものが読込時点でデコードされ保存時点でエンコードし直されて、微妙に画質が落ちてしまいます。 僕が Delphi で作ったアプリは、メタデータだけ読取して変更するようにしていました。 これを VB.NET で書き直しました。C# のコードも用意しました。 Exif 情報の日時を取得する VB.NET Dim Path As String = "●●●●.jpg" '対象とするファイルのパス Dim TimeUpdated As DateTime '更新日時を返す Dim UpdatedValid As Boolean = False '更新日時を取得できたか返す Dim TimeOriginal As DateTime '撮影日時を返す Dim OriginalValid As Boolean = False '撮影日時を取得できたか返す Dim TimeDigitized As DateTime 'デジタル化日時を返す Dim DigitizedValid As Boolean = False 'デジタル化日時を取得できたか返す If Not System.IO.File.Exists(Path) Then Exit Function End If Try 'ファイルを読取 Using fs As New System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.Read) 'TiffHeader を読取 Dim TiffHeaderPos As Long Dim IsLittleEndian As Boolean Dim IfdNum As Integer If Not ReadTiffHeader(fs, TiffHeaderPos, IsLittleEndian, IfdNum) Then Exit Function End If Dim IfdPos As Long = fs.Position For n As Integer = 0 To IfdNum - 1 Dim WordBuff As Byte() = {0, 0} 'IFD を読取 Dim IfdBuff As Byte() = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} fs.Seek(IfdPos + n * IfdBuff.Length, System.IO.SeekOrigin.Begin) If fs.Read(IfdBuff, 0, IfdBuff.Length) <> IfdBuff.Length Then Exit Function Dim Tag As UInt16 = BitConverter.ToUInt16(If(IsLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(0), IfdBuff(1)}, {IfdBuff(1), IfdBuff(0)}), 0) Select Case Tag Case &H132 '最後に変更された日時 If GetTimeFromIFD(fs, TiffHeaderPos, IsLittleEndian, IfdBuff, TimeUpdated) Then UpdatedValid = True End If Case &H8769 'Exif データが見つかった 'エントリ数を取得 Dim Offset As Long = BitConverter.ToUInt32(If(IsLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(8), IfdBuff(9), IfdBuff(10), IfdBuff(11)}, {IfdBuff(11), IfdBuff(10), IfdBuff(9), IfdBuff(8)}), 0) fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin) If fs.Read(WordBuff, 0, WordBuff.Length) <> WordBuff.Length Then Exit Function Dim EntryNum As Integer = BitConverter.ToUInt16(If(IsLittleEndian And BitConverter.IsLittleEndian, {WordBuff(0), WordBuff(1)}, {WordBuff(1), WordBuff(0)}), 0) Dim EntryPos As Long = fs.Position For i As Integer = 0 To EntryNum - 1 'エントリを読取 fs.Seek(EntryPos + i * IfdBuff.Length, System.IO.SeekOrigin.Begin) If fs.Read(IfdBuff, 0, IfdBuff.Length) <> IfdBuff.Length Then Exit Function Tag = BitConverter.ToUInt16(If(IsLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(0), IfdBuff(1)}, {IfdBuff(1), IfdBuff(0)}), 0) Select Case Tag Case &H9003 '撮影された日時 If GetTimeFromIFD(fs, TiffHeaderPos, IsLittleEndian, IfdBuff, TimeOriginal) Then OriginalValid = True End If Case &H9004 'デジタル化された日時 If GetTimeFromIFD(fs, TiffHeaderPos, IsLittleEndian, IfdBuff, TimeDigitized) Then DigitizedValid = True End If End Select Next End Select Next End Using Catch ex As Exception Exit Function End Try C# string Path = "●●●●.jpg"; // 対象とするファイルのパス DateTime TimeUpdated; // 更新日時を返す bool UpdatedValid = false; // 更新日時を取得できたか返す DateTime TimeOrijinal; // 撮影日時を返す bool OriginalValid = false; // 撮影日時を取得できたか返す DateTime TimeDigitized; // デジタル化日時を返す bool DigitizedValid = false; // デジタル化日時を取得できたか返す if (!System.IO.File.Exists(Path)) { return false; } try { // ファイルを読取 using (System.IO.FileStream fs = new System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { // TiffHeader を読取 long TiffHeaderPos = 0; bool isLittleEndian = true; int IfdNum = 0; if (!RaadTiffHeader(fs, ref TiffHeaderPos, ref isLittleEndian, ref IfdNum)) { return false; } long IfdPos = fs.Position; for (int n = 0; n < IfdNum; n++) { byte[] WordBuff = new byte[2]; // IFD を読取 byte[] IfdBuff = new byte[12]; fs.Seek(IfdPos + n * IfdBuff.Length, System.IO.SeekOrigin.Begin); if (fs.Read(IfdBuff, 0, IfdBuff.Length) != IfdBuff.Length) return false; UInt16 Tag = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[0], IfdBuff[1]} : new byte[] {IfdBuff[1], IfdBuff[0]}, 0); switch (Tag) { case 0x0132: // 最後に変更された日時 if (GetTimeFromIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, ref TimeUpdated)) { UpdatedValid = true; } break; case 0x8769: // Exif データが見つかった // エントリ数を取得 long Offset = BitConverter.ToUInt32(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[8], IfdBuff[9], IfdBuff[10], IfdBuff[11]} : new byte[] {IfdBuff[11], IfdBuff[10], IfdBuff[9], IfdBuff[8]}, 0); fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin); if (fs.Read(WordBuff, 0, WordBuff.Length) != WordBuff.Length) return false; int EntryNum = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {WordBuff[0], WordBuff[1]} : new byte[] {WordBuff[1], WordBuff[0]}, 0); long EntryPos = fs.Position; for (int i = 0; i < EntryNum; i++) { // エントリを読取 fs.Seek(EntryPos + i * IfdBuff.Length, System.IO.SeekOrigin.Begin); if (fs.Read(IfdBuff, 0, IfdBuff.Length) != IfdBuff.Length) return false; Tag = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[0], IfdBuff[1]} : new byte[] {IfdBuff[1], IfdBuff[0]}, 0); switch (Tag) { case 0x9003: // 撮影された日時 if (GetTimeFromIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, ref TimeOriginal)) { OriginalValid = true; } break; case 9004: // デジタル化された日時 if (GetTimeFromIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, ref TimeDigitized)) { DigitizedValid = true; } break; } } break; } } } } catch (Exception) { return false; } 以下はヘルパー関数です。変更するときも使用します。 VB.NET Imports System.Runtime.InteropServices Private Function ReadTiffHeader(fs As System.IO.FileStream, ByRef TiffHeaderPos As Long, ByRef isLittleEndian As Boolean, ByRef IfdNum As Integer) ReadTiffHeader = False Dim WordBuff As Byte() = {0, 0} 'JPEG ヘッダを確認 If fs.Read(WordBuff, 0, WordBuff.Length) <> WordBuff.Length Then Exit Function If WordBuff(0) <> &HFF Or WordBuff(1) <> &HD8 Then Exit Function 'APP1 マーカーを確認 If fs.Read(WordBuff, 0, WordBuff.Length) <> WordBuff.Length Then Exit Function If WordBuff(0) <> &HFF Or WordBuff(1) <> &HE1 Then Exit Function 'EXIF ヘッダを確認 fs.Seek(2, System.IO.SeekOrigin.Current) Dim ExifHeaderBuff As Byte() = {0, 0, 0, 0, 0, 0} If fs.Read(ExifHeaderBuff, 0, ExifHeaderBuff.Length) <> ExifHeaderBuff.Length Then Exit Function If System.Text.Encoding.ASCII.GetString(ExifHeaderBuff).ToUpper().TrimEnd(vbNullChar) <> "EXIF" Then Exit Function 'TIFF ヘッダを読取 TiffHeaderPos = fs.Position Dim TiffHeaderBuff As Byte() = {0, 0, 0, 0, 0, 0, 0, 0} If fs.Read(TiffHeaderBuff, 0, TiffHeaderBuff.Length) <> TiffHeaderBuff.Length Then Exit Function If (TiffHeaderBuff(0) <> &H49 Or TiffHeaderBuff(1) <> &H49) And (TiffHeaderBuff(0) <> &H4D Or TiffHeaderBuff(1) <> &H4D) Then Exit Function isLittleEndian = (TiffHeaderBuff(0) = &H49 And TiffHeaderBuff(1) = &H49) Dim Tag As UInt16 = BitConverter.ToUInt16(If(isLittleEndian And BitConverter.IsLittleEndian, {TiffHeaderBuff(2), TiffHeaderBuff(3)}, {TiffHeaderBuff(3), TiffHeaderBuff(2)}), 0) If Tag <> &H2A Then Exit Function fs.Seek(2, System.IO.SeekOrigin.Current) 'IFD 数を取得 Dim Offset As Long = BitConverter.ToUInt32(If(isLittleEndian And BitConverter.IsLittleEndian, {TiffHeaderBuff(4), TiffHeaderBuff(5), TiffHeaderBuff(6), TiffHeaderBuff(7)}, {TiffHeaderBuff(7), TiffHeaderBuff(6), TiffHeaderBuff(5), TiffHeaderBuff(4)}), 0) fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin) If fs.Read(WordBuff, 0, WordBuff.Length) <> WordBuff.Length Then Exit Function IfdNum = BitConverter.ToUInt16(If(isLittleEndian And BitConverter.IsLittleEndian, {WordBuff(0), WordBuff(1)}, {WordBuff(1), WordBuff(0)}), 0) ReadTiffHeader = True End Function Private Function GetTimeFromIFD(fs As System.IO.FileStream, TiffHeaderPos As Long, isLittleEndian As Boolean, IfdBuf As Byte(), ByRef Time As DateTime) As Boolean GetTimeFromIFD = False Dim DataType As UInt16 = BitConverter.ToUInt16(If(isLittleEndian And BitConverter.IsLittleEndian, {IfdBuf(2), IfdBuf(3)}, {IfdBuf(3), IfdBuf(2)}), 0) If DataType <> 2 Then Exit Function Dim Offset As Long = BitConverter.ToUInt32(If(isLittleEndian And BitConverter.IsLittleEndian, {IfdBuf(8), IfdBuf(9), IfdBuf(10), IfdBuf(11)}, {IfdBuf(11), IfdBuf(10), IfdBuf(9), IfdBuf(8)}), 0) fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin) Dim Buff As Byte() = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} If fs.Read(Buff, 0, Buff.Length) <> Buff.Length Then Exit Function Try Dim yy As Integer = CInt(System.Text.Encoding.ASCII.GetString({Buff(0), Buff(1), Buff(2), Buff(3)})) Dim mm As Integer = CInt(System.Text.Encoding.ASCII.GetString({Buff(5), Buff(6)})) Dim dd As Integer = CInt(System.Text.Encoding.ASCII.GetString({Buff(8), Buff(9)})) Dim hh As Integer = CInt(System.Text.Encoding.ASCII.GetString({Buff(11), Buff(12)})) Dim nn As Integer = CInt(System.Text.Encoding.ASCII.GetString({Buff(14), Buff(15)})) Dim ss As Integer = CInt(System.Text.Encoding.ASCII.GetString({Buff(17), Buff(18)})) Time = New DateTime(yy, mm, dd, hh, nn, ss) Catch ex As Exception Exit Function End Try GetTimeFromIFD = True End Function C# private bool RaadTiffHeader(System.IO.FileStream fs, ref long TiffHeaderPos, ref bool isLittleEndian, ref int IfdNum) { byte[] WordBuff = new byte[2]; // JPEG ヘッダを確認 if (fs.Read(WordBuff, 0, WordBuff.Length) != WordBuff.Length) return false; if (WordBuff[0] != 0xff || WordBuff[1] != 0xd8) return false; // APP1 マーカーを確認 if (fs.Read(WordBuff, 0, WordBuff.Length) != WordBuff.Length) return false; if (WordBuff[0] != 0xff || WordBuff[1] != 0xe1) return false; // EXIF ヘッダを確認 fs.Seek(2, System.IO.SeekOrigin.Current); byte[] ExifHeaderBuff = new byte[6]; if (fs.Read(ExifHeaderBuff, 0, ExifHeaderBuff.Length) != ExifHeaderBuff.Length) return false; if (System.Text.Encoding.ASCII.GetString(ExifHeaderBuff).ToUpper().TrimEnd('\0') != "EXIF") return false; // TIFF ヘッダを読取 TiffHeaderPos = fs.Position; byte[] TiffHeaderBuff = new byte[8]; if (fs.Read(TiffHeaderBuff, 0, TiffHeaderBuff.Length) != TiffHeaderBuff.Length) return false; if ((TiffHeaderBuff[0] != 0x49 || TiffHeaderBuff[1] != 0x49) && (TiffHeaderBuff[0] != 0x4d || TiffHeaderBuff[1] != 0x4d)) return false; isLittleEndian = (TiffHeaderBuff[0] == 0x49 || TiffHeaderBuff[1] == 0x49); UInt16 Tag = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {TiffHeaderBuff[2], TiffHeaderBuff[3]} : new byte[] {TiffHeaderBuff[3], TiffHeaderBuff[2]}, 0); if (Tag != 0x2a) return false; fs.Seek(2, System.IO.SeekOrigin.Current); // IFD 数を取得 long Offset = BitConverter.ToUInt32(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {TiffHeaderBuff[4], TiffHeaderBuff[5], TiffHeaderBuff[6], TiffHeaderBuff[7]} : new byte[] {TiffHeaderBuff[7], TiffHeaderBuff[6], TiffHeaderBuff[5], TiffHeaderBuff[4]}, 0); fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin); if (fs.Read(WordBuff, 0, WordBuff.Length) != WordBuff.Length) return false; IfdNum = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {WordBuff[0], WordBuff[1]} : new byte[] {WordBuff[1], WordBuff[0]}, 0); return true; } private bool GetTimeFromIFD(System.IO.FileStream fs, long TiffHeaderPos, bool isLittleEndian, byte[] IfdBuff, ref DateTime Time) { UInt16 DataType = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[2], IfdBuff[3]} : new byte[] {IfdBuff[3], IfdBuff[2]}, 0); if (DataType != 2) return false; long Offset = BitConverter.ToUInt32(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[8], IfdBuff[9], IfdBuff[10], IfdBuff[11]} : new byte[] {IfdBuff[11], IfdBuff[10], IfdBuff[9], IfdBuff[8]}, 0); fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin); byte[] Buff = new byte[20]; if (fs.Read(Buff, 0, Buff.Length) != Buff.Length) return false; try { int yy = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {Buff[0], Buff[1], Buff[2], Buff[3]})); int mm = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {Buff[5], Buff[6]})); int dd = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {Buff[8], Buff[9]})); int hh = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {Buff[11], Buff[12]})); int nn = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {Buff[14], Buff[15]})); int ss = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {Buff[17], Buff[18]})); Time = new DateTime(yy, mm, dd, hh, nn, ss); } catch { return false; } return true; } Exif 情報の日時を取得するだけなら、.NET の Image オブジェクトを使えばいいかも知れません。 Exif 情報の日時を変更する VB.NET Dim Path As String = "●●●●.jpg" '対象とするファイルのパス Dim TimeUpdated As DateTime '変更する更新日時を指定する Dim UpdatedValid As Boolean = True '更新日時を変更するか指定する Dim TimeOriginal As DateTime '変更する撮影日時を指定する Dim OriginalValid As Boolean = True '撮影日時を変更するか指定する Dim TimeDigitized As DateTime '変更するデジタル化日時を指定する Dim DigitizedValid As Boolean = True 'デジタル化日時を変更するか指定する If Not System.IO.File.Exists(Path) Then Exit Function End If Try 'ファイルを読取 Using fs As New System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite) 'TiffHeader を読取 Dim TiffHeaderPos As Long Dim isLittleEndian As Boolean Dim IfdNum As Integer If Not ReadTiffHeader(fs, TiffHeaderPos, isLittleEndian, IfdNum) Then Exit Function End If Dim IfdPos As Long = fs.Position For n As Integer = 0 To IfdNum - 1 Dim WordBuff As Byte() = {0, 0} 'IFD を読取 Dim IfdBuff As Byte() = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} fs.Seek(IfdPos + n * IfdBuff.Length, System.IO.SeekOrigin.Begin) If fs.Read(IfdBuff, 0, IfdBuff.Length) <> IfdBuff.Length Then Exit Function Dim Tag As UInt16 = BitConverter.ToUInt16(If(isLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(0), IfdBuff(1)}, {IfdBuff(1), IfdBuff(0)}), 0) Select Case Tag Case &H132 '最後に変更された日時 If Not UpdatedValid Then Continue For End If If Not SetTimeToIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, TimeUpdated) Then Exit Function End If Case &H8769 'Exif データが見つかった Dim Offset As Long = BitConverter.ToUInt32(If(isLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(8), IfdBuff(9), IfdBuff(10), IfdBuff(11)}, {IfdBuff(11), IfdBuff(10), IfdBuff(9), IfdBuff(8)}), 0) fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin) 'エントリ数を取得 If fs.Read(WordBuff, 0, WordBuff.Length) <> WordBuff.Length Then Exit Function Dim EntryNum As Integer = BitConverter.ToUInt16(If(isLittleEndian And BitConverter.IsLittleEndian, {WordBuff(0), WordBuff(1)}, {WordBuff(1), WordBuff(0)}), 0) Dim EntryPos As Long = fs.Position For i As Integer = 0 To EntryNum - 1 'エントリを読取 fs.Seek(EntryPos + i * IfdBuff.Length, System.IO.SeekOrigin.Begin) If fs.Read(IfdBuff, 0, IfdBuff.Length) <> IfdBuff.Length Then Exit Function Tag = BitConverter.ToUInt16(If(isLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(0), IfdBuff(1)}, {IfdBuff(1), IfdBuff(0)}), 0) Select Case Tag Case &H9003 '撮影された日時 If Not OriginalValid Then Continue For End If If Not SetTimeToIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, TimeOriginal) Then Exit Function End If Case &H9004 'デジタル化された日時 If Not DigitizedValid Then Continue For End If If Not SetTimeToIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, TimeDigitized) Then Exit Function End If End Select Next End Select Next End Using Catch ex As Exception Exit Function End Try End Function C# string Path = "●●●●.jpg"; // 対象とするファイルのパス DateTime TimeUpdated; // 変更する更新日時を指定する bool UpdatedValid = true; // 更新日時を変更するか指定する DateTime TimeOrijinal; // 変更する撮影日時を指定する bool OriginalValid = true; // 撮影日時を変更するか指定する DateTime TimeDigitized; // 変更するデジタル化日時を指定する bool DigitizedValid = true; // デジタル化日時を変更するか指定する if (!System.IO.File.Exists(Path)) { return false; } try { // ファイルを読取 using (System.IO.FileStream fs = new System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite)) { // TiffHeader を読取 long TiffHeaderPos = 0; bool isLittleEndian = true; int IfdNum = 0; if (!RaadTiffHeader(fs, ref TiffHeaderPos, ref isLittleEndian, ref IfdNum)) { return false; } long IfdPos = fs.Position; for (int n = 0; n < IfdNum; n++) { byte[] WordBuff = new byte[2]; // IFD を読取 byte[] IfdBuff = new byte[12]; fs.Seek(IfdPos + n * IfdBuff.Length, System.IO.SeekOrigin.Begin); if (fs.Read(IfdBuff, 0, IfdBuff.Length) != IfdBuff.Length) return false; UInt16 Tag = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[0], IfdBuff[1]} : new byte[] {IfdBuff[1], IfdBuff[0]}, 0); switch (Tag) { case 0x0132: // 最後に変更された日時 if (!UpdatedValid) { continue; } if (!SetTimeToIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, TimeUpdated)) { return false; } break; case 0x8769: // Exif データが見つかった // エントリ数を取得 long Offset = BitConverter.ToUInt32(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[8], IfdBuff[9], IfdBuff[10], IfdBuff[11]} : new byte[] {IfdBuff[11], IfdBuff[10], IfdBuff[9], IfdBuff[8]}, 0); fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin); if (fs.Read(WordBuff, 0, WordBuff.Length) != WordBuff.Length) return false; int EntryNum = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {WordBuff[0], WordBuff[1]} : new byte[] {WordBuff[1], WordBuff[0]}, 0); long EntryPos = fs.Position; for (int i = 0; i < EntryNum; i++) { // エントリを読取 fs.Seek(EntryPos + i * IfdBuff.Length, System.IO.SeekOrigin.Begin); if (fs.Read(IfdBuff, 0, IfdBuff.Length) != IfdBuff.Length) return false; Tag = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[0], IfdBuff[1]} : new byte[] {IfdBuff[1], IfdBuff[0]}, 0); switch (Tag) { case 0x9003: // 撮影された日時 if (!OriginalValid) { continue; } if (!SetTimeToIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, TimeOriginal)) { return false; } break; case 9004: // デジタル化された日時 if (!DigitizedValid) { continue; } if (!SetTimeToIFD(fs, TiffHeaderPos, isLittleEndian, IfdBuff, TimeDigitized)) { return false; } break; } } break; } } } } catch (Exception) { return false; } 以下はヘルパー関数です。 VB.NET Private Function SetTimeToIFD(fs As System.IO.FileStream, TiffHeaderPos As Long, isLittleEndian As Boolean, IfdBuff As Byte(), Time As DateTime) As Boolean SetTimeToIFD = False Dim DataType As UInt16 = BitConverter.ToUInt16(If(isLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(2), IfdBuff(3)}, {IfdBuff(3), IfdBuff(2)}), 0) If DataType <> 2 Then Exit Function Try Dim Offset As Long = BitConverter.ToUInt32(If(isLittleEndian And BitConverter.IsLittleEndian, {IfdBuff(8), IfdBuff(9), IfdBuff(10), IfdBuff(11)}, {IfdBuff(11), IfdBuff(10), IfdBuff(9), IfdBuff(8)}), 0) fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin) Dim Buff As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(Time, "yyyy:MM:dd HH:mm:ss")) fs.Write(Buff, 0, Buff.Length) Catch ex As Exception Exit Function End Try SetTimeToIFD = True End Function C# private bool SetTimeToIFD(System.IO.FileStream fs, long TiffHeaderPos, bool isLittleEndian, byte[] IfdBuff, DateTime Time) { UInt16 DataType = BitConverter.ToUInt16(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[2], IfdBuff[3]} : new byte[] {IfdBuff[3], IfdBuff[2]}, 0); if (DataType != 2) return false; try { long Offset = BitConverter.ToUInt32(isLittleEndian && BitConverter.IsLittleEndian ? new byte[] {IfdBuff[8], IfdBuff[9], IfdBuff[10], IfdBuff[11]} : new byte[] {IfdBuff[11], IfdBuff[10], IfdBuff[9], IfdBuff[8]}, 0); fs.Seek(TiffHeaderPos + Offset, System.IO.SeekOrigin.Begin); byte[] Buff = System.Text.Encoding.ASCII.GetBytes(Time.ToString("yyyy:MM:dd HH:mm:ss")); fs.Write(Buff, 0, Buff.Length); } catch { return false; } return true; } 上記のコードは、Exif 情報にセットされている既存の日時を書換しています。既存の日時がセットされていないと書換できません。 そのときは、.NET の Image オブジェクトを使用するしかないですね。

Viewing all articles
Browse latest Browse all 9707

Trending Articles