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 Windows.Data.Pdf; using System.Reflection; // Assembly. #if COMMENT 重要: デスクトップやコンソールアプリで、UWP APIを使う(PDF関連を含む) https://codezine.jp/article/detail/10654 ■追補:Windows 10 1803以降の場合   以降で説明しているUwpDesktopではなく、Windows 10 WinRT API Packを使います。 これは2019年9月にリリースされたものです。 ■セットアップ 15063以降では、最小限必要なファイルは以下の2つです。 System.Runtime.WindowsRuntime.dll C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\{.NETバージョン}\ Windows.WinMD C:\Program Files (x86)\Windows Kits\10\UnionMetadata\{SDKバージョン}\ ■WinMDファイルをビルドに含めないようにする #endif // ref:https://water2litter.net/rum/post/cs_pdf_wpf/ namespace CSRender { class Program { static void dispHelp() { var pgName = Path.GetFileName(Path.GetFileName(Assembly.GetExecutingAssembly().Location));// プログラム名 string msg = $"{pgName} / \n" +$"* Render of PDF file\n" +$" /F ; pdfファイル名。/Fは省略可能。明示的な指定" +"\n"; Console.Write(msg); } static int Main(string[] args) { var pdfPath=""; //static async Task Main(string[] args) {// おまじない awaitが書けるようになる C# 7.1からじゃないと書けない var qu = new Queue(args); // 引数をQueに登録 (qu.Enqueue(a)でも可能) while (qu.Count > 0) { // 引数のパース qu.Dequeue()で次の要素を取得する var wd = qu.Dequeue(); // 取り出し var wdi = wd.ToUpper(); // 大文字で比較 if (wdi == "/?" || wdi == "/H") { dispHelp(); return -1; } else if (wdi == "/F") { pdfPath = qu.Dequeue();// next word. } else if (wdi == "/M") { mode = qu.Dequeue();// next word. } else { // 引数をファイル名として取得する if (pdfPath == "") { pdfPath = wd; } } } if (pdfPath=="") { Console.WriteLine("pdfファイルが指定されてません"); dispHelp(); return -1; } if ( !File.Exists(pdfPath) ) { Console.WriteLine($"ファイルが存在しません:{pdfPath}"); return -1; } var watch = System.Diagnostics.Stopwatch.StartNew(); // 生成と計測開始を同時に行う Task t = renderPDF(Path.GetFullPath(pdfPath)); int ret = t.Result;// スレッドの終了まで「待つ」 // asyncメソッド内では await呼び出しができる。ここはasyncではない。 watch.Stop(); Console.WriteLine( $"main end result={ret},time={ watch.ElapsedMilliseconds / 1000.0 } sec,ret={ret}"); //Console.WriteLine("stop"); //Console.ReadLine(); return ret;//sucess. } //https://csharp.hotexamples.com/examples/-/PdfPageRenderOptions/-/php-pdfpagerenderoptions-class-examples.html#0x66958305801896177d9d64dbfb6e64ebd307d702f05b801316d258b19a8d91d6-70,,84, private static async Task RenderPage(Windows.Data.Pdf.PdfPage page, string otPath, Windows.Storage.StorageFolder output) { var pagePath = string.Format("{0}.png", page.Index); //var pageFile = await output.CreateFileAsync(pagePath, Windows.Storage.CreationCollisionOption.ReplaceExisting); var dir = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(Directory.GetParent(otPath).FullName); // var imgFile = await dir.CreateFileAsync(Path.GetFileName(otPath), Windows.Storage.CreationCollisionOption.ReplaceExisting); Console.WriteLine($"task PageNum={page.Index},Tread={Thread.CurrentThread.ManagedThreadId}"); var th = Thread.CurrentThread.ManagedThreadId; //using (var imageStream = await pageFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite)) using (var memStrm = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { //var imageStream = await pageFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite); //var memStrm = new Windows.Storage.Streams.InMemoryRandomAccessStream(); //Console.WriteLine($"[start render{th}]"); //await page.RenderToStreamAsync(imageStream); //await imageStream.FlushAsync(); await page.RenderToStreamAsync(memStrm); await memStrm.FlushAsync(); //Console.WriteLine($"[end render:{th}]"); { BitmapImage image = new BitmapImage(); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; //Console.Write("[AsStrem ST]"); image.StreamSource = memStrm.AsStream(); //Console.Write("[AsStrem ED]"); image.EndInit(); BitmapEncoder enc = new JpegBitmapEncoder(); if (enc == null) { Console.WriteLine("encode取得失敗"); return "NG"; } enc.Frames.Add(BitmapFrame.Create(image)); //var p = Path.Combine(otDir, $"{otBaseName}.{nPage + 1}.jpg"); //var p = Path.ChangeExtension(pageFile.Path,"jpg");// otPath; Console.WriteLine("ot=" + otPath); using (FileStream fs = new FileStream(otPath, System.IO.FileMode.Create)) { enc.Save(fs); } } //imageStream.Dispose(); memStrm.Dispose(); } Console.WriteLine($"save end:{pagePath}"); return output.Path + pagePath; } // これが並列がだめなもの public static async Task renderPageNG(PdfPage page, string otPath) { Console.WriteLine($"task PageNum={page.Index},Tread={Thread.CurrentThread.ManagedThreadId}"); //PdfPage page = doc.GetPage((uint)nPage);// using しない /*using (PdfPage page = doc.GetPage( (uint)nPage) )*/ { BitmapImage image = new BitmapImage(); using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { Console.Write("[start stream}"); //Console.Write("[GetPage ST]"); //PdfPage page = doc.GetPage((uint)nPage); //Console.Write("[GetPage EN]"); Console.Write("[Rendersync ST]"); await page.RenderToStreamAsync(stream); Console.Write("[Rendersync EN]"); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; Console.Write("[AsStrem ST]"); image.StreamSource = stream.AsStream(); Console.Write("[AsStrem ED]"); image.EndInit(); // xamlへの返却 imgPdf.Source = image; // 画像BitmapImageの画像への保存 https://yoshiiz.blog.fc2.com/blog-entry-818.html // JPEG Q https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level // https://water2litter.net/gin/?p=979 BitmapEncoder enc = new JpegBitmapEncoder(); if (enc == null) { Console.WriteLine("encode取得失敗"); return "NG"; } enc.Frames.Add(BitmapFrame.Create(image)); //var p = Path.Combine(otDir, $"{otBaseName}.{nPage + 1}.jpg"); var p = otPath; Console.WriteLine("ot=" + p); using (FileStream fs = new FileStream(p, System.IO.FileMode.Create)) { enc.Save(fs); } } } return "SUCCESS"; } private static async Task RenderPageOrg(Windows.Data.Pdf.PdfPage page, Windows.Storage.StorageFolder output) { var pagePath = string.Format("{0}.jpeg", page.Index); var pageFile = await output.CreateFileAsync(pagePath, Windows.Storage.CreationCollisionOption.ReplaceExisting); Console.WriteLine($"task PageNum={page.Index},Tread={Thread.CurrentThread.ManagedThreadId}"); using (var imageStream = await pageFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite)) { await page.RenderToStreamAsync(imageStream); PdfPageRenderOptions pdfPageRenderOptions = new PdfPageRenderOptions(); pdfPageRenderOptions.BitmapEncoderId = Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId;// JPEG pdfPageRenderOptions.IsIgnoringHighContrast = false; await page.RenderToStreamAsync(imageStream, pdfPageRenderOptions); await imageStream.FlushAsync(); } Console.WriteLine("save end" + pagePath); return output.Path + pagePath; } public static string mode = ""; public static async Task renderPDF (string pdfPath) { var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(pdfPath); var otDir = ""; var otBaseName=Path.GetFileName(pdfPath);//*.pdf { // 出力ディレクトリの作成 // 元PDFの同一フォルダにIMGフォルダを作成する otDir = Path.Combine(Directory.GetParent(pdfPath).FullName,"IMG"); if ( !Directory.Exists(otDir)) { Directory.CreateDirectory(otDir); }; } // PDFファイルを読み込む var pdfDoc = await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(file); if (pdfDoc == null) { Console.WriteLine("pdfDoc取得失敗"); return -1; } var n = pdfDoc.PageCount; if ( mode == "file" ) { Console.WriteLine("mode=file"); var taskList = new List>(); for ( int i = 0; i < n; i++ ) { taskList.Add( renderFILE(pdfPath, i,Path.Combine(otDir, $"{otBaseName}.{i + 1}.jpg")) ); //renderPage(pdfDoc, i, Path.Combine(otDir, $"{otBaseName}.{i + 1}.jpg")); //var ret = await renderPage(pdfDoc, i,Path.Combine(otDir, $"{otBaseName}.{i + 1}.jpg")); } //int[] arrayInt = await Task.WhenAll(taskList);// すべてのタスクを待つ。 } else if ( mode == "page" ) { Console.WriteLine("mode=page"); var taskList = new List>(); for (int i = 0; i < n; i++) { taskList.Add(renderPage(pdfDoc, pdfDoc.GetPage((uint)i),i, Path.Combine(otDir, $"{otBaseName}.{i + 1}.jpg"))); // docを渡す } //int[] arrayInt = await Task.WhenAll(taskList);// すべてのタスクを待つ。 } else if (mode == "spl") { var dir = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(otDir); //private static async Task RenderPage(Windows.Data.Pdf.PdfPage page, Windows.Storage.StorageFolder output) Console.WriteLine("mode=spl"); var taskList = new List>(); for (int i = 0; i < n; i++) { taskList.Add(RenderPage(pdfDoc.GetPage((uint)i), Path.Combine(otDir, $"{otBaseName}.{i + 1}.jpg"), dir)); // docを渡す } //int[] arrayInt = await Task.WhenAll(taskList);// すべてのタスクを待つ。 } else if (mode == "org") { // 並列で動くユイツのもの var dir = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(otDir); //private static async Task RenderPage(Windows.Data.Pdf.PdfPage page, Windows.Storage.StorageFolder output) Console.WriteLine("mode=org"); var taskList = new List>(); for (int i = 0; i < n; i++) { taskList.Add(RenderPageOrg(pdfDoc.GetPage((uint)i), dir)); // docを渡す } //int[] arrayInt = await Task.WhenAll(taskList);// すべてのタスクを待つ。 } else { Console.WriteLine("mode=none"); // スレッド無し for (int i = 0; i < n; i++) {// 1ページ目を読み込む using (Windows.Data.Pdf.PdfPage page = pdfDoc.GetPage((uint)i)) { BitmapImage image = new BitmapImage(); using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { await page.RenderToStreamAsync(stream); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = stream.AsStream(); image.EndInit(); BitmapEncoder enc = new JpegBitmapEncoder(); if (enc == null) { Console.WriteLine("encode取得失敗"); return -2; } enc.Frames.Add(BitmapFrame.Create(image)); var p = Path.Combine(otDir, $"{otBaseName}.{i + 1}.jpg"); Console.WriteLine("ot=" + p); using (FileStream fs = new FileStream(p, System.IO.FileMode.Create)) { enc.Save(fs); } } } } } /* readner option sample: https://csharp.hotexamples.com/examples/-/PdfPageRenderOptions/-/php-pdfpagerenderoptions-class-examples.html for ( int i = 0; i < n; i++ ) {// 1ページ目を読み込む using (Windows.Data.Pdf.PdfPage page = pdfDoc.GetPage((uint)i)) { BitmapImage image = new BitmapImage(); using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { await page.RenderToStreamAsync(stream); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = stream.AsStream(); image.EndInit(); // xamlへの返却 imgPdf.Source = image; // 画像BitmapImageの画像への保存 https://yoshiiz.blog.fc2.com/blog-entry-818.html // JPEG Q https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level // https://water2litter.net/gin/?p=979 // このソースじゃないかな? https://gogowaten.hatenablog.com/entry/2020/01/02/125352 BitmapEncoder enc = new JpegBitmapEncoder(); if ( enc == null ) { Console.WriteLine("encode取得失敗");return -2; } enc.Frames.Add(BitmapFrame.Create(image)); var p = Path.Combine(otDir, $"{otBaseName}.{i+1}.jpg"); Console.WriteLine("ot="+p); using (FileStream fs = new FileStream(p, System.IO.FileMode.Create)) { enc.Save(fs); } } } } */ return 0;// success. } /// /// pdfのページを画像に出力する /// /// PdfDocument /// ページ番号 /// 出力ファイル名 /// 正常:0 public static async Task renderPage(PdfDocument doc, PdfPage page,int nPage, string otPath ) { //おまじない このダミーのawait入れないとスレッドにならない var dir = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(Directory.GetParent(otPath).FullName); //Console.WriteLine("これをかくとスレッド1 " + Thread.CurrentThread.ManagedThreadId); //Console.WriteLine($"task PageNum={nPage},Tread={Thread.CurrentThread.ManagedThreadId}"); var th = Thread.CurrentThread.ManagedThreadId; // //PdfPage page = doc.GetPage((uint)nPage);// using しない /*using (PdfPage page = doc.GetPage( (uint)nPage) )*/ { using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { //Console.Write("[start stream}"); //Console.Write("[GetPage ST]"); //PdfPage page = doc.GetPage((uint)nPage); //Console.Write("[GetPage EN]"); //Console.Write("[Rendersync ST]"); await page.RenderToStreamAsync(stream); //Console.Write("[Rendersync EN]"); { BitmapImage image = new BitmapImage(); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; //Console.Write("[AsStrem ST]"); image.StreamSource = stream.AsStream(); //Console.Write("[AsStrem ED]"); image.EndInit(); // xamlへの返却 imgPdf.Source = image; // 画像BitmapImageの画像への保存 https://yoshiiz.blog.fc2.com/blog-entry-818.html // JPEG Q https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level // https://water2litter.net/gin/?p=979 BitmapEncoder enc = new JpegBitmapEncoder(); if (enc == null) { Console.WriteLine("encode取得失敗"); return -2; } enc.Frames.Add(BitmapFrame.Create(image)); //var p = Path.Combine(otDir, $"{otBaseName}.{nPage + 1}.jpg"); var p = otPath; Console.WriteLine($"ot[{Thread.CurrentThread.ManagedThreadId}]=" + p ); using (FileStream fs = new FileStream(p, System.IO.FileMode.Create)) { enc.Save(fs); } } } } return 0;//success } public static async Task renderFILE(string inPath, int nPage, string otPath) { // ファイルから開いてみる var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(inPath); var doc = await Windows.Data.Pdf.PdfDocument.LoadFromFileAsync(file); if (doc == null) { Console.WriteLine("pdfDoc取得失敗 in renderFile"); return -1; } Console.WriteLine($"task PageNum={nPage},Tread={Thread.CurrentThread.ManagedThreadId}"); using (PdfPage page = doc.GetPage((uint)nPage)) { BitmapImage image = new BitmapImage(); using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream()) { await page.RenderToStreamAsync(stream); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = stream.AsStream(); image.EndInit(); // xamlへの返却 imgPdf.Source = image; // 画像BitmapImageの画像への保存 https://yoshiiz.blog.fc2.com/blog-entry-818.html // JPEG Q https://docs.microsoft.com/ja-jp/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level // https://water2litter.net/gin/?p=979 BitmapEncoder enc = new JpegBitmapEncoder(); if (enc == null) { Console.WriteLine("encode取得失敗"); return -2; } enc.Frames.Add(BitmapFrame.Create(image)); //var p = Path.Combine(otDir, $"{otBaseName}.{nPage + 1}.jpg"); var p = otPath; Console.WriteLine("ot=" + p); using (FileStream fs = new FileStream(p, System.IO.FileMode.Create)) { enc.Save(fs); } } } return 0;//success } } } /* Tips: スレッド内のSleep Thread.Sleep(4560); // 何か重い処理をしている... await Task.... */ /* - メモ c# acync await https://qiita.com/rawr/items/5d49960a4e4d3823722f https://qiita.com/4_mio_11/items/f9b19c04509328b1e5c1 https://tech-lab.sios.jp/archives/15711 http://www.ifelse.jp/blog/csharp-01 async定義内で重い処理を直接記載せずに await Task.Run(() => {} で呼び出す。重い通常関数を呼び出すとメインスレッドで動いてしまう(だめなわけじゃない) */