实现效果:

问题点: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>