From 2f4cb33cca33196b9e6afe8e3b7f5affb831f7e9 Mon Sep 17 00:00:00 2001 From: Ben Olden-Cooligan Date: Fri, 12 Apr 2019 20:29:05 -0400 Subject: [PATCH] Pdfium renderer impl (untested) --- NAPS2.Lib.Common/Modules/CommonModule.cs | 2 +- NAPS2.Sdk/Images/Storage/GdiConverters.cs | 4 +- .../Pdf/GhostscriptPdfRenderer.cs | 5 +- NAPS2.Sdk/ImportExport/Pdf/IPdfRenderer.cs | 4 +- .../ImportExport/Pdf/PdfiumNativeMethods.cs | 64 +++++++++++++++++++ .../ImportExport/Pdf/PdfiumPdfRenderer.cs | 58 +++++++++++++++++ NAPS2.Sdk/NAPS2.Sdk.csproj | 2 + 7 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 NAPS2.Sdk/ImportExport/Pdf/PdfiumNativeMethods.cs create mode 100644 NAPS2.Sdk/ImportExport/Pdf/PdfiumPdfRenderer.cs diff --git a/NAPS2.Lib.Common/Modules/CommonModule.cs b/NAPS2.Lib.Common/Modules/CommonModule.cs index bcf08c3e4..05306b1b9 100644 --- a/NAPS2.Lib.Common/Modules/CommonModule.cs +++ b/NAPS2.Lib.Common/Modules/CommonModule.cs @@ -34,7 +34,7 @@ namespace NAPS2.Modules Bind().To(); Bind().To(); Bind().To(); - Bind().To().InSingletonScope(); + Bind().To().InSingletonScope(); // Export Bind().To(); diff --git a/NAPS2.Sdk/Images/Storage/GdiConverters.cs b/NAPS2.Sdk/Images/Storage/GdiConverters.cs index 5d21d200c..27af467ec 100644 --- a/NAPS2.Sdk/Images/Storage/GdiConverters.cs +++ b/NAPS2.Sdk/Images/Storage/GdiConverters.cs @@ -36,8 +36,8 @@ namespace NAPS2.Images.Storage // Then we can have a PDF->Image converter that returns null if it's not a pdf file. if (IsPdfFile(input)) { - var renderer = new GhostscriptPdfRenderer(null); - return new GdiImage(renderer.Render(input.FullPath).Single()); + var renderer = new PdfiumPdfRenderer(); + return (GdiImage)renderer.Render(input.FullPath).Single(); } else { diff --git a/NAPS2.Sdk/ImportExport/Pdf/GhostscriptPdfRenderer.cs b/NAPS2.Sdk/ImportExport/Pdf/GhostscriptPdfRenderer.cs index c9bacc761..2b69086d1 100644 --- a/NAPS2.Sdk/ImportExport/Pdf/GhostscriptPdfRenderer.cs +++ b/NAPS2.Sdk/ImportExport/Pdf/GhostscriptPdfRenderer.cs @@ -6,6 +6,7 @@ using System.Linq; using Ghostscript.NET.Rasterizer; using NAPS2.Config.Experimental; using NAPS2.Dependencies; +using NAPS2.Images.Storage; using NAPS2.Lang.Resources; using NAPS2.Scan; using NAPS2.Util; @@ -24,7 +25,7 @@ namespace NAPS2.ImportExport.Pdf this.configProvider = configProvider; } - public IEnumerable Render(string path) + public IEnumerable Render(string path) { // TODO // ThrowIfCantRender(); @@ -53,7 +54,7 @@ namespace NAPS2.ImportExport.Pdf { var bitmap = (Bitmap)rasterizer.GetPage(dpi, dpi, pageNumber); bitmap.SafeSetResolution(dpi, dpi); - yield return bitmap; + yield return new GdiImage(bitmap); } } } diff --git a/NAPS2.Sdk/ImportExport/Pdf/IPdfRenderer.cs b/NAPS2.Sdk/ImportExport/Pdf/IPdfRenderer.cs index 8c48ab8b5..b73719f0f 100644 --- a/NAPS2.Sdk/ImportExport/Pdf/IPdfRenderer.cs +++ b/NAPS2.Sdk/ImportExport/Pdf/IPdfRenderer.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; -using System.Drawing; using System.Linq; using NAPS2.Dependencies; +using NAPS2.Images.Storage; namespace NAPS2.ImportExport.Pdf { public interface IPdfRenderer { - IEnumerable Render(string path); + IEnumerable Render(string path); void ThrowIfCantRender(); void PromptToInstallIfNeeded(IComponentInstallPrompt componentInstallPrompt); } diff --git a/NAPS2.Sdk/ImportExport/Pdf/PdfiumNativeMethods.cs b/NAPS2.Sdk/ImportExport/Pdf/PdfiumNativeMethods.cs new file mode 100644 index 000000000..d89171edc --- /dev/null +++ b/NAPS2.Sdk/ImportExport/Pdf/PdfiumNativeMethods.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using NAPS2.Platform; +using NAPS2.Util; + +namespace NAPS2.ImportExport.Pdf +{ + public class PdfiumNativeMethods + { + public const int FPDFBitmap_BGR = 2; + public const int FPDFBitmap_BGRA = 4; + + public const int FPDF_PRINTING = 0x800; + public const int FPDF_REVERSE_BYTE_ORDER = 0x10; + + static PdfiumNativeMethods() + { + if (PlatformCompat.System.CanUseWin32) + { + string libDir = Environment.Is64BitProcess ? "_win64" : "_win32"; + var location = Assembly.GetExecutingAssembly().Location; + var coreDllDir = System.IO.Path.GetDirectoryName(location); + if (coreDllDir != null) + { + Win32.SetDllDirectory(System.IO.Path.Combine(coreDllDir, libDir)); + } + } + } + + [DllImport("pdfium.dll")] + public static extern IntPtr FPDFBitmap_Create(int width, int height, int alpha); + + [DllImport("pdfium.dll")] + public static extern IntPtr FPDFBitmap_CreateEx(int width, int height, int format, IntPtr firstScan, int stride); + + [DllImport("pdfium.dll")] + public static extern void FPDFBitmap_Destroy(IntPtr bitmap); + + [DllImport("pdfium.dll")] + public static extern IntPtr FPDF_LoadDocument(string filePath, string password); + + [DllImport("pdfium.dll")] + public static extern IntPtr FPDF_LoadMemDocument(IntPtr buffer, int size, string password); + + [DllImport("pdfium.dll")] + public static extern int FPDF_GetPageCount(IntPtr document); + + [DllImport("pdfium.dll")] + public static extern IntPtr FPDF_LoadPage(IntPtr document, int pageIndex); + + [DllImport("pdfium.dll")] + public static extern double FPDF_GetPageWidth(IntPtr page); + + [DllImport("pdfium.dll")] + public static extern double FPDF_GetPageHeight(IntPtr page); + + [DllImport("pdfium.dll")] + public static extern void FPDF_RenderPageBitmap(IntPtr bitmap, IntPtr page, int startX, int startY, int sizeX, int sizeY, int rotate, int flags); + + } +} diff --git a/NAPS2.Sdk/ImportExport/Pdf/PdfiumPdfRenderer.cs b/NAPS2.Sdk/ImportExport/Pdf/PdfiumPdfRenderer.cs new file mode 100644 index 000000000..6f69b64b6 --- /dev/null +++ b/NAPS2.Sdk/ImportExport/Pdf/PdfiumPdfRenderer.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NAPS2.Dependencies; +using NAPS2.Images.Storage; +using NAPS2.Scan; + +namespace NAPS2.ImportExport.Pdf +{ + public class PdfiumPdfRenderer : IPdfRenderer + { + private const int RENDER_FLAGS = PdfiumNativeMethods.FPDF_PRINTING | PdfiumNativeMethods.FPDF_REVERSE_BYTE_ORDER; + + public IEnumerable Render(string path) + { + // TODO: Maybe allow this to be configured + int dpi = ScanDpi.Dpi300.ToIntDpi(); + + var doc = PdfiumNativeMethods.FPDF_LoadDocument(path, null); + var pageCount = PdfiumNativeMethods.FPDF_GetPageCount(doc); + + for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) + { + var page = PdfiumNativeMethods.FPDF_LoadPage(doc, pageIndex); + var widthInInches = PdfiumNativeMethods.FPDF_GetPageWidth(page) * 72; + var heightInInches = PdfiumNativeMethods.FPDF_GetPageWidth(page) * 72; + int widthInPx = (int)Math.Round(widthInInches * dpi); + int heightInPx = (int)Math.Round(heightInInches * dpi); + + // Cap the resolution to 10k pixels in each dimension + dpi = Math.Min(dpi, (int)(10000 / heightInInches)); + dpi = Math.Min(dpi, (int)(10000 / widthInInches)); + + var bitmap = StorageManager.ImageFactory.FromDimensions(widthInPx, heightInPx, StoragePixelFormat.RGB24); + bitmap.SetResolution(dpi, dpi); + var bitmapData = bitmap.Lock(LockMode.ReadWrite, out var scan0, out var stride); + try + { + var pdfiumBitmap = PdfiumNativeMethods.FPDFBitmap_CreateEx(widthInPx, heightInPx, PdfiumNativeMethods.FPDFBitmap_BGR, scan0, stride); + PdfiumNativeMethods.FPDF_RenderPageBitmap(pdfiumBitmap, page, 0, 0, widthInPx, heightInPx, 0, RENDER_FLAGS); + yield return bitmap; + } + finally + { + bitmap.Unlock(bitmapData); + } + } + } + + public void PromptToInstallIfNeeded(IComponentInstallPrompt componentInstallPrompt) + { + } + + public void ThrowIfCantRender() + { + } + } +} diff --git a/NAPS2.Sdk/NAPS2.Sdk.csproj b/NAPS2.Sdk/NAPS2.Sdk.csproj index d739d8fab..c61c2669c 100644 --- a/NAPS2.Sdk/NAPS2.Sdk.csproj +++ b/NAPS2.Sdk/NAPS2.Sdk.csproj @@ -206,6 +206,8 @@ + +