WinForms中实现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并保存替换当前的文件。

c# 复制代码
		 /// <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);`

调用代码:

C# 复制代码
		  /// <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>
相关推荐
代码AI弗森11 小时前
PDF OCR + 大模型:让文档理解不止停留在识字
pdf·ocr
小周同学:1 天前
在 Vue2 中使用 pdf.js + pdf-lib 实现 PDF 预览、手写签名、文字批注与高保真导出
开发语言·前端·javascript·vue.js·pdf
Kyln.Wu1 天前
【python实用小脚本-187】Python一键批量改PDF文字:拖进来秒出新文件——再也不用Acrobat来回导
python·pdf·c#
迪尔~3 天前
Apache POI中通过WorkBook写入图片后出现导出PDF文件时在不同页重复写入该图片问题,如何在通过sheet获取绘图对象清除该图片
java·pdf·excel
忆~遂愿3 天前
Python实战教程:PDF文档自动化编辑与图表绘制全攻略
python·pdf·自动化
Rust语言中文社区3 天前
简单好用的在线工具:轻松把图片添加到 PDF
pdf
Crazy Struggle3 天前
告别手动更新!WinForm 应用轻松集成自动升级功能 (HHUpdateApp)
c#·winform·自动更新
鲁班AI3 天前
pdf怎么转换成ppt?AI工具与传统方法深度对比
人工智能·pdf·powerpoint
大山运维3 天前
免费专业PDF文档扫描效果生成器
pdf
伊织code4 天前
pdftk - macOS 上安装使用
macos·pdf·pdftk