diff --git a/QuickLook.Common b/QuickLook.Common index 44448c9..ef280ae 160000 --- a/QuickLook.Common +++ b/QuickLook.Common @@ -1 +1 @@ -Subproject commit 44448c9f28dc826db4c15e3e1b0b352b743b0885 +Subproject commit ef280ae2a568a2d41023c3747e6e1d91e88ed6c6 diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/AsyncPageToThumbnailConverter.cs b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/AsyncPageToThumbnailConverter.cs new file mode 100644 index 0000000..08552a4 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/AsyncPageToThumbnailConverter.cs @@ -0,0 +1,62 @@ +// Copyright © 2018 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Globalization; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Windows.Media.Imaging; + +namespace QuickLook.Plugin.PDFViewer +{ + internal class AsyncPageToThumbnailConverter : IMultiValueConverter + { + private static readonly BitmapImage Loading = + new BitmapImage( + new Uri("pack://application:,,,/QuickLook.Plugin.PdfViewer;component/Resources/loading.png")); + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Length < 2) + throw new Exception("PageIdToImageConverter"); + + if (!(values[0] is PdfDocumentWrapper handle)) return null; + + var pageId = (int) values[1]; + if (pageId < 0) return null; + + var task = Task.Run(() => + { + try + { + return handle.RenderThumbnail(pageId); + } + catch (Exception) + { + return Loading; + } + }); + + return new NotifyTaskCompletion(task, Loading); + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/NotifyTaskCompletion.cs b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/NotifyTaskCompletion.cs new file mode 100644 index 0000000..e9e8d1f --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/NotifyTaskCompletion.cs @@ -0,0 +1,89 @@ +// Copyright © 2018 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace QuickLook.Plugin.PDFViewer +{ + public sealed class NotifyTaskCompletion : INotifyPropertyChanged + { + private readonly TResult _loading; + + public NotifyTaskCompletion(Task task, TResult loading = default(TResult)) + { + Task = task; + _loading = loading; + if (!task.IsCompleted) + { + var _ = WatchTaskAsync(task); + } + } + + public Task Task { get; } + public TResult Result => Task.Status == TaskStatus.RanToCompletion ? Task.Result : _loading; + public TaskStatus Status => Task.Status; + public bool IsCompleted => Task.IsCompleted; + public bool IsNotCompleted => !Task.IsCompleted; + public bool IsSuccessfullyCompleted => Task.Status == + TaskStatus.RanToCompletion; + public bool IsCanceled => Task.IsCanceled; + public bool IsFaulted => Task.IsFaulted; + public AggregateException Exception => Task.Exception; + public Exception InnerException => Exception?.InnerException; + public string ErrorMessage => InnerException?.Message; + public event PropertyChangedEventHandler PropertyChanged; + + private async Task WatchTaskAsync(Task task) + { + try + { + await task; + } + catch + { + // ignored + } + + var propertyChanged = PropertyChanged; + if (propertyChanged == null) + return; + propertyChanged(this, new PropertyChangedEventArgs("Status")); + propertyChanged(this, new PropertyChangedEventArgs("IsCompleted")); + propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted")); + if (task.IsCanceled) + { + propertyChanged(this, new PropertyChangedEventArgs("IsCanceled")); + } + else if (task.IsFaulted) + { + propertyChanged(this, new PropertyChangedEventArgs("IsFaulted")); + propertyChanged(this, new PropertyChangedEventArgs("Exception")); + propertyChanged(this, + new PropertyChangedEventArgs("InnerException")); + propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage")); + } + else + { + propertyChanged(this, + new PropertyChangedEventArgs("IsSuccessfullyCompleted")); + propertyChanged(this, new PropertyChangedEventArgs("Result")); + } + } + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfDocumentWrapper.cs b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfDocumentWrapper.cs new file mode 100644 index 0000000..ca8d446 --- /dev/null +++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfDocumentWrapper.cs @@ -0,0 +1,58 @@ +// Copyright © 2018 Paddy Xu +// +// This file is part of QuickLook program. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.IO; +using PdfiumViewer; + +namespace QuickLook.Plugin.PDFViewer +{ + public class PdfDocumentWrapper : IDisposable + { + public PdfDocumentWrapper(Stream stream) + { + PdfStream = new MemoryStream((int) stream.Length); + stream.CopyTo(PdfStream); + + PdfDocument = PdfDocument.Load(PdfStream); + } + + public PdfDocument PdfDocument { get; private set; } + + public MemoryStream PdfStream { get; private set; } + + public void Dispose() + { + PdfDocument.Dispose(); + PdfDocument = null; + PdfStream.Dispose(); + PdfStream = null; + } + + public void Refresh() + { + var oldD = PdfDocument; + + PdfStream.Position = 0; + var newObj = new PdfDocumentWrapper(PdfStream); + PdfDocument = newObj.PdfDocument; + PdfStream = newObj.PdfStream; + + oldD.Dispose(); + } + } +} \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfPageExtension.cs b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfPageExtension.cs index e1b1d79..a65203d 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfPageExtension.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfPageExtension.cs @@ -26,27 +26,45 @@ namespace QuickLook.Plugin.PDFViewer { internal static class PdfPageExtension { - public static BitmapSource RenderThumbnail(this PdfDocument doc, int page) + private static int _renderCount; + private static readonly object LockObj = new object(); + + public static BitmapSource RenderThumbnail(this PdfDocumentWrapper doc, int page) { - var size = doc.PageSizes[page]; - var factorX = 130d / size.Width; - var factorY = 210d / size.Height; + lock (LockObj) + { + if (_renderCount++ == 50) + { + doc.Refresh(); + _renderCount = 0; + } + } + + var size = doc.PdfDocument.PageSizes[page]; + var factorX = 60d / size.Width; + var factorY = 120d / size.Height; return doc.Render(page, Math.Min(factorX, factorY), false); } - public static BitmapSource Render(this PdfDocument doc, int page, double factor, bool fixDpi = true) + public static BitmapSource Render(this PdfDocumentWrapper doc, int page, double factor, bool fixDpi = true) { - var size = doc.PageSizes[page]; var scale = DpiHelper.GetCurrentScaleFactor(); var dpiX = fixDpi ? scale.Horizontal * DpiHelper.DefaultDpi : 96; var dpiY = fixDpi ? scale.Vertical * DpiHelper.DefaultDpi : 96; - var realWidth = (int) Math.Round(size.Width * scale.Horizontal * factor); - var realHeight = (int) Math.Round(size.Height * scale.Vertical * factor); + Bitmap bitmap; - var bitmap = doc.Render(page, realWidth, realHeight, dpiX, dpiY, - PdfRenderFlags.LimitImageCacheSize | PdfRenderFlags.LcdText | PdfRenderFlags.Annotations|PdfRenderFlags.ForPrinting) as Bitmap; + lock (LockObj) + { + var size = doc.PdfDocument.PageSizes[page]; + var realWidth = (int) Math.Round(size.Width * scale.Horizontal * factor); + var realHeight = (int) Math.Round(size.Height * scale.Vertical * factor); + + bitmap = doc.PdfDocument.Render(page, realWidth, realHeight, dpiX, dpiY, + PdfRenderFlags.LimitImageCacheSize | PdfRenderFlags.LcdText | PdfRenderFlags.Annotations | + PdfRenderFlags.ForPrinting) as Bitmap; + } var bs = bitmap?.ToBitmapSource(); bitmap?.Dispose(); diff --git a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml index 4a5488c..f249174 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml +++ b/QuickLook.Plugin/QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml @@ -12,6 +12,7 @@ d:DesignWidth="720.29"> + @@ -23,17 +24,20 @@ - - @@ -45,8 +49,15 @@ - - + + + + + + + + +