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
{
///
/// Rendering Condition
///
[DataContract]
public class RenderConditionParams
{
[DataMember(Order = 0)]
public double Dpi = 72.0;
[DataMember(Order = 1)]
public string BoxType = "Crop";
[DataMember(Order = 2)]
public string ImageType = "JPEG";
[DataMember(Order = 3)]
public int JpegQ = 91;
// Method non
}
// 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 wa() => await rmStrageStream.WriteAsync(ib);
var wa_ret = wa().Result;
}
async Task 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>();
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 ) **************************************************************************************
///
/// PDFのレンダリング
///
///
///
/// レンダリングの条件
///
///
///
/// 未使用,常にtrueで使用
///
///
public static int RenderPdfDoc(
string pdfPath,
string inDir = "",
RenderConditionParams pm = null,
string inPageRange = "1-*",
string inMode = "pageBitMap",
bool bSaveImage = true, //ハッシュ値のみ計算するときにfalseにする
bool bHash = false,
UtHash.HashData inHashData = null,
bool bPDFium = false
) {
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 GetStrageFilePath(string path)
=> await Windows.Storage.StorageFile.GetFileFromPathAsync(path);
var file = GetStrageFilePath(pdfPath).Result;
async Task Load(Windows.Storage.StorageFile path)
=> await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(file);
var pdfDoc = Load(file).Result;
// 2つを合わせることができそう
//async Task 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>();
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.
}
///
/// pdfのページを画像に出力する
///
/// PdfDocument
/// ページ番号
/// 出力ファイル名
/// 正常:0
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() {
// 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 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 ) **************************************************************************************
///
/// レンダリング(比較モード)
///
///
///
///
/// レンダリング条件
///
///
/// 未使用常にtrueで利用する
///
///
/// 一致した場合は0、不一致の場合は!0を返却する
public static int RenderPdfDocCompare(
string pdfPath,
string refPdfPath,
string resultDataPath,
string inDir = "",
RenderConditionParams pm = null,
string inPageRange = "1-*",
string inMode = "pageBitMap",
bool bHash = false,
UtHash.HashData inHashDataTgt = null,
UtHash.HashData inHashDataRef = null,
bool bPDFium = false
// 比較結果を返す ページ番号とメッセージ
) {
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 GetStrageFilePath(string path)
=> await Windows.Storage.StorageFile.GetFileFromPathAsync(path);
var file = GetStrageFilePath(pdfPath).Result;
async Task 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();// 比較結果を返す ページ番号とメッセージ
// 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]:.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.
}
///
/// 比較モード
///
/// PdfDocument
/// ページ番号
/// 出力ファイル名
/// 正常:0
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() {
// 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 FlushX() => await memStrm.FlushAsync();
//var dmy =FlushX().Result;
FlushX().Wait();
}
//page.Dispose();
return memStrm;//
}
///
/// ページ1から始まるページ範囲の配列を返す
///
///
///
///
private static uint[] makePageRange(string pageRange = "1", uint endPage = 100) {
var range = new SortedSet();
var vec = pageRange.Split(',');//カンマで分割
var reRange = new Regex($@"([\d]+)[\s]*\-[\s]*([\d]*|\*)");// n-m or n- or n-*
var reOne = new Regex($@"([\d]+)"); // n
bool isRange(uint x) => (0 < x && x <= endPage); // 範囲チェック
void swap(ref T a, ref T b) { T tmp = a; a = b; b = tmp; }
foreach (var s in vec) {
//範囲指定
var m = reRange.Match(s);
if (m.Success) {
uint st = uint.Parse(m.Groups[1].Value);
uint en = 0;
if (!isRange(st))
continue;//開始値が範囲外で無効
uint.TryParse(m.Groups[2].Value, out en);
if (!isRange(en))
en = endPage;// 最終値が無効なため最終ページ
if (st > en) swap(ref st, ref en);
// 範囲登録
foreach (var g in Enumerable.Range((int)st, (int)(en - st + 1)))
range.Add((uint)g);
continue;
}
//ページ指定
m = reOne.Match(s);
if (m.Success) {
var n = uint.Parse(m.Groups[1].Value);
if (isRange(n))
range.Add(n);
}
}
return range.ToArray();
}
public static string GetHashValue(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();
}
}
///
/// ファイルのハッシュ値のIO
///
namespace UtHash
// https://qiita.com/Akasaki/items/dee137b24aea4b7e2bcb
// http://mokake.hatenablog.com/entry/2017/09/28/234433
// https://lifetime-engineer.com/csharp-create-json-indent/
// DataMemberでOrderを指定することで順序を確定。
// JsonReaderWriterFactory.CreateJsonWriterでindent=trueにすることでjsonの可視性向上
{
[DataContract]
public class HashHead
{
public bool Dirty = false; // 書き換えられたらTrue 保存対象外
[DataMember(Order = 0)]
public string HashBaseName; // 保存時にベース名を設定する
[DataMember(Order=1)]
public string HashFilePath;
[DataMember(Order=2)]
public string Dpi;
[DataMember(Order=3)]
public string Box;
[DataMember(Order=4)]
public string ImType;
[DataMember(Order=5)]
public string JQ;
// メソッド
public HashHead() {
SetRenderInfo("0", "Bx", "IMx", "xx");
}
public HashHead SetRenderInfo(string dpi, string box, string imType, string jq) {
Dpi = dpi; Box = box; ImType = imType; JQ = jq;
return this;
}
public string GetRenderInfoStr() { return $@"{Dpi}_{Box}_{ImType}_{JQ}"; }
}
[DataContract]
public class HashFile
{
[DataMember(Order=0)]
public DateTime UpdateTime; // ファイルの更新日
[DataMember(Order=1)]
public string UpdateTimeStr; // ファイルの更新日(Json目視用)
[DataMember(Order=2)]
public List PageHashCode = new List(); // ページ毎のハッシュ値の配列
// メソッド
public string GetHashValue(int i) {
// 範囲チェック
if ( !(0 <= i && i < PageHashCode.Count) ){
return "";
}
return PageHashCode[i];
}
}
[DataContract]
public class HashData
{
[DataMember(Order=0)]
public HashHead Head = new HashHead();
[DataMember(Order=1)]
public SortedDictionary Files = new SortedDictionary();
// メソッド
static public string GetHashFileName(string baseName, string dpi, string box, string imType, string jq) {
var h = new HashHead();
var f = baseName + "_" + h.SetRenderInfo(dpi, box, imType, jq).GetRenderInfoStr() + ".json";
return f;
}
static public HashData load(string pdfPath/*or Dir*/, string baseName, string dpi, string box, string imType, string jq, ref string otPath) {
var hashPath = "";
if (Path.GetExtension(pdfPath).ToLower() == ".pdf") {
var paPath = Directory.GetParent(Path.GetFullPath(pdfPath)).ToString();
var hashFName = GetHashFileName(baseName, dpi, box, imType, jq);
hashPath = Path.Combine(paPath, hashFName);
} else if (File.GetAttributes(pdfPath).HasFlag(FileAttributes.Directory)) {
var hashFName = GetHashFileName(baseName, dpi, box, imType, jq);
hashPath = Path.Combine(pdfPath, hashFName);
}
//Console.WriteLine($@"hashPath={hashPath}");
if (otPath != null) {
otPath = hashPath;
}
var hd = HashData.load(hashPath); //ファイルの存在はこっちに任す
return hd;
}
static public HashData load(string hashPath) {
if (!File.Exists(hashPath)) {
echo("not find load hash data file");
return null;
}
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HashData));
using (var rd = new FileStream(hashPath, FileMode.Open,FileAccess.Read,FileShare.ReadWrite)) {
var a = (HashData)serializer.ReadObject(rd);
a.Head.Dirty = false;// clear
return a;
}
}
static public HashData loadFromString(string s) {
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(HashData));
using (var rd = new MemoryStream( Encoding.UTF8.GetBytes(s) )) {
var a = (HashData)serializer.ReadObject(rd);
a.Head.Dirty = false;// clear
return a;
}
}
public override string ToString() {
//文字列に書き出す
using (var m = new MemoryStream())
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(m, Encoding.UTF8, true, true, " ")) {
var serializer = new DataContractJsonSerializer(typeof(HashData));
serializer.WriteObject(writer, this);
var x= Encoding.UTF8.GetString(m.ToArray());// 文字列に変換
return x;
}
}
public bool save(string path) {
// JSONに変換するデータを作る。
// 再保存の抑制
//this.Head.Dirtyがfalseなら更新されていないことになる。
//またかつ保存パスが同じ this.Head.HashFilePath = path;
echo($@"HashData.save:{path}");
this.Head.HashFilePath = Path.GetFullPath(path);
this.Head.HashBaseName = this.Head.GetRenderInfoStr();
try {
using (var fs = new FileStream(path, FileMode.Create))
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(fs, Encoding.UTF8, true, true, " ")) {
var serializer = new DataContractJsonSerializer(typeof(HashData));
serializer.WriteObject(writer, this);
return true;
}
} catch (Exception e) {
Console.WriteLine($@"error Save json anything error:{e}");
return false;
}
}
public bool SetFile(string pathPDF, int pageNum) {
var fi = new FileInfo(pathPDF);
var fName = fi.Name;
HashFile hf;
if (!Files.ContainsKey(fName)) {
this.Head.Dirty = true;// 変更あり
hf = new HashFile() {
UpdateTime = fi.LastWriteTimeUtc,
UpdateTimeStr= fi.LastWriteTime.ToString(),
PageHashCode = new List(pageNum)
};
// Console.WriteLine($@"update={hf.UpdateTime}");
for (var i = 0; i < pageNum; i++) {
hf.PageHashCode.Add("");
}
Files.Add(fName,hf);
} else {
// 既に存在、pageNumがかわらないことだけチェック
var pageNumOrg = Files[fName].PageHashCode.Count;
if (Files[fName].UpdateTime.ToString() != fi.LastWriteTimeUtc.ToString()) {
// 日付が一致しなければ更新(ToStringで比較しないとだめです
this.Head.Dirty = true;// 変更あり
Files[fName].UpdateTime = fi.LastWriteTimeUtc;
Files[fName].UpdateTimeStr = fi.LastWriteTime.ToString();
Files[fName].PageHashCode = new List(pageNum);
for (var i = 0; i < pageNum; i++) {
Files[fName].PageHashCode.Add("");
}
}
// ありえないけど、同一日付でページ数が異なる
if (pageNumOrg != pageNum) {
Console.WriteLine($@"Mismatch pageNum:setP:{pageNum},DbP:{Files[fName].PageHashCode.Count}");
this.Head.Dirty = true;// 変更あり
return false; // Page数が一致しない
}
}
return true;
}
public bool AddHashCode(string pathPDF, int pageNo/*1開始*/, string hashCode) {
//Console.WriteLine("call addHashCode:" + pathPDF + ":" + pageNo);
var fi = new FileInfo(pathPDF);
var fName = fi.Name;
var n = pageNo - 1;//0始まり
if (!Files.ContainsKey(fName)) {
this.Head.Dirty = true;// 変更あり
return false;
}
if (Files[fName].PageHashCode[n] != hashCode) {
this.Head.Dirty = true;// 変更あり
Files[fName].PageHashCode[n] = hashCode;
}
return true;
}
public string GetHashCode(string pathPDF, int pageNo/*1開始*/) {
var fi = new FileInfo(pathPDF);
var fName = fi.Name;
var n = pageNo - 1;//0始まり
if (!Files.ContainsKey(fName)) {
return "";
}
var v = Files[fName].PageHashCode;
return (n < v.Count) ? v[n] : "";
}
}
}
namespace UtImage // Image保存のユーティリティー
{
// ref:https://water2litter.net/rum/post/cs_pdf_wpf/
//using EncType = System.Drawing.Imaging.Encoder;// 別名
//using EncParamType = System.Drawing.Imaging.EncoderParameter;//別名
///
/// BitMapのSaveに使うImage Encodeパラメータの設定値の生成と 画像保存
/// var enc = new UtImage.Enc("jpeg");
/// //enc.SetImageType("PNG"); // 後で変更できる
/// //enc.JJpegQuality = 100; JpegのQuarty変更
/// enc.SaveImage(BitMap,path);
/// // enc.SaveImage(BitMap,path,"PNG");
///
///
public class Enc
{
// SEE:https://dobon.net/vb/dotnet/graphics/encoderparameters.html
public Enc(string imageType = "jpeg") { SetImageType(imageType); }
public long JpegQuality { get; set; } = 91;// 指定しないときの値は91と一致する
private string ImageType = "jpeg";
public Enc SetImageType(string imageType) {
imageType = imageType.ToLower();
if (imageType == "jpeg" || imageType == "jpg") {
ImageType = "jpeg";
} else if (imageType == "png") {
ImageType = "png";
} else if (imageType == "tif" || imageType == "tiff") {
ImageType = "tiff";
} else {
ImageType = imageType;// 小文字を設定
}
return this;
}
public int SaveImage(System.Drawing.Bitmap bitmap, string path, string imageType = null) {
var p = GetParams();
string imType = imageType?.ToLower();
if (p != null) {
bitmap.Save(path, GetInfo(imType), GetParams());// imageTypeがnullならSetImageType()のものが使われる
} else {// パラメータが空の場合
var imageFormat = new System.Drawing.Imaging.ImageFormat(GetInfo(imType).FormatID);
bitmap.Save(path, imageFormat);
}
return 0;
}
///
/// サポートするEncodeパラメータ一覧
/// 現状Errorが発生して取得できない
///
/// 対象のBitMap
public static void GetSupportedParameters(System.Drawing.Bitmap bitmap1 = null) {
// https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/advanced/how-to-determine-the-parameters-supported-by-an-encoder
try {
if (bitmap1 == null) {
bitmap1 = new System.Drawing.Bitmap(100, 100);
var destBitmapData = bitmap1.LockBits(
new System.Drawing.Rectangle(0, 0, 100, 100),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
bitmap1.PixelFormat
);
bitmap1.UnlockBits(destBitmapData);
}
var jpgEncoder = GetEncoderInfo(System.Drawing.Imaging.ImageFormat.Tiff);
var paramList = bitmap1.GetEncoderParameterList(jpgEncoder.Clsid);
var encParams = paramList.Param;
for (int i = 0; i < encParams.Length; i++) {
Console.WriteLine("Param " + i + " holds " + encParams[i].NumberOfValues +
" items of type " +
encParams[i].ValueType + "\r\n" + "Guid category: " + encParams[i].Encoder.Guid + "\r\n");
}
} catch (Exception e) {
Console.WriteLine($"Ignore error ={e.ToString()}");
}
}
// inner method.
private System.Drawing.Imaging.EncoderParameters GetParams() {
var encList = new List();
//encList.Clear();
// 今はJPEGパラメータだけ
encList.Add(new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, JpegQuality));
//...
// 結合
var eps = new System.Drawing.Imaging.EncoderParameters(encList.Count);
for (var i = 0; i < encList.Count; i++)
eps.Param[i] = encList[i];
return eps;
}
private System.Drawing.Imaging.ImageCodecInfo GetInfo(string it = null) {
if (it == null) {// SetImageType()で指定したものを使う
it = ImageType;
}
return GetEncoderInfo($"image/{it}");
}
//ImageFormatで指定されたImageCodecInfoを探して返す
private static System.Drawing.Imaging.ImageCodecInfo GetEncoderInfo(System.Drawing.Imaging.ImageFormat f) {
var encs = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
foreach (var enc in encs) {
if (enc.FormatID == f.Guid)
return enc;
}
return null;
}
private static System.Drawing.Imaging.ImageCodecInfo GetEncoderInfo(string mineType) {
var encs = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
//指定されたMimeTypeを探して見つかれば返す
foreach (System.Drawing.Imaging.ImageCodecInfo enc in encs) {
if (enc.MimeType == mineType)
return enc;
}
return null;
}
}
}
}