using System;
using System.IO;
using System.Collections.Generic; // List<>
using System.Linq; // for Enumration
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Security.Cryptography; // for Hash
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices; // for DLL import
// PDFiumの追加
using PdfiumViewer;
// no need
//using System.Windows; // No need
//using System.Windows.Media.Imaging; // no need
// for Stream conv.
//using System.Runtime.InteropServices.WindowsRuntime;
//using Windows.Storage.Streams;
//using System.Reflection; // for Assembly.
using static CSRender.RenderPDF;
namespace CSRender {
#pragma warning disable IDE1006 // 小文字のメソッドを許可
static public class Check
{
//https://gist.github.com/retorillo/4e0c4a3cf4c7096e05ac
static public bool bDump = false;
static public bool bThDump = false;
[DllImport("user32.dll")]
extern static bool SetProcessDPIAware();
[DllImport("user32.dll")]
extern static IntPtr GetWindowDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
extern static int GetDeviceCaps(IntPtr hdc, int index);
[DllImport("user32.dll")]
extern static int ReleaseDC(IntPtr hwnd, IntPtr hdc);
public static int GetDPI() {
// モニタの解像度の取得
//return 96;
SetProcessDPIAware();// もしくはdpiAwareをマニフェストで設定すればよい。
var dc = GetWindowDC(IntPtr.Zero);
var dpi = GetDeviceCaps(dc, 88/*LOGPIXELSX*/);
ReleaseDC(IntPtr.Zero, dc);
return dpi;// 96;// dpi;
}
}
public static class RenderPDF
{
///
/// Rendering Condition
///
[DataContract]
public class RenderConditionParams
{
[DataMember(Order = 0)]
public double Dpi = 72.0;
[DataMember(Order = 1)]
public string BoxType = "Crop";
[DataMember(Order = 2)]
public string ImageType = "JPEG";
[DataMember(Order = 3)]
public int JpegQ = 91;
// Method non
}
/*
* Console Echo関係
*/
public static bool bEcho = false;
public static void setEcho(bool b) {bEcho = b;}
public static void echo(params object[] args) {
if (!bEcho) return;
var s = "";
foreach (object a in args) {
s += a.ToString();
}
Console.WriteLine(s);
}
///
/// PDFのレンダリング
///
///
///
/// レンダリングの条件
///
///
///
/// 未使用,常にtrueで使用
///
///
public static int RenderPdfDoc(
string pdfPath,
string inDir = "",
RenderConditionParams pm = null,
string inPageRange = "1-*",
string inMode = "pageBitMap",
bool bSaveImage = true, //ハッシュ値のみ計算するときにfalseにする
bool bHash = false,
UtHash.HashData inHashData = null,
bool bPDFium = false,
int nPageThreadNum = 4
) {
if (pm == null) {
pm = new RenderConditionParams();
}
var otDir = inDir;
var otBaseName = Path.GetFileName(pdfPath);//*.pdf
var otExtention = pm.ImageType.ToLower();
if (bSaveImage) {
if ( otDir == "" ) {
// 出力ディレクトリが空→元PDFの同一フォルダにIMGフォルダを作成する
otDir = Path.Combine(Directory.GetParent(pdfPath).FullName, "IMG");
}
if (!Directory.Exists(otDir))
Directory.CreateDirectory(otDir);
echo("ouptput dir=",otDir);
}
// Open pdf by PDFium.
int pageCount = 0;
using(var docG = PdfiumViewer.PdfDocument.Load(pdfPath)){
pageCount = docG.PageCount;
inMode = "pageBitMap";
var taskList = new List>();
uint[] rNo = makePageRange(inPageRange, (uint)pageCount);
if (inHashData != null) {
echo("Create Hash");
// ハッシュ対象の最大ページ数の設定必須。
inHashData.SetFile(pdfPath, (int)pageCount);
}
echo($"Page.ParallelOptions:Thread={nPageThreadNum},Pages={rNo.Length}");
/////並行処理するスレッド数を指定(2-4ぐらいが穏便な数値)
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = nPageThreadNum };
Parallel.ForEach(rNo, options, i => {
//Console.WriteLine($"*****{i}*****/{rNo.Length}");
var hashValue = "";
var ret = RenderPage(
docG: docG,
index: (int)i-1,
otPath: Path.Combine(otDir, $"{otBaseName}.{i}.{otExtention}"),
pm: pm,
bSaveImage: bSaveImage, // ファイル保存
bHash: bHash,
otHashCode: ref hashValue
);
if (inHashData != null) {
lock (inHashData) {
inHashData.AddHashCode(pdfPath, (int)i, hashValue);
}
}
});
echo("End Para");
docG.Dispose();
}
return pageCount;// ページ数を返却します
}
///
/// pdfのページを画像に出力する
///
/// PdfDocument
/// ページ番号
/// 出力ファイル名
/// 正常:0
public static int RenderPage(
PdfiumViewer.PdfDocument docG,
int index,
string otPath,
ref string otHashCode,
// Optional:
RenderConditionParams pm = null,
bool bSaveImage = true,// ファイル保存有無。ハッシュ値計算のみのときにfalseにする
bool bHash = false
){
var bDump = Check.bDump;
var bThDump = Check.bThDump;
if ( pm == null )
pm = new RenderConditionParams();
using (var memStrm = new MemoryStream()) {
PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi);
using(var img = docG.Render(index, (float)pm.Dpi, (float)pm.Dpi, flg)){
img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Bmp);
//img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Jpeg);
/*
// ImageFormatJPEGの時
dpi = 350,TIFF
2691.777 = 44分51.777秒
10335ファイルで20G
ーー
ImageFormat.Bmpに変更
result=0,time=2850.955[sec]
10335ファイルで12.2G
ーー
pagePara=2
para=4
ImageFormat.Bmp
result=0,time=2824.644[sec]
10335ファイルで12.2G
ーー
pagePara=2
para=8
ImageFormat.Bmp
result=0,time=2828.631[sec] => 47分
10335ファイルで12.2G
*/
// [注意] PDFuim用Flush()必要しないとだめです
memStrm.Flush();
img.Dispose();
}
var bmp = new System.Drawing.Bitmap(memStrm);
if (bDump) {
echo($"bmp=w:{bmp.Size.Width},h:{bmp.Size.Height}");
}
//Console.WriteLine($"OrgReso({bmp.HorizontalResolution},{bmp.VerticalResolution})");
if (bHash) {
var h = GetHashValue(memStrm);
//Console.WriteLine($"HashString:{h}");
otHashCode = h;
}
var th = Thread.CurrentThread.ManagedThreadId;
if (!bSaveImage) { //ファイル保存しない.ハッシュ計算のみ
if (bThDump) {
echo($"ot[th{th},{(index + 1)}/{docG.PageCount}]=,{Path.GetFileName(otPath)},hash:{otHashCode}");
}
return 0;
}
// image encodeを作成
var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers.
bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi); // imageクラスでもPropertyTagX(Y)Resolutionで設定できそう
if (bThDump) {
echo($"ot[th{th},{(index + 1)}/{docG.PageCount}]={Path.GetFileName(otPath)}({Directory.GetParent(otPath)})");
}
//
imEnc.SaveImage(bmp, otPath);
//
bmp.Dispose();
//memStrm.Seek(0);
memStrm.Dispose();
}
return 0;//success
}
// Sync version( 比較モード(/FC ) **************************************************************************************
///
/// レンダリング(比較モード)
///
///
///
///
/// レンダリング条件
///
///
/// 未使用常にtrueで利用する
///
///
/// 一致した場合は0、不一致の場合は!0を返却する
public static int RenderPdfDocCompare(
string pdfPath,
string refPdfPath,
string resultDataPath,
string inDir = "",
RenderConditionParams pm = null,
string inPageRange = "1-*",
string inMode = "pageBitMap",
bool bHash = false,
UtHash.HashData inHashDataTgt = null,
UtHash.HashData inHashDataRef = null,
bool bPDFium = false,
int nPageThreadNum = 4
// 比較結果を返す ページ番号とメッセージ
) {
var bDump = Check.bDump;
var bThDump = Check.bThDump;
if(bDump)
echo("RenderPdfDocCompare & diff Image! & non Para");
if (pm == null)
pm = new RenderConditionParams();
var otDir = inDir;
var otBaseName = Path.GetFileName(pdfPath);//xxx.pdf
var otExtention = pm.ImageType.ToLower();
if (otDir == "") {
Console.WriteLine("Error:No output dir");
return -1;// error
}
if (!Directory.Exists(otDir))
Directory.CreateDirectory(otDir);
// PDFファイルを読み込む by PDFium.
var docG = PdfiumViewer.PdfDocument.Load(pdfPath);
var docGRef = PdfiumViewer.PdfDocument.Load(refPdfPath);
var pageCount = docG.PageCount;
uint[] rNo = makePageRange(inPageRange, (uint)pageCount);
int nRetAll = 0;// トータルの不一致ページ数
// ハッシュ情報の確認
/*
- 双方のハッシュ値が存在するか確認
- tgtののみなら、refのrender
- refのみなら、tgtのみrender
- とう感じ
*/
UtHash.HashFile tgtHf=null;
if (inHashDataTgt!=null) {
var f = Path.GetFileName(pdfPath);
if (inHashDataTgt.Files.ContainsKey(f)) {
tgtHf = inHashDataTgt.Files[f];
}
}
UtHash.HashFile refHf = null;
if (inHashDataRef != null) {
var f = Path.GetFileName(refPdfPath);
if ( inHashDataRef.Files.ContainsKey(f) ) {
refHf = inHashDataRef.Files[f];
}
}
Console.WriteLine($@"UsingHashFile:tgt({(tgtHf==null?0:1)}),ref({(refHf == null ? 0 : 1)})");
// tgtHf,refHfを構築完了
var fcResultMsg = new SortedDictionary();// 比較結果を返す ページ番号とメッセージ
/////並行処理するスレッド数を指定(2-4ぐらいが穏便な数値)
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.ForEach(rNo, options, i => {
MemoryStream tgtStrm = null, refStrm = null;
string tgtHash = "", refHash = "";
// ターゲット レンダラ定義
MemoryStream RendPageTGT() {
var strm = RenderPageStream(docG:docG,index:(int)i-1, pm:pm);
return strm;
}
// リファレンス レンダラ定義
MemoryStream RendPageREF() {
var strm = RenderPageStream(docG:docGRef, index:(int)i-1, pm:pm);
return strm;
}
// ターゲット処理
if (tgtHf == null) {
tgtStrm = RendPageTGT();// Render
tgtHash = GetHashValue(tgtStrm);
} else {
lock (inHashDataTgt) {
tgtHash = tgtHf.GetHashValue((int)(i - 1));
}
}
// リファレンス処理
if (refHf == null) {
refStrm = RendPageREF();// Render
refHash = GetHashValue(refStrm);
} else {
lock (inHashDataRef) {
refHash = refHf.GetHashValue((int)(i - 1));
}
}
// compare stream;
// int nRet = 1;//異なる(diff).
var dateStr = DateTime.Now.ToString();
//Console.WriteLine($@"tgt={i}:{tgtHash}");
//Console.WriteLine($@"ref={i}:{refHash}");
if (tgtHash != refHash) {
// no match
// nRet = 1;
nRetAll++;// = nRet;//全体の不一致設定
echo($"{i}:NotMatch:{i},tgt[{tgtHash}],ref[{refHash}]");
//2020年3月2日 15:23:55:[@Difference]:.diff.jpg
lock (fcResultMsg) {
fcResultMsg.Add((int)i, $@"{dateStr}:[@Difference]:{Path.GetFileName(pdfPath)}.{i}");
}
echo("***** save diff image");
// diffのjpegを書き出してみる(tgt)
if (tgtStrm == null)
tgtStrm = RendPageTGT();
var otPath = Path.Combine(otDir, $"{otBaseName}.{i}.tgt.{otExtention}");
using(var bmp = new System.Drawing.Bitmap(tgtStrm)){
var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers.
bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi);
imEnc.SaveImage(bmp, otPath);
bmp.Dispose();
}
// diffのjpegを書き出してみる(ref)
if (refStrm == null)
refStrm = RendPageREF();
otPath = Path.Combine(otDir, $"{otBaseName}.{i}.ref.{otExtention}");
using(var bmp = new System.Drawing.Bitmap(refStrm)){
var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers.
bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi);
imEnc.SaveImage(bmp, otPath);
bmp.Dispose();
}
} else {
// match
//nRet = 0;
lock (fcResultMsg) {
fcResultMsg.Add((int)i, $@"{dateStr}:[OK]:{Path.GetFileName(pdfPath)}.{i}");
// Ex 2019年12月18日 19:31:38:[OK]:fname.pdf.1.png
}
}
// dispose:
if (refStrm != null) refStrm.Dispose();
if (tgtStrm != null) tgtStrm.Dispose();
});
if ( docG != null)
docG.Dispose();
if (docGRef != null)
docGRef.Dispose();
// まとめて表示
foreach ( var v in fcResultMsg){
Console.WriteLine("@CMP@"+v.Value);
}
if (resultDataPath != "") {
// まとめて書き出し
var resCont =($"{pdfPath}\n");
foreach (var v in fcResultMsg) {
resCont += ("@CMP@" + v.Value) + "\n";
}
File.AppendAllText(resultDataPath, resCont);
}
return nRetAll;// success. 不一致数を返す
}
///
/// 比較モード
///
/// ドキュメントハンドル
/// ページ番号(0-)
/// 正常:0
public static MemoryStream RenderPageStream(
PdfiumViewer.PdfDocument docG,
int index,
// Optional:
RenderConditionParams pm = null
) {
var memStrm = new MemoryStream();
var bDump = Check.bDump;
var bThDump = Check.bThDump;
if (pm == null)
pm = new RenderConditionParams();
PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi);
System.Drawing.Image img = docG.Render(index, (float)pm.Dpi, (float)pm.Dpi, flg);
img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Bmp);
//img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Jpeg);
// [注意] PDFuim用にFlush()必要
memStrm.Flush();
img.Dispose();
return memStrm;
}
///
/// ページ1から始まるページ範囲の配列を返す
///
///
///
///
private static uint[] makePageRange(string pageRange = "1", uint endPage = 100) {
var range = new SortedSet();
var vec = pageRange.Split(',');//カンマで分割
var reRange = new Regex($@"([\d]+)[\s]*\-[\s]*([\d]*|\*)");// n-m or n- or n-*
var reOne = new Regex($@"([\d]+)"); // n
bool isRange(uint x) => (0 < x && x <= endPage); // 範囲チェック
void swap(ref T a, ref T b) { T tmp = a; a = b; b = tmp; }
foreach (var s in vec) {
//範囲指定
var m = reRange.Match(s);
if (m.Success) {
uint st = uint.Parse(m.Groups[1].Value);
uint en = 0;
if (!isRange(st))
continue;//開始値が範囲外で無効
uint.TryParse(m.Groups[2].Value, out en);
if (!isRange(en))
en = endPage;// 最終値が無効なため最終ページ
if (st > en) swap(ref st, ref en);
// 範囲登録
foreach (var g in Enumerable.Range((int)st, (int)(en - st + 1)))
range.Add((uint)g);
continue;
}
//ページ指定
m = reOne.Match(s);
if (m.Success) {
var n = uint.Parse(m.Groups[1].Value);
if (isRange(n))
range.Add(n);
}
}
return range.ToArray();
}
public static string GetHashValue(MemoryStream strm) {
var alg = new SHA256CryptoServiceProvider();
strm.Seek(0, SeekOrigin.Begin);
var bin = alg.ComputeHash(strm);
alg.Clear();
// バイト配列をUTF8エンコードで文字列化
var hashedText = new StringBuilder();
foreach (var b in bin) {
hashedText.AppendFormat("{0:X2}", b);
}
return hashedText.ToString();
}
}
///
/// ファイルのハッシュ値のIO
///
namespace UtHash
// https://qiita.com/Akasaki/items/dee137b24aea4b7e2bcb
// http://mokake.hatenablog.com/entry/2017/09/28/234433
// https://lifetime-engineer.com/csharp-create-json-indent/
// DataMemberでOrderを指定することで順序を確定。
// JsonReaderWriterFactory.CreateJsonWriterでindent=trueにすることでjsonの可視性向上
{
[DataContract]
public class HashHead
{
public bool Dirty = false; // 書き換えられたらTrue 保存対象外
[DataMember(Order = 0)]
public string HashBaseName; // 保存時にベース名を設定する
[DataMember(Order=1)]
public string HashFilePath;
[DataMember(Order=2)]
public string Dpi;
[DataMember(Order=3)]
public string Box;
[DataMember(Order=4)]
public string ImType;
[DataMember(Order=5)]
public string JQ;
// メソッド
public HashHead() {
SetRenderInfo("0", "Bx", "IMx", "xx");
}
public HashHead SetRenderInfo(string dpi, string box, string imType, string jq) {
Dpi = dpi; Box = box; ImType = imType; JQ = jq;
return this;
}
public string GetRenderInfoStr() { return $@"{Dpi}_{Box}_{ImType}_{JQ}"; }
}
[DataContract]
public class HashFile {
[DataMember(Order=0)]
public DateTime UpdateTime; // ファイルの更新日
[DataMember(Order=1)]
public string UpdateTimeStr; // ファイルの更新日(Json目視用)
[DataMember(Order=2)]
public List PageHashCode = new List(); // ページ毎のハッシュ値の配列
// メソッド
public string GetHashValue(int i) {
// 範囲チェック
if ( !(0 <= i && i < PageHashCode.Count) ){
return "";
}
return PageHashCode[i];
}
}
[DataContract]
public class HashData {
[DataMember(Order=0)]
public HashHead Head = new HashHead();
[DataMember(Order=1)]
public SortedDictionary Files = new SortedDictionary();
// メソッド
static public string GetHashFileName(string baseName, string dpi, string box, string imType, string jq) {
var h = new HashHead();
var f = baseName + "_" + h.SetRenderInfo(dpi, box, imType, jq).GetRenderInfoStr() + ".json";
return f;
}
static public HashData load(string pdfPath/*or Dir*/, string baseName, string dpi, string box, string imType, string jq, ref string otPath) {
var hashPath = "";
if (Path.GetExtension(pdfPath).ToLower() == ".pdf") {
var paPath = Directory.GetParent(Path.GetFullPath(pdfPath)).ToString();
var hashFName = GetHashFileName(baseName, dpi, box, imType, jq);
hashPath = Path.Combine(paPath, hashFName);
} else if (File.GetAttributes(pdfPath).HasFlag(FileAttributes.Directory)) {
var hashFName = GetHashFileName(baseName, dpi, box, imType, jq);
hashPath = Path.Combine(pdfPath, hashFName);
}
//Console.WriteLine($@"hashPath={hashPath}");
if (otPath != null) {
otPath = hashPath;
}
var hd = HashData.load(hashPath); //ファイルの存在はこっちに任す
return hd;
}
static public HashData load(string hashPath) {
if (!File.Exists(hashPath)) {
echo("not find load hash data file");
return null;
}
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HashData));
using (var rd = new FileStream(hashPath, FileMode.Open,FileAccess.Read,FileShare.ReadWrite)) {
var a = (HashData)serializer.ReadObject(rd);
a.Head.Dirty = false;// clear
return a;
}
}
static public HashData loadFromString(string s) {
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HashData));
using (var rd = new MemoryStream( Encoding.UTF8.GetBytes(s) )) {
var a = (HashData)serializer.ReadObject(rd);
a.Head.Dirty = false;// clear
return a;
}
}
public override string ToString() {
//文字列に書き出す
using (var m = new MemoryStream())
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(m, Encoding.UTF8, true, true, " ")) {
var serializer = new DataContractJsonSerializer(typeof(HashData));
serializer.WriteObject(writer, this);
var x= Encoding.UTF8.GetString(m.ToArray());// 文字列に変換
return x;
}
}
public bool save(string path) {
// JSONに変換するデータを作る。
// 再保存の抑制
//this.Head.Dirtyがfalseなら更新されていないことになる。
//またかつ保存パスが同じ this.Head.HashFilePath = path;
echo($@"HashData.save:{path}");
this.Head.HashFilePath = Path.GetFullPath(path);
this.Head.HashBaseName = this.Head.GetRenderInfoStr();
try {
using (var fs = new FileStream(path, FileMode.Create))
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(fs, Encoding.UTF8, true, true, " ")) {
var serializer = new DataContractJsonSerializer(typeof(HashData));
serializer.WriteObject(writer, this);
return true;
}
} catch (Exception e) {
Console.WriteLine($@"error Save json anything error:{e}");
return false;
}
}
public bool SetFile(string pathPDF, int pageNum) {
var fi = new FileInfo(pathPDF);
var fName = fi.Name;
HashFile hf;
if (!Files.ContainsKey(fName)) {
this.Head.Dirty = true;// 変更あり
hf = new HashFile() {
UpdateTime = fi.LastWriteTimeUtc,
UpdateTimeStr= fi.LastWriteTime.ToString(),
PageHashCode = new List(pageNum)
};
// Console.WriteLine($@"update={hf.UpdateTime}");
for (var i = 0; i < pageNum; i++) {
hf.PageHashCode.Add("");
}
Files.Add(fName,hf);
} else {
// 既に存在、pageNumがかわらないことだけチェック
var pageNumOrg = Files[fName].PageHashCode.Count;
if (Files[fName].UpdateTime.ToString() != fi.LastWriteTimeUtc.ToString()) {
// 日付が一致しなければ更新(ToStringで比較しないとだめです
this.Head.Dirty = true;// 変更あり
Files[fName].UpdateTime = fi.LastWriteTimeUtc;
Files[fName].UpdateTimeStr = fi.LastWriteTime.ToString();
Files[fName].PageHashCode = new List(pageNum);
for (var i = 0; i < pageNum; i++) {
Files[fName].PageHashCode.Add("");
}
}
// ありえないけど、同一日付でページ数が異なる
if (pageNumOrg != pageNum) {
Console.WriteLine($@"Mismatch pageNum:setP:{pageNum},DbP:{Files[fName].PageHashCode.Count}");
this.Head.Dirty = true;// 変更あり
return false; // Page数が一致しない
}
}
return true;
}
public bool AddHashCode(string pathPDF, int pageNo/*1開始*/, string hashCode) {
//Console.WriteLine("call addHashCode:" + pathPDF + ":" + pageNo);
var fi = new FileInfo(pathPDF);
var fName = fi.Name;
var n = pageNo - 1;//0始まり
if (!Files.ContainsKey(fName)) {
this.Head.Dirty = true;// 変更あり
return false;
}
if (Files[fName].PageHashCode[n] != hashCode) {
this.Head.Dirty = true;// 変更あり
Files[fName].PageHashCode[n] = hashCode;
}
return true;
}
public string GetHashCode(string pathPDF, int pageNo/*1開始*/) {
var fi = new FileInfo(pathPDF);
var fName = fi.Name;
var n = pageNo - 1;//0始まり
if (!Files.ContainsKey(fName)) {
return "";
}
var v = Files[fName].PageHashCode;
return (n < v.Count) ? v[n] : "";
}
}
}
namespace UtImage // Image保存のユーティリティー
{
// ref:https://water2litter.net/rum/post/cs_pdf_wpf/
//using EncType = System.Drawing.Imaging.Encoder;// 別名
//using EncParamType = System.Drawing.Imaging.EncoderParameter;//別名
///
/// BitMapのSaveに使うImage Encodeパラメータの設定値の生成と 画像保存
/// var enc = new UtImage.Enc("jpeg");
/// //enc.SetImageType("PNG"); // 後で変更できる
/// //enc.JJpegQuality = 100; JpegのQuarty変更
/// enc.SaveImage(BitMap,path);
/// // enc.SaveImage(BitMap,path,"PNG");
///
///
public class Enc
{
// SEE:https://dobon.net/vb/dotnet/graphics/encoderparameters.html
public Enc(string imageType = "jpeg") { SetImageType(imageType); }
public long JpegQuality { get; set; } = 91;// 指定しないときの値は91と一致する
private string ImageType = "jpeg";
public Enc SetImageType(string imageType) {
imageType = imageType.ToLower();
if (imageType == "jpeg" || imageType == "jpg") {
ImageType = "jpeg";
} else if (imageType == "png") {
ImageType = "png";
} else if (imageType == "tif" || imageType == "tiff") {
ImageType = "tiff";
} else {
ImageType = imageType;// 小文字を設定
}
return this;
}
public int SaveImage(System.Drawing.Bitmap bitmap, string path, string imageType = null) {
var p = GetParams();
string imType = imageType?.ToLower();
if (p != null) {
bitmap.Save(path, GetInfo(imType), GetParams());// imageTypeがnullならSetImageType()のものが使われる
} else {// パラメータが空の場合
var imageFormat = new System.Drawing.Imaging.ImageFormat(GetInfo(imType).FormatID);
bitmap.Save(path, imageFormat);
}
return 0;
}
// Image版
public int SaveImage(System.Drawing.Image image, string path, string imageType = null)
{
var p = GetParams();
string imType = imageType?.ToLower();
if (p != null) {
image.Save(path, GetInfo(imType), GetParams());// imageTypeがnullならSetImageType()のものが使われる
}
else
{// パラメータが空の場合
var imageFormat = new System.Drawing.Imaging.ImageFormat(GetInfo(imType).FormatID);
image.Save(path, imageFormat);
}
return 0;
}
///
/// サポートするEncodeパラメータ一覧
/// 現状Errorが発生して取得できない
///
/// 対象のBitMap
public static void GetSupportedParameters(System.Drawing.Bitmap bitmap1 = null) {
// https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/advanced/how-to-determine-the-parameters-supported-by-an-encoder
try {
if (bitmap1 == null) {
bitmap1 = new System.Drawing.Bitmap(100, 100);
var destBitmapData = bitmap1.LockBits(
new System.Drawing.Rectangle(0, 0, 100, 100),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
bitmap1.PixelFormat
);
bitmap1.UnlockBits(destBitmapData);
}
var jpgEncoder = GetEncoderInfo(System.Drawing.Imaging.ImageFormat.Tiff);
var paramList = bitmap1.GetEncoderParameterList(jpgEncoder.Clsid);
var encParams = paramList.Param;
for (int i = 0; i < encParams.Length; i++) {
Console.WriteLine("Param " + i + " holds " + encParams[i].NumberOfValues +
" items of type " +
encParams[i].ValueType + "\r\n" + "Guid category: " + encParams[i].Encoder.Guid + "\r\n");
}
} catch (Exception e) {
Console.WriteLine($"Ignore error ={e.ToString()}");
}
}
// inner method.
private System.Drawing.Imaging.EncoderParameters GetParams() {
var encList = new List();
//encList.Clear();
// 今はJPEGパラメータだけ
encList.Add(new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, JpegQuality));
//...
// 結合
var eps = new System.Drawing.Imaging.EncoderParameters(encList.Count);
for (var i = 0; i < encList.Count; i++)
eps.Param[i] = encList[i];
return eps;
}
private System.Drawing.Imaging.ImageCodecInfo GetInfo(string it = null) {
if (it == null) {// SetImageType()で指定したものを使う
it = ImageType;
}
return GetEncoderInfo($"image/{it}");
}
//ImageFormatで指定されたImageCodecInfoを探して返す
private static System.Drawing.Imaging.ImageCodecInfo GetEncoderInfo(System.Drawing.Imaging.ImageFormat f) {
var encs = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
foreach (var enc in encs) {
if (enc.FormatID == f.Guid)
return enc;
}
return null;
}
private static System.Drawing.Imaging.ImageCodecInfo GetEncoderInfo(string mineType) {
var encs = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
//指定されたMimeTypeを探して見つかれば返す
foreach (System.Drawing.Imaging.ImageCodecInfo enc in encs) {
if (enc.MimeType == mineType)
return enc;
}
return null;
}
}
}
}