WinForm中实现Adobe PDF Reader实现旋转PDF功能
实现效果:
问题点:Adobe PDF Reader中并没有可以直接旋转的方法
LoadFile | 加载文件,文件URL地址 |
---|---|
GotoFirstPage | 到第一页 |
GotoLastPage | 到最后一页 |
GotoPreviousPage | 上一页 |
GotoNextPape | 下一页 |
SetCurrentpage | 到指定页 |
Setshowscrollbars | 设置是否显示 Acrobat Reader的滚动条。带一个参数,该参数设为0时不显示滚动条,设为1时显示滚动条 |
SetshowToolbar | 设置是否显示 Acrobat Reader的工具栏。带一个参数,该参数设为时不显示,设为1时显示。 |
Setview | 设置显示效果。Fit:适应窗口大小; FitH:适合宽度 |
setZoom | 设置文件的显示比例;默认是100 |
解决办法:引入PdfiumViewer旋转PDF并保存替换当前的文件。
/// <summary>
/// 旋转保存PDF文件并释放文件锁定
/// </summary>
/// <param name="axControl"></param>
/// <param name="filePath"></param>
/// <param name="pdfRotation"></param>
/// <returns></returns>
public bool SafeSavePdfWithRelease(AxAcroPDFLib.AxAcroPDF axControl, string filePath, PdfRotation pdfRotation)
{
const int MAX_RETRY = 3;
const int RETRY_DELAY = 500;
for (int attempt = 0; attempt < MAX_RETRY; attempt++)
{
try
{
// 步骤1:创建临时副本
string tempPath = Path.GetTempFileName().Replace(".tmp", ".pdf");
File.Copy(filePath, tempPath, true);
// 步骤2:使用内存流操作
using (var ms = new MemoryStream(File.ReadAllBytes(tempPath)))
using (var document = PdfiumViewer.PdfDocument.Load(ms))
{
for (int pageIndex = 0; pageIndex < document.PageCount; pageIndex++)
{
document.RotatePage(pageIndex, pdfRotation);
// 可选:验证旋转结果
// var currentRotation = document.Pages[pageIndex].Rotation;
// Debug.Assert(currentRotation == (int)rotation);
}
// 执行修改操作(示例:旋转第一页)
//document.RotatePage(1, PdfRotation.Rotate90);
// 步骤3:保存到临时文件
byte[] pdfBytes;
using (var outputStream = new MemoryStream())
{
document.Save(outputStream);
pdfBytes = outputStream.ToArray();
}
// 步骤4:强制释放文件锁定
ForceReleasePdfFile(axControl, filePath);
// 步骤5:原子替换文件
File.WriteAllBytes(tempPath, pdfBytes);
// File.Replace(tempPath, filePath, null, true);
// 1. 复制替换文件到目标路径
File.Copy(tempPath, filePath, overwrite: true);
// 2. 删除临时文件(可选)
File.Delete(tempPath);
// 步骤6:验证加载
axControl.LoadFile(filePath);
return true;
}
}
catch (IOException ex) when (ex.HResult == -2147024864)
{
if (attempt == MAX_RETRY - 1) throw;
Thread.Sleep(RETRY_DELAY);
}
}
return false;
}
public void ForceReleasePdfFile(AxAcroPDFLib.AxAcroPDF axControl, string filePath)
{
// 步骤1:深度释放COM对象
ReleaseComObject(axControl);
// 步骤2:内核级文件解锁
UnlockFileHandle(filePath);
// 步骤3:延迟重载验证
Thread.Sleep(200);
axControl.LoadFile(filePath);
}
private void ReleaseComObject(AxAcroPDFLib.AxAcroPDF axControl)
{
try
{
// 显式释放ActiveX资源
if (axControl.IsDisposed) return;
// 反射调用内部释放方法
var type = axControl.GetType();
var method = type.GetMethod("ReleaseOCX", BindingFlags.Instance | BindingFlags.NonPublic);
method?.Invoke(axControl, null);
// 强制垃圾回收
GC.Collect();
GC.WaitForPendingFinalizers();
}
catch (Exception ex)
{
}
}
// 修改后的P/Invoke声明
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
FileMode dwCreationDisposition, // 改用.NET枚举
FileAttributes dwFlagsAndAttributes, // 改用.NET枚举
IntPtr hTemplateFile);
// 修改后的UnlockFileHandle方法
private void UnlockFileHandle(string filePath)
{
const uint FILE_SHARE_READ = 0x00000001;
const uint FILE_SHARE_WRITE = 0x00000002;
const uint GENERIC_READ = 0x80000000;
IntPtr hFile = CreateFile(
filePath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
FileMode.Open, // 对应原生OPEN_EXISTING
FileAttributes.Normal, // 对应原生FILE_ATTRIBUTE_NORMAL
IntPtr.Zero);
if (hFile != IntPtr.Zero && hFile != new IntPtr(-1))
{
CloseHandle(hFile);
}
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);`
调用代码:
/// <summary>
/// 当前旋转角度
/// </summary>
public static int currentRotation = 0;
/// <summary>
/// 逆时针旋转
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureEdit3_Click(object sender, EventArgs e)
{
if (axAcroPDF1.Visible)
{
currentRotation -= 90;
PdfRotation pdfRotation = GetCounterClockwiseRotation(currentRotation);
var path = axAcroPDF1.src;
//调用旋转PDF保存方法
SafeSavePdfWithRelease(axAcroPDF1, path,pdfRotation);
axAcroPDF1.LoadFile(path);
axAcroPDF1.setView("Fit"); //适应窗口大小
}
}
/// <summary>
/// 顺时针旋转
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void pictureEdit2_Click(object sender, EventArgs e)
{
if (axAcroPDF1.Visible)
{
currentRotation += 90;
PdfRotation pdfRotation = GetCounterClockwiseRotation(currentRotation);
var path = axAcroPDF1.src;
//调用旋转PDF保存方法
SafeSavePdfWithRelease(axAcroPDF1, path, pdfRotation);
axAcroPDF1.LoadFile(path);
axAcroPDF1.setView("Fit"); //适应窗口大小
}
}
/// <summary>
/// 通过旋转度数计算旋转的角度
/// </summary>
/// <param name="counterClockwiseDegrees">当前旋转角度</param>
public static PdfRotation GetCounterClockwiseRotation(int counterClockwiseDegrees)
{
const int fullCircle = 360;
int effectiveDegrees = counterClockwiseDegrees % fullCircle;
if (effectiveDegrees < 0) effectiveDegrees += fullCircle; // 处理负角度
if (currentRotation >= 360)
{
currentRotation = 0;
}
if (currentRotation <= -360)
{
currentRotation = 0;
}
switch (effectiveDegrees)
{
case 90:
return PdfRotation.Rotate90;
case 180:
return PdfRotation.Rotate180;
case 270:
return PdfRotation.Rotate270;
case 0:
default:
return PdfRotation.Rotate0;
}
}
/// <summary>