0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

.NET で PDF ファイルの日時を変更する

Last updated at Posted at 2021-11-20

はじめに

僕は以前 Delphi でファイルの日時を変更する Windows アプリを作りました。
[簡単エクスプローラ拡張 EzExpEx]
(http://hp.vector.co.jp/authors/VA029585/EzExpEx/)
これは残念なことにユニコードの名称のファイルを扱えないんですよね。
最新の Delphi でビルドし直せばいいのですが、僕が持っている Delphi ではだめなんです。
これを VB.NET で作り直したいとずっと思っていました。

僕のアプリが扱えるファイルの日時は
・ファイル自体の作成日時、更新日時
・Exif 情報の撮影日時、更新日時
・PDF ファイルの作成日時、更新日時
・概要情報の作成日時、更新日時

ファイル自体の日時を変更するのは .NET の標準機能で対応できますが、PDF ファイルの日時などはどうでしょう。

PDF ファイルの日時を変更する

PDF ファイルは、Windows が管理するファイルそのものの日時と別に、ファイルの内容に作成日時、更新日時を持っています。

PDF ファイルが持っている日時は、.NET の標準機能で変更できません。
PDF ファイルを扱うライブラリが多く作られています。例えば
・[PDFsharp - A .NET library for processing PDF]
(http://www.pdfsharp.com/PDFsharp/)
・[iTextSharp]
(https://github.com/itext/itextsharp)
・[iText for .NET]
(https://github.com/itext/itext7-dotnet)
これは楽でいいのですが、この方法で PDF ファイルを変更すると、読込時点でデコードされ保存時点でエンコードし直されるようです。

僕が Delphi で作ったアプリは、メタデータだけ読取して変更するようにしていました。
これを VB.NET で書き直しました。C# のコードも用意しました。

PDF ファイルの日時を取得する

VB.NET
        Dim Path As String = "●●●●.jpg"    '対象とするファイルのパス
        Dim CreationTime As DateTime    '作成日時を返す
        Dim CreationValid As Boolenan = False    '作成日時を取得できたか返す
        Dim ModTime As DateTime    '更新日時を返す
        Dim ModValid 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)
                'PDF ファイルか確認
                Dim PdfHeaderBuff As Byte() = {0, 0, 0, 0, 0}
                If fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) <> PdfHeaderBuff.Length Then Exit Function
                If System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper() <> "%PDF-" Then
                    Exit Function
                End If
            End Using
            Dim buff As Byte() = System.IO.File.ReadAllBytes(Path)
            With Me
                '作成日時を取得
                Dim p As Long = 0
                Do While p <= buff.Length - 13
                    If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12)}) = "/CreationDate" Then Exit Do
                    p += 1
                Loop
                Do While p <= buff.Length - 3
                    If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
                    p += 1
                Loop
                p += 3
                If p <= buff.Length - 14 Then
                    Dim yy As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 0), buff(p + 1), buff(p + 2), buff(p + 3)}))
                    Dim mm As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 4), buff(p + 5)}))
                    Dim dd As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 6), buff(p + 7)}))
                    Dim hh As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 8), buff(p + 9)}))
                    Dim nn As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 10), buff(p + 11)}))
                    Dim ss As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 12), buff(p + 13)}))
                    CreationTime = New DateTime(yy, mm, dd, hh, nn, ss)
                    CreationValid = True
                End If
                '更新日時を取得
                p = 0
                Do While p <= buff.Length - 8
                    If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7)}) = "/ModDate" Then Exit Do
                    p += 1
                Loop
                Do While p <= buff.Length - 3
                    If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
                    p += 1
                Loop
                p += 3
                If p <= buff.Length - 14 Then
                    Dim yy As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 0), buff(p + 1), buff(p + 2), buff(p + 3)}))
                    Dim mm As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 4), buff(p + 5)}))
                    Dim dd As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 6), buff(p + 7)}))
                    Dim hh As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 8), buff(p + 9)}))
                    Dim nn As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 10), buff(p + 11)}))
                    Dim ss As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 12), buff(p + 13)}))
                    ModTime = New DateTime(yy, mm, dd, hh, nn, ss)
                    ModValid = True
                End If
            End With
        Catch ex As Exception
            Exit Function
        End Try
C#
            string Path = "●●●●.jpg";    // 対象とするファイルのパス
            DateTime CreationTime;    // 作成日時を返す
            bool CreationValid = false;    // 作成日時を取得できたか返す
            DateTime ModTime;    // 更新日時を返す
            bool ModValid = 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)) {
                    // PDF ファイルか確認
                    byte[] PdfHeaderBuff = new byte[5];
                    if (fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) != PdfHeaderBuff.Length) return false;
                    if (System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper() != "%PDF-") {
                        return false;
                    }
                }
                byte[] buff = System.IO.File.ReadAllBytes(Path);
                {
                    // 作成日時を取得
                    long p = 0;
                    while(p <= buff.Length - 13) {
                        if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12]}) == "/CreationDate") break;
                        p++;
                    }
                    while (p <= buff.Length - 3) {
                        if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2]}) == "(D:") break;
                        p++;
                    }
                    p += 3;
                    if (p <= buff.Length - 14) {
                        int yy = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 0], buff[p + 1], buff[p + 2], buff[p + 3]}));
                        int mm = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 4], buff[p + 5]}));
                        int dd = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 6], buff[p + 7]}));
                        int hh = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 8], buff[p + 9]}));
                        int nn = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 10], buff[p + 11]}));
                        int ss = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 12], buff[p + 13]}));
                        CreationTime = new DateTime(yy, mm, dd, hh, nn, ss);
                        CreationValid = true;
                    }
                    // 更新日時を取得
                    p = 0;
                    while (p <= buff.Length - 13) {
                        if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7]}) == "/ModDate") break;
                        p++;
                    }
                    while (p <= buff.Length - 3) {
                        if (System.Text.Encoding.ASCII.GetString(new byte[] { buff[p], buff[p + 1], buff[p + 2] }) == "(D:") break;
                        p++;
                    }
                    p += 3;
                    if (p <= buff.Length - 14) {
                        int yy = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 0], buff[p + 1], buff[p + 2], buff[p + 3]}));
                        int mm = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 4], buff[p + 5]}));
                        int dd = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 6], buff[p + 7]}));
                        int hh = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 8], buff[p + 9]}));
                        int nn = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 10], buff[p + 11]}));
                        int ss = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 12], buff[p + 13]}));
                        ModTime = new DateTime(yy, mm, dd, hh, nn, ss);
                        ModValid = true;
                    }
                }
            }
            catch (Exception) {
                return false;
            }

PDF ファイルの日時を変更する

VB.NET
        Dim Path As String = "●●●●.jpg"    '対象とするファイルのパス
        Dim CreationTime As DateTime    '作成日時を指定する
        Dim CreationValid As Boolean = True    '作成日時を変更するか指定する
        Dim ModTime As DateTime    '更新日時を指定する
        Dim ModValid 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.Read)
                'PDF ファイルか確認
                Dim PdfHeaderBuff As Byte() = {0, 0, 0, 0, 0}
                If fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) <> PdfHeaderBuff.Length Then Exit Function
                If System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper().TrimEnd(vbNullChar) <> "%PDF-" Then
                    Exit Function
                End If
            End Using
            Dim buff As Byte() = System.IO.File.ReadAllBytes(Path)
            With Me
                '作成日時を更新
                If CreationValid Then
                    Dim p As Long = 0
                    Do While p <= buff.Length - 13
                        If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12)}) = "/CreationDate" Then Exit Do
                        p += 1
                    Loop
                    Do While p <= buff.Length - 3
                        If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
                        p += 1
                    Loop
                    p += 3
                    If p <= buff.Length - 14 Then
                        Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(CreationTime, "yyyyMMddHHmmss"))
                        Buffer.BlockCopy(s, 0, buff, p, s.Length)
                    End If
                    '作成日時を更新
                    p = 0
                    Do While p <= buff.Length - 16
                        If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12), buff(p + 13), buff(p + 14), buff(p + 15)}) = "<xmp:CreateDate>" Then Exit Do
                        p += 1
                    Loop
                    p += 16
                    If p <= buff.Length - 19 Then
                        Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(CreationTime, "yyyy-MM-ddTHH:mm:ss"))
                        Buffer.BlockCopy(s, 0, buff, p, s.Length)
                    End If
                End If
                '更新日時を更新
                If CreationValid Then
                    Dim p As Long = 0
                    Do While p <= buff.Length - 8
                        If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7)}) = "/ModDate" Then Exit Do
                        p += 1
                    Loop
                    Do While p <= buff.Length - 3
                        If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
                        p += 1
                    Loop
                    p += 3
                    If p <= buff.Length - 14 Then
                        Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(ModTime, "yyyyMMddHHmmss"))
                        Buffer.BlockCopy(s, 0, buff, p, s.Length)
                    End If
                    '更新日時を更新
                    p = 0
                    Do While p <= buff.Length - 16
                        If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12), buff(p + 13), buff(p + 14), buff(p + 15)}) = "<xmp:ModifyDate>" Then Exit Do
                        p += 1
                    Loop
                    p += 16
                    If p <= buff.Length - 19 Then
                        Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(ModTime, "yyyy-MM-ddTHH:mm:ss"))
                        Buffer.BlockCopy(s, 0, buff, p, s.Length)
                    End If
                End If
            End With
            'ファイルに書込
            System.IO.File.WriteAllBytes(Path, buff)
        Catch ex As Exception
            Exit Function
        End Try
C#
            string Path = "●●●●.jpg";    // 対象とするファイルのパス
            DateTime CreationTime;    // 作成日時を指定する
            bool CreationValid;    // 作成日時を変更するか指定する
            DateTime ModTime;    // 更新日時を指定する
            bool ModValid = 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)) {
                    // PDF ファイルか確認
                    byte[] PdfHeaderBuff = new byte[5];
                    if (fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) != PdfHeaderBuff.Length) return false;
                    if (System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper() != "%PDF-") {
                        return false;
                    }
                }
                byte[] buff = System.IO.File.ReadAllBytes(Path);
                {
                    // 作成日時を更新
                    if (CreationValid) {
                        int p = 0;
                        while (p <= buff.Length - 13) {
                            if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12]}) == "/CreationDate") break;
                            p++;
                        }
                        while (p <= buff.Length - 3) {
                            if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2]}) == "(D:") break;
                            p++;
                        }
                        p += 3;
                        if (p <= buff.Length - 14) { 
                            byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyyMMddHHmmss"));
                            Buffer.BlockCopy(s, 0, buff, p, s.Length);
                        }
                        // 作成日時を更新
                        p = 0;
                        while (p <= buff.Length - 16) {
                            if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12], buff[p + 13], buff[p + 14], buff[p + 15]}) == "<xmp:CreateDate>") break;
                            p++;
                        }
                        p += 16;
                        if (p <= buff.Length - 19) {
                            byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyy-MM-ddTHH:mm:ss"));
                            Buffer.BlockCopy(s, 0, buff, p, s.Length);
                        }
                    }
                    // 更新日時を更新
                    if (CreationValid) {
                        int p = 0;
                        while (p <= buff.Length - 8) {
                            if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7]}) == "/ModDate") break;
                            p++;
                        }
                        while (p <= buff.Length - 3) {
                            if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2]}) == "(D:") break;
                            p++;
                        }
                        p += 3;
                        if (p <= buff.Length - 14) {
                            byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyyMMddHHmmss"));
                            Buffer.BlockCopy(s, 0, buff, p, s.Length);
                        }
                        // 更新日時を更新
                        p = 0;
                        while (p <= buff.Length - 16) {
                            if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12], buff[p + 13], buff[p + 14], buff[p + 15] }) == "<xmp:ModifyDate>") break;
                            p++;
                        }
                        p += 16;
                        if (p <= buff.Length - 19) {
                            byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyy-MM-ddTHH:mm:ss"));
                            Buffer.BlockCopy(s, 0, buff, p, s.Length);
                        }
                    }
                }
                // ファイルに書込
                System.IO.File.WriteAllBytes(Path, buff);
            }
            catch (Exception) {
                return false;
            }
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?