Files
community/Ink Canvas/Controls/PdfEmbeddedView.cs
T
2026-04-05 18:42:06 +08:00

146 lines
4.8 KiB
C#

using Ink_Canvas.Helpers;
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Ink_Canvas.Controls
{
/// <summary>
/// 画布上的多页 PDF:仅显示当前页;翻页与页码由主窗口 PDF 侧栏控制(无 XAML 文件)。
/// </summary>
public class PdfEmbeddedView : UserControl
{
private readonly Image _pageImage;
private string _pdfPath;
private uint _pageCount;
private uint _currentIndex;
private bool _compressLargePictures;
private bool _isPagingBusy;
private bool _layoutSizeCommitted;
/// <summary>页码或可翻页状态变化(用于更新侧栏)。</summary>
public event EventHandler PageNavigationStateChanged;
public PdfEmbeddedView()
{
MinWidth = 80;
MinHeight = 60;
var grid = new Grid { ClipToBounds = true };
_pageImage = new Image
{
Stretch = Stretch.Uniform,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
grid.Children.Add(_pageImage);
Content = grid;
}
/// <summary>
/// 初始化并显示指定页;由 MainWindow 在 UI 线程创建后调用。
/// </summary>
/// <param name="initialPageIndex">从 0 开始的页码,超出范围时夹紧到合法区间。</param>
public async Task InitializeAsync(string pdfFilePath, uint pageCount, bool compressLargePictures, uint initialPageIndex = 0)
{
_pdfPath = pdfFilePath ?? throw new ArgumentNullException(nameof(pdfFilePath));
_pageCount = pageCount;
_compressLargePictures = compressLargePictures;
if (_pageCount == 0)
_currentIndex = 0;
else
_currentIndex = initialPageIndex >= _pageCount ? _pageCount - 1 : initialPageIndex;
await ShowPageAsync(_currentIndex);
}
public string PdfPath => _pdfPath;
public uint PageCount => _pageCount;
public uint CurrentPageIndex => _currentIndex;
public string PageLabelText => _pageCount == 0 ? "" : $"{_currentIndex + 1} / {_pageCount}";
public bool CanGoPrevious => !_isPagingBusy && _pageCount > 1 && _currentIndex > 0;
public bool CanGoNext => !_isPagingBusy && _pageCount > 1 && _currentIndex < _pageCount - 1;
public async Task GoToPreviousPageAsync()
{
await GoRelativeAsync(-1);
}
public async Task GoToNextPageAsync()
{
await GoRelativeAsync(1);
}
private void NotifyPageNavigationStateChanged()
{
PageNavigationStateChanged?.Invoke(this, EventArgs.Empty);
}
private async Task GoRelativeAsync(int delta)
{
if (_isPagingBusy || _pageCount <= 1)
return;
int next = (int)_currentIndex + delta;
if (next < 0 || next >= _pageCount)
return;
_currentIndex = (uint)next;
await ShowPageAsync(_currentIndex);
}
private async Task ShowPageAsync(uint pageIndex)
{
_isPagingBusy = true;
NotifyPageNavigationStateChanged();
try
{
BitmapSource raw = await PdfDocumentRenderHelper.RenderPageToBitmapSourceAsync(_pdfPath, pageIndex);
if (raw == null)
return;
BitmapSource display = ApplyCompressionIfNeeded(raw);
_pageImage.Source = display;
if (!_layoutSizeCommitted)
{
bool callerSized = !double.IsNaN(Width) && Width > 0 && !double.IsNaN(Height) && Height > 0;
if (!callerSized)
{
Width = display.PixelWidth;
Height = display.PixelHeight;
}
_layoutSizeCommitted = true;
}
}
finally
{
_isPagingBusy = false;
NotifyPageNavigationStateChanged();
}
}
private BitmapSource ApplyCompressionIfNeeded(BitmapSource rendered)
{
int width = rendered.PixelWidth;
int height = rendered.PixelHeight;
if (_compressLargePictures && (width > 1920 || height > 1080))
{
double scaleX = 1920.0 / width;
double scaleY = 1080.0 / height;
double scale = Math.Min(scaleX, scaleY);
return new TransformedBitmap(rendered, new ScaleTransform(scale, scale));
}
return rendered;
}
}
}