diff --git a/Ink Canvas/Controls/PdfEmbeddedView.cs b/Ink Canvas/Controls/PdfEmbeddedView.cs index 6c4d21f7..5885a37a 100644 --- a/Ink Canvas/Controls/PdfEmbeddedView.cs +++ b/Ink Canvas/Controls/PdfEmbeddedView.cs @@ -102,7 +102,7 @@ namespace Ink_Canvas.Controls NotifyPageNavigationStateChanged(); try { - BitmapSource raw = await PdfWinRtHelper.RenderPageToBitmapSourceAsync(_pdfPath, pageIndex); + BitmapSource raw = await PdfDocumentRenderHelper.RenderPageToBitmapSourceAsync(_pdfPath, pageIndex); if (raw == null) return; diff --git a/Ink Canvas/Helpers/PdfDocumentRenderHelper.cs b/Ink Canvas/Helpers/PdfDocumentRenderHelper.cs new file mode 100644 index 00000000..2a42383a --- /dev/null +++ b/Ink Canvas/Helpers/PdfDocumentRenderHelper.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Media.Imaging; + +namespace Ink_Canvas.Helpers +{ + /// + /// PDF 页数与位图渲染:优先 (Windows.Data.Pdf),失败或无效结果时使用 PDFtoImage(MIT) 备用。 + /// + internal static class PdfDocumentRenderHelper + { + public static async Task GetPageCountAsync(string pdfPath) + { + uint winRt = 0; + try + { + winRt = await PdfWinRtHelper.GetPageCountAsync(pdfPath).ConfigureAwait(false); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PDF WinRT 获取页数失败,将尝试 PDFtoImage(MIT): {ex.Message}", LogHelper.LogType.Warning); + } + + if (winRt > 0) + return winRt; + + try + { + return await Task.Run(() => PdfToImageMitHelper.GetPageCount(pdfPath)).ConfigureAwait(false); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PDFtoImage(MIT) 获取页数失败: {ex.Message}", LogHelper.LogType.Warning); + return 0; + } + } + + public static async Task RenderPageToBitmapSourceAsync(string pdfPath, uint pageIndex) + { + try + { + BitmapSource win = await PdfWinRtHelper.RenderPageToBitmapSourceAsync(pdfPath, pageIndex).ConfigureAwait(false); + if (win != null) + return win; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PDF WinRT 渲染失败,将尝试 PDFtoImage(MIT): {ex.Message}", LogHelper.LogType.Warning); + } + + try + { + return await PdfToImageMitHelper.RenderPageToBitmapSourceAsync(pdfPath, pageIndex).ConfigureAwait(false); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PDFtoImage(MIT) 渲染失败: {ex.Message}", LogHelper.LogType.Warning); + return null; + } + } + } +} diff --git a/Ink Canvas/Helpers/PdfToImageMitHelper.cs b/Ink Canvas/Helpers/PdfToImageMitHelper.cs new file mode 100644 index 00000000..970e103a --- /dev/null +++ b/Ink Canvas/Helpers/PdfToImageMitHelper.cs @@ -0,0 +1,96 @@ +using PDFtoImage; +using SkiaSharp; +using System; +using System.IO; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media.Imaging; + +namespace Ink_Canvas.Helpers +{ + /// + /// 使用 NuGet「PDFtoImage」(MIT,基于 PDFium/SkiaSharp) 解析/渲染 PDF,作为 WinRT 不可用时的备用实现。 + /// + internal static class PdfToImageMitHelper + { + public static uint GetPageCount(string pdfPath) + { + if (string.IsNullOrWhiteSpace(pdfPath) || !File.Exists(pdfPath)) + return 0; + + try + { + using (var fs = new FileStream(pdfPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + int n = Conversion.GetPageCount(fs, leaveOpen: true, password: null); + return n < 0 ? 0u : (uint)n; + } + } + catch + { + return 0; + } + } + + /// + /// 在工作线程加载 PDF 页为 ,在 UI 线程编码为 WPF 。 + /// + public static async Task RenderPageToBitmapSourceAsync(string pdfPath, uint pageIndex) + { + if (string.IsNullOrWhiteSpace(pdfPath) || !File.Exists(pdfPath)) + return null; + + int page = checked((int)pageIndex); + + SKBitmap skBitmap = await Task.Run(() => + { + try + { + using (var fs = new FileStream(pdfPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + return Conversion.ToImage(fs, System.Index.FromStart(page), leaveOpen: true, password: null, options: default); + } + } + catch + { + return null; + } + }).ConfigureAwait(false); + + if (skBitmap == null) + return null; + + try + { + if (Application.Current?.Dispatcher == null) + return null; + + return await Application.Current.Dispatcher.InvokeAsync(() => EncodeSkBitmapToBitmapSource(skBitmap)); + } + finally + { + skBitmap.Dispose(); + } + } + + private static BitmapSource EncodeSkBitmapToBitmapSource(SKBitmap bitmap) + { + using (var image = SKImage.FromBitmap(bitmap)) + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + { + var ms = new MemoryStream(); + data.SaveTo(ms); + ms.Position = 0; + + var bi = new BitmapImage(); + bi.BeginInit(); + bi.StreamSource = ms; + bi.CacheOption = BitmapCacheOption.OnLoad; + bi.EndInit(); + bi.Freeze(); + ms.Dispose(); + return bi; + } + } + } +} diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj index 44764346..2607a500 100644 --- a/Ink Canvas/InkCanvasForClass.csproj +++ b/Ink Canvas/InkCanvasForClass.csproj @@ -141,6 +141,7 @@ + diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs index e91bcb73..d8856924 100644 --- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs +++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs @@ -1007,7 +1007,7 @@ namespace Ink_Canvas string newFilePath = Path.Combine(savePath, timestamp + ".pdf"); await Task.Run(() => File.Copy(filePath, newFilePath, true)); - uint pageCount = await PdfWinRtHelper.GetPageCountAsync(newFilePath); + uint pageCount = await PdfDocumentRenderHelper.GetPageCountAsync(newFilePath); if (pageCount == 0) { ShowNotification("无法打开 PDF(可能已加密、损坏或不支持)。"); @@ -1037,7 +1037,7 @@ namespace Ink_Canvas try { - uint pageCount = await PdfWinRtHelper.GetPageCountAsync(info.SourcePath); + uint pageCount = await PdfDocumentRenderHelper.GetPageCountAsync(info.SourcePath); if (pageCount == 0) return; bool compress = isLoaded && Settings.Canvas.IsCompressPicturesUploaded; diff --git a/Ink Canvas/packages.lock.json b/Ink Canvas/packages.lock.json index d96e90d0..ed98aa02 100644 --- a/Ink Canvas/packages.lock.json +++ b/Ink Canvas/packages.lock.json @@ -157,6 +157,21 @@ "Microsoft.Win32.Registry": "5.0.0" } }, + "PDFtoImage": { + "type": "Direct", + "requested": "[5.1.0, )", + "resolved": "5.1.0", + "contentHash": "QbGA2pdMCdQFI0MpXXDD7teGJsOR3ic9T8iGv3hXTtyMSjJkVgFDKRxRU9Ri31DbkkpvY7azzNeUTWdf5AqPZg==", + "dependencies": { + "SkiaSharp": "3.119.0", + "SkiaSharp.NativeAssets.Linux.NoDependencies": "3.119.0", + "SkiaSharp.NativeAssets.Win32": "3.119.0", + "SkiaSharp.NativeAssets.macOS": "3.119.0", + "bblanchon.PDFium.Linux": "137.0.7149", + "bblanchon.PDFium.Win32": "137.0.7149", + "bblanchon.PDFium.macOS": "137.0.7149" + } + }, "Sentry": { "type": "Direct", "requested": "[6.2.0, )", @@ -190,6 +205,21 @@ "resolved": "6.3.0.90", "contentHash": "WVTb5MxwGqKdeasd3nG5udlV4t6OpvkFanziwI133K0/QJ5FvZmfzRQgpAjGTJhQfIA8GP7AzKQ3sTY9JOFk8Q==" }, + "bblanchon.PDFium.Linux": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "tT2GeyKE39ci3THZ/ddzhvZ3efgfCdEsZtDfEH+kDV1dfl2hxFeZesdTcRnxj3hp/ScnxTNJNa0RZtwOwc0I3g==" + }, + "bblanchon.PDFium.macOS": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "OXZJhlJ/jynL5dz9/OJ65UFCIqZBepdyd84xE11AAWgG6UPERi2p4tEmmIAbloZ+aRBtaCRWNk5pgyF2WDe7vQ==" + }, + "bblanchon.PDFium.Win32": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "M3C2UCvKD1X6hJ/VB6arBMhI91OYHzybWMAOVFUKuEpfTR1nKfbYa/1dNVHKPmc8ST0SAYeVOlkv+XlJrsXy/Q==" + }, "Fody": { "type": "Transitive", "resolved": "6.8.2", @@ -240,6 +270,31 @@ "resolved": "4.0.0", "contentHash": "5HKzttVKWeKoDQKJd3+J7Dy1MW6gbNNYfftkVufe2ddFQD0kXjnT1IN3ZJBfF6QVEQmHpQSp+/PT7Jo2YyHFcw==" }, + "SkiaSharp": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "gR9yVoOta2Mc1Rxt15LD65AckfHMfwjIs/3kkD59C9bT2nYYISsE6uz3t4aMPNHA6CgsIL0Ssn+jE5OVilZ1yw==", + "dependencies": { + "SkiaSharp.NativeAssets.Win32": "3.119.0", + "SkiaSharp.NativeAssets.macOS": "3.119.0", + "System.Memory": "4.5.5" + } + }, + "SkiaSharp.NativeAssets.Linux.NoDependencies": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "e92vdqf1VOETPjy1T67Fs1zPxfGMM1nbrpt69GM5foXSI/iIbq6L9avPz/bl/DbWtb81D0yF/NKjRmXuOZoLcg==" + }, + "SkiaSharp.NativeAssets.macOS": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "YE1vNn0Nyw2PWtv7hw1PYkKJO0itFiQp9vSqGppZUKzQJqwp28a2jgdCMPfYtOiR8KCnDgZqQoynqJRRaE2ZVg==" + }, + "SkiaSharp.NativeAssets.Win32": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "IwC9yx36lOdXVT2DjgmWHl1qkVspfj8ctd4+li8CNnvqdfaTolXCOh6TLznURcPAvzatx9K/tLOB7zT6T8EA9w==" + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -351,6 +406,21 @@ } }, ".NETFramework,Version=v4.7.2/win": { + "bblanchon.PDFium.Linux": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "tT2GeyKE39ci3THZ/ddzhvZ3efgfCdEsZtDfEH+kDV1dfl2hxFeZesdTcRnxj3hp/ScnxTNJNa0RZtwOwc0I3g==" + }, + "bblanchon.PDFium.macOS": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "OXZJhlJ/jynL5dz9/OJ65UFCIqZBepdyd84xE11AAWgG6UPERi2p4tEmmIAbloZ+aRBtaCRWNk5pgyF2WDe7vQ==" + }, + "bblanchon.PDFium.Win32": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "M3C2UCvKD1X6hJ/VB6arBMhI91OYHzybWMAOVFUKuEpfTR1nKfbYa/1dNVHKPmc8ST0SAYeVOlkv+XlJrsXy/Q==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -360,6 +430,21 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "SkiaSharp.NativeAssets.Linux.NoDependencies": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "e92vdqf1VOETPjy1T67Fs1zPxfGMM1nbrpt69GM5foXSI/iIbq6L9avPz/bl/DbWtb81D0yF/NKjRmXuOZoLcg==" + }, + "SkiaSharp.NativeAssets.macOS": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "YE1vNn0Nyw2PWtv7hw1PYkKJO0itFiQp9vSqGppZUKzQJqwp28a2jgdCMPfYtOiR8KCnDgZqQoynqJRRaE2ZVg==" + }, + "SkiaSharp.NativeAssets.Win32": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "IwC9yx36lOdXVT2DjgmWHl1qkVspfj8ctd4+li8CNnvqdfaTolXCOh6TLznURcPAvzatx9K/tLOB7zT6T8EA9w==" + }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", "resolved": "4.3.0", @@ -380,6 +465,21 @@ } }, ".NETFramework,Version=v4.7.2/win-arm64": { + "bblanchon.PDFium.Linux": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "tT2GeyKE39ci3THZ/ddzhvZ3efgfCdEsZtDfEH+kDV1dfl2hxFeZesdTcRnxj3hp/ScnxTNJNa0RZtwOwc0I3g==" + }, + "bblanchon.PDFium.macOS": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "OXZJhlJ/jynL5dz9/OJ65UFCIqZBepdyd84xE11AAWgG6UPERi2p4tEmmIAbloZ+aRBtaCRWNk5pgyF2WDe7vQ==" + }, + "bblanchon.PDFium.Win32": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "M3C2UCvKD1X6hJ/VB6arBMhI91OYHzybWMAOVFUKuEpfTR1nKfbYa/1dNVHKPmc8ST0SAYeVOlkv+XlJrsXy/Q==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -389,6 +489,21 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "SkiaSharp.NativeAssets.Linux.NoDependencies": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "e92vdqf1VOETPjy1T67Fs1zPxfGMM1nbrpt69GM5foXSI/iIbq6L9avPz/bl/DbWtb81D0yF/NKjRmXuOZoLcg==" + }, + "SkiaSharp.NativeAssets.macOS": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "YE1vNn0Nyw2PWtv7hw1PYkKJO0itFiQp9vSqGppZUKzQJqwp28a2jgdCMPfYtOiR8KCnDgZqQoynqJRRaE2ZVg==" + }, + "SkiaSharp.NativeAssets.Win32": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "IwC9yx36lOdXVT2DjgmWHl1qkVspfj8ctd4+li8CNnvqdfaTolXCOh6TLznURcPAvzatx9K/tLOB7zT6T8EA9w==" + }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", "resolved": "4.3.0", @@ -409,6 +524,21 @@ } }, ".NETFramework,Version=v4.7.2/win-x64": { + "bblanchon.PDFium.Linux": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "tT2GeyKE39ci3THZ/ddzhvZ3efgfCdEsZtDfEH+kDV1dfl2hxFeZesdTcRnxj3hp/ScnxTNJNa0RZtwOwc0I3g==" + }, + "bblanchon.PDFium.macOS": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "OXZJhlJ/jynL5dz9/OJ65UFCIqZBepdyd84xE11AAWgG6UPERi2p4tEmmIAbloZ+aRBtaCRWNk5pgyF2WDe7vQ==" + }, + "bblanchon.PDFium.Win32": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "M3C2UCvKD1X6hJ/VB6arBMhI91OYHzybWMAOVFUKuEpfTR1nKfbYa/1dNVHKPmc8ST0SAYeVOlkv+XlJrsXy/Q==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -418,6 +548,21 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "SkiaSharp.NativeAssets.Linux.NoDependencies": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "e92vdqf1VOETPjy1T67Fs1zPxfGMM1nbrpt69GM5foXSI/iIbq6L9avPz/bl/DbWtb81D0yF/NKjRmXuOZoLcg==" + }, + "SkiaSharp.NativeAssets.macOS": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "YE1vNn0Nyw2PWtv7hw1PYkKJO0itFiQp9vSqGppZUKzQJqwp28a2jgdCMPfYtOiR8KCnDgZqQoynqJRRaE2ZVg==" + }, + "SkiaSharp.NativeAssets.Win32": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "IwC9yx36lOdXVT2DjgmWHl1qkVspfj8ctd4+li8CNnvqdfaTolXCOh6TLznURcPAvzatx9K/tLOB7zT6T8EA9w==" + }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", "resolved": "4.3.0", @@ -438,6 +583,21 @@ } }, ".NETFramework,Version=v4.7.2/win-x86": { + "bblanchon.PDFium.Linux": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "tT2GeyKE39ci3THZ/ddzhvZ3efgfCdEsZtDfEH+kDV1dfl2hxFeZesdTcRnxj3hp/ScnxTNJNa0RZtwOwc0I3g==" + }, + "bblanchon.PDFium.macOS": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "OXZJhlJ/jynL5dz9/OJ65UFCIqZBepdyd84xE11AAWgG6UPERi2p4tEmmIAbloZ+aRBtaCRWNk5pgyF2WDe7vQ==" + }, + "bblanchon.PDFium.Win32": { + "type": "Transitive", + "resolved": "137.0.7149", + "contentHash": "M3C2UCvKD1X6hJ/VB6arBMhI91OYHzybWMAOVFUKuEpfTR1nKfbYa/1dNVHKPmc8ST0SAYeVOlkv+XlJrsXy/Q==" + }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -447,6 +607,21 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "SkiaSharp.NativeAssets.Linux.NoDependencies": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "e92vdqf1VOETPjy1T67Fs1zPxfGMM1nbrpt69GM5foXSI/iIbq6L9avPz/bl/DbWtb81D0yF/NKjRmXuOZoLcg==" + }, + "SkiaSharp.NativeAssets.macOS": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "YE1vNn0Nyw2PWtv7hw1PYkKJO0itFiQp9vSqGppZUKzQJqwp28a2jgdCMPfYtOiR8KCnDgZqQoynqJRRaE2ZVg==" + }, + "SkiaSharp.NativeAssets.Win32": { + "type": "Transitive", + "resolved": "3.119.0", + "contentHash": "IwC9yx36lOdXVT2DjgmWHl1qkVspfj8ctd4+li8CNnvqdfaTolXCOh6TLznURcPAvzatx9K/tLOB7zT6T8EA9w==" + }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", "resolved": "4.3.0",