Commit 6f700b7f authored by AP matsuo koji's avatar AP matsuo koji 😲

CSRender整理

Suppert paraPage(Page処理スレッド)数 指定
UWPアプリ、Data.pdfを除外
parent 2b5bad34
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic; // List<>
using System.Linq; using System.Linq; // for Enumration
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Reflection; // Assembly.
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Security.Cryptography; // for Hash
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Runtime.Serialization.Json; using System.Runtime.Serialization.Json;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Runtime.InteropServices; // for DLL import
using System.Runtime.InteropServices;
// for Stream conv.
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;
//using System.IO.WindowsRuntimeStreamExtensions;
//
//using Codeplex.Data; // DynamicJson
// UWP-APIの使用
using Windows.Data.Pdf;
// PDFiumの追加 // PDFiumの追加
using PdfiumViewer; using PdfiumViewer;
using static CSRender.RenderPDF; // no need
//using System.Windows; // No need
//using System.Windows.Media.Imaging; // no need
//https://proself2.screen.co.jp/public/OcyIQAHPksNAnE4Bs3BxIPQApnAEmSCIBOxsCZ946Uur // for Stream conv.
//Screen8080 //using System.Runtime.InteropServices.WindowsRuntime;
//1_A4縦_2×1_両面_可変長レコード.pdf //using Windows.Storage.Streams;
// TOshiba: //using System.Reflection; // for Assembly.
//#region アセンブリ Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime
// C:\UserData\GIT_JSH\WinRT\CSRender\CSRender\bin\Debug\Windows.winmd
// WindowsRuntime 1.3
//#endregion
// MacBookPro:
// WindowsRuntime 1.4
//CSRender, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null
// mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime,Ver(255.255.255.255),VerComp(SameMachine)
// System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a,Ver(4.0.0.0),VerComp(SameMachine)
// System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// clrVer=4.0.30319.42000
// 参考: zoomパレメータなど
// https://www.syncfusion.com/kb/8767/how-to-print-pdf-documents-in-xamarin-forms-platform
//var DisplayInformation = Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
//var dpi = DisplayInformation.LogicalDpi / 96;
// https://elesynd.blogspot.com/2018/11/hDpiForm.html using static CSRender.RenderPDF;
//Program.csでDPIAwareする
//FormクラスのAutoScaleModeをUIを使って"Dpi"に変更する
//それでもズレるコントロールは、FontをUIを使って明示的に指定してみる
// https://qiita.com/felis_silv/items/efee4b1a397b0b95100a
// スケーリング grph.FromImage(bmp)後にスケール
namespace CSRender { namespace CSRender {
#pragma warning disable IDE1006 // 小文字のメソッド含む #pragma warning disable IDE1006 // 小文字のメソッドを許可
static public class Check static public class Check
{ {
//https://gist.github.com/retorillo/4e0c4a3cf4c7096e05ac //https://gist.github.com/retorillo/4e0c4a3cf4c7096e05ac
...@@ -83,31 +37,7 @@ namespace CSRender { ...@@ -83,31 +37,7 @@ namespace CSRender {
static public bool bDump = false; static public bool bDump = false;
static public bool bThDump = false; static public bool bThDump = false;
const int LOGPIXELSX = 88; [DllImport("user32.dll")]
const int LOGPIXELSY = 90;
//
//
const int HORZSIZE = 4;//物理画面の幅・高さ(ミリメートル単位)
const int VERTSIZE = 5;
const int HORZRES = 8; //画面の幅・高さ(ピクセル単位)
const int VERZRES = 10;
//
const int RASTERCAPS = 38;
// 返却値のマスク
//1 (RC_BITBLT) ビットマップの転送
//2 (RC_BANDING) バンド処理のサポートが必要
//4 (RC_SCALING) スケーリング
//8 (RC_BITMAP64) 64KB より大きいビットマップ
//0x0080 (RC_DI_BITMAP) SetDIBits 関数と GetDIBits 関数
//0x0100 (RC_PALETTE) デバイスはパレットベースのデバイスである
//0x0200 (RC_DIBTODEV) SetDIBitsToDevice 関数
//0x0800 (RC_STRETCHBLT) StretchBlt 関数
//0x1000 (RC_FLOODFILL) 塗りつぶし
//0x2000 (RC_STRETCHDIB) StretchDIBits 関数
const int SCALINGFACTORX = 114;//x 軸・ y 軸のスケーリングファクター
const int SCALINGFACTORY = 115;
[DllImport("user32.dll")]
extern static bool SetProcessDPIAware(); extern static bool SetProcessDPIAware();
[DllImport("user32.dll")] [DllImport("user32.dll")]
extern static IntPtr GetWindowDC(IntPtr hwnd); extern static IntPtr GetWindowDC(IntPtr hwnd);
...@@ -116,38 +46,17 @@ namespace CSRender { ...@@ -116,38 +46,17 @@ namespace CSRender {
[DllImport("user32.dll")] [DllImport("user32.dll")]
extern static int ReleaseDC(IntPtr hwnd, IntPtr hdc); extern static int ReleaseDC(IntPtr hwnd, IntPtr hdc);
public static int GetDPI() { public static int GetDPI() {
// モニタの解像度の取得
//return 96; //return 96;
SetProcessDPIAware();// もしくはdpiAwareをマニフェストで設定すればよい。 SetProcessDPIAware();// もしくはdpiAwareをマニフェストで設定すればよい。
var dc = GetWindowDC(IntPtr.Zero); var dc = GetWindowDC(IntPtr.Zero);
var dpi = GetDeviceCaps(dc, LOGPIXELSX); var dpi = GetDeviceCaps(dc, 88/*LOGPIXELSX*/);
if ( bDump ) {
Console.WriteLine("DpiX: {0}", GetDeviceCaps(dc, LOGPIXELSX));
Console.WriteLine("DpiY: {0}", GetDeviceCaps(dc, LOGPIXELSY));
Console.WriteLine("SCALINGFACTORYX: {0}", GetDeviceCaps(dc, SCALINGFACTORX));
Console.WriteLine("SCALINGFACTORY: {0}", GetDeviceCaps(dc, SCALINGFACTORY));
//if ( false ) {// これは倍率で変更されない
// int width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; // 幅(pixel)
// int height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; // 高さ(pixel)
// // 取得したスクリーンのサイズをコンソールに出力する
// string message = string.Format("This screen size is {0} x {1} (pixel)", width, height);
// System.Console.WriteLine(message);
//}
//if (false){// コンソールアプリでは呼び出せない
// var di = Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
// Console.WriteLine($@"di.LogicalDpi: {di.LogicalDpi}");
// Console.WriteLine($@"di.RawDpiX: {di.RawDpiX}");
// Console.WriteLine($@"di.ResoSacle: {di.ResolutionScale.ToString()}");
// Console.WriteLine($@"di.StereoEnabled: {di.StereoEnabled}");
// Console.WriteLine($@"di: {di.ToString()}");
//}
}
ReleaseDC(IntPtr.Zero, dc); ReleaseDC(IntPtr.Zero, dc);
return dpi;// 96;// dpi; return dpi;// 96;// dpi;
} }
} }
public static class RenderPDF public static class RenderPDF
{ {
/// <summary> /// <summary>
...@@ -167,11 +76,11 @@ namespace CSRender { ...@@ -167,11 +76,11 @@ namespace CSRender {
// Method non // Method non
} }
// Verbose /*
public static void setEcho(bool b) { * Console Echo関係
bEcho = b; */
}
public static bool bEcho = false; public static bool bEcho = false;
public static void setEcho(bool b) {bEcho = b;}
public static void echo(params object[] args) { public static void echo(params object[] args) {
if (!bEcho) return; if (!bEcho) return;
var s = ""; var s = "";
...@@ -181,59 +90,6 @@ namespace CSRender { ...@@ -181,59 +90,6 @@ namespace CSRender {
Console.WriteLine(s); Console.WriteLine(s);
} }
// 事前チェック BashHash
public static bool RenderPdfInitCheck( Stream stream /* PDFストリーム */) {
Windows.Storage.Streams.InMemoryRandomAccessStream rmStrageStream=null;
{// Stream->Memstream -> byte array -> IBuffer -> InMemmory...Stream()
var ms = new MemoryStream();
stream.CopyTo(ms);
var bAray = ms.ToArray();
IBuffer ib = bAray.AsBuffer();
rmStrageStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
//await s.WriteAsync(ib);
async Task<uint> wa() => await rmStrageStream.WriteAsync(ib);
var wa_ret = wa().Result;
}
async Task<Windows.Data.Pdf.PdfDocument> LoadS() => await Windows.Data.Pdf.PdfDocument.LoadFromStreamAsync(rmStrageStream);
var pdfDoc = LoadS().Result;
if (pdfDoc == null) {
Console.WriteLine("error: get PdfDocument failed");
return false;
}
// var memStream = new MemoryStream();
// memStream.AsInputStream();
// WindowsRuntimeStreamExtensions.
// var ras = WindowsRuntimeStreamExtensions.AsRandomAccessStream(stream);
//https://blog.ch3cooh.jp/entry/20131207/1386342000
//https://csharp.hotexamples.com/site/file?hash=0xe951daae5be57f9f35507c45eb09a1085819b0a42b846aaa211ac138a2d1b5af&fullName=src/Nutrition/Nutrition.WP/WPOCRService.cs&project=SamirHafez/Nutrition
//なんだけどな。。。→ .NetFrameworkにAsRandomAccessStream(x)が存在しない。.Net Coreのみみたい。
// exeと同一場所に"baseRender.jpg"
string otPath = Path.Combine(Directory.GetParent(Assembly.GetEntryAssembly().Location).FullName,"baseRender.jpg");
bool bSaveImage = false;// Debug時にtrueにする
Console.WriteLine($"mode=Hash base check");
var taskList = new List<Task<int>>();
var hashValue = "";
var pm = new RenderConditionParams();
var ret = RenderPage(
doc: pdfDoc,
page: pdfDoc.GetPage(0),
otPath: otPath,
pm:pm,
bSaveImage: bSaveImage,
bHash: true,
otHashCode: ref hashValue
);
Console.WriteLine($@"Hash={hashValue},Host={Environment.MachineName},{Environment.OSVersion}");
return true;// true:OK, false:NG
}
// Sync version( no async -> Pallale Task ) **************************************************************************************
/// <summary> /// <summary>
/// PDFのレンダリング /// PDFのレンダリング
/// </summary> /// </summary>
...@@ -255,7 +111,8 @@ namespace CSRender { ...@@ -255,7 +111,8 @@ namespace CSRender {
bool bSaveImage = true, //ハッシュ値のみ計算するときにfalseにする bool bSaveImage = true, //ハッシュ値のみ計算するときにfalseにする
bool bHash = false, bool bHash = false,
UtHash.HashData inHashData = null, UtHash.HashData inHashData = null,
bool bPDFium = false bool bPDFium = false,
int nPageThreadNum = 4
) { ) {
if (pm == null) { if (pm == null) {
pm = new RenderConditionParams(); pm = new RenderConditionParams();
...@@ -263,79 +120,54 @@ namespace CSRender { ...@@ -263,79 +120,54 @@ namespace CSRender {
var otDir = inDir; var otDir = inDir;
var otBaseName = Path.GetFileName(pdfPath);//*.pdf var otBaseName = Path.GetFileName(pdfPath);//*.pdf
var otExtention = pm.ImageType.ToLower(); var otExtention = pm.ImageType.ToLower();
if (bSaveImage && otDir == "") { if (bSaveImage) {
// 出力ディレクトリが空→元PDFの同一フォルダにIMGフォルダを作成する if ( otDir == "" ) {
otDir = Path.Combine(Directory.GetParent(pdfPath).FullName, "IMG"); // 出力ディレクトリが空→元PDFの同一フォルダにIMGフォルダを作成する
otDir = Path.Combine(Directory.GetParent(pdfPath).FullName, "IMG");
}
if (!Directory.Exists(otDir)) if (!Directory.Exists(otDir))
Directory.CreateDirectory(otDir); Directory.CreateDirectory(otDir);
echo("ouptput dir=",otDir);
} }
// PDFファイルを読み込む // Open pdf by PDFium.
async Task<Windows.Storage.StorageFile> GetStrageFilePath(string path) int pageCount = 0;
=> await Windows.Storage.StorageFile.GetFileFromPathAsync(path); using(var docG = PdfiumViewer.PdfDocument.Load(pdfPath)){
var file = GetStrageFilePath(pdfPath).Result; pageCount = docG.PageCount;
inMode = "pageBitMap";
var taskList = new List<Task<int>>();
async Task<Windows.Data.Pdf.PdfDocument> Load(Windows.Storage.StorageFile path) uint[] rNo = makePageRange(inPageRange, (uint)pageCount);
=> await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(file);
var pdfDoc = Load(file).Result;
// 2つを合わせることができそう
//async Task<Windows.Data.Pdf.PdfDocument> GetLoadFromPath(string path)
//{
// var a1 = await Windows.Storage.StorageFile.GetFileFromPathAsync(path);
// var a2 = await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(a1);
// return a2;
//};
//var pdfDoc2 = GetLoadFromPath(pdfPath).Result;
if (pdfDoc == null) {
Console.WriteLine("error: get PdfDocument failed");
return -1;
}
var n = pdfDoc.PageCount;
inMode = "pageBitMap";
//Console.WriteLine($"mode={inMode},bPDFium={bPDFium}");
var taskList = new List<Task<int>>();
uint[] rNo = makePageRange(inPageRange, pdfDoc.PageCount);
if (inHashData != null) {
// ハッシュ対象の最大ページ数の設定必須。
inHashData.SetFile(pdfPath, (int)pdfDoc.PageCount);
}
// PDFiumViewer
PdfiumViewer.PdfDocument docG = null;
if (bPDFium) {
docG = PdfiumViewer.PdfDocument.Load(pdfPath);
}
//Console.WriteLine($@"dpi={pm.Dpi}");
/////並行処理するスレッド数を指定(2-4ぐらいが穏便な数値)
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.ForEach(rNo, options, i => {
//Console.WriteLine($"*****{i}*****/{rNo.Length}");
var hashValue = "";
var ret = RenderPage(
doc: pdfDoc,
page: pdfDoc.GetPage((uint)(i - 1)),
otPath: Path.Combine(otDir, $"{otBaseName}.{i}.{otExtention}"),
pm: pm,
bSaveImage: bSaveImage, // ファイル保存
bHash: bHash,
otHashCode: ref hashValue,
docG : docG
);
if (inHashData != null) { if (inHashData != null) {
lock (inHashData) { echo("Create Hash");
inHashData.AddHashCode(pdfPath, (int)i, hashValue); // ハッシュ対象の最大ページ数の設定必須。
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");
if ( docG != null)
docG.Dispose(); docG.Dispose();
}
return (int)pdfDoc.PageCount;// ページ数を返却します return pageCount;// ページ数を返却します
//return 0;// success.
} }
/// <summary> /// <summary>
/// pdfのページを画像に出力する /// pdfのページを画像に出力する
...@@ -345,113 +177,82 @@ namespace CSRender { ...@@ -345,113 +177,82 @@ namespace CSRender {
/// <param name="otPath">出力ファイル名</param> /// <param name="otPath">出力ファイル名</param>
/// <returns>正常:0 </returns> /// <returns>正常:0 </returns>
public static int RenderPage( public static int RenderPage(
// Need: PdfiumViewer.PdfDocument docG,
Windows.Data.Pdf.PdfDocument doc, // Need GetPage() only. (Total page number); int index,
PdfPage page, // Target Page Data(page.index:0-x)
string otPath, string otPath,
ref string otHashCode, ref string otHashCode,
// Optional: // Optional:
RenderConditionParams pm = null, RenderConditionParams pm = null,
bool bSaveImage = true,// ファイル保存有無。ハッシュ値計算のみのときにfalseにする bool bSaveImage = true,// ファイル保存有無。ハッシュ値計算のみのときにfalseにする
bool bHash = false, bool bHash = false
PdfiumViewer.PdfDocument docG = null //
){ ){
if( pm == null) {
pm = new RenderConditionParams();
}
double dpiWin = Check.GetDPI(); //96.0;/* , dpiPDF = 72.0*/
//double resoScale = (96.0 / dpiWin);
double resoScale = (pm.Dpi / 96.0);
//double resoScale = (inDpi / dpiWin);
var bDump = Check.bDump; var bDump = Check.bDump;
var bThDump = Check.bThDump; var bThDump = Check.bThDump;
if ( pm == null )
pm = new RenderConditionParams();
var opt = new PdfPageRenderOptions() { using (var memStrm = new MemoryStream()) {
// SEE:https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/PdfDocument/cs/Scenario1_Render.xaml.cs PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi);
//BitmapEncoderId = Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId,// JPEG using(var img = docG.Render(index, (float)pm.Dpi, (float)pm.Dpi, flg)){
IsIgnoringHighContrast = false img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Bmp);
}; //img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Jpeg);
var boxDic = new Dictionary<string, Windows.Foundation.Rect>() { /*
// Init // ImageFormatJPEGの時
["Media"] = page.Dimensions.MediaBox, dpi = 350,TIFF
["Trim"] = page.Dimensions.TrimBox, 2691.777 = 44分51.777秒
["Bleed"] = page.Dimensions.BleedBox, 10335ファイルで20G
["Crop"] = page.Dimensions.CropBox, ーー
["Art"] = page.Dimensions.ArtBox ImageFormat.Bmpに変更
}; result=0,time=2850.955[sec]
//Dump: 10335ファイルで12.2G
if ( bDump){ ーー
Console.WriteLine($@"PageZoom={page.PreferredZoom}"); pagePara=2
Console.WriteLine($@"PageRotate={page.Rotation.ToString()}"); para=4
ImageFormat.Bmp
Console.WriteLine($"{"Size",-5}=({page.Size.Width,8:f2},{page.Size.Height,8:f2})"); result=0,time=2824.644[sec]
foreach (var b in boxDic.Keys) {
var r = boxDic[b]; 10335ファイルで12.2G
Console.WriteLine($"{b,-5}=({r.X,8:f2},{r.Y,8:f2}) W({r.Width,8:f2},{r.Height,8:f2})"); ーー
Console.WriteLine($"{b,-5}=L {r.Left,-8:f2} R {r.Right,-8:f2}) T {r.Top,-8:f2} B {r.Bottom,-8:f2})"); pagePara=2
} para=8
} ImageFormat.Bmp
result=0,time=2828.631[sec] => 47分
opt.SourceRect = boxDic.ContainsKey(pm.BoxType) ? boxDic[pm.BoxType] : boxDic["Crop"];// Cannot find -> "Crop"; 10335ファイルで12.2G
//Console.WriteLine($"Select {inBoxType} box.");
*/
// 何も変倍しないとどうなる // [注意] PDFuim用Flush()必要しないとだめです
opt.DestinationWidth = (uint)(opt.SourceRect.Width * resoScale + 0.5); memStrm.Flush();
opt.DestinationHeight = (uint)(opt.SourceRect.Height * resoScale + 0.5); img.Dispose();
}
// 150%の時は * 0.5358を設定するとよい解像度になるが、この数値の計算方法がわからない var bmp = new System.Drawing.Bitmap(memStrm);
//opt.DestinationWidth = (uint)(opt.SourceRect.Width * 0.5358/*resoScale*/ + 0.5); if (bDump) {
//opt.DestinationHeight = (uint)(opt.SourceRect.Height * 0.5358/*resoScale*/ + 0.5); echo($"bmp=w:{bmp.Size.Width},h:{bmp.Size.Height}");
}
if (bDump) { //Console.WriteLine($"OrgReso({bmp.HorizontalResolution},{bmp.VerticalResolution})");
Console.WriteLine($"dpi={pm.Dpi},scale={resoScale},width(org)={page.Size.Width}->{opt.DestinationWidth}"); if (bHash) {
Console.WriteLine($"dpi={pm.Dpi},scale={resoScale},height(org)={page.Size.Height}->{opt.DestinationHeight}"); var h = GetHashValue(memStrm);
} //Console.WriteLine($"HashString:{h}");
using (var memStrm = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { otHashCode = h;
}
if (docG == null) { var th = Thread.CurrentThread.ManagedThreadId;
async Task RenderToStream(PdfPage p) => await p.RenderToStreamAsync(memStrm, opt); if (!bSaveImage) { //ファイル保存しない.ハッシュ計算のみ
RenderToStream(page).Wait(); if (bThDump) {
} else { echo($"ot[th{th},{(index + 1)}/{docG.PageCount}]=,{Path.GetFileName(otPath)},hash:{otHashCode}");
PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi); }
System.Drawing.Image img = docG.Render((int)(page.Index), (float)pm.Dpi, (float)pm.Dpi, flg); return 0;
img.Save(memStrm.AsStream(), System.Drawing.Imaging.ImageFormat.Jpeg); }
// [注意] PDFuim用に直接 AsStream()に書き込んでいるので、AsStream().Flush()しないとだめです // 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で設定できそう
async Task<bool> FlushX() => await memStrm.FlushAsync();
FlushX().Wait();
var bmp = new System.Drawing.Bitmap(memStrm.AsStream());
if (bDump) {
Console.WriteLine($"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 ) {
Console.WriteLine($"ot[th{th},{(page.Index + 1)}/{doc.PageCount}]=,{Path.GetFileName(otPath)},hash:{otHashCode}");
}
return 0;
}
var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers.
bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi);
if (bThDump) { if (bThDump) {
Console.WriteLine($"ot[th{th},{(page.Index + 1)}/{doc.PageCount}]={Path.GetFileName(otPath)}({Directory.GetParent(otPath)})"); echo($"ot[th{th},{(index + 1)}/{docG.PageCount}]={Path.GetFileName(otPath)}({Directory.GetParent(otPath)})");
} }
// //
imEnc.SaveImage(bmp, otPath); imEnc.SaveImage(bmp, otPath);
// //
bmp.Dispose(); bmp.Dispose();
//memStrm.Seek(0); //memStrm.Seek(0);
memStrm.Dispose();
} }
return 0;//success return 0;//success
} }
...@@ -482,62 +283,36 @@ namespace CSRender { ...@@ -482,62 +283,36 @@ namespace CSRender {
bool bHash = false, bool bHash = false,
UtHash.HashData inHashDataTgt = null, UtHash.HashData inHashDataTgt = null,
UtHash.HashData inHashDataRef = null, UtHash.HashData inHashDataRef = null,
bool bPDFium = false bool bPDFium = false,
int nPageThreadNum = 4
// 比較結果を返す ページ番号とメッセージ // 比較結果を返す ページ番号とメッセージ
) { ) {
var bDump = Check.bDump; var bDump = Check.bDump;
var bThDump = Check.bThDump; var bThDump = Check.bThDump;
if(bDump) if(bDump)
Console.WriteLine("RenderPdfDocCompare & diff Image! & non Para"); echo("RenderPdfDocCompare & diff Image! & non Para");
if (pm == null) if (pm == null)
pm = new RenderConditionParams(); pm = new RenderConditionParams();
var otDir = inDir;
var otDir = inDir;// 今は未使用
var otBaseName = Path.GetFileName(pdfPath);//xxx.pdf var otBaseName = Path.GetFileName(pdfPath);//xxx.pdf
var otExtention = pm.ImageType.ToLower(); var otExtention = pm.ImageType.ToLower();
if (otDir == "") { if (otDir == "") {
// 出力ディレクトリが空→元PDFの同一フォルダにIMGフォルダを作成する Console.WriteLine("Error:No output dir");
otDir = Path.Combine(Directory.GetParent(pdfPath).FullName, "IMG"); return -1;// error
if (!Directory.Exists(otDir))
Directory.CreateDirectory(otDir);
} }
// PDFファイルを読み込む if (!Directory.Exists(otDir))
Directory.CreateDirectory(otDir);
// 以下のコードで良いみたい。
// [変更待ち] var file2 = Windows.Storage.StorageFile.GetFileFromPathAsync(pdfPath).GetAwaiter().GetResult();
async Task<Windows.Storage.StorageFile> GetStrageFilePath(string path) // PDFファイルを読み込む by PDFium.
=> await Windows.Storage.StorageFile.GetFileFromPathAsync(path); var docG = PdfiumViewer.PdfDocument.Load(pdfPath);
var file = GetStrageFilePath(pdfPath).Result; var docGRef = PdfiumViewer.PdfDocument.Load(refPdfPath);
async Task<Windows.Data.Pdf.PdfDocument> Load(Windows.Storage.StorageFile path)
=> await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(path);
var pdfDoc = Load(file).Result;
if (pdfDoc == null) {
Console.WriteLine("error: get PdfDocument failed");
return -1;
}
// ref var pageCount = docG.PageCount;
var fileRef = GetStrageFilePath(refPdfPath).Result;
var pdfDocRef = Load(fileRef).Result;
if (pdfDocRef == null) {
Console.WriteLine("error: get PdfDocument(ref) failed");
return -1;
}
var n = pdfDoc.PageCount; uint[] rNo = makePageRange(inPageRange, (uint)pageCount);
if ( n != pdfDocRef.PageCount) {
Console.WriteLine($"error: differ page length:{n}:{pdfDocRef.PageCount}");
return -1;
}
uint[] rNo = makePageRange(inPageRange, n);
int nRetAll = 0;// 一致(def) int nRetAll = 0;// トータルの不一致ページ数
// ハッシュ情報の確認 // ハッシュ情報の確認
/* /*
...@@ -565,43 +340,34 @@ namespace CSRender { ...@@ -565,43 +340,34 @@ namespace CSRender {
var fcResultMsg = new SortedDictionary<int,string>();// 比較結果を返す ページ番号とメッセージ var fcResultMsg = new SortedDictionary<int,string>();// 比較結果を返す ページ番号とメッセージ
// PDFiumViewer
PdfiumViewer.PdfDocument docG = null;
PdfiumViewer.PdfDocument docGRef = null;
if (bPDFium) {
docG = PdfiumViewer.PdfDocument.Load(pdfPath);
docGRef = PdfiumViewer.PdfDocument.Load(refPdfPath);
}
/////並行処理するスレッド数を指定(2-4ぐらいが穏便な数値) /////並行処理するスレッド数を指定(2-4ぐらいが穏便な数値)
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.ForEach(rNo, options, i => { Parallel.ForEach(rNo, options, i => {
Windows.Storage.Streams.InMemoryRandomAccessStream tgtStrm = null, refStrm = null; MemoryStream tgtStrm = null, refStrm = null;
string tgtHash = "", refHash = ""; string tgtHash = "", refHash = "";
// ターゲット レンダ // ターゲット レンダラ定義
InMemoryRandomAccessStream RendPageTGT() { MemoryStream RendPageTGT() {
var strm = RenderPageStream(doc:pdfDoc,page:pdfDoc.GetPage((uint)(i-1)), pm:pm,docG:docG); var strm = RenderPageStream(docG:docG,index:(int)i-1, pm:pm);
return strm; return strm;
} }
// リファレンス レンダ // リファレンス レンダラ定義
InMemoryRandomAccessStream RendPageREF() { MemoryStream RendPageREF() {
var strm = RenderPageStream(doc:pdfDocRef,page:pdfDocRef.GetPage((uint)(i-1)), pm:pm,docG:docGRef); var strm = RenderPageStream(docG:docGRef, index:(int)i-1, pm:pm);
return strm; return strm;
} }
//tgtStrm = RendPageTGT();
// ターゲット処理
if (tgtHf == null) { if (tgtHf == null) {
tgtStrm = RendPageTGT(); tgtStrm = RendPageTGT();// Render
tgtHash = GetHashValue(tgtStrm); tgtHash = GetHashValue(tgtStrm);
} else { } else {
lock (inHashDataTgt) { lock (inHashDataTgt) {
tgtHash = tgtHf.GetHashValue((int)(i - 1)); tgtHash = tgtHf.GetHashValue((int)(i - 1));
} }
} }
// リファレンス // リファレンス処理
if (refHf == null) { if (refHf == null) {
refStrm = RendPageREF(); refStrm = RendPageREF();// Render
refHash = GetHashValue(refStrm); refHash = GetHashValue(refStrm);
} else { } else {
lock (inHashDataRef) { lock (inHashDataRef) {
...@@ -609,52 +375,49 @@ namespace CSRender { ...@@ -609,52 +375,49 @@ namespace CSRender {
} }
} }
// compare stream; // compare stream;
int nRet = 1;//異なる(def). // int nRet = 1;//異なる(diff).
var dateStr = DateTime.Now.ToString(); var dateStr = DateTime.Now.ToString();
//Console.WriteLine($@"tgt={i}:{tgtHash}"); //Console.WriteLine($@"tgt={i}:{tgtHash}");
//Console.WriteLine($@"ref={i}:{refHash}"); //Console.WriteLine($@"ref={i}:{refHash}");
if (tgtHash != refHash) { if (tgtHash != refHash) {
nRet = 1; // no match
nRetAll = nRet;//全体の不一致設定 // nRet = 1;
nRetAll++;// = nRet;//全体の不一致設定
echo($"{i}:NotMatch:{i},tgt[{tgtHash}],ref[{refHash}]"); echo($"{i}:NotMatch:{i},tgt[{tgtHash}],ref[{refHash}]");
//2020年3月2日 15:23:55:[@Difference]:<fullpath>.diff.jpg //2020年3月2日 15:23:55:[@Difference]:<fullpath>.diff.jpg
lock (fcResultMsg) { lock (fcResultMsg) {
fcResultMsg.Add((int)i, $@"{dateStr}:[@Difference]:{pdfPath}.diff.jpg"); fcResultMsg.Add((int)i, $@"{dateStr}:[@Difference]:{Path.GetFileName(pdfPath)}.{i}");
} }
echo("***** save diff image");
//if ( true && tgtStrm != null && bSave) { // diffのjpegを書き出してみる(tgt)
{ if (tgtStrm == null)
echo("***** save diff image"); tgtStrm = RendPageTGT();
// diffのjpegを書き出してみる(tgt) var otPath = Path.Combine(otDir, $"{otBaseName}.{i}.tgt.{otExtention}");
if (tgtStrm == null) tgtStrm = RendPageTGT(); using(var bmp = new System.Drawing.Bitmap(tgtStrm)){
var otPath = Path.Combine(otDir, $"{otBaseName}.{i}.tgt.{otExtention}");
var bmp = new System.Drawing.Bitmap(tgtStrm.AsStream());
var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers. var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers.
bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi); bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi);
imEnc.SaveImage(bmp, otPath); imEnc.SaveImage(bmp, otPath);
bmp.Dispose(); bmp.Dispose();
} }
//if (true && refStrm != null && bSave) { // diffのjpegを書き出してみる(ref)
{ if (refStrm == null)
// diffのjpegを書き出してみる(ref) refStrm = RendPageREF();
if (refStrm == null) refStrm = RendPageREF(); otPath = Path.Combine(otDir, $"{otBaseName}.{i}.ref.{otExtention}");
var otPath = Path.Combine(otDir, $"{otBaseName}.{i}.ref.{otExtention}"); using(var bmp = new System.Drawing.Bitmap(refStrm)){
var bmp = new System.Drawing.Bitmap(refStrm.AsStream());
var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers. var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers.
bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi); bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi);
imEnc.SaveImage(bmp, otPath); imEnc.SaveImage(bmp, otPath);
bmp.Dispose(); bmp.Dispose();
} }
} else { } else {
nRet = 0;// match // match
//Console.WriteLine($"{i}:Match:{i}"); //nRet = 0;
// 2019年12月18日 19:31:38:[OK]:fname.pdf.1.png
lock (fcResultMsg) { lock (fcResultMsg) {
fcResultMsg.Add((int)i, $@"{dateStr}:[OK]:{Path.GetFileName(pdfPath)}.{i}.png"); fcResultMsg.Add((int)i, $@"{dateStr}:[OK]:{Path.GetFileName(pdfPath)}.{i}");
} // Ex 2019年12月18日 19:31:38:[OK]:fname.pdf.1.png
} }
}
// dispose: // dispose:
if (refStrm != null) refStrm.Dispose(); if (refStrm != null) refStrm.Dispose();
if (tgtStrm != null) tgtStrm.Dispose(); if (tgtStrm != null) tgtStrm.Dispose();
...@@ -676,75 +439,35 @@ namespace CSRender { ...@@ -676,75 +439,35 @@ namespace CSRender {
} }
File.AppendAllText(resultDataPath, resCont); File.AppendAllText(resultDataPath, resCont);
} }
return nRetAll;// success. return nRetAll;// success. 不一致数を返す
} }
/// <summary> /// <summary>
/// 比較モード /// 比較モード
/// </summary> /// </summary>
/// <param name="doc">PdfDocument</param> /// <param name="docG">ドキュメントハンドル</param>
/// <param name="nPage">ページ番号</param> /// <param name="index">ページ番号(0-)</param>
/// <param name="otPath">出力ファイル名</param>
/// <returns>正常:0 </returns> /// <returns>正常:0 </returns>
public static Windows.Storage.Streams.InMemoryRandomAccessStream RenderPageStream( public static MemoryStream RenderPageStream(
// Need: PdfiumViewer.PdfDocument docG,
Windows.Data.Pdf.PdfDocument doc, // Need GetPage() only. (Total page number); int index,
PdfPage page, // Target Page Data(page.index:0-x) // Optional:
//string otPath, RenderConditionParams pm = null
// Optional:
RenderConditionParams pm = null,
PdfiumViewer.PdfDocument docG = null //
) { ) {
var memStrm = new MemoryStream();
var bDump = Check.bDump; var bDump = Check.bDump;
var bThDump = Check.bThDump; var bThDump = Check.bThDump;
if (pm == null) if (pm == null)
pm = new RenderConditionParams(); pm = new RenderConditionParams();
double dpiWin = Check.GetDPI(); //96.0;/* , dpiPDF = 72.0*/ PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi);
double resoScale = (pm.Dpi / dpiWin); System.Drawing.Image img = docG.Render(index, (float)pm.Dpi, (float)pm.Dpi, flg);
var opt = new PdfPageRenderOptions() { img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Bmp);
// SEE:https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/PdfDocument/cs/Scenario1_Render.xaml.cs //img.Save(memStrm, System.Drawing.Imaging.ImageFormat.Jpeg);
//BitmapEncoderId = Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId,// JPEG // [注意] PDFuim用にFlush()必要
IsIgnoringHighContrast = false memStrm.Flush();
}; img.Dispose();
// return memStrm;
//async Task PreparePage(PdfPage p) => await p.PreparePageAsync();//ページの完成を待つ
//PreparePage(page).Wait();
//page.PreparePageAsync
var boxDic = new Dictionary<string, Windows.Foundation.Rect>() {
// Init
["Media"] = page.Dimensions.MediaBox,
["Trim"] = page.Dimensions.TrimBox,
["Bleed"] = page.Dimensions.BleedBox,
["Crop"] = page.Dimensions.CropBox,
["Art"] = page.Dimensions.ArtBox
};
opt.SourceRect = boxDic.ContainsKey(pm.BoxType) ? boxDic[pm.BoxType] : boxDic["Crop"];// Cannot find -> "Crop";
opt.DestinationWidth = (uint)(opt.SourceRect.Width * resoScale + 0.5);
opt.DestinationHeight = (uint)(opt.SourceRect.Height * resoScale + 0.5);
if (bDump)
Console.WriteLine($"dpi={pm.Dpi},scale={resoScale},height(org)={page.Size.Height}->{opt.DestinationHeight}");
var memStrm = new Windows.Storage.Streams.InMemoryRandomAccessStream();
if(true) {
if (docG == null) {
async Task RenderToStream(PdfPage p) => await p.RenderToStreamAsync(memStrm, opt);
RenderToStream(page).Wait();
} else {
PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi);
System.Drawing.Image img = docG.Render((int)(page.Index), (float)pm.Dpi, (float)pm.Dpi, flg);
img.Save(memStrm.AsStream(), System.Drawing.Imaging.ImageFormat.Jpeg);
// [注意] PDFuim用に直接 AsStream()に書き込んでいるので、AsStream().Flush()しないとだめです
memStrm.AsStream().Flush();
}
async Task<bool> FlushX() => await memStrm.FlushAsync();
//var dmy =FlushX().Result;
FlushX().Wait();
}
//page.Dispose();
return memStrm;//
} }
/// <summary> /// <summary>
...@@ -787,10 +510,10 @@ namespace CSRender { ...@@ -787,10 +510,10 @@ namespace CSRender {
} }
return range.ToArray<uint>(); return range.ToArray<uint>();
} }
public static string GetHashValue(Windows.Storage.Streams.InMemoryRandomAccessStream strm) { public static string GetHashValue(MemoryStream strm) {
var alg = new SHA256CryptoServiceProvider(); var alg = new SHA256CryptoServiceProvider();
strm.Seek(0); strm.Seek(0, SeekOrigin.Begin);
var bin = alg.ComputeHash(strm.AsStream()); var bin = alg.ComputeHash(strm);
alg.Clear(); alg.Clear();
// バイト配列をUTF8エンコードで文字列化 // バイト配列をUTF8エンコードで文字列化
var hashedText = new StringBuilder(); var hashedText = new StringBuilder();
...@@ -840,9 +563,7 @@ namespace CSRender { ...@@ -840,9 +563,7 @@ namespace CSRender {
} }
[DataContract] [DataContract]
public class HashFile public class HashFile {
{
[DataMember(Order=0)] [DataMember(Order=0)]
public DateTime UpdateTime; // ファイルの更新日 public DateTime UpdateTime; // ファイルの更新日
[DataMember(Order=1)] [DataMember(Order=1)]
...@@ -859,14 +580,11 @@ namespace CSRender { ...@@ -859,14 +580,11 @@ namespace CSRender {
} }
} }
[DataContract] [DataContract]
public class HashData public class HashData {
{
[DataMember(Order=0)] [DataMember(Order=0)]
public HashHead Head = new HashHead(); public HashHead Head = new HashHead();
[DataMember(Order=1)] [DataMember(Order=1)]
public SortedDictionary<string, HashFile> Files = new SortedDictionary<string, HashFile>(); public SortedDictionary<string, HashFile> Files = new SortedDictionary<string, HashFile>();
// メソッド // メソッド
static public string GetHashFileName(string baseName, string dpi, string box, string imType, string jq) { static public string GetHashFileName(string baseName, string dpi, string box, string imType, string jq) {
var h = new HashHead(); var h = new HashHead();
...@@ -890,7 +608,6 @@ namespace CSRender { ...@@ -890,7 +608,6 @@ namespace CSRender {
var hd = HashData.load(hashPath); //ファイルの存在はこっちに任す var hd = HashData.load(hashPath); //ファイルの存在はこっちに任す
return hd; return hd;
} }
static public HashData load(string hashPath) { static public HashData load(string hashPath) {
if (!File.Exists(hashPath)) { if (!File.Exists(hashPath)) {
echo("not find load hash data file"); echo("not find load hash data file");
...@@ -921,7 +638,6 @@ namespace CSRender { ...@@ -921,7 +638,6 @@ namespace CSRender {
return x; return x;
} }
} }
public bool save(string path) { public bool save(string path) {
// JSONに変換するデータを作る。 // JSONに変換するデータを作る。
// 再保存の抑制 // 再保存の抑制
...@@ -954,9 +670,7 @@ namespace CSRender { ...@@ -954,9 +670,7 @@ namespace CSRender {
UpdateTimeStr= fi.LastWriteTime.ToString(), UpdateTimeStr= fi.LastWriteTime.ToString(),
PageHashCode = new List<string>(pageNum) PageHashCode = new List<string>(pageNum)
}; };
// Console.WriteLine($@"update={hf.UpdateTime}"); // Console.WriteLine($@"update={hf.UpdateTime}");
for (var i = 0; i < pageNum; i++) { for (var i = 0; i < pageNum; i++) {
hf.PageHashCode.Add(""); hf.PageHashCode.Add("");
} }
...@@ -985,7 +699,6 @@ namespace CSRender { ...@@ -985,7 +699,6 @@ namespace CSRender {
} }
public bool AddHashCode(string pathPDF, int pageNo/*1開始*/, string hashCode) { public bool AddHashCode(string pathPDF, int pageNo/*1開始*/, string hashCode) {
//Console.WriteLine("call addHashCode:" + pathPDF + ":" + pageNo); //Console.WriteLine("call addHashCode:" + pathPDF + ":" + pageNo);
var fi = new FileInfo(pathPDF); var fi = new FileInfo(pathPDF);
var fName = fi.Name; var fName = fi.Name;
var n = pageNo - 1;//0始まり var n = pageNo - 1;//0始まり
...@@ -993,7 +706,6 @@ namespace CSRender { ...@@ -993,7 +706,6 @@ namespace CSRender {
this.Head.Dirty = true;// 変更あり this.Head.Dirty = true;// 変更あり
return false; return false;
} }
if (Files[fName].PageHashCode[n] != hashCode) { if (Files[fName].PageHashCode[n] != hashCode) {
this.Head.Dirty = true;// 変更あり this.Head.Dirty = true;// 変更あり
Files[fName].PageHashCode[n] = hashCode; Files[fName].PageHashCode[n] = hashCode;
...@@ -1060,6 +772,21 @@ namespace CSRender { ...@@ -1060,6 +772,21 @@ namespace CSRender {
} }
return 0; 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;
}
/// <summary> /// <summary>
/// サポートするEncodeパラメータ一覧 /// サポートするEncodeパラメータ一覧
/// 現状Errorが発生して取得できない /// 現状Errorが発生して取得できない
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\bin\Release\</OutputPath> <OutputPath>..\bin\Release\</OutputPath>
...@@ -76,7 +76,6 @@ ...@@ -76,7 +76,6 @@
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Windows, Version=255.255.255.255, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Windows, Version=255.255.255.255, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
......
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic; // For List<>
using System.Linq; using System.Linq; // For Enumration
//using System.Text;
//using System.Threading;
//using System.Threading.Tasks;
//using System.Windows;
//using System.Windows.Media.Imaging;
using System.Reflection; // Assembly. using System.Reflection; // Assembly.
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.ServiceModel; // WCF using System.ServiceModel; // for WCF
using System.Diagnostics; // for Process using System.Diagnostics; // for Process
// no need
//using System.Runtime.InteropServices;
// 作成クラス // 作成クラス
using CSRender; using CSRender;
...@@ -37,14 +32,16 @@ namespace CSRenderMain { ...@@ -37,14 +32,16 @@ namespace CSRenderMain {
[DataMember] [DataMember]
public string pdfPath = ""; //対象ファイル public string pdfPath = ""; //対象ファイル
[DataMember] [DataMember]
public string pdfPath2 = ""; //比較ファイル public string pdfPathRef = ""; //比較ファイル
[DataMember] [DataMember]
public string outuptImageDir = ""; // /O public string outuptImageDir = ""; // /O
[DataMember] [DataMember]
public string dpi = "72.0"; // /D public string dpi = "72.0"; // /D
[DataMember] [DataMember]
public int para = 4; // 並行数 public int para = 4; // 並行数(プロセス数)
[DataMember] [DataMember]
public int paraPage = 4; // 並行数(page処理スレッド)
[DataMember]
public string boxSelect = "Crop"; // /B<x> public string boxSelect = "Crop"; // /B<x>
[DataMember] [DataMember]
public string pageRange = "1-*"; // /P public string pageRange = "1-*"; // /P
...@@ -76,141 +73,113 @@ namespace CSRenderMain { ...@@ -76,141 +73,113 @@ namespace CSRenderMain {
public ParamData Clone() { public ParamData Clone() {
return (ParamData)MemberwiseClone(); return (ParamData)MemberwiseClone();
} }
} }
public class Program { public class Program {
static void DispHelp() { static void DispHelpNoArg() {
var pgName = Path.GetFileName(Path.GetFileName(Assembly.GetExecutingAssembly().Location));// プログラム名 var asmName = Assembly.GetExecutingAssembly().GetName();
var pgNameFullPath = Assembly.GetExecutingAssembly().Location;// プログラム名 var pgName = asmName.Name;// "アセンブリ名"
Console.WriteLine("pageNameF="+pgNameFullPath); var v1 = asmName.Version; // "1.0.0.0"
// 逐語的文字列リテラル $@, ダブルクウォートは2つでエスケープされる
// HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
Console.Write( $@"
Version={ v1}
{pgName} [/<Opts>] <PDFPath or PDFDir> :Rendering(to JPEG,TIFF,...)
{pgName} /MkHash [/<Opts>] <PDFPath or PDFDir> :Make Imaged hash keys
{pgName} /FC [/<Opts>] <TgtPDF Dir> <RefPDF Dir>:Compare files
string msg = Render of PDF file.available 3 command mode:[Basic Rendering] [Make Hash command] [Compare command], and[Render Options]
$"{pgName} [/<Opts>] <PDFPath or PDFDir>\n" PDFの画像化は[Basic Rendering]。
+ $"* Render of PDF file. available 3 command mode:[Basic Rendering] [Make Hash command] [Compare command], and [Render Options]\n" 比較は/MkHash([Make Hash command]) 後に/FC([Compare command]) で高速実行できます。
+ $"\tPDFの画像化は[Basic Rendering]。\n" For more information,see /H /? or /?
+ $"\t比較は/MkHash([Make Hash command])後に、/FC([Compare command])で高速実行できます。\n" ");
+ $"\n" // HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+ $"[Basic Rendering] 基本的なレンダリング\n" }
+ $"\t{pgName} [/<Render Options>] <PDFPath|PDFDir>\n" static void DispHelpDetail() {
+ $"\t/F <pdfPath|pdfDir> : pdfPath(pdfファイル名|ディレクトリ) /Fは省略可能\n" var asmName = Assembly.GetExecutingAssembly().GetName();
+ $"\t/O <output directory> : 出力ディレクトリ。省略時は\"IMG\"フォルダ\n" var pgName = asmName.Name;// "アセンブリ名"
+ $"\n" var v1 = asmName.Version; // "1.0.0.0"
+ $"[Render Options] レンダリングオプション\n" DispHelpNoArg();
+ $"\t/D <解像度> : 解像度指定 9 - 300dpi(default=72dpi)\n" // 逐語的文字列リテラル $@, ダブルクウォートは2つでエスケープされる
+ $"\t/JPG,/JPEG,/PNG,/TIF,/TIFF,/GIF,/BMP: Select one output format.(default=/JPG)\n" // HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+ $"\t/JPEGQ <quality>: Jpegの品質指定1-100(default=91)\n" Console.Write($@"
+ $"\n" [Basic Rendering] 基本的なレンダリング
+ $"\t/P <PageRange> : ページの範囲を指定する(省略時は全ページ)\n" {pgName} [/<Render Options>] <PDFPath|PDFDir>
+ $"\t\t連続した範囲を指定する場合は、ハイフン('-')を用いる。終了側を省略すると最終pageまで。\n" /F <pdfPath|pdfDir> : pdfPath(pdfファイル名|ディレクトリ) /Fは省略可能
+ $"\t\t複数のページを指定する場合は、カンマ(',')を用いる\n" /O <output directory> : 出力ディレクトリ。省略時は""IMG""フォルダ
+ $"\t\tEx. /P \"1,2,30-100\" //1,2pages and 30-100pages.\n"
+ $"\t[Unsupport] 未対応↓\n"
+ $"\t/L <input List text> : 入力PDFファイルリスト(*unsupport)\n"
+ $"\t/T <tempPath> : テンポラリフォルダを指定(省略時は出力先フォルダと同じ(*unsupport no need)\n"
+ $"\t/OP <0|1> : オーバープリントのOn/Off (省略時は1)(*unsupport allways on[1])\n"
+ $"\t/U <0|1> : 同名上書き設定 0:上書きしない 1:上書き(*unsupport allways overwrite[1])\n"
+ $"\t/OFFSET <X> <Y> : ミリ単位でオフセットを指定する(省略時は共に0mm)(*unsupport)\n"
+ $"\t/PDFium <0or1>: GoogoleのPDFiumViewerエンジンを使用する(default=1>\n"
+ $"\t【未】/BM,/BT,/BA,/BA,/BC: Select one box.(default=/BC:CrobBox): Boxies:MediaBox/BleedBox/TrimBox/ArtBox/CropBox\n"
+ $"\n" [Render Options] レンダリングオプション
+ $"[Make Hash command] 比較用ハッシュ値作成コマンド\n" /D <解像度> : 解像度指定 9 - 300dpi(default=72dpi)
+ $"\t{pgName} /MkHash ...<Render Options>... <PDF|PDF dir>\n" /JPG,/JPEG,/PNG,/TIF,/TIFF,/GIF,/BMP: Select one output format.(default=/JPG)
+ $"\t/MKHash : ハッシュ値を出力する。前記の[Render Options]を指定すること\n" /JPEGQ <quality>: Jpegの品質指定1-100(default=91)
+ $"\t[HASHファイル作成]\n"
+ $"\n" /P <PageRange> : ページの範囲を指定する(省略時は全ページ)
+ $"[Compare command] 比較コマンド\n" 連続した範囲を指定する場合は、ハイフン('-')を用いる。終了側を省略すると最終pageまで。
+ $"\t{pgName} /FC ...<Render Options>... <Target PDF|PDF dir> <Reference PDF|PDF dir>\n" 複数のページを指定する場合は、カンマ(',')を用いる
+ $"\t/FC : 2つのPDFを比較する。前記の[Render Options]を指定すること。無名引数が2つ必要です\n" Ex. /P ""1,2,30-100"" //1,2pages and 30-100pages.
+ $"\t 事前に/MkHashを実行しておくことで高速に処理できる\n" [Unsupport] 未対応↓
+ $"\n" /L <input List text> : 入力PDFファイルリスト(*unsupport)
+ $"\t/RESULT <result file> : 比較結果を格納するファイルパス\n" /T <tempPath> : テンポラリフォルダを指定(省略時は出力先フォルダと同じ(*unsupport no need)
+ $"\t\t/FCコマンドを指定すると一致したら0,不一致なら1を返却するようになる\n" /OP <0|1> : オーバープリントのOn/Off (省略時は1)(*unsupport allways on[1])
+ $"\t\t<result file>は、<pageNum(1始まり)>,<[OK] or [@Difference]>の行で構成される\n" /U <0|1> : 同名上書き設定 0:上書きしない 1:上書き(*unsupport allways overwrite[1])
/OFFSET <X> <Y> : ミリ単位でオフセットを指定する(省略時は共に0mm)(*unsupport)
+ $"\n" /PDFium <0or1>: GoogoleのPDFiumViewerエンジンを使用する(default=1>
+ $"[ELSE ] その他のオプション\n" 【未】/BM,/BT,/BA,/BA,/BC: Select one box.(default=/BC:CrobBox): Boxies:MediaBox/BleedBox/TrimBox/ArtBox/CropBox
+ $"\t/PDFium <0|1>:PDFiumライブラリを使う,デフォルト=1\n"
+ $"\t/NoExeSepa :実行分離しない(遅い)\n"
+ $"\t/para <並行数>:並行数を指定(デフォルト4)\n"
+ $"\t内部コマンド:/SubExe <WCF_PipeAddress> :実行分離,PDF単位で別Processで処理\n"
+ $"\n"
+ $"/H or /? : This help\n"
[Make Hash command] 比較用ハッシュ値作成コマンド
{pgName} /MkHash ...<Render Options>... <PDF|PDF dir>
/MKHash : ハッシュ値を出力する。前記の[Render Options]を指定すること
[HASHファイル作成]
/* PDFと同じフォルダに [Compare command] 比較コマンド
data.<出力条件>.jsonファイルを作成する {pgName} /FC ...<Render Options>... <Target PDF|PDF dir> <Reference PDF|PDF dir>
出力条件:<dpi>+<Box>+<IMFormat>+<JQ> /FC : 2つのPDFを比較する。前記の[Render Options]を指定すること。無名引数が2つ必要です
data.72_BT_JPG_Q34.json 事前に/MkHashを実行しておくことで高速に処理できる
既に存在したら、それを読んでから、追記(もしくは書き換える>。 /RESULT <result file> : 比較結果を格納するファイルパス
*/ /FCコマンドを指定すると一致したら0,不一致なら1を返却するようになる
<result file>は、<pageNum(1始まり)>,<[OK] or [@Difference]>の行で構成される
// Remove TEST [ELSE ] その他のオプション
//+$"\t[for TEST]\n" /NoExeSepa :実行分離しない(遅い)
//+$"\t/M Sync(default) or ASync\n" /Para <プロセス並行数>:本Exeの並行数を指定(デフォルト4)
//+$"\t/M <mode> : pageBitMap(default), pageBitMapImage, page(same as pageBitMap), org(orginal source)\n" /ParaPage <ページ処理スレッド数>:ページ処理のスレッド数を指定(デフォルト4)
+ "\n";
Console.Write(msg); /H or /? : This help");
// HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
} }
#if COMENT
// 設計の見直し
* ハッシュ作成のみのコマンド
/MkHashをつけると ハッシュファイルのみ作成。ただし、ディレクトリ指定に限る
CSRender /MkHash <PDFディレクトリ> <各種Renderパラメータ>
* 単純なRenderを維持。それに同時にハッシュファイルのOn/Off
/MkHashをつけなければよい。
CSRender <PDFディレクトリor PDFファイル> <各種Renderパラメータ>
* 比較モードは、Autoハッシュで、存在しなければ最初に作成。Target/Refともに。比較しながら作成してよし
Manualでハッシュなしで、遅いけどできるようにする。ハッシュ動作の検証用に
/FCをつけると、デフォルトでハッシュ優先で検査(Auto相当)、ハッシュ検査を無効にしたければ
/NoHash追加でハッシュを無視してTarget/Refとも再計算。ハッシュを使いたければ、事前に/MkHash
作成しておけばよい。
* ヘルプは <単純なRender> <ハッシュ作成> <比較>にわける
* <単純なレンダラ>: 1ファイルおよびディレクトリの比較。 Renderingパラメータの一覧(項目分ける)まで.
* <ハッシュ作成>: /MkHashで高速比較のための前準備。ディレクトリモードでハッシュファイルのみを作成する。Renderingパラメータは合わせること
* <比較>:/FCコマンドで比較の説明
* ここにAsiccDocを埋められないの? 情報なし*
// 遅くなるので
* 比較モード時の差異があったとき、Diffを出す上限値をLimitDiffNumがほしい(デフォルトは各PDF1pageとしたい)
* 比較NGになって、フルでDIFF画像を出したいときはLimitDiffを解除すべき。単独ファイル指定のとき。
// 設計の見直し(END)
#endif
/// <summary> /// <summary>
/// PDF renderer main /// PDF render main
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns>0 正常終了</returns>
static int Main(string[] args) { static int Main(string[] args) {
var argsStr = String.Join(@" ", args);
// 引数の保持 var pm = new ParamData(); // 引数の保持
var pm = new ParamData(); bool bDirMode = false; // ディレクト指定の場合
bool bDirMode = false; // ディレクト指定の場合
//var pdfPathLst = new List<string>();
string[] pdfPathLst = null; string[] pdfPathLst = null;
string[] pdfPathLst2 = null; string[] pdfPathLst2 = null;
var pdfPathLstBoth = new List<string>(); var pdfPathLstBoth = new List<string>();
var pdfPathLstNoBoth = new List<string>(); var pdfPathLstNoBoth = new List<string>();
//ハッシュ関係 //ハッシュ関係
string hashBaseName = "RenderHash"; string hashBaseName = "RenderHash";
CSRender.Check.GetDPI();
// No CSRender.Check.GetDPI();
// 未使用
//{// ベースハッシュ値の計算テスト
// var streamPDF = ResData.GetBaseHashPDF();
// RenderPDF.RenderPdfInitCheck(streamPDF);
//}
//{// ベースハッシュ値の計算テスト var logicalProcessNum = Environment.ProcessorCount;
// // 一旦使用しない
// var streamPDF = ResData.GetBaseHashPDF();
// RenderPDF.RenderPdfInitCheck(streamPDF);
//}
var qu = new Queue<string>(args); // 引数をQueに登録 (qu.Enqueue(a)でも可能) if ( logicalProcessNum >= 10 ) {// 元々 4(exe)+4(page)なので+2 = プロセス数が10以上の時にexe数を増やす
pm.para = logicalProcessNum - pm.paraPage;
}
/*
* ↓引数解析
* */
var qu = new Queue<string>(args); // 引数をQueに登録 (qu.Enqueue(a)でも可能)
while (qu.Count > 0) { while (qu.Count > 0) {
// 引数のパース // 引数のパース
var wd = qu.Dequeue(); // 取り出しqu.Dequeue()で次の要素を取得する var wd = qu.Dequeue(); // 取り出しqu.Dequeue()で次の要素を取得する
...@@ -227,8 +196,8 @@ namespace CSRenderMain { ...@@ -227,8 +196,8 @@ namespace CSRenderMain {
["/BB"] = "Bleed", ["/BC"] = "Crop", ["/BB"] = "Bleed", ["/BC"] = "Crop",
["/BA"] = "Art" ["/BA"] = "Art"
}; };
if (isOpt("/?", "/H")) { if (isOpt("/?", "/H","/Help")) {
DispHelp(); DispHelpDetail();
return -1; return -1;
} else if (isOpt("/F")) { } else if (isOpt("/F")) {
pm.pdfPath = (qu.Count > 0) ? qu.Dequeue() : "";// next word. pm.pdfPath = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
...@@ -238,17 +207,24 @@ namespace CSRenderMain { ...@@ -238,17 +207,24 @@ namespace CSRenderMain {
pm.dpi = (qu.Count > 0) ? qu.Dequeue() : "";// next word. pm.dpi = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!double.TryParse(pm.dpi, out double dmy)) { if (!double.TryParse(pm.dpi, out double dmy)) {
Console.WriteLine($"解像度が不正です:/D {pm.dpi}"); Console.WriteLine($"解像度が不正です:/D {pm.dpi}");
DispHelp(); DispHelpNoArg();
return -1; return -1;
} }
} else if (isOpt("/Para")) { } else if (isOpt("/Para")) {
string paraNum = (qu.Count > 0) ? qu.Dequeue() : "";// next word. string paraNum = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!int.TryParse(paraNum, out pm.para)) { if (!int.TryParse(paraNum, out pm.para)) {
Console.WriteLine($"並行数が不正です:/para {paraNum}"); Console.WriteLine($"並行数が不正です:/para {paraNum}");
DispHelp(); DispHelpNoArg();
return -1; return -1;
} }
} else if (isOpt("/P")) { } else if (isOpt("/ParaPage")) {
string paraNum = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!int.TryParse(paraNum, out pm.paraPage)){
Console.WriteLine($"並行数(ページスレッド数が不正です:/paraPage {paraNum}");
DispHelpNoArg();
return -1;
}
} else if (isOpt("/P")) {
pm.pageRange = (qu.Count > 0) ? qu.Dequeue() : "";// next word. pm.pageRange = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
} else if (isOpt("/JPG", "/JPEG")) { } else if (isOpt("/JPG", "/JPEG")) {
pm.imageType = "JPG"; pm.imageType = "JPG";
...@@ -264,7 +240,7 @@ namespace CSRenderMain { ...@@ -264,7 +240,7 @@ namespace CSRenderMain {
pm.jpegQ = (qu.Count > 0) ? qu.Dequeue() : "";// next word. pm.jpegQ = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!int.TryParse(pm.jpegQ, out int dmy)) { if (!int.TryParse(pm.jpegQ, out int dmy)) {
Console.WriteLine($"JPEG Qualityが不正です:/JPEGQ {pm.jpegQ}"); Console.WriteLine($"JPEG Qualityが不正です:/JPEGQ {pm.jpegQ}");
DispHelp(); DispHelpNoArg();
return -1; return -1;
} }
if (!(0 < dmy && dmy <= 100)) { if (!(0 < dmy && dmy <= 100)) {
...@@ -323,13 +299,14 @@ namespace CSRenderMain { ...@@ -323,13 +299,14 @@ namespace CSRenderMain {
pm.pdfPath = wd; pm.pdfPath = wd;
continue; continue;
} }
if (pm.bFC && (pm.pdfPath2 == "")) { if (pm.bFC && (pm.pdfPathRef == "")) {
pm.pdfPath2 = wd;// 比較時のみ取得 pm.pdfPathRef = wd;// 比較時のみ取得
continue; continue;
} }
} }
} }
// ↑引数解析終わり // ↑引数解析終わり
setEcho(pm.bVerbose); setEcho(pm.bVerbose);
if (pm.bVerbose) if (pm.bVerbose)
echo("Varbose Mode!"); echo("Varbose Mode!");
...@@ -339,6 +316,7 @@ namespace CSRenderMain { ...@@ -339,6 +316,7 @@ namespace CSRenderMain {
if (!bSubExe) { if (!bSubExe) {
/// サーバー側 /// サーバー側
echo("++MainProcess start"); echo("++MainProcess start");
Console.WriteLine($"LogicalProcNum={logicalProcessNum},ProcessParallelTasks={pm.para},PageParallelTasks={pm.paraPage}");
} else { } else {
// SubExe側の動作に差し替える // SubExe側の動作に差し替える
var sp = pm.subExe.Split(':'); var sp = pm.subExe.Split(':');
...@@ -352,7 +330,7 @@ namespace CSRenderMain { ...@@ -352,7 +330,7 @@ namespace CSRenderMain {
if (pm.pdfPath == "") { if (pm.pdfPath == "") {
Console.WriteLine("pdfファイルが指定されてません"); Console.WriteLine("pdfファイルが指定されてません");
DispHelp(); DispHelpNoArg();
return -1; return -1;
} }
if (!(File.Exists(pm.pdfPath) || Directory.Exists(pm.pdfPath))) { if (!(File.Exists(pm.pdfPath) || Directory.Exists(pm.pdfPath))) {
...@@ -371,15 +349,15 @@ namespace CSRenderMain { ...@@ -371,15 +349,15 @@ namespace CSRenderMain {
// Directoryが指定されたのでファイルリストアップ // Directoryが指定されたのでファイルリストアップ
bDirMode = true; bDirMode = true;
pdfPathLst = System.IO.Directory.GetFiles(pm.pdfPath, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/); pdfPathLst = System.IO.Directory.GetFiles(pm.pdfPath, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/);
pdfPathLst = Array.ConvertAll(pdfPathLst, f => Path.GetFileName(f)); // 配列書き換え(ファイル名のみにする pdfPathLst = Array.ConvertAll(pdfPathLst, f => Path.GetFileName(f));
// var enumLst = pdfPathLst.Select(f => Path.GetFileName(f)); LINQ式に置き換えることも可能(返り値は配列ではない) // 配列書き換え(ファイル名のみにする
//foreach ( var f in pdfPathLst) { // var enumLst = pdfPathLst.Select(f => Path.GetFileName(f)); LINQ式に置き換えることも可能(返り値は配列ではない)
// Console.WriteLine($@"path1={f}"); //foreach ( var f in pdfPathLst) {
//} // Console.WriteLine($@"path1={f}");
//}
//Console.WriteLine($@"path1.Len={pdfPathLst.Count()}"); //Console.WriteLine($@"path1.Len={pdfPathLst.Count()}");
} }
var resultData = new SortedDictionary<int, string>(); var resultData = new SortedDictionary<int, string>();
if ((!pm.bFC) && bDir) { if ((!pm.bFC) && bDir) {
...@@ -389,22 +367,22 @@ namespace CSRenderMain { ...@@ -389,22 +367,22 @@ namespace CSRenderMain {
} }
} }
if (pm.bFC) {// 比較モード時 if (pm.bFC) {// 比較モード時
if (pm.pdfPath2 == "") { if (pm.pdfPathRef == "") {
Console.WriteLine("比較モードで2つ目のpdfファイルが指定されてません"); Console.WriteLine("比較モードで2つ目のpdfファイルが指定されてません");
return -1; return -1;
} }
if (!(File.Exists(pm.pdfPath2) || Directory.Exists(pm.pdfPath2))) { if (!(File.Exists(pm.pdfPathRef) || Directory.Exists(pm.pdfPathRef))) {
Console.WriteLine($"比較ファイルが存在しません:{pm.pdfPath2}"); Console.WriteLine($"比較ファイルが存在しません:{pm.pdfPathRef}");
return -1; return -1;
} }
bool bDir2 = File.GetAttributes(pm.pdfPath2).HasFlag(FileAttributes.Directory); bool bDir2 = File.GetAttributes(pm.pdfPathRef).HasFlag(FileAttributes.Directory);
if (bDir2) { if (bDir2) {
if (!bDir) { if (!bDir) {
Console.WriteLine($"比較対象はファイルパスでないといけません:{pm.pdfPath2}"); Console.WriteLine($"比較対象はファイルパスでないといけません:{pm.pdfPathRef}");
return -1; return -1;
} }
// 2つ目のファイルリストアップ // 2つ目のファイルリストアップ
pdfPathLst2 = System.IO.Directory.GetFiles(pm.pdfPath2, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/); pdfPathLst2 = System.IO.Directory.GetFiles(pm.pdfPathRef, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/);
pdfPathLst2 = Array.ConvertAll(pdfPathLst2, f => Path.GetFileName(f));// 配列の書き換え pdfPathLst2 = Array.ConvertAll(pdfPathLst2, f => Path.GetFileName(f));// 配列の書き換え
// 共通のファイルを見つける。Lstの要素がLst2に含まれているかどうか // 共通のファイルを見つける。Lstの要素がLst2に含まれているかどうか
...@@ -412,13 +390,13 @@ namespace CSRenderMain { ...@@ -412,13 +390,13 @@ namespace CSRenderMain {
if (pdfPathLst2.Contains(f)) { if (pdfPathLst2.Contains(f)) {
pdfPathLstBoth.Add(f); pdfPathLstBoth.Add(f);
} else { } else {
pdfPathLstNoBoth.Add(f);// LstがLst2に含まれていない pdfPathLstNoBoth.Add("tgt:"+f);// LstがLst2に含まれていない
} }
} }
// Lst2のみファイルをNoBothに登録 // Lst2のみファイルをNoBothに登録
foreach (var f in pdfPathLst2) { foreach (var f in pdfPathLst2) {
if (!pdfPathLstBoth.Contains(f)) { if (!pdfPathLstBoth.Contains(f)) {
pdfPathLstNoBoth.Add(f); pdfPathLstNoBoth.Add("ref:"+f);
} }
} }
// pdfPathBoth,pdfPathNoBothが作成済み // pdfPathBoth,pdfPathNoBothが作成済み
...@@ -433,12 +411,18 @@ namespace CSRenderMain { ...@@ -433,12 +411,18 @@ namespace CSRenderMain {
var watch = System.Diagnostics.Stopwatch.StartNew(); // 時間の生成と計測開始を同時に行う var watch = System.Diagnostics.Stopwatch.StartNew(); // 時間の生成と計測開始を同時に行う
var otDir = pm.outuptImageDir; var otDir = pm.outuptImageDir;
if (otDir == "") { if (otDir == "") {
// 出力ディレクトリの作成 // 元PDFの同一フォルダにIMGフォルダを作成する if ( bDir ) {
otDir = Path.Combine(Directory.GetParent(pm.pdfPath).FullName, "IMG"); var suffix = pm.bFC ? ".DiffImage": ".IMG";
} otDir = Path.Combine( Directory.GetParent(pm.pdfPath).FullName, Path.GetFileName( pm.pdfPath )+suffix);
echo($"****Output dir={otDir} ******\n");
if (!Directory.Exists(otDir) /* && (!bMkHash)*/ ) { // MkHashのとき不要→必要 if (!Directory.Exists(otDir) && (!pm.bMkHash) ) { //
Directory.CreateDirectory(otDir); Directory.CreateDirectory(otDir);
}
} else {
// 出力ディレクトリの作成 // 元PDFの同一フォルダにIMGフォルダを作成する
otDir = Path.Combine(Directory.GetParent(pm.pdfPath).FullName, "IMG");
}
pm.outuptImageDir = otDir;// pmに戻す
} }
var otHashPath = ""; var otHashPath = "";
var otHashPath2 = ""; var otHashPath2 = "";
...@@ -450,10 +434,10 @@ namespace CSRenderMain { ...@@ -450,10 +434,10 @@ namespace CSRenderMain {
//比較モード //比較モード
// ハッシュデータの読み込み // ハッシュデータの読み込み
echo($@"pre pdfPath={pm.pdfPath}"); echo($@"pre pdfPath={pm.pdfPath}");
echo($@"pre pdfPath2={pm.pdfPath2}"); echo($@"pre pdfPathRef={pm.pdfPathRef}");
var hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath); var hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath);
var hashDataRef = HashData.load(pm.pdfPath2, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2); var hashDataRef = HashData.load(pm.pdfPathRef, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2);
//Console.WriteLine("<Compare Sync(Paralles) Version!>"); //Console.WriteLine("<Compare Sync(Paralles) Version!>");
if (pm.resultPath != "") {// 初期化 if (pm.resultPath != "") {// 初期化
...@@ -462,14 +446,15 @@ namespace CSRenderMain { ...@@ -462,14 +446,15 @@ namespace CSRenderMain {
ret = RenderPDF.RenderPdfDocCompare( ret = RenderPDF.RenderPdfDocCompare(
pdfPath: Path.GetFullPath(pm.pdfPath), pdfPath: Path.GetFullPath(pm.pdfPath),
refPdfPath: Path.GetFullPath(pm.pdfPath2), refPdfPath: Path.GetFullPath(pm.pdfPathRef),
resultDataPath: pm.resultPath, resultDataPath: pm.resultPath,
inDir : otDir, inDir : otDir,
pm : rdCond, pm : rdCond,
inPageRange : pm.pageRange, inPageRange : pm.pageRange,
inHashDataTgt: hashDataTgt, inHashDataTgt: hashDataTgt,
inHashDataRef: hashDataRef, inHashDataRef: hashDataRef,
bPDFium : pm.bPDFium bPDFium : pm.bPDFium,
nPageThreadNum: pm.paraPage
); );
} else { } else {
// Sync version. // Sync version.
...@@ -488,8 +473,9 @@ namespace CSRenderMain { ...@@ -488,8 +473,9 @@ namespace CSRenderMain {
inPageRange : pm.pageRange, inPageRange : pm.pageRange,
bSaveImage : pm.bMkHash ? false : true,// ハッシュ値生成ではイメージ保存しない。 bSaveImage : pm.bMkHash ? false : true,// ハッシュ値生成ではイメージ保存しない。
bHash : pm.bHash, bHash : pm.bHash,
inHashData : hashDataTgt, inHashData : pm.bMkHash ? hashDataTgt : null, //ハッシュコマンドモードのみDataを渡す
bPDFium : pm.bPDFium bPDFium : pm.bPDFium,
nPageThreadNum : pm.paraPage
); );
if (pm.bMkHash) { if (pm.bMkHash) {
/// ここでMain側に通信データを返せばよい /// ここでMain側に通信データを返せばよい
...@@ -503,10 +489,10 @@ namespace CSRenderMain { ...@@ -503,10 +489,10 @@ namespace CSRenderMain {
//表示のみ まだやめておく: //表示のみ まだやめておく:
} }
} }
// retはページ数です。 // retはページ数を返す
} }
} else { } else {
// 対象がディレクトリ // 対象がディレクトリの場合
echo("For Directory mode"); echo("For Directory mode");
HashData hashDataTgt = null, hashDataRef = null; HashData hashDataTgt = null, hashDataRef = null;
hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath); hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath);
...@@ -520,23 +506,16 @@ namespace CSRenderMain { ...@@ -520,23 +506,16 @@ namespace CSRenderMain {
} }
if (pm.bFC) { if (pm.bFC) {
// リファレンス側のハッシュの読み込み // リファレンス側のハッシュの読み込み
hashDataRef = HashData.load(pm.pdfPath2, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2); hashDataRef = HashData.load(pm.pdfPathRef, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2);
if (hashDataRef == null) { if (hashDataRef == null) {
// 空のハッシュデータを作成 // 空のハッシュデータを作成
hashDataRef = new HashData(); hashDataRef = new HashData();
hashDataRef.Head.SetRenderInfo(pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ); hashDataRef.Head.SetRenderInfo(pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ);
} }
} }
// ハッシュデータが適切に作成済みがどうかを確認。もしハッシュデータが存在しない場合は
// ハッシュデータを渡してはいけない。ハッシュのFilesで確認するか?
// ハッシュデータの作成プロセスをもっと簡単にすべき
echo($@"Start render:Count={pdfPathLstBoth.Count}"); echo($@"Start render:Count={pdfPathLstBoth.Count}");
//Console.WriteLine("<Render Sync(Paralles) Version!>");[ //Console.WriteLine("<Render Sync(Paralles) Version!>");[
var count = pdfPathLstBoth.Count; var count = pdfPathLstBoth.Count;
//ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 6 };
//Parallel.For(0,pdfPathLstBoth.Count, options, index => {
//逆に遅くなる UWPコール(render)は対応していない?
if (pm.bExeSepa &&(!pm.bFC)) { // FCモードは除外します。 if (pm.bExeSepa &&(!pm.bFC)) { // FCモードは除外します。
var tokenSource = new CancellationTokenSource(); var tokenSource = new CancellationTokenSource();
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = pm.para }; ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = pm.para };
...@@ -544,12 +523,12 @@ namespace CSRenderMain { ...@@ -544,12 +523,12 @@ namespace CSRenderMain {
var loopResult = Parallel.For(0,pdfPathLstBoth.Count, options, (index,lpState) => { var loopResult = Parallel.For(0,pdfPathLstBoth.Count, options, (index,lpState) => {
//for (var index = 0; index < pdfPathLstBoth.Count; index++) { //for (var index = 0; index < pdfPathLstBoth.Count; index++) {
var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]); var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]);
var tgtID = $@"{index}/{pdfPathLstBoth.Count}"; var tgtID = $@"{index+1}/{pdfPathLstBoth.Count}";
Console.WriteLine($@"**Remain({count})****tgt({tgtID})={tgt}"); Console.WriteLine($@"**Remain({count})****tgt({tgtID})={tgt}");
if (pm.bFC) { if (pm.bFC) {
//比較モード //比較モード
var reff = Path.Combine(pm.pdfPath2, pdfPathLstBoth[index]); var reff = Path.Combine(pm.pdfPathRef, pdfPathLstBoth[index]);
} else { // Sync version(Paralles). } else { // Sync version(Paralles).
var svrData = new xChangeWCFPipe.XData(); var svrData = new xChangeWCFPipe.XData();
var pmClone = pm.Clone(); var pmClone = pm.Clone();
...@@ -583,7 +562,7 @@ namespace CSRenderMain { ...@@ -583,7 +562,7 @@ namespace CSRenderMain {
if ( retProc < 1 ) { if ( retProc < 1 ) {
//PDFページ数以外が返った時 //PDFページ数以外が返った時
ret = -1; ret = -1;
Console.WriteLine( $@"Warning ::Cannot get PDFPage:{retProc}"); Console.WriteLine( $@"Warning ::Cannot get PDFPage:{retProc},tgt={tgt},{argsStr}");
} else { } else {
;// 初期値 ret=0; ;// 初期値 ret=0;
} }
...@@ -613,13 +592,13 @@ namespace CSRenderMain { ...@@ -613,13 +592,13 @@ namespace CSRenderMain {
proc.Dispose(); proc.Dispose();
svrHost.Close(); svrHost.Close();
svrData = null; svrData = null;
Console.WriteLine($@"End tgt({tgtID})"); echo($@"End tgt({tgtID})");
} }
//GC.Collect();// これでメモリリークが解決した Paraの中ではやめておく //GC.Collect();// これでメモリリークが解決した Paraの中ではやめておく
count--; count--;
}); });
if ( !loopResult.IsCompleted ) { if ( !loopResult.IsCompleted ) {
Console.WriteLine("中断"); Console.WriteLine("Abort!");
pm.bMkHash = false;// Hash値保存抑制 pm.bMkHash = false;// Hash値保存抑制
} }
////tokenSource.Cancel();// 処理のキャンセル ////tokenSource.Cancel();// 処理のキャンセル
...@@ -627,8 +606,90 @@ namespace CSRenderMain { ...@@ -627,8 +606,90 @@ namespace CSRenderMain {
// pm.bMkHash = false;// Hash値保存抑制 // pm.bMkHash = false;// Hash値保存抑制
//} //}
//}; // Non Para Block //}; // Non Para Block
} else { // NoExeSepa echo($"********** コマンド終了*****{argsStr} ");
} else if (pm.bExeSepa &&(pm.bFC)) { // セパのFCモード
/*
refのハッシュが存在しなければWCFで作成
tgtのハッシュは強制的にWCFで作成
*/
echo($"****************セパのFCモード******{argsStr}************");
hashDataRef = HashData.load(pm.pdfPathRef, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2);
if ( hashDataRef == null ) {
var myExePath = Assembly.GetExecutingAssembly().Location;
var proc = Ut.Ut.DoCmd(
cmdAndArgs: new string[] { myExePath, "/MkHash",pm.pdfPathRef,"/D",pm.dpi,$"/{pm.imageType }",$"/JPEGQ",pm.jpegQ }
, bEcho: true
, bSameConsole: true
);
proc.WaitForExit();
hashDataRef = HashData.load(pm.pdfPathRef, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2);
if ( hashDataRef != null ) {
echo($" OK hash ref");
}
} else {
echo($" OK hash ref(already exist)");
}
if (true){
var myExePath = Assembly.GetExecutingAssembly().Location;
var proc = Ut.Ut.DoCmd(
cmdAndArgs: new string[] { myExePath, "/MkHash",pm.pdfPath,"/D",pm.dpi, $"/{pm.imageType }",$"/JPEGQ",pm.jpegQ }
, bEcho: true
, bSameConsole: true
);
proc.WaitForExit();
hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath);
if ( hashDataTgt != null ) {
echo($" OK hash tgt");
}
}
echo($@"****************FC開始*****{argsStr}**************");
if (pm.resultPath != "") {// 初期化
File.WriteAllText(Path.GetFullPath(pm.resultPath), "");
}
int noMatchPageNum=0;
int noMatchFileNum=0;
for (var index = 0; index < pdfPathLstBoth.Count; index++) {
var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]);
Console.WriteLine($@"tgt={index+1}/{pdfPathLstBoth.Count}:{tgt}");
var reff = Path.Combine(pm.pdfPathRef, pdfPathLstBoth[index]);
ret = RenderPDF.RenderPdfDocCompare(
pdfPath: Path.GetFullPath(tgt),
refPdfPath: Path.GetFullPath(reff),
resultDataPath: pm.resultPath,
inDir: otDir,
pm: rdCond,
inPageRange: pm.pageRange,
inHashDataTgt: hashDataTgt,
inHashDataRef: hashDataRef,
bPDFium: pm.bPDFium,
nPageThreadNum:pm.paraPage
);
if ( ret > 0 ) {
// retは不一致ページ数
noMatchFileNum++;
noMatchPageNum+= ret;
}
};
watch.Stop();
var SummaryResult = $"\n[結果]\n";
SummaryResult += $"不整合ファイル群={pdfPathLstNoBoth.Count()}\n";
foreach (var f in pdfPathLstNoBoth) {
SummaryResult += $"\tWarning [no match]={f}\n";
}
SummaryResult += $"NGファイル数={noMatchFileNum}\n";
SummaryResult += $"NGページ数 ={noMatchPageNum}\n";
SummaryResult += $"Result={ret},time={ watch.ElapsedMilliseconds / 1000.0 }[sec]";
Console.WriteLine(SummaryResult);// 結果標準出力
if (pm.resultPath != "") {// ファイル出力の先頭に追記
var rData = File.ReadAllText(pm.resultPath);
SummaryResult += "\n[詳細] *find @Difference\n";
File.WriteAllText(Path.GetFullPath(pm.resultPath), (SummaryResult + rData));
}
echo($"****************[終了]セパのFCモード***{argsStr}***************");
} else { // NoExeSepa
echo($@"****************NoExeSepa*****{argsStr}**************");
if (pm.resultPath != "") {// 初期化 if (pm.resultPath != "") {// 初期化
File.WriteAllText(Path.GetFullPath(pm.resultPath), ""); File.WriteAllText(Path.GetFullPath(pm.resultPath), "");
} }
...@@ -637,7 +698,7 @@ namespace CSRenderMain { ...@@ -637,7 +698,7 @@ namespace CSRenderMain {
Console.WriteLine($@"tgt={index+1}/{pdfPathLstBoth.Count}:{tgt}"); Console.WriteLine($@"tgt={index+1}/{pdfPathLstBoth.Count}:{tgt}");
if (pm.bFC) { if (pm.bFC) {
//比較モード //比較モード
var reff = Path.Combine(pm.pdfPath2, pdfPathLstBoth[index]); var reff = Path.Combine(pm.pdfPathRef, pdfPathLstBoth[index]);
//Console.WriteLine("<Compare Sync(Paralles) Version!>"); //Console.WriteLine("<Compare Sync(Paralles) Version!>");
ret = RenderPDF.RenderPdfDocCompare( ret = RenderPDF.RenderPdfDocCompare(
pdfPath: Path.GetFullPath(tgt), pdfPath: Path.GetFullPath(tgt),
...@@ -648,8 +709,10 @@ namespace CSRenderMain { ...@@ -648,8 +709,10 @@ namespace CSRenderMain {
inPageRange: pm.pageRange, inPageRange: pm.pageRange,
inHashDataTgt: hashDataTgt, inHashDataTgt: hashDataTgt,
inHashDataRef: hashDataRef, inHashDataRef: hashDataRef,
bPDFium: pm.bPDFium bPDFium: pm.bPDFium,
nPageThreadNum:pm.paraPage
); );
} else { // Sync version(Paralles). } else { // Sync version(Paralles).
ret = RenderPDF.RenderPdfDoc( ret = RenderPDF.RenderPdfDoc(
pdfPath: Path.GetFullPath(tgt), pdfPath: Path.GetFullPath(tgt),
...@@ -659,7 +722,8 @@ namespace CSRenderMain { ...@@ -659,7 +722,8 @@ namespace CSRenderMain {
bSaveImage: pm.bMkHash ? false : true,// ハッシュ値生成ではイメージ保存しない。 bSaveImage: pm.bMkHash ? false : true,// ハッシュ値生成ではイメージ保存しない。
bHash: pm.bHash,// bHash,dumy bHash: pm.bHash,// bHash,dumy
inHashData: pm.bMkHash ? hashDataTgt : null, //ハッシュコマンドモードのみDataを渡す inHashData: pm.bMkHash ? hashDataTgt : null, //ハッシュコマンドモードのみDataを渡す
bPDFium: pm.bPDFium bPDFium: pm.bPDFium,
nPageThreadNum: pm.paraPage
); );
} }
count--; count--;
...@@ -672,16 +736,6 @@ namespace CSRenderMain { ...@@ -672,16 +736,6 @@ namespace CSRenderMain {
} }
watch.Stop(); watch.Stop();
Console.WriteLine($"result={ret},time={ watch.ElapsedMilliseconds / 1000.0 }[sec]"); Console.WriteLine($"result={ret},time={ watch.ElapsedMilliseconds / 1000.0 }[sec]");
#if COMMNET
Assembly assm = Assembly.GetExecutingAssembly();
Console.WriteLine(assm.FullName);
// 参照しているすべてのアセンブリを取得し、表示する
foreach (AssemblyName refassm in assm.GetReferencedAssemblies()) {
Console.WriteLine($@" {refassm.FullName},Ver({refassm.Version}),VerComp({refassm.VersionCompatibility})" );
}
string clrVersion = System.Environment.Version.ToString();
Console.WriteLine($@"clrVer={clrVersion}");
#endif
return ret;//success. return ret;//success.
} }
...@@ -861,22 +915,6 @@ namespace xChangeWCFPipeGEN ...@@ -861,22 +915,6 @@ namespace xChangeWCFPipeGEN
_service = oInstance; _service = oInstance;
// デリゲート登録 // デリゲート登録
//callDelegate = () => { Console.WriteLine("svr:Delegate func called"); } //callDelegate = () => { Console.WriteLine("svr:Delegate func called"); }
#if false
{
// インターフェースを求める
// AddServiceEndpoint()にわたすIXDataはXData.Interfaces()から求められるが
// インタフェースは一つとは限らないのでやめておく。
Type interfType = null;
Type type = _service.GetType();
Type[] interfaces = type.GetInterfaces();
Console.WriteLine($@"GetInterfaces***********************************");
foreach (Type t in interfaces) {
Console.WriteLine(t.ToString());
interfType = t;
};
Console.WriteLine($@"End of GetInterfaces***********************************");
}
#endif
_serviceHost = new ServiceHost(_service, new Uri(uri)); _serviceHost = new ServiceHost(_service, new Uri(uri));
try { try {
_serviceHost.AddServiceEndpoint(_baseIf/*typeof(Ti)*/, new NetNamedPipeBinding(), pipeAddress); _serviceHost.AddServiceEndpoint(_baseIf/*typeof(Ti)*/, new NetNamedPipeBinding(), pipeAddress);
...@@ -902,14 +940,9 @@ namespace xChangeWCFPipeGEN ...@@ -902,14 +940,9 @@ namespace xChangeWCFPipeGEN
static public Ti makeCLT<Ti>(string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") { static public Ti makeCLT<Ti>(string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") {
const string pipeBase = "net.pipe://localhost"; const string pipeBase = "net.pipe://localhost";
var address = pipeBase + "/" + pipeName + "/" + pipeAddress; var address = pipeBase + "/" + pipeName + "/" + pipeAddress;
//try {
var factory = (new ChannelFactory<Ti>(new NetNamedPipeBinding(), new EndpointAddress(address))); var factory = (new ChannelFactory<Ti>(new NetNamedPipeBinding(), new EndpointAddress(address)));
Ti toMain = factory.CreateChannel(); Ti toMain = factory.CreateChannel();
return toMain; return toMain;
//} catch (Exception e) {
// Console.WriteLine("makeCLT failed:" + e.ToString());
// return null;
//}
} }
} }
} }
...@@ -997,6 +1030,7 @@ namespace Ut ...@@ -997,6 +1030,7 @@ namespace Ut
/// </summary> /// </summary>
/// <returns>ProcessID</returns> /// <returns>ProcessID</returns>
static int GetCurrentProcessId() { return Process.GetCurrentProcess().Id; } static int GetCurrentProcessId() { return Process.GetCurrentProcess().Id; }
/// <summary> /// <summary>
/// 外部プログラムを起動する /// 外部プログラムを起動する
/// </summary> /// </summary>
...@@ -1008,7 +1042,8 @@ namespace Ut ...@@ -1008,7 +1042,8 @@ namespace Ut
/// <returns></returns> /// <returns></returns>
public static Process DoCmd(IEnumerable<string> cmdAndArgs, bool bSameConsole = true, bool bEcho = true, bool bWait = false, int nWaitLimitMSec = -1) { public static Process DoCmd(IEnumerable<string> cmdAndArgs, bool bSameConsole = true, bool bEcho = true, bool bWait = false, int nWaitLimitMSec = -1) {
var cmdName = cmdAndArgs.First();//[0]; var cmdName = cmdAndArgs.First();//[0];
var argStr = makeCmdLine(cmdAndArgs.Skip(1));// 先頭を除外してコピー new ArraySegment<string>(cmdAndArgs.ToArray(), 1, cmdAndArgs.Count()-1) var argStr = makeCmdLine(cmdAndArgs.Skip(1));
// 先頭を除外してコピー new ArraySegment<string>(cmdAndArgs.ToArray(), 1, cmdAndArgs.Count()-1)
//Console.WriteLine($@"DoCmd:{cmdName}:args[{argStr}]"); //Console.WriteLine($@"DoCmd:{cmdName}:args[{argStr}]");
//using (var p = new Process()) { //using (var p = new Process()) {
var p = new Process(); var p = new Process();
......
using System.Reflection; using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
...@@ -10,7 +11,7 @@ using System.Runtime.InteropServices; ...@@ -10,7 +11,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CSRender")] [assembly: AssemblyProduct("CSRender")]
[assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
...@@ -32,9 +33,11 @@ using System.Runtime.InteropServices; ...@@ -32,9 +33,11 @@ using System.Runtime.InteropServices;
// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます // すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます
// 既定値にすることができます: // 既定値にすることができます:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3.0.5")] [assembly: AssemblyVersion("1.3.1.0")]
[assembly: AssemblyFileVersion("1.3.0.5")] [assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: NeutralResourcesLanguage("ja")]
// rev:1.3.1.x:2021/10/05 :不要コード削除(Windows.Data.pdf)
// rev:1.3.0.5:2021/09/14 :/Paraで並行プロセス最大値の設定追加 // rev:1.3.0.5:2021/09/14 :/Paraで並行プロセス最大値の設定追加
// rev:1.3.0.4:2021/04/29 :Helpの記述抜け "/Result"の対応 // rev:1.3.0.4:2021/04/29 :Helpの記述抜け "/Result"の対応
// rev:1.3.0.3:2020/11/22 :/Result <比較結果> を実装。diff画像を出力 // rev:1.3.0.3:2020/11/22 :/Result <比較結果> を実装。diff画像を出力
......
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Reflection; // Assembly.
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
// for Stream conv.
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;
//using System.IO.WindowsRuntimeStreamExtensions;
//
//using Codeplex.Data; // DynamicJson
// UWP-APIの使用
using Windows.Data.Pdf;
// PDFiumの追加
using PdfiumViewer;
using static CSRender.RenderPDF;
//https://proself2.screen.co.jp/public/OcyIQAHPksNAnE4Bs3BxIPQApnAEmSCIBOxsCZ946Uur
//Screen8080
//1_A4縦_2×1_両面_可変長レコード.pdf
// TOshiba:
//#region アセンブリ Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime
// C:\UserData\GIT_JSH\WinRT\CSRender\CSRender\bin\Debug\Windows.winmd
// WindowsRuntime 1.3
//#endregion
// MacBookPro:
// WindowsRuntime 1.4
//CSRender, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null
// mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime,Ver(255.255.255.255),VerComp(SameMachine)
// System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a,Ver(4.0.0.0),VerComp(SameMachine)
// System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089,Ver(4.0.0.0),VerComp(SameMachine)
// clrVer=4.0.30319.42000
// 参考: zoomパレメータなど
// https://www.syncfusion.com/kb/8767/how-to-print-pdf-documents-in-xamarin-forms-platform
//var DisplayInformation = Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
//var dpi = DisplayInformation.LogicalDpi / 96;
// https://elesynd.blogspot.com/2018/11/hDpiForm.html
//Program.csでDPIAwareする
//FormクラスのAutoScaleModeをUIを使って"Dpi"に変更する
//それでもズレるコントロールは、FontをUIを使って明示的に指定してみる
// https://qiita.com/felis_silv/items/efee4b1a397b0b95100a
// スケーリング grph.FromImage(bmp)後にスケール
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;
const int LOGPIXELSX = 88;
const int LOGPIXELSY = 90;
//
//
const int HORZSIZE = 4;//物理画面の幅・高さ(ミリメートル単位)
const int VERTSIZE = 5;
const int HORZRES = 8; //画面の幅・高さ(ピクセル単位)
const int VERZRES = 10;
//
const int RASTERCAPS = 38;
// 返却値のマスク
//1 (RC_BITBLT) ビットマップの転送
//2 (RC_BANDING) バンド処理のサポートが必要
//4 (RC_SCALING) スケーリング
//8 (RC_BITMAP64) 64KB より大きいビットマップ
//0x0080 (RC_DI_BITMAP) SetDIBits 関数と GetDIBits 関数
//0x0100 (RC_PALETTE) デバイスはパレットベースのデバイスである
//0x0200 (RC_DIBTODEV) SetDIBitsToDevice 関数
//0x0800 (RC_STRETCHBLT) StretchBlt 関数
//0x1000 (RC_FLOODFILL) 塗りつぶし
//0x2000 (RC_STRETCHDIB) StretchDIBits 関数
const int SCALINGFACTORX = 114;//x 軸・ y 軸のスケーリングファクター
const int SCALINGFACTORY = 115;
[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, LOGPIXELSX);
if ( bDump ) {
Console.WriteLine("DpiX: {0}", GetDeviceCaps(dc, LOGPIXELSX));
Console.WriteLine("DpiY: {0}", GetDeviceCaps(dc, LOGPIXELSY));
Console.WriteLine("SCALINGFACTORYX: {0}", GetDeviceCaps(dc, SCALINGFACTORX));
Console.WriteLine("SCALINGFACTORY: {0}", GetDeviceCaps(dc, SCALINGFACTORY));
//if ( false ) {// これは倍率で変更されない
// int width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width; // 幅(pixel)
// int height = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; // 高さ(pixel)
// // 取得したスクリーンのサイズをコンソールに出力する
// string message = string.Format("This screen size is {0} x {1} (pixel)", width, height);
// System.Console.WriteLine(message);
//}
//if (false){// コンソールアプリでは呼び出せない
// var di = Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
// Console.WriteLine($@"di.LogicalDpi: {di.LogicalDpi}");
// Console.WriteLine($@"di.RawDpiX: {di.RawDpiX}");
// Console.WriteLine($@"di.ResoSacle: {di.ResolutionScale.ToString()}");
// Console.WriteLine($@"di.StereoEnabled: {di.StereoEnabled}");
// Console.WriteLine($@"di: {di.ToString()}");
//}
}
ReleaseDC(IntPtr.Zero, dc);
return dpi;// 96;// dpi;
}
}
public static class RenderPDF
{
/// <summary>
/// Rendering Condition
/// </summary>
[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
}
// Verbose
public static void setEcho(bool b) {
bEcho = b;
}
public static bool bEcho = false;
public static void echo(params object[] args) {
if (!bEcho) return;
var s = "";
foreach (object a in args) {
s += a.ToString();
}
Console.WriteLine(s);
}
// 事前チェック BashHash
public static bool RenderPdfInitCheck( Stream stream /* PDFストリーム */) {
Windows.Storage.Streams.InMemoryRandomAccessStream rmStrageStream=null;
{// Stream->Memstream -> byte array -> IBuffer -> InMemmory...Stream()
var ms = new MemoryStream();
stream.CopyTo(ms);
var bAray = ms.ToArray();
IBuffer ib = bAray.AsBuffer();
rmStrageStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
//await s.WriteAsync(ib);
async Task<uint> wa() => await rmStrageStream.WriteAsync(ib);
var wa_ret = wa().Result;
}
async Task<Windows.Data.Pdf.PdfDocument> LoadS() => await Windows.Data.Pdf.PdfDocument.LoadFromStreamAsync(rmStrageStream);
var pdfDoc = LoadS().Result;
if (pdfDoc == null) {
Console.WriteLine("error: get PdfDocument failed");
return false;
}
// var memStream = new MemoryStream();
// memStream.AsInputStream();
// WindowsRuntimeStreamExtensions.
// var ras = WindowsRuntimeStreamExtensions.AsRandomAccessStream(stream);
//https://blog.ch3cooh.jp/entry/20131207/1386342000
//https://csharp.hotexamples.com/site/file?hash=0xe951daae5be57f9f35507c45eb09a1085819b0a42b846aaa211ac138a2d1b5af&fullName=src/Nutrition/Nutrition.WP/WPOCRService.cs&project=SamirHafez/Nutrition
//なんだけどな。。。→ .NetFrameworkにAsRandomAccessStream(x)が存在しない。.Net Coreのみみたい。
// exeと同一場所に"baseRender.jpg"
string otPath = Path.Combine(Directory.GetParent(Assembly.GetEntryAssembly().Location).FullName,"baseRender.jpg");
bool bSaveImage = false;// Debug時にtrueにする
Console.WriteLine($"mode=Hash base check");
var taskList = new List<Task<int>>();
var hashValue = "";
var pm = new RenderConditionParams();
var ret = RenderPage(
doc: pdfDoc,
page: pdfDoc.GetPage(0),
otPath: otPath,
pm:pm,
bSaveImage: bSaveImage,
bHash: true,
otHashCode: ref hashValue
);
Console.WriteLine($@"Hash={hashValue},Host={Environment.MachineName},{Environment.OSVersion}");
return true;// true:OK, false:NG
}
// Sync version( no async -> Pallale Task ) **************************************************************************************
/// <summary>
/// PDFのレンダリング
/// </summary>
/// <param name="pdfPath"></param>
/// <param name="inDir"></param>
/// <param name="pm">レンダリングの条件</param>
/// <param name="inPageRange"></param>
/// <param name="inMode"></param>
/// <param name="bSaveImage"></param>
/// <param name="bHash">未使用,常にtrueで使用</param>
/// <param name="inHashData"></param>
/// <returns></returns>
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
) {
if (pm == null) {
pm = new RenderConditionParams();
}
var otDir = inDir;
var otBaseName = Path.GetFileName(pdfPath);//*.pdf
var otExtention = pm.ImageType.ToLower();
if (bSaveImage && otDir == "") {
// 出力ディレクトリが空→元PDFの同一フォルダにIMGフォルダを作成する
otDir = Path.Combine(Directory.GetParent(pdfPath).FullName, "IMG");
if (!Directory.Exists(otDir))
Directory.CreateDirectory(otDir);
}
// PDFファイルを読み込む
async Task<Windows.Storage.StorageFile> GetStrageFilePath(string path)
=> await Windows.Storage.StorageFile.GetFileFromPathAsync(path);
var file = GetStrageFilePath(pdfPath).Result;
async Task<Windows.Data.Pdf.PdfDocument> Load(Windows.Storage.StorageFile path)
=> await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(file);
var pdfDoc = Load(file).Result;
// 2つを合わせることができそう
//async Task<Windows.Data.Pdf.PdfDocument> GetLoadFromPath(string path)
//{
// var a1 = await Windows.Storage.StorageFile.GetFileFromPathAsync(path);
// var a2 = await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(a1);
// return a2;
//};
//var pdfDoc2 = GetLoadFromPath(pdfPath).Result;
if (pdfDoc == null) {
Console.WriteLine("error: get PdfDocument failed");
return -1;
}
var n = pdfDoc.PageCount;
inMode = "pageBitMap";
//Console.WriteLine($"mode={inMode},bPDFium={bPDFium}");
var taskList = new List<Task<int>>();
uint[] rNo = makePageRange(inPageRange, pdfDoc.PageCount);
if (inHashData != null) {
// ハッシュ対象の最大ページ数の設定必須。
inHashData.SetFile(pdfPath, (int)pdfDoc.PageCount);
}
// PDFiumViewer
PdfiumViewer.PdfDocument docG = null;
if (bPDFium) {
docG = PdfiumViewer.PdfDocument.Load(pdfPath);
}
//Console.WriteLine($@"dpi={pm.Dpi}");
/////並行処理するスレッド数を指定(2-4ぐらいが穏便な数値)
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.ForEach(rNo, options, i => {
//Console.WriteLine($"*****{i}*****/{rNo.Length}");
var hashValue = "";
var ret = RenderPage(
doc: pdfDoc,
page: pdfDoc.GetPage((uint)(i - 1)),
otPath: Path.Combine(otDir, $"{otBaseName}.{i}.{otExtention}"),
pm: pm,
bSaveImage: bSaveImage, // ファイル保存
bHash: bHash,
otHashCode: ref hashValue,
docG : docG
);
if (inHashData != null) {
lock (inHashData) {
inHashData.AddHashCode(pdfPath, (int)i, hashValue);
}
}
});
if ( docG != null)
docG.Dispose();
return (int)pdfDoc.PageCount;// ページ数を返却します
//return 0;// success.
}
/// <summary>
/// pdfのページを画像に出力する
/// </summary>
/// <param name="doc">PdfDocument</param>
/// <param name="nPage">ページ番号</param>
/// <param name="otPath">出力ファイル名</param>
/// <returns>正常:0 </returns>
public static int RenderPage(
// Need:
Windows.Data.Pdf.PdfDocument doc, // Need GetPage() only. (Total page number);
PdfPage page, // Target Page Data(page.index:0-x)
string otPath,
ref string otHashCode,
// Optional:
RenderConditionParams pm = null,
bool bSaveImage = true,// ファイル保存有無。ハッシュ値計算のみのときにfalseにする
bool bHash = false,
PdfiumViewer.PdfDocument docG = null //
){
if( pm == null) {
pm = new RenderConditionParams();
}
double dpiWin = Check.GetDPI(); //96.0;/* , dpiPDF = 72.0*/
//double resoScale = (96.0 / dpiWin);
double resoScale = (pm.Dpi / 96.0);
//double resoScale = (inDpi / dpiWin);
var bDump = Check.bDump;
var bThDump = Check.bThDump;
var opt = new PdfPageRenderOptions() {
// SEE:https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/PdfDocument/cs/Scenario1_Render.xaml.cs
//BitmapEncoderId = Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId,// JPEG
IsIgnoringHighContrast = false
};
var boxDic = new Dictionary<string, Windows.Foundation.Rect>() {
// Init
["Media"] = page.Dimensions.MediaBox,
["Trim"] = page.Dimensions.TrimBox,
["Bleed"] = page.Dimensions.BleedBox,
["Crop"] = page.Dimensions.CropBox,
["Art"] = page.Dimensions.ArtBox
};
//Dump:
if ( bDump){
Console.WriteLine($@"PageZoom={page.PreferredZoom}");
Console.WriteLine($@"PageRotate={page.Rotation.ToString()}");
Console.WriteLine($"{"Size",-5}=({page.Size.Width,8:f2},{page.Size.Height,8:f2})");
foreach (var b in boxDic.Keys) {
var r = boxDic[b];
Console.WriteLine($"{b,-5}=({r.X,8:f2},{r.Y,8:f2}) W({r.Width,8:f2},{r.Height,8:f2})");
Console.WriteLine($"{b,-5}=L {r.Left,-8:f2} R {r.Right,-8:f2}) T {r.Top,-8:f2} B {r.Bottom,-8:f2})");
}
}
opt.SourceRect = boxDic.ContainsKey(pm.BoxType) ? boxDic[pm.BoxType] : boxDic["Crop"];// Cannot find -> "Crop";
//Console.WriteLine($"Select {inBoxType} box.");
// 何も変倍しないとどうなる
opt.DestinationWidth = (uint)(opt.SourceRect.Width * resoScale + 0.5);
opt.DestinationHeight = (uint)(opt.SourceRect.Height * resoScale + 0.5);
// 150%の時は * 0.5358を設定するとよい解像度になるが、この数値の計算方法がわからない
//opt.DestinationWidth = (uint)(opt.SourceRect.Width * 0.5358/*resoScale*/ + 0.5);
//opt.DestinationHeight = (uint)(opt.SourceRect.Height * 0.5358/*resoScale*/ + 0.5);
if (bDump) {
Console.WriteLine($"dpi={pm.Dpi},scale={resoScale},width(org)={page.Size.Width}->{opt.DestinationWidth}");
Console.WriteLine($"dpi={pm.Dpi},scale={resoScale},height(org)={page.Size.Height}->{opt.DestinationHeight}");
}
using (var memStrm = new Windows.Storage.Streams.InMemoryRandomAccessStream()) {
if (docG == null) {
async Task RenderToStream(PdfPage p) => await p.RenderToStreamAsync(memStrm, opt);
RenderToStream(page).Wait();
} else {
PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi);
System.Drawing.Image img = docG.Render((int)(page.Index), (float)pm.Dpi, (float)pm.Dpi, flg);
img.Save(memStrm.AsStream(), System.Drawing.Imaging.ImageFormat.Jpeg);
// [注意] PDFuim用に直接 AsStream()に書き込んでいるので、AsStream().Flush()しないとだめです
}
async Task<bool> FlushX() => await memStrm.FlushAsync();
FlushX().Wait();
var bmp = new System.Drawing.Bitmap(memStrm.AsStream());
if (bDump) {
Console.WriteLine($"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 ) {
Console.WriteLine($"ot[th{th},{(page.Index + 1)}/{doc.PageCount}]=,{Path.GetFileName(otPath)},hash:{otHashCode}");
}
return 0;
}
var imEnc = new UtImage.Enc(pm.ImageType) { JpegQuality = pm.JpegQ };// Init membrers.
bmp.SetResolution((float)pm.Dpi, (float)pm.Dpi);
if (bThDump) {
Console.WriteLine($"ot[th{th},{(page.Index + 1)}/{doc.PageCount}]={Path.GetFileName(otPath)}({Directory.GetParent(otPath)})");
}
//
imEnc.SaveImage(bmp, otPath);
//
bmp.Dispose();
//memStrm.Seek(0);
}
return 0;//success
}
// Sync version( 比較モード(/FC ) **************************************************************************************
/// <summary>
/// レンダリング(比較モード)
/// </summary>
/// <param name="pdfPath"></param>
/// <param name="refPdfPath"></param>
/// <param name="inDir"></param>
/// <param name="pm">レンダリング条件</param>
/// <param name="inPageRange"></param>
/// <param name="inMode"></param>
/// <param name="bHash">未使用常にtrueで利用する</param>
/// <param name="inHashDataTgt"></param>
/// <param name="inHashDataRef"></param>
/// <returns>一致した場合は0、不一致の場合は!0を返却する</returns>
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
// 比較結果を返す ページ番号とメッセージ
) {
var bDump = Check.bDump;
var bThDump = Check.bThDump;
if(bDump)
Console.WriteLine("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 == "") {
// 出力ディレクトリが空→元PDFの同一フォルダにIMGフォルダを作成する
otDir = Path.Combine(Directory.GetParent(pdfPath).FullName, "IMG");
if (!Directory.Exists(otDir))
Directory.CreateDirectory(otDir);
}
// PDFファイルを読み込む
// 以下のコードで良いみたい。
// [変更待ち] var file2 = Windows.Storage.StorageFile.GetFileFromPathAsync(pdfPath).GetAwaiter().GetResult();
async Task<Windows.Storage.StorageFile> GetStrageFilePath(string path)
=> await Windows.Storage.StorageFile.GetFileFromPathAsync(path);
var file = GetStrageFilePath(pdfPath).Result;
async Task<Windows.Data.Pdf.PdfDocument> Load(Windows.Storage.StorageFile path)
=> await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(path);
var pdfDoc = Load(file).Result;
if (pdfDoc == null) {
Console.WriteLine("error: get PdfDocument failed");
return -1;
}
// ref
var fileRef = GetStrageFilePath(refPdfPath).Result;
var pdfDocRef = Load(fileRef).Result;
if (pdfDocRef == null) {
Console.WriteLine("error: get PdfDocument(ref) failed");
return -1;
}
var n = pdfDoc.PageCount;
if ( n != pdfDocRef.PageCount) {
Console.WriteLine($"error: differ page length:{n}:{pdfDocRef.PageCount}");
return -1;
}
uint[] rNo = makePageRange(inPageRange, n);
int nRetAll = 0;// 一致(def)
// ハッシュ情報の確認
/*
- 双方のハッシュ値が存在するか確認
- 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<int,string>();// 比較結果を返す ページ番号とメッセージ
// PDFiumViewer
PdfiumViewer.PdfDocument docG = null;
PdfiumViewer.PdfDocument docGRef = null;
if (bPDFium) {
docG = PdfiumViewer.PdfDocument.Load(pdfPath);
docGRef = PdfiumViewer.PdfDocument.Load(refPdfPath);
}
/////並行処理するスレッド数を指定(2-4ぐらいが穏便な数値)
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.ForEach(rNo, options, i => {
Windows.Storage.Streams.InMemoryRandomAccessStream tgtStrm = null, refStrm = null;
string tgtHash = "", refHash = "";
// ターゲット レンダー
InMemoryRandomAccessStream RendPageTGT() {
var strm = RenderPageStream(doc:pdfDoc,page:pdfDoc.GetPage((uint)(i-1)), pm:pm,docG:docG);
return strm;
}
// リファレンス レンダー
InMemoryRandomAccessStream RendPageREF() {
var strm = RenderPageStream(doc:pdfDocRef,page:pdfDocRef.GetPage((uint)(i-1)), pm:pm,docG:docGRef);
return strm;
}
//tgtStrm = RendPageTGT();
if (tgtHf == null) {
tgtStrm = RendPageTGT();
tgtHash = GetHashValue(tgtStrm);
} else {
lock (inHashDataTgt) {
tgtHash = tgtHf.GetHashValue((int)(i - 1));
}
}
// リファレンス
if (refHf == null) {
refStrm = RendPageREF();
refHash = GetHashValue(refStrm);
} else {
lock (inHashDataRef) {
refHash = refHf.GetHashValue((int)(i - 1));
}
}
// compare stream;
int nRet = 1;//異なる(def).
var dateStr = DateTime.Now.ToString();
//Console.WriteLine($@"tgt={i}:{tgtHash}");
//Console.WriteLine($@"ref={i}:{refHash}");
if (tgtHash != refHash) {
nRet = 1;
nRetAll = nRet;//全体の不一致設定
echo($"{i}:NotMatch:{i},tgt[{tgtHash}],ref[{refHash}]");
//2020年3月2日 15:23:55:[@Difference]:<fullpath>.diff.jpg
lock (fcResultMsg) {
fcResultMsg.Add((int)i, $@"{dateStr}:[@Difference]:{pdfPath}.diff.jpg");
}
//if ( true && tgtStrm != null && bSave) {
{
echo("***** save diff image");
// diffのjpegを書き出してみる(tgt)
if (tgtStrm == null) tgtStrm = RendPageTGT();
var otPath = Path.Combine(otDir, $"{otBaseName}.{i}.tgt.{otExtention}");
var bmp = new System.Drawing.Bitmap(tgtStrm.AsStream());
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();
}
//if (true && refStrm != null && bSave) {
{
// diffのjpegを書き出してみる(ref)
if (refStrm == null) refStrm = RendPageREF();
var otPath = Path.Combine(otDir, $"{otBaseName}.{i}.ref.{otExtention}");
var bmp = new System.Drawing.Bitmap(refStrm.AsStream());
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 {
nRet = 0;// match
//Console.WriteLine($"{i}:Match:{i}");
// 2019年12月18日 19:31:38:[OK]:fname.pdf.1.png
lock (fcResultMsg) {
fcResultMsg.Add((int)i, $@"{dateStr}:[OK]:{Path.GetFileName(pdfPath)}.{i}.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.
}
/// <summary>
/// 比較モード
/// </summary>
/// <param name="doc">PdfDocument</param>
/// <param name="nPage">ページ番号</param>
/// <param name="otPath">出力ファイル名</param>
/// <returns>正常:0 </returns>
public static Windows.Storage.Streams.InMemoryRandomAccessStream RenderPageStream(
// Need:
Windows.Data.Pdf.PdfDocument doc, // Need GetPage() only. (Total page number);
PdfPage page, // Target Page Data(page.index:0-x)
//string otPath,
// Optional:
RenderConditionParams pm = null,
PdfiumViewer.PdfDocument docG = null //
) {
var bDump = Check.bDump;
var bThDump = Check.bThDump;
if (pm == null)
pm = new RenderConditionParams();
double dpiWin = Check.GetDPI(); //96.0;/* , dpiPDF = 72.0*/
double resoScale = (pm.Dpi / dpiWin);
var opt = new PdfPageRenderOptions() {
// SEE:https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/PdfDocument/cs/Scenario1_Render.xaml.cs
//BitmapEncoderId = Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId,// JPEG
IsIgnoringHighContrast = false
};
//
//async Task PreparePage(PdfPage p) => await p.PreparePageAsync();//ページの完成を待つ
//PreparePage(page).Wait();
//page.PreparePageAsync
var boxDic = new Dictionary<string, Windows.Foundation.Rect>() {
// Init
["Media"] = page.Dimensions.MediaBox,
["Trim"] = page.Dimensions.TrimBox,
["Bleed"] = page.Dimensions.BleedBox,
["Crop"] = page.Dimensions.CropBox,
["Art"] = page.Dimensions.ArtBox
};
opt.SourceRect = boxDic.ContainsKey(pm.BoxType) ? boxDic[pm.BoxType] : boxDic["Crop"];// Cannot find -> "Crop";
opt.DestinationWidth = (uint)(opt.SourceRect.Width * resoScale + 0.5);
opt.DestinationHeight = (uint)(opt.SourceRect.Height * resoScale + 0.5);
if (bDump)
Console.WriteLine($"dpi={pm.Dpi},scale={resoScale},height(org)={page.Size.Height}->{opt.DestinationHeight}");
var memStrm = new Windows.Storage.Streams.InMemoryRandomAccessStream();
if(true) {
if (docG == null) {
async Task RenderToStream(PdfPage p) => await p.RenderToStreamAsync(memStrm, opt);
RenderToStream(page).Wait();
} else {
PdfRenderFlags flg = (PdfRenderFlags.ForPrinting | PdfRenderFlags.CorrectFromDpi);
System.Drawing.Image img = docG.Render((int)(page.Index), (float)pm.Dpi, (float)pm.Dpi, flg);
img.Save(memStrm.AsStream(), System.Drawing.Imaging.ImageFormat.Jpeg);
// [注意] PDFuim用に直接 AsStream()に書き込んでいるので、AsStream().Flush()しないとだめです
memStrm.AsStream().Flush();
}
async Task<bool> FlushX() => await memStrm.FlushAsync();
//var dmy =FlushX().Result;
FlushX().Wait();
}
//page.Dispose();
return memStrm;//
}
/// <summary>
/// ページ1から始まるページ範囲の配列を返す
/// </summary>
/// <param name="pageRange"></param>
/// <param name="maxPage"></param>
/// <returns></returns>
private static uint[] makePageRange(string pageRange = "1", uint endPage = 100) {
var range = new SortedSet<uint>();
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<T>(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<uint>();
}
public static string GetHashValue(Windows.Storage.Streams.InMemoryRandomAccessStream strm) {
var alg = new SHA256CryptoServiceProvider();
strm.Seek(0);
var bin = alg.ComputeHash(strm.AsStream());
alg.Clear();
// バイト配列をUTF8エンコードで文字列化
var hashedText = new StringBuilder();
foreach (var b in bin) {
hashedText.AppendFormat("{0:X2}", b);
}
return hashedText.ToString();
}
}
/// <summary>
/// ファイルのハッシュ値のIO
/// </summary>
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<string> PageHashCode = new List<string>(); // ページ毎のハッシュ値の配列
// メソッド
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<string, HashFile> Files = new SortedDictionary<string, HashFile>();
// メソッド
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<string>(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<string>(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;//別名
/// <summary>
/// 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");
/// </summary>
///
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;
}
/// <summary>
/// サポートするEncodeパラメータ一覧
/// 現状Errorが発生して取得できない
/// </summary>
/// <param name="bitmap1">対象のBitMap</param>
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<System.Drawing.Imaging.EncoderParameter>();
//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;
}
}
}
}
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
//using System.Text;
//using System.Threading;
//using System.Threading.Tasks;
//using System.Windows;
//using System.Windows.Media.Imaging;
using System.Reflection; // Assembly.
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.ServiceModel; // WCF
using System.Diagnostics; // for Process
// 作成クラス
using CSRender;
using CSRender.UtHash;
using static CSRender.RenderPDF;
// *重要* デスクトップアプリで UWP Api を呼び出す プロジェクトの設定方法が記載されている
// https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-enhance
// 参照はwindows. winmd :C:\Program Files (x86) \Windows Kits\10\UnionMetadata\<sdk バージョン>/ファサード
namespace CSRenderMain {
[DataContract]
//[Serializable()]
public class ParamData {
[DataMember]
public string pdfPath = ""; //対象ファイル
[DataMember]
public string pdfPath2 = ""; //比較ファイル
[DataMember]
public string outuptImageDir = ""; // /O
[DataMember]
public string dpi = "72.0"; // /D
[DataMember]
public int para = 4; // 並行数
[DataMember]
public string boxSelect = "Crop"; // /B<x>
[DataMember]
public string pageRange = "1-*"; // /P
[DataMember]
public string mode = "page"; // /MODE
[DataMember]
public string imageType = "JPG"; // /JPG or /PNG
[DataMember]
public string jpegQ = "91"; // JPEGQ
[DataMember]
public bool bHash = false; // ハッシュ値を生成する
[DataMember]
public bool bMkHash = false; // ハッシュファイルを作成する
[DataMember]
public bool bFC = false;
[DataMember]
public string resultPath = "";
[DataMember]
public bool bPDFium = true;
//
[DataMember]
public bool bExeSepa = true;// 実行分離
[DataMember]
public string subExe = null;// "Sub"が指定されたら、処理をExe分離する。内部コマンド
// データはWCFの通信データを使う。引数はPipeName:PortAddressNameとする
[DataMember]
public bool bVerbose = false;// 詳細デバッグ
public ParamData Clone() {
return (ParamData)MemberwiseClone();
}
}
public class Program {
static void DispHelp() {
var pgName = Path.GetFileName(Path.GetFileName(Assembly.GetExecutingAssembly().Location));// プログラム名
var pgNameFullPath = Assembly.GetExecutingAssembly().Location;// プログラム名
Console.WriteLine("pageNameF="+pgNameFullPath);
string msg =
$"{pgName} [/<Opts>] <PDFPath or PDFDir>\n"
+ $"* Render of PDF file. available 3 command mode:[Basic Rendering] [Make Hash command] [Compare command], and [Render Options]\n"
+ $"\tPDFの画像化は[Basic Rendering]。\n"
+ $"\t比較は/MkHash([Make Hash command])後に、/FC([Compare command])で高速実行できます。\n"
+ $"\n"
+ $"[Basic Rendering] 基本的なレンダリング\n"
+ $"\t{pgName} [/<Render Options>] <PDFPath|PDFDir>\n"
+ $"\t/F <pdfPath|pdfDir> : pdfPath(pdfファイル名|ディレクトリ) /Fは省略可能\n"
+ $"\t/O <output directory> : 出力ディレクトリ。省略時は\"IMG\"フォルダ\n"
+ $"\n"
+ $"[Render Options] レンダリングオプション\n"
+ $"\t/D <解像度> : 解像度指定 9 - 300dpi(default=72dpi)\n"
+ $"\t/JPG,/JPEG,/PNG,/TIF,/TIFF,/GIF,/BMP: Select one output format.(default=/JPG)\n"
+ $"\t/JPEGQ <quality>: Jpegの品質指定1-100(default=91)\n"
+ $"\n"
+ $"\t/P <PageRange> : ページの範囲を指定する(省略時は全ページ)\n"
+ $"\t\t連続した範囲を指定する場合は、ハイフン('-')を用いる。終了側を省略すると最終pageまで。\n"
+ $"\t\t複数のページを指定する場合は、カンマ(',')を用いる\n"
+ $"\t\tEx. /P \"1,2,30-100\" //1,2pages and 30-100pages.\n"
+ $"\t[Unsupport] 未対応↓\n"
+ $"\t/L <input List text> : 入力PDFファイルリスト(*unsupport)\n"
+ $"\t/T <tempPath> : テンポラリフォルダを指定(省略時は出力先フォルダと同じ(*unsupport no need)\n"
+ $"\t/OP <0|1> : オーバープリントのOn/Off (省略時は1)(*unsupport allways on[1])\n"
+ $"\t/U <0|1> : 同名上書き設定 0:上書きしない 1:上書き(*unsupport allways overwrite[1])\n"
+ $"\t/OFFSET <X> <Y> : ミリ単位でオフセットを指定する(省略時は共に0mm)(*unsupport)\n"
+ $"\t/PDFium <0or1>: GoogoleのPDFiumViewerエンジンを使用する(default=1>\n"
+ $"\t【未】/BM,/BT,/BA,/BA,/BC: Select one box.(default=/BC:CrobBox): Boxies:MediaBox/BleedBox/TrimBox/ArtBox/CropBox\n"
+ $"\n"
+ $"[Make Hash command] 比較用ハッシュ値作成コマンド\n"
+ $"\t{pgName} /MkHash ...<Render Options>... <PDF|PDF dir>\n"
+ $"\t/MKHash : ハッシュ値を出力する。前記の[Render Options]を指定すること\n"
+ $"\t[HASHファイル作成]\n"
+ $"\n"
+ $"[Compare command] 比較コマンド\n"
+ $"\t{pgName} /FC ...<Render Options>... <Target PDF|PDF dir> <Reference PDF|PDF dir>\n"
+ $"\t/FC : 2つのPDFを比較する。前記の[Render Options]を指定すること。無名引数が2つ必要です\n"
+ $"\t 事前に/MkHashを実行しておくことで高速に処理できる\n"
+ $"\n"
+ $"\t/RESULT <result file> : 比較結果を格納するファイルパス\n"
+ $"\t\t/FCコマンドを指定すると一致したら0,不一致なら1を返却するようになる\n"
+ $"\t\t<result file>は、<pageNum(1始まり)>,<[OK] or [@Difference]>の行で構成される\n"
+ $"\n"
+ $"[ELSE ] その他のオプション\n"
+ $"\t/PDFium <0|1>:PDFiumライブラリを使う,デフォルト=1\n"
+ $"\t/NoExeSepa :実行分離しない(遅い)\n"
+ $"\t/para <並行数>:並行数を指定(デフォルト4)\n"
+ $"\t内部コマンド:/SubExe <WCF_PipeAddress> :実行分離,PDF単位で別Processで処理\n"
+ $"\n"
+ $"/H or /? : This help\n"
/* PDFと同じフォルダに
data.<出力条件>.jsonファイルを作成する
出力条件:<dpi>+<Box>+<IMFormat>+<JQ>
data.72_BT_JPG_Q34.json
既に存在したら、それを読んでから、追記(もしくは書き換える>。
*/
// Remove TEST
//+$"\t[for TEST]\n"
//+$"\t/M Sync(default) or ASync\n"
//+$"\t/M <mode> : pageBitMap(default), pageBitMapImage, page(same as pageBitMap), org(orginal source)\n"
+ "\n";
Console.Write(msg);
}
#if COMENT
// 設計の見直し
* ハッシュ作成のみのコマンド
/MkHashをつけると ハッシュファイルのみ作成。ただし、ディレクトリ指定に限る
CSRender /MkHash <PDFディレクトリ> <各種Renderパラメータ>
* 単純なRenderを維持。それに同時にハッシュファイルのOn/Off
/MkHashをつけなければよい。
CSRender <PDFディレクトリor PDFファイル> <各種Renderパラメータ>
* 比較モードは、Autoハッシュで、存在しなければ最初に作成。Target/Refともに。比較しながら作成してよし
Manualでハッシュなしで、遅いけどできるようにする。ハッシュ動作の検証用に
/FCをつけると、デフォルトでハッシュ優先で検査(Auto相当)、ハッシュ検査を無効にしたければ
/NoHash追加でハッシュを無視してTarget/Refとも再計算。ハッシュを使いたければ、事前に/MkHash
作成しておけばよい。
* ヘルプは <単純なRender> <ハッシュ作成> <比較>にわける
* <単純なレンダラ>: 1ファイルおよびディレクトリの比較。 Renderingパラメータの一覧(項目分ける)まで.
* <ハッシュ作成>: /MkHashで高速比較のための前準備。ディレクトリモードでハッシュファイルのみを作成する。Renderingパラメータは合わせること
* <比較>:/FCコマンドで比較の説明
* ここにAsiccDocを埋められないの? 情報なし*
// 遅くなるので
* 比較モード時の差異があったとき、Diffを出す上限値をLimitDiffNumがほしい(デフォルトは各PDF1pageとしたい)
* 比較NGになって、フルでDIFF画像を出したいときはLimitDiffを解除すべき。単独ファイル指定のとき。
// 設計の見直し(END)
#endif
/// <summary>
/// PDF renderer main
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
static int Main(string[] args) {
// 引数の保持
var pm = new ParamData();
bool bDirMode = false; // ディレクト指定の場合
//var pdfPathLst = new List<string>();
string[] pdfPathLst = null;
string[] pdfPathLst2 = null;
var pdfPathLstBoth = new List<string>();
var pdfPathLstNoBoth = new List<string>();
//ハッシュ関係
string hashBaseName = "RenderHash";
CSRender.Check.GetDPI();
//{// ベースハッシュ値の計算テスト
// // 一旦使用しない
// var streamPDF = ResData.GetBaseHashPDF();
// RenderPDF.RenderPdfInitCheck(streamPDF);
//}
var qu = new Queue<string>(args); // 引数をQueに登録 (qu.Enqueue(a)でも可能)
while (qu.Count > 0) {
// 引数のパース
var wd = qu.Dequeue(); // 取り出しqu.Dequeue()で次の要素を取得する
if (wd.First() == '-') {
// 先頭-(ハイフンもオプション扱いにする)→"/"に置換
wd = Regex.Replace(wd, @"^\-", "/");
}
//大文字小文字無視でオプションチェック
var eIgnoreCase = StringComparer.OrdinalIgnoreCase;
// オプションチェックローカル関数
bool isOpt(params string[] opts) => opts.Contains<string>(wd, eIgnoreCase);
// ボックスオプション辞書
var BoxSelOptDic = new Dictionary<string, string>(eIgnoreCase) { ["/BM"] = "Media", ["/BT"] = "Trim",
["/BB"] = "Bleed", ["/BC"] = "Crop",
["/BA"] = "Art"
};
if (isOpt("/?", "/H")) {
DispHelp();
return -1;
} else if (isOpt("/F")) {
pm.pdfPath = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
} else if (isOpt("/O")) {
pm.outuptImageDir = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
} else if (isOpt("/D")) {
pm.dpi = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!double.TryParse(pm.dpi, out double dmy)) {
Console.WriteLine($"解像度が不正です:/D {pm.dpi}");
DispHelp();
return -1;
}
} else if (isOpt("/Para")) {
string paraNum = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!int.TryParse(paraNum, out pm.para)) {
Console.WriteLine($"並行数が不正です:/para {paraNum}");
DispHelp();
return -1;
}
} else if (isOpt("/P")) {
pm.pageRange = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
} else if (isOpt("/JPG", "/JPEG")) {
pm.imageType = "JPG";
} else if (isOpt("/PNG")) {
pm.imageType = "PNG";
} else if (isOpt("/TIF", "/TIFF")) {
pm.imageType = "TIFF";
} else if (isOpt("/GIF", "/GIFF")) {
pm.imageType = "GIF";
} else if (isOpt("/BMP")) {
pm.imageType = "BMP";
} else if (isOpt("/JPEGQ")) {
pm.jpegQ = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!int.TryParse(pm.jpegQ, out int dmy)) {
Console.WriteLine($"JPEG Qualityが不正です:/JPEGQ {pm.jpegQ}");
DispHelp();
return -1;
}
if (!(0 < dmy && dmy <= 100)) {
Console.WriteLine($"JPEG Qualityが不正です(not 1-100):/JPEGQ {dmy}");
return -1;
}
} else if (isOpt("/M")) {
pm.mode = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
} else if (BoxSelOptDic.ContainsKey(wd)) {
pm.boxSelect = BoxSelOptDic[wd];// "/BT" -> "Trim",...
} else if (isOpt("/HASH")) {
pm.bHash = true;
} else if (isOpt("/MKHASH")) {
pm.bMkHash = true;
pm.bHash = true;
} else if (isOpt("/FC")) {
pm.bFC = true;
} else if (isOpt("/PDFium")) {
pm.bPDFium = true;
var flgStr = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
if (!int.TryParse(flgStr, out int dmy)) {
Console.WriteLine($"PDFiumフラグが不正です:/PDFium {flgStr}");
return -1;
}
if (dmy == 0) {
pm.bPDFium = false;
} else if (dmy == 1) {
pm.bPDFium = true;
} else {
Console.WriteLine($"PDFiumフラグが不正です:/PDFium {flgStr}");
return -1;
}
} else if (isOpt("/Verbose")) {
var arg = (qu.Count > 0) ? qu.Dequeue() : "True";// next word.
string[] sel = {"True","1","ON"};
pm.bVerbose = sel.Contains(arg, eIgnoreCase);
} else if (isOpt("/SubExe")) {
pm.subExe = (qu.Count > 0) ? qu.Dequeue() : null;// next word.
var sp = pm.subExe.Split(':');
if (sp.Length < 2 ) {
Console.WriteLine($"SubExe指定は\":\"区切りが必要: {pm.subExe}");
return -1;
}
} else if (isOpt("/NoExeSepa")) {
pm.bExeSepa = false;
} else if (isOpt("/RESULT")) {
pm.resultPath = (qu.Count > 0) ? qu.Dequeue() : "";// next word.
} else if (wd.First() == '/') {
// 処理の無いオプションを明示的に無視する
Console.WriteLine($"Warning::Ignore opt:{wd}");
} else {
// 引数をファイル名として取得する
if (pm.pdfPath == "") {
pm.pdfPath = wd;
continue;
}
if (pm.bFC && (pm.pdfPath2 == "")) {
pm.pdfPath2 = wd;// 比較時のみ取得
continue;
}
}
}
// ↑引数解析終わり
setEcho(pm.bVerbose);
if (pm.bVerbose)
echo("Varbose Mode!");
//
bool bSubExe = (pm.subExe != null);// SubExeで起動されている場合。
xChangeWCFPipe.IXData cltSrv = null;
if (!bSubExe) {
/// サーバー側
echo("++MainProcess start");
} else {
// SubExe側の動作に差し替える
var sp = pm.subExe.Split(':');
var pipeName = sp[0]; var pipeAddr = sp[1];
cltSrv = xChangeWCFPipeGEN.CLT_T.makeCLT<xChangeWCFPipe.IXData>(pipeName:pipeName,pipeAddress:pipeAddr);
Ut.Ut.SetAutoSelfKillByMainProcEnd(); // 起動元が終了した場合に自身を終了させる
pm = cltSrv.GetParam(); // パラメータを書き換える
setEcho(pm.bVerbose);
echo("++SubProcess start:" + pm.subExe);
}
if (pm.pdfPath == "") {
Console.WriteLine("pdfファイルが指定されてません");
DispHelp();
return -1;
}
if (!(File.Exists(pm.pdfPath) || Directory.Exists(pm.pdfPath))) {
// ファイルもしくはディレクトも見つからない場合
Console.WriteLine($"ファイルが存在しません:{pm.pdfPath}");
return -1;
}
var rdCond = new RenderPDF.RenderConditionParams {
Dpi = double.Parse(pm.dpi),
ImageType = pm.imageType,
BoxType = pm.boxSelect,
JpegQ = int.Parse(pm.jpegQ)
};
bool bDir = File.GetAttributes(pm.pdfPath).HasFlag(FileAttributes.Directory);
if (bDir) {
// Directoryが指定されたのでファイルリストアップ
bDirMode = true;
pdfPathLst = System.IO.Directory.GetFiles(pm.pdfPath, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/);
pdfPathLst = Array.ConvertAll(pdfPathLst, f => Path.GetFileName(f)); // 配列書き換え(ファイル名のみにする
// var enumLst = pdfPathLst.Select(f => Path.GetFileName(f)); LINQ式に置き換えることも可能(返り値は配列ではない)
//foreach ( var f in pdfPathLst) {
// Console.WriteLine($@"path1={f}");
//}
//Console.WriteLine($@"path1.Len={pdfPathLst.Count()}");
}
var resultData = new SortedDictionary<int, string>();
if ((!pm.bFC) && bDir) {
// 単純レンダリング時 かつ ディレクト時に 対象リストに追加
foreach (var f in pdfPathLst) {
pdfPathLstBoth.Add(f);
}
}
if (pm.bFC) {// 比較モード時
if (pm.pdfPath2 == "") {
Console.WriteLine("比較モードで2つ目のpdfファイルが指定されてません");
return -1;
}
if (!(File.Exists(pm.pdfPath2) || Directory.Exists(pm.pdfPath2))) {
Console.WriteLine($"比較ファイルが存在しません:{pm.pdfPath2}");
return -1;
}
bool bDir2 = File.GetAttributes(pm.pdfPath2).HasFlag(FileAttributes.Directory);
if (bDir2) {
if (!bDir) {
Console.WriteLine($"比較対象はファイルパスでないといけません:{pm.pdfPath2}");
return -1;
}
// 2つ目のファイルリストアップ
pdfPathLst2 = System.IO.Directory.GetFiles(pm.pdfPath2, "*.pdf"/*, System.IO.SearchOption.AllDirectories*/);
pdfPathLst2 = Array.ConvertAll(pdfPathLst2, f => Path.GetFileName(f));// 配列の書き換え
// 共通のファイルを見つける。Lstの要素がLst2に含まれているかどうか
foreach (var f in pdfPathLst) {
if (pdfPathLst2.Contains(f)) {
pdfPathLstBoth.Add(f);
} else {
pdfPathLstNoBoth.Add(f);// LstがLst2に含まれていない
}
}
// Lst2のみファイルをNoBothに登録
foreach (var f in pdfPathLst2) {
if (!pdfPathLstBoth.Contains(f)) {
pdfPathLstNoBoth.Add(f);
}
}
// pdfPathBoth,pdfPathNoBothが作成済み
if (pdfPathLstNoBoth.Count() != 0) {
Console.WriteLine($@"不一致のファイル={pdfPathLstNoBoth.Count()}");
foreach (var f in pdfPathLstNoBoth) {
Console.WriteLine($@"warning [no match]={f}");
}
}
}
}
var watch = System.Diagnostics.Stopwatch.StartNew(); // 時間の生成と計測開始を同時に行う
var otDir = pm.outuptImageDir;
if (otDir == "") {
// 出力ディレクトリの作成 // 元PDFの同一フォルダにIMGフォルダを作成する
otDir = Path.Combine(Directory.GetParent(pm.pdfPath).FullName, "IMG");
}
if (!Directory.Exists(otDir) /* && (!bMkHash)*/ ) { // MkHashのとき不要→必要
Directory.CreateDirectory(otDir);
}
var otHashPath = "";
var otHashPath2 = "";
int ret = -1;
//Console.WriteLine($@"Use bPDFium={pm.bPDFium}");
if (!bDirMode) {
if (pm.bFC) {
//比較モード
// ハッシュデータの読み込み
echo($@"pre pdfPath={pm.pdfPath}");
echo($@"pre pdfPath2={pm.pdfPath2}");
var hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath);
var hashDataRef = HashData.load(pm.pdfPath2, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2);
//Console.WriteLine("<Compare Sync(Paralles) Version!>");
if (pm.resultPath != "") {// 初期化
File.WriteAllText(Path.GetFullPath(pm.resultPath), "");
}
ret = RenderPDF.RenderPdfDocCompare(
pdfPath: Path.GetFullPath(pm.pdfPath),
refPdfPath: Path.GetFullPath(pm.pdfPath2),
resultDataPath: pm.resultPath,
inDir : otDir,
pm : rdCond,
inPageRange : pm.pageRange,
inHashDataTgt: hashDataTgt,
inHashDataRef: hashDataRef,
bPDFium : pm.bPDFium
);
} else {
// Sync version.
// Console.WriteLine("<Render Sync(Paralles) Version!>");
// シングル指定では既存のハッシュファイルを読みださない → 単独名のハッシュファイル<pdfFName+".hash")
// 空のハッシュデータを作成
var hashDataTgt = new HashData();
hashDataTgt.Head.SetRenderInfo(pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ);
//retはページ数
// rdCond
ret = RenderPDF.RenderPdfDoc(
pdfPath : Path.GetFullPath(pm.pdfPath),
inDir : otDir,
pm : rdCond,
inPageRange : pm.pageRange,
bSaveImage : pm.bMkHash ? false : true,// ハッシュ値生成ではイメージ保存しない。
bHash : pm.bHash,
inHashData : hashDataTgt,
bPDFium : pm.bPDFium
);
if (pm.bMkHash) {
/// ここでMain側に通信データを返せばよい
if (bSubExe) {
cltSrv.SetHashData(hashDataTgt);
} else {
// 個別のファイル名+"hash"拡張子で保存します。
var othashPath = Path.Combine(otDir, Path.GetFileName(pm.pdfPath) + ".hash");
hashDataTgt.save(othashPath);//ハッシュモードで保存する(Dutyチェックもいるでしょう
//不要:hashDataTgt.save(otHashPath);//比較モード,MakeHashに限定すること
//表示のみ まだやめておく:
}
}
// retはページ数です。
}
} else {
// 対象がディレクトリ
echo("For Directory mode");
HashData hashDataTgt = null, hashDataRef = null;
hashDataTgt = HashData.load(pm.pdfPath, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath);
if (pm.bMkHash) {
hashDataTgt = null;// MKHashの時にはLoadしない → 一旦nullで除去して、otHashPathを利用する
}
if (hashDataTgt == null) {
// 空のハッシュデータを作成
hashDataTgt = new HashData();
hashDataTgt.Head.SetRenderInfo(pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ);
}
if (pm.bFC) {
// リファレンス側のハッシュの読み込み
hashDataRef = HashData.load(pm.pdfPath2, hashBaseName, pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ, ref otHashPath2);
if (hashDataRef == null) {
// 空のハッシュデータを作成
hashDataRef = new HashData();
hashDataRef.Head.SetRenderInfo(pm.dpi, pm.boxSelect, pm.imageType, pm.jpegQ);
}
}
// ハッシュデータが適切に作成済みがどうかを確認。もしハッシュデータが存在しない場合は
// ハッシュデータを渡してはいけない。ハッシュのFilesで確認するか?
// ハッシュデータの作成プロセスをもっと簡単にすべき
echo($@"Start render:Count={pdfPathLstBoth.Count}");
//Console.WriteLine("<Render Sync(Paralles) Version!>");[
var count = pdfPathLstBoth.Count;
//ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 6 };
//Parallel.For(0,pdfPathLstBoth.Count, options, index => {
//逆に遅くなる UWPコール(render)は対応していない?
if (pm.bExeSepa &&(!pm.bFC)) { // FCモードは除外します。
var tokenSource = new CancellationTokenSource();
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = pm.para };
ret = 0;//Success
var loopResult = Parallel.For(0,pdfPathLstBoth.Count, options, (index,lpState) => {
//for (var index = 0; index < pdfPathLstBoth.Count; index++) {
var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]);
var tgtID = $@"{index}/{pdfPathLstBoth.Count}";
Console.WriteLine($@"**Remain({count})****tgt({tgtID})={tgt}");
if (pm.bFC) {
//比較モード
var reff = Path.Combine(pm.pdfPath2, pdfPathLstBoth[index]);
} else { // Sync version(Paralles).
var svrData = new xChangeWCFPipe.XData();
var pmClone = pm.Clone();
pmClone.pdfPath = tgt;// 引数のターゲットのみを書き換える
svrData.SetParam(pmClone);
string guidStr = Guid.NewGuid().ToString("N");
string pipeName = $"PN_{guidStr}";
string pipeAddress = $"P_{index}_{guidStr}";
var svrHost = xChangeWCFPipeGEN.SVR_T.makeSVR(
baseIf : typeof(xChangeWCFPipe.IXData),
obj : svrData,
pipeName : pipeName,
pipeAddress : pipeAddress
);
if ( svrHost == null) {
Console.WriteLine("WCF cannot make");
//tokenSource.Cancel();// 処理のキャンセル( throwされる)
lpState.Stop();
return;
}
var myExePath = Assembly.GetExecutingAssembly().Location;
var proc = Ut.Ut.DoCmd(
cmdAndArgs: new string[] { myExePath, "/SubExe",$"{pipeName}:{pipeAddress}" }
, bEcho: false
, bSameConsole: true
);
proc.WaitForExit();
var retProc = proc.ExitCode;
echo($@"retProc={retProc}");
if ( retProc < 1 ) {
//PDFページ数以外が返った時
ret = -1;
Console.WriteLine( $@"Warning ::Cannot get PDFPage:{retProc}");
} else {
;// 初期値 ret=0;
}
//while (!proc.HasExited) {
// System.Threading.Thread.Sleep(2 * 1000);
//}
var tmpH = svrData.GetHashData();
if (tmpH == null) {
echo("tmpH is null");
}
if ( (hashDataTgt!=null)&&(tmpH!=null)) {
lock(hashDataTgt) {
//hashDataTgt.Files += tmpH.Files;
if ( tmpH.Files.Count() < 1 ) {// 中身が空なら警告
Console.WriteLine($@"Warning tempF.Count={tmpH.Files.Count()}");
}
var merged = hashDataTgt.Files
.Concat(tmpH.Files.Where(pair =>
!hashDataTgt.Files.ContainsKey(pair.Key))
).ToDictionary(
pair => pair.Key,
pair => pair.Value
);
hashDataTgt.Files = new SortedDictionary<string, HashFile>(merged);// hashを合成した。
}
}
proc.Dispose();
svrHost.Close();
svrData = null;
Console.WriteLine($@"End tgt({tgtID})");
}
//GC.Collect();// これでメモリリークが解決した Paraの中ではやめておく
count--;
});
if ( !loopResult.IsCompleted ) {
Console.WriteLine("中断");
pm.bMkHash = false;// Hash値保存抑制
}
////tokenSource.Cancel();// 処理のキャンセル
//if ( tokenSource.IsCancellationRequested ) {
// pm.bMkHash = false;// Hash値保存抑制
//}
//}; // Non Para Block
} else { // NoExeSepa
if (pm.resultPath != "") {// 初期化
File.WriteAllText(Path.GetFullPath(pm.resultPath), "");
}
for (var index = 0; index < pdfPathLstBoth.Count; index++) {
var tgt = Path.Combine(pm.pdfPath, pdfPathLstBoth[index]);
Console.WriteLine($@"tgt={index+1}/{pdfPathLstBoth.Count}:{tgt}");
if (pm.bFC) {
//比較モード
var reff = Path.Combine(pm.pdfPath2, pdfPathLstBoth[index]);
//Console.WriteLine("<Compare Sync(Paralles) Version!>");
ret = RenderPDF.RenderPdfDocCompare(
pdfPath: Path.GetFullPath(tgt),
refPdfPath: Path.GetFullPath(reff),
resultDataPath: pm.resultPath,
inDir: otDir,
pm: rdCond,
inPageRange: pm.pageRange,
inHashDataTgt: hashDataTgt,
inHashDataRef: hashDataRef,
bPDFium: pm.bPDFium
);
} else { // Sync version(Paralles).
ret = RenderPDF.RenderPdfDoc(
pdfPath: Path.GetFullPath(tgt),
inDir: otDir,
pm: rdCond,
inPageRange: pm.pageRange,
bSaveImage: pm.bMkHash ? false : true,// ハッシュ値生成ではイメージ保存しない。
bHash: pm.bHash,// bHash,dumy
inHashData: pm.bMkHash ? hashDataTgt : null, //ハッシュコマンドモードのみDataを渡す
bPDFium: pm.bPDFium
);
}
count--;
GC.Collect();// これでメモリリークが解決した
};
}
if (pm.bMkHash) {
hashDataTgt.save(otHashPath);//ハッシュモードで保存する(Dutyチェックもいるでしょう
}
}
watch.Stop();
Console.WriteLine($"result={ret},time={ watch.ElapsedMilliseconds / 1000.0 }[sec]");
#if COMMNET
Assembly assm = Assembly.GetExecutingAssembly();
Console.WriteLine(assm.FullName);
// 参照しているすべてのアセンブリを取得し、表示する
foreach (AssemblyName refassm in assm.GetReferencedAssemblies()) {
Console.WriteLine($@" {refassm.FullName},Ver({refassm.Version}),VerComp({refassm.VersionCompatibility})" );
}
string clrVersion = System.Environment.Version.ToString();
Console.WriteLine($@"clrVer={clrVersion}");
#endif
return ret;//success.
}
// リソースからの取得
public static class ResData {
public static Stream GetBaseHashPDF(string resName = "CSRender.RES.BaseHash.pdf") {
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
var sel = from x in asm.GetManifestResourceNames() select resName;
if (sel.Count() == 1) {
return asm.GetManifestResourceStream(sel.First());
}
return null;
}
}
}
}
/// <summary>
/// WCF通信モジュールのラッパクラス。
/// 2つのExe間をWCFパイプモードで通信を行う
/// </summary>
namespace xChangeWCFPipe
{
/// シリアライズ可能にすること
/// </summary>
[ServiceContract]
public interface IXData {
[OperationContract(IsOneWay = true)]
void Execute();
[OperationContract]
bool SetMessage(string msg);
[OperationContract]
bool SetProgress(int per);//0-100.
[OperationContract]
HashData GetHashData();
[OperationContract]
void SetHashData(HashData d);
[OperationContract]
CSRenderMain.ParamData GetParam();
//CSRenderMain.Program.ParamData GetParam();
}
// ServiceContract https://tnakamura.hatenablog.com/entry/20080606/1220023868
// https://devlights.hatenablog.com/entry/20111023/p2
/// <summary>
/// 通信用複雑データ。スカラー型(int,double,,,.)以外は[DataCOntract]属性を
/// つけて、通知用Interfaceの引数や、返値で利用する
/// </summary>
[DataContract]
public class DataContainer
{
[DataMember]
public CSRenderMain.ParamData pm;
[DataMember]
public HashData hdata;
}
}
namespace xChangeWCFPipe
{
/// <summary>
/// Main(サーバー)側 データ定義
/// </summary>
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
public class XData : IXData
{
private readonly Object LockObj = new object();// 排他制御用
public delegate void callFunc();
public callFunc callDelegate;
public XData() {
_dc.pm = new CSRenderMain.ParamData();
}
public void Execute() {
lock (LockObj) {
callDelegate(); // 実装はデリゲートします
}
}
public bool SetMessage(string msg) {
lock (LockObj) {
Console.WriteLine($"SVR:called setMessage({msg}) from CLT");
_msg = msg;
}
return true;
}
public bool SetProgress(int per) {//0-100.
lock (LockObj) {
Console.WriteLine($"SVR:called setProgress({per}) from CLT");
_progress = per;
}
return true;
}
public void SetHashData(HashData h) {
lock (LockObj) {
_dc.hdata = h;
}
}
public HashData GetHashData() {
lock (LockObj) {
return _dc.hdata;
}
}
public CSRenderMain.ParamData GetParam() {
lock (LockObj) {
return _dc.pm;
}
}
// クライアントに公開しなくてもよいI/F
public void SetParam(CSRenderMain.ParamData pm) {
lock (LockObj) {
_dc.pm = pm;
}
}
// private data
private string _msg = "init msg";
private int _progress = 0;// 0-100
private DataContainer _dc = new DataContainer();
}
}
namespace xChangeWCFPipeGEN
{
/// <summary>
/// Main(サーバー)側 データ定義
/// </summary>
//[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
//public class XData : IXData
//{
// ...
//}
/// <summary>
/// Main(サーバー)側クラス
/// </summary>
///
public class SVR_T
{
// - static -
/// <summary>
///
/// </summary>
/// <param name="baseIf"> 公開するインターフェースクラス </param>
/// <param name="x">実装インスタンス(baseIfを含むこと)</param>
/// <param name="pipeName"></param>
/// <param name="pipeAddress"></param>
/// <returns></returns>
static public SVR_T makeSVR(Type baseIf, object obj/*XData*/ , string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") {
Type oType = obj.GetType();
bool hasBaseIf = oType.GetInterfaces().Any(t => (t == baseIf) );
if ( !hasBaseIf) {
Console.WriteLine($@"Error:'{oType.FullName}' have not the interface '{baseIf.FullName}'");//A have not the interface B.
return null;
}
return (new SVR_T(baseIf, obj, pipeName, pipeAddress));
}
// - public I/F -
public void Close() {
echo($"Close Service:{_service != null},{_serviceHost != null}");
_service = null;
_serviceHost.Close();
}
// - private -
private object _service = null;// 要らないかも
private Type _baseIf = null;
private ServiceHost _serviceHost = null;
private SVR_T() { }
private SVR_T(Type baseIf, object oInstance, string pipeName, string pipeAddress) {
const string pipeBase = "net.pipe://localhost";
var uri = pipeBase + "/" + pipeName;
_baseIf = baseIf;
_service = oInstance;
// デリゲート登録
//callDelegate = () => { Console.WriteLine("svr:Delegate func called"); }
#if false
{
// インターフェースを求める
// AddServiceEndpoint()にわたすIXDataはXData.Interfaces()から求められるが
// インタフェースは一つとは限らないのでやめておく。
Type interfType = null;
Type type = _service.GetType();
Type[] interfaces = type.GetInterfaces();
Console.WriteLine($@"GetInterfaces***********************************");
foreach (Type t in interfaces) {
Console.WriteLine(t.ToString());
interfType = t;
};
Console.WriteLine($@"End of GetInterfaces***********************************");
}
#endif
_serviceHost = new ServiceHost(_service, new Uri(uri));
try {
_serviceHost.AddServiceEndpoint(_baseIf/*typeof(Ti)*/, new NetNamedPipeBinding(), pipeAddress);
_serviceHost.Open();
} catch (AddressAlreadyInUseException) {
Console.WriteLine("既にサービスは起動しています。");
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
}
/// <summary>
/// Sub(クライアント)側クラス
/// </summary>
public class CLT_T
{
/// <summary>
/// Sub側の初期化処理。Main側とpipeName,pipeAddressを同じにすること
/// </summary>
/// <param name="pipeName"></param>
/// <param name="pipeAddress"></param>
/// <returns></returns>
static public Ti makeCLT<Ti>(string pipeName = "PDFormstudioESorGS", string pipeAddress = "SubModuleAddress") {
const string pipeBase = "net.pipe://localhost";
var address = pipeBase + "/" + pipeName + "/" + pipeAddress;
//try {
var factory = (new ChannelFactory<Ti>(new NetNamedPipeBinding(), new EndpointAddress(address)));
Ti toMain = factory.CreateChannel();
return toMain;
//} catch (Exception e) {
// Console.WriteLine("makeCLT failed:" + e.ToString());
// return null;
//}
}
}
}
namespace Ut
{
public class Ut
{
// Util
// --------------------------------------------------------------
/// <summary>
/// 親プロセスが終了したら、自身を終了させる
/// </summary>
/// <param name="bEnable"></param>
public static void SetAutoSelfKillByMainProcEnd(bool bEnable = true) {
if (_SingletonProc != null)
return;//一度しか呼び出せない
if (bEnable == false)
return;
const string fname = "SetAutoSelfKillByMainProcEnd";
// 自動的に親のプロセスがいなくなったら自動的にキルモードを設定する
// メインプロセスの終了チェック
// 親プロセスIDでProcessハンドルを取得、そのExitedイベントに自身の終了関数を設定(Environment.Exit())
int paProcID = GetParentProcessId();
var paProc = Process.GetProcessById(paProcID);
paProc.EnableRaisingEvents = true;
//Console.WriteLine($@"{fname}:ParrentProcessName ={paProc.ProcessName}({paProcID})");
paProc.Exited += new EventHandler(
(object s, EventArgs a) => {
Console.WriteLine($@"{fname}:Exited Event!!!!");
var ss = s as Process;
Console.WriteLine($@"Sure ProcName is {ss.ProcessName}({ss.Id}) {ss.StartInfo.Arguments}"); ;
System.Threading.Thread.Sleep(10 * 1000);
ss.Close();
ss.Dispose();
Console.WriteLine($@"{fname}:Exited Event!!!!(afer10sec)");
//Environment.Exit(-1);
});
//
_SingletonProc = paProc;
}
private static System.Diagnostics.Process _SingletonProc = null;
/// <summary>
/// コマンドライン引数複数個をエンコードして、スペースで結合
/// </summary>
/// <param name="values">string[] コマンドライン引数</param>
/// <returns>コマンドライン文字列(Escaped)</returns>
public static string makeCmdLine(IEnumerable<string> args) {
if (args == null)
throw new ArgumentNullException("args");
string EscapeCmdLineArg(string v) {
if (string.IsNullOrEmpty(v)) return "";
var containsSpace = v.IndexOfAny(new[] { ' ', '\t' }) != -1;
v = ReCommandLineEscapePattern.Replace(v, @"$1\$&");//「\…\"」をエスケープ.「"」直前の「\」の数を 2倍+1
if (containsSpace) {
v = "\"" + ReLastBackSlashPattern.Replace(v, "$1$1") + "\"";
}
return v;
}
return string.Join(" ", args.Select(v => EscapeCmdLineArg(v)));
}
private static Regex ReCommandLineEscapePattern = new Regex("(\\\\*)\"");
private static Regex ReLastBackSlashPattern = new Regex(@"(\\+)$");
/// <summary>
/// 親のプロセスIDを取得する
/// </summary>
/// <returns>親ProcessID</returns>
static int GetParentProcessId() {
var myProcId = GetCurrentProcessId();
var query = string.Format($@"SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {myProcId}");
//クエリから結果を取得
using (var search = new System.Management.ManagementObjectSearcher(@"root\CIMV2", query))
using (var results = search.Get().GetEnumerator()) {
if (!results.MoveNext())
throw new ApplicationException("Couldn't Get ParrentProcessId.");
var queryResult = results.Current;
//親プロセスのPIDを取得
uint pa = (uint)(queryResult["ParentProcessId"]);
return (int)(pa);
}
}
/// <summary>
/// 自身のプロセスIDを取得する
/// </summary>
/// <returns>ProcessID</returns>
static int GetCurrentProcessId() { return Process.GetCurrentProcess().Id; }
/// <summary>
/// 外部プログラムを起動する
/// </summary>
/// <param name="cmdAndArgs">引数文字列の配列</param>
/// <param name="bSameConsole">true:コンソールを呼び出しと共有 false:別Console Windows</param>
/// <param name="bEcho">標準出力(未実装)</param>
/// <param name="bWait">コマンドが終了するまで待つ(false:未実装,常に待つ)</param>
/// <param name="nWaitLimitMSec">タイムアウト時間ms(未実装)</param>
/// <returns></returns>
public static Process DoCmd(IEnumerable<string> cmdAndArgs, bool bSameConsole = true, bool bEcho = true, bool bWait = false, int nWaitLimitMSec = -1) {
var cmdName = cmdAndArgs.First();//[0];
var argStr = makeCmdLine(cmdAndArgs.Skip(1));// 先頭を除外してコピー new ArraySegment<string>(cmdAndArgs.ToArray(), 1, cmdAndArgs.Count()-1)
//Console.WriteLine($@"DoCmd:{cmdName}:args[{argStr}]");
//using (var p = new Process()) {
var p = new Process();
{
var info = p.StartInfo;
p.StartInfo.FileName = cmdName;
p.StartInfo.Arguments = argStr;
if (bSameConsole) {
//Console.WriteLine("Same windows mode");
p.StartInfo.UseShellExecute = false; // Shell経由で実行しない(必須) true: 別ウインドウ。false:コンソールを共有
p.StartInfo.CreateNoWindow = false; // ウィンドウを作成しない
} else {
//Console.WriteLine("New windows mode");
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = true;
}
p.Start();// 実行
if (bWait == false) {
return p;
}
if (nWaitLimitMSec == -1) {
p.WaitForExit();// タイムアウト無し
} else {
bool bExit = p.WaitForExit(nWaitLimitMSec);
if (!bExit) {// 処理の終了を待つ
Console.WriteLine("処理が終了しないので強制終了");
p.Kill(); // 強制終了する
return p;
}
}
return p;
}
}
}
}
\ No newline at end of file
No preview for this file type
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup> </startup>
<runtime> </configuration>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections.Concurrent" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.EventBasedAsync" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Data.Common" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Contracts" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Debug" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.StackTrace" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Tools" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Tracing" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Dynamic.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Globalization" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Globalization.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq.Expressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq.Parallel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq.Queryable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.NetworkInformation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Requests" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Sockets" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ObjectModel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Resources.ResourceManager" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices.RuntimeInformation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Numerics" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Json" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Xml" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.3.0" newVersion="4.1.3.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Principal" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.SecureString" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Text.Encoding" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Text.Encoding.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Overlapped" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Parallel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Timer" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.ReaderWriter" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.XDocument" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.XmlSerializer" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.11.0" newVersion="4.0.11.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.XPath.XDocument" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment