🚰

C# - 文書洗浄 - タイムスタンプ更新とプロパティ削除

に公開

はじめに

公開文書、外部送付ファイルは、スッキリ洗浄してから提供することがありました。
サンプルソースは、処理毎で2つに分離しましたが、ひとつにまとめてツール化していました。

テスト環境

ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。

  • Windows Forms - .NET Framework 4.8
  • Windows Forms - .NET 8
  • WPF - .NET Framework 4.8
  • WPF - .NET 8

タイムスタンプ更新

複数ファイルの場合、タイムスタンプがそれぞれ異なる状態よりも、揃っていたほうが良いと思い、全て同一日時のタイムスタンプとしていました。

サンプルコード

var folder = "FOLDER_PATH";                // TODO
var today = DateTime.Today.AddHours(10);   // 本日の 10:00:00
SetTimeStamp(folder, today);
// フォルダ下のファイルについて、タイムスタンプを更新
private void SetTimeStamp(string target, DateTime today)
{
  // サブフォルダ下も処理する場合はコメントを外す
  // var folders = Directory.GetDirectories(target);
  // if (folders != null)
  // {
  //   foreach(var folder in folders)
  //   {
  //     SetTimeStamp(folder, today);
  //   }
  // }

  // フォルダ内のファイル取得
  var files = Directory.GetFiles(target);
  if (files != null)
  {
    foreach (var file in files)
    {
      // タイムスタンプ更新
      File.SetCreationTime(file, today);
      File.SetLastWriteTime(file, today);
    }
  }
}

プロパティ削除

文書プロパティに個人情報などが格納されている場合、クリアしたほうが良いと思います。
本記事では、下記文書を対象とします。

  • Microsoft Office 文書(Word, Excel, Power Point)
  • PDF 文書

Microsoft Office 文書は、NuGet Gallery | DocumentFormat.OpenXml を利用します。
PDF 文書は、AGPL は避けて、NuGet Gallery | PDFsharp を利用します。
PDFSharp は Version 1.50.5147 までが .NET Framework 用で、Version 6.0.0 以降が .NET 用となっています。.NET Framework で利用する場合は、バージョン指定して取得しましょう。

  • .NET
    • PM> NuGet\Install-Package DocumentFormat.OpenXml
    • PM> NuGet\Install-Package PDFsharp
  • .NET Framework
    • PM> NuGet\Install-Package DocumentFormat.OpenXml
    • PM> NuGet\Install-Package PdfSharp -Version 1.50.5147

Microsoft Office 文書

エクスプローラ、プロパティで参照できる下記情報の削除を行います。

Word、Excel、Power Point で利用するクラスが異なりますが、それ以外のコードは同一です。

文書 利用するクラス
Word WordprocessingDocument
Excel SpreadsheetDocument
Power Poinet PresentationDocument

Word サンプルコードを直接掲載して、それ以外はアコーディオンで掲載します。
サンプルコードで、削除不要なプロパティがありましたら、コメントアウトしてください。

using DocumentFormat.OpenXml.Packaging;
// Word 文書プロパティ削除
private void CleanupWordProperties(string file)
{
  // Word ファイルを開く
  using (var document = WordprocessingDocument.Open(file, true))
  {
    // ビルトインプロパティ
    var coreProperties = document.PackageProperties;
    if (coreProperties != null)
    {
      // プロパティを null に設定して削除
      coreProperties.Title = null;           // タイトル
      coreProperties.Subject = null;         // 件名
      coreProperties.Keywords = null;        // タグ
      coreProperties.Category = null;        // 分類項目
      coreProperties.Description = null;     // コメント
      coreProperties.Creator = null;         // 作成者
      coreProperties.LastModifiedBy = null;  // 最終保存者
      coreProperties.Revision = null;        // 改定番号 
      coreProperties.Modified = null;        // 最終更新日時
      coreProperties.LastModifiedBy = null;  // 前回保存者
      coreProperties.Created = null;         // コンテンツの作成日時
      coreProperties.Modified = null;        // 前回保存日時
      coreProperties.LastPrinted = null;     // 前回印刷日
    }
    // 拡張プロパティ
    var extendedProperties = document.ExtendedFilePropertiesPart?.Properties;
    if (extendedProperties != null)
    {
      // プロパティを null に設定して削除
      extendedProperties.Company = null;      // 会社
      extendedProperties.Manager = null;      // マネージャ
      extendedProperties.TotalTime = null;    // 編集総時間
      extendedProperties.Save();
    }
  }
}
Excel 文書プロパティ削除

// Excel 文書プロパティ削除
private void CleanupExcelProperties(string file)
{
  // Word ファイルを開く
  using (var document = SpreadsheetDocument.Open(file, true))
  {
    // ビルトインプロパティ
    var coreProperties = document.PackageProperties;
    if (coreProperties != null)
    {
      // プロパティを null に設定して削除
      coreProperties.Title = null;           // タイトル
      coreProperties.Subject = null;         // 件名
      coreProperties.Keywords = null;        // タグ
      coreProperties.Category = null;        // 分類項目
      coreProperties.Description = null;     // コメント
      coreProperties.Creator = null;         // 作成者
      coreProperties.LastModifiedBy = null;  // 最終保存者
      coreProperties.Revision = null;        // 改定番号 
      coreProperties.Modified = null;        // 最終更新日時
      coreProperties.LastModifiedBy = null;  // 前回保存者
      coreProperties.Created = null;         // コンテンツの作成日時
      coreProperties.Modified = null;        // 前回保存日時
      coreProperties.LastPrinted = null;     // 前回印刷日
    }
    // 拡張プロパティ
    var extendedProperties = document.ExtendedFilePropertiesPart?.Properties;
    if (extendedProperties != null)
    {
      // プロパティを null に設定して削除
      extendedProperties.Company = null;      // 会社
      extendedProperties.Manager = null;      // マネージャ
      extendedProperties.TotalTime = null;    // 編集総時間
      extendedProperties.Save();
    }
  }
}
Power Point 文書プロパティ削除
// Power Point 文書プロパティ削除
private void CleanupPowerPointProperties(string file)
{
  // Word ファイルを開く
  using (var document = PresentationDocument.Open(file, true))
  {
    // ビルトインプロパティ
    var coreProperties = document.PackageProperties;
    if (coreProperties != null)
    {
      // プロパティを null に設定して削除
      coreProperties.Title = null;           // タイトル
      coreProperties.Subject = null;         // 件名
      coreProperties.Keywords = null;        // タグ
      coreProperties.Category = null;        // 分類項目
      coreProperties.Description = null;     // コメント
      coreProperties.Creator = null;         // 作成者
      coreProperties.LastModifiedBy = null;  // 最終保存者
      coreProperties.Revision = null;        // 改定番号 
      coreProperties.Modified = null;        // 最終更新日時
      coreProperties.LastModifiedBy = null;  // 前回保存者
      coreProperties.Created = null;         // コンテンツの作成日時
      coreProperties.Modified = null;        // 前回保存日時
      coreProperties.LastPrinted = null;     // 前回印刷日
    }
    // 拡張プロパティ
    var extendedProperties = document.ExtendedFilePropertiesPart?.Properties;
    if (extendedProperties != null)
    {
      // プロパティを null に設定して削除
      extendedProperties.Company = null;      // 会社
      extendedProperties.Manager = null;      // マネージャ
      extendedProperties.TotalTime = null;    // 編集総時間
      extendedProperties.Save();
    }
  }
}

PDF 文書

PDF は提供時に PDF 化していたこともあり、文書プロパティをあまり気にしていませんでしたが、Microsoft Office 文書と同様に対処すべきだったと思い、本記事では掲載することにしました。
PDF編集ソフト(本来は Acrobat Pro で確認すべきですがライセンスを持っていないので、CubePDF Utility を利用)で参照できる下記情報の削除を行います。

using PdfSharp.Pdf.IO;
// PDF 文書プロパティ削除
private void CleanupPdfProperties(string target)
{
  // PDF ファイルを開く
  using (var document = PdfReader.Open(target, PdfDocumentOpenMode.Modify))
  {
    // プロパティを string.Empty に設定して削除
    document.Info.Title = string.Empty;       // タイトル
    document.Info.Author = string.Empty;      // 作成者
    document.Info.Subject = string.Empty;     // サブタイトル
    document.Info.Keywords = string.Empty;    // キーワード

    // PDF編集ソフトによっては、下記も参照できるので、必要に応じてコメントアウトを外す
    // document.Info.Comment = string.Empty;
    // document.Info.Creator = string.Empty;
    // document.Info.CreationDate = DateTime.Now;
    // document.Info.ModificationDate = DateTime.Now;

    document.Save(target);
  }
}

全体処理

フォルダ下の対象文書を処理する全体処理のソースコードを掲載します。

var folder = "FOLDER_PATH";                // TODO
ClearDocumentProperties(folder);
// フォルダ下の文書について、文書プロパティを削除
private void ClearDocumentProperties(string target)
{
  // サブフォルダ下も処理する場合はコメントを外す
  // var folders = Directory.GetDirectories(target);
  // if (folders != null)
  // {
  //   foreach(var folder in folders)
  //   {
  //     ClearDocumentProperties(folder);
  //   }
  // }

  // フォルダ内のファイル取得
  var files = Directory.GetFiles(target);
  if (files != null)
  {
    foreach (var file in files)
    {
      // 拡張子を利用した分岐
      var fileType = Path.GetExtension(file)?.ToLower();
      switch (fileType)
      {
        case ".docx":
          CleanupWordProperties(file);
          break;
        case ".xlsx":
          CleanupExcelProperties(file);
          break;
        case ".pptx":
          CleanupPowerPointProperties(file);
          break;
        case ".pdf":
          CleanupPdfProperties(file);
          break;
      } 
    }
  }
}

.NET 8 は switch 式が使えるので、switch 式を利用するとスッキリしたソースとなりますね。

// 拡張子を利用した分岐 - switch 式
var fileType = Path.GetExtension(file)?.ToLower();
Action<string>? action = fileType switch
{
  ".docx" => CleanupWordProperties,
  ".xlsx" => CleanupExcelProperties,
  ".pptx" => CleanupPowerPointProperties,
  ".pdf" => CleanupPdfProperties,
  _ => null
};
// 拡張子に基づく action を実行
action?.Invoke(file);

出典

本記事は、2025/04/21 Qiita 投稿記事の転載です。

C# - 文書洗浄 - タイムスタンプ更新とプロパティ削除

Discussion