はじめに
公開文書、外部送付ファイルは、スッキリ洗浄してから提供することがありました。
サンプルソースは、処理毎で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
Visual Studio 2022 - .NET Framework 4.8 は、C# 7.3 が既定です。
このため、サンプルコードは、基本的に C# 7.3 機能範囲で記述しています。
タイムスタンプ更新
複数ファイルの場合、タイムスタンプがそれぞれ異なる状態よりも、揃っていたほうが良いと思い、全て同一日時のタイムスタンプとしていました。
休日、もしくは、定時勤務時間以外のタイムスタンプは、それ自体が勤務情報として認知されることもあります。
サンプルコード
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();
}
}
}
コンテンツの作成日時、前回保存日時を削除した場合、それぞれ、対象ファイルのタイムスタンプ CreationTime, LastWriteTime を参照するみたいです。
前述、タイムスタンプ更新とあわせて対処することで、表示情報のリセットは可能です。
Excel 文書プロパティ削除
// Excel 文書プロパティ削除
private void CleanupExcelProperties(string file)
{
// Excel ファイルを開く
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)
{
// Power Point ファイルを開く
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 式
var fileType = Path.GetExtension(file)?.ToLower();
Action<string>? action = fileType switch
{
".docx" => CleanupWordProperties,
".xlsx" => CleanupExcelProperties,
".pptx" => CleanupPowerPointProperties,
".pdf" => CleanupPdfProperties,
_ => null
};
// 拡張子に基づく action を実行
action?.Invoke(file);
.NET Framework でも、Visual Studio と C#、および、フレームワーク(プラットフォーム)の関係 で記載した C# バージョン設定で 8.0 以降にすれは、上記 switch 式を利用できます。
※null 許容参照型の明示は不要なので、下記修正は必要です。
// Action<string>? を Action<string> に修正
Action<string> action = fileType switch