WinForm中实现Adobe PDF Reader实现旋转PDF功能

发布于:2025-06-13 ⋅ 阅读:(18) ⋅ 点赞:(0)

实现效果:

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

<span style="color:#333333"><span style="background-color:#ffffff"><code class="language-c language-c#">		 <span style="color:#008000">/// <summary></span>
        <span style="color:#008000">/// 旋转保存PDF文件并释放文件锁定</span>
        <span style="color:#008000">/// </summary></span>
        <span style="color:#008000">/// <param name="axControl"></param></span>
        <span style="color:#008000">/// <param name="filePath"></param></span>
        <span style="color:#008000">/// <param name="pdfRotation"></param></span>
        <span style="color:#008000">/// <returns></returns>    </span>
public <span style="color:#a31515">bool</span> <span style="color:#a31515">SafeSavePdfWithRelease</span>(AxAcroPDFLib.AxAcroPDF axControl, <span style="color:#0000ff">string</span> filePath, PdfRotation pdfRotation)
    {
        <span style="color:#a31515">const</span> <span style="color:#a31515">int</span> MAX_RETRY = <span style="color:#880000">3</span>;
        <span style="color:#a31515">const</span> <span style="color:#a31515">int</span> RETRY_DELAY = <span style="color:#880000">500</span>;

        <span style="color:#0000ff">for</span> (<span style="color:#a31515">int</span> attempt = <span style="color:#880000">0</span>; attempt < MAX_RETRY; attempt++)
        {
            try
            {
                <span style="color:#008000">// 步骤1:创建临时副本</span>
                <span style="color:#0000ff">string</span> tempPath = Path.GetTempFileName().Replace(<span style="color:#a31515">".tmp"</span>, <span style="color:#a31515">".pdf"</span>);
                File.Copy(filePath, tempPath, <span style="color:#a31515">true</span>);

                <span style="color:#008000">// 步骤2:使用内存流操作</span>
                using (var ms = new MemoryStream(File.ReadAllBytes(tempPath)))
                using (var document = PdfiumViewer.PdfDocument.Load(ms))
                {
                    <span style="color:#0000ff">for</span> (<span style="color:#a31515">int</span> pageIndex = <span style="color:#880000">0</span>; pageIndex < document.PageCount; pageIndex++)
                    {
                        document.RotatePage(pageIndex, pdfRotation);

                        <span style="color:#008000">// 可选:验证旋转结果</span>
                        <span style="color:#008000">// var currentRotation = document.Pages[pageIndex].Rotation;</span>
                        <span style="color:#008000">// Debug.Assert(currentRotation == (int)rotation);</span>
                    }
                    <span style="color:#008000">// 执行修改操作(示例:旋转第一页)</span>
                    <span style="color:#008000">//document.RotatePage(1, PdfRotation.Rotate90);</span>

                    <span style="color:#008000">// 步骤3:保存到临时文件</span>
                    byte[] pdfBytes;
                    using (var outputStream = new MemoryStream())
                    {
                        document.Save(outputStream);
                        pdfBytes = outputStream.ToArray();
                    }

                    <span style="color:#008000">// 步骤4:强制释放文件锁定</span>
                    ForceReleasePdfFile(axControl, filePath);

                    <span style="color:#008000">// 步骤5:原子替换文件</span>
                    File.WriteAllBytes(tempPath, pdfBytes);
                    <span style="color:#008000">// File.Replace(tempPath, filePath, null, true);</span>

                    <span style="color:#008000">// 1. 复制替换文件到目标路径</span>
                    File.Copy(tempPath, filePath, overwrite: <span style="color:#a31515">true</span>);

                    <span style="color:#008000">// 2. 删除临时文件(可选)</span>
                    File.Delete(tempPath);

                    <span style="color:#008000">// 步骤6:验证加载</span>
                    axControl.LoadFile(filePath);
                    <span style="color:#0000ff">return</span> <span style="color:#a31515">true</span>;
                }
            }
            catch (IOException ex) when (ex.HResult == <span style="color:#880000">-2147024864</span>)
            {
                <span style="color:#0000ff">if</span> (attempt == MAX_RETRY - <span style="color:#880000">1</span>) throw;
                Thread.Sleep(RETRY_DELAY);
            }
        }
        <span style="color:#0000ff">return</span> <span style="color:#a31515">false</span>;
    }
    public <span style="color:#a31515">void</span> <span style="color:#a31515">ForceReleasePdfFile</span>(AxAcroPDFLib.AxAcroPDF axControl, <span style="color:#0000ff">string</span> filePath)
    {
        <span style="color:#008000">// 步骤1:深度释放COM对象</span>
        ReleaseComObject(axControl);

        <span style="color:#008000">// 步骤2:内核级文件解锁</span>
        UnlockFileHandle(filePath);

        <span style="color:#008000">// 步骤3:延迟重载验证</span>
        Thread.Sleep(<span style="color:#880000">200</span>);
        axControl.LoadFile(filePath);
    }

    private <span style="color:#a31515">void</span> <span style="color:#a31515">ReleaseComObject</span>(AxAcroPDFLib.AxAcroPDF axControl)
    {
        try
        {
            <span style="color:#008000">// 显式释放ActiveX资源</span>
            <span style="color:#0000ff">if</span> (axControl.IsDisposed) <span style="color:#0000ff">return</span>;

            <span style="color:#008000">// 反射调用内部释放方法</span>
            var type = axControl.GetType();
            var method = type.GetMethod(<span style="color:#a31515">"ReleaseOCX"</span>, BindingFlags.Instance | BindingFlags.NonPublic);
            method?.Invoke(axControl, null);

            <span style="color:#008000">// 强制垃圾回收</span>
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
        catch (Exception ex)
        {

        }
    }
    <span style="color:#008000">// 修改后的P/Invoke声明</span>
    [DllImport(<span style="color:#a31515">"kernel32.dll"</span>, SetLastError = <span style="color:#a31515">true</span>, CharSet = CharSet.Auto)]
    private <span style="color:#a31515">static</span> <span style="color:#0000ff">extern</span> IntPtr <span style="color:#a31515">CreateFile</span>(
        <span style="color:#0000ff">string</span> lpFileName,
        uint dwDesiredAccess,
        uint dwShareMode,
        IntPtr lpSecurityAttributes,
        FileMode dwCreationDisposition,  <span style="color:#008000">// 改用.NET枚举</span>
        FileAttributes dwFlagsAndAttributes,  <span style="color:#008000">// 改用.NET枚举</span>
        IntPtr hTemplateFile);

    <span style="color:#008000">// 修改后的UnlockFileHandle方法</span>
    private <span style="color:#a31515">void</span> <span style="color:#a31515">UnlockFileHandle</span>(<span style="color:#0000ff">string</span> filePath)
    {
        <span style="color:#a31515">const</span> uint FILE_SHARE_READ = <span style="color:#880000">0x00000001</span>;
        <span style="color:#a31515">const</span> uint FILE_SHARE_WRITE = <span style="color:#880000">0x00000002</span>;
        <span style="color:#a31515">const</span> uint GENERIC_READ = <span style="color:#880000">0x80000000</span>;

        IntPtr hFile = CreateFile(
            filePath,
            GENERIC_READ,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            IntPtr.Zero,
            FileMode.Open,  <span style="color:#008000">// 对应原生OPEN_EXISTING</span>
            FileAttributes.Normal,  <span style="color:#008000">// 对应原生FILE_ATTRIBUTE_NORMAL</span>
            IntPtr.Zero);

        <span style="color:#0000ff">if</span> (hFile != IntPtr.Zero && hFile != new IntPtr(<span style="color:#880000">-1</span>))
        {
            CloseHandle(hFile);
        }
    }

    [DllImport(<span style="color:#a31515">"kernel32.dll"</span>, SetLastError = <span style="color:#a31515">true</span>)]
    [<span style="color:#0000ff">return</span>: MarshalAs(UnmanagedType.Bool)]
    private <span style="color:#a31515">static</span> <span style="color:#0000ff">extern</span> <span style="color:#a31515">bool</span> CloseHandle(IntPtr hObject);`
</code></span></span>

调用代码:

<span style="color:#333333"><span style="background-color:#ffffff"><code class="language-C language-C#">		  <span style="color:#008000">/// <summary></span>
        <span style="color:#008000">/// 当前旋转角度</span>
        <span style="color:#008000">/// </summary></span>
        public <span style="color:#a31515">static</span> <span style="color:#a31515">int</span> currentRotation = <span style="color:#880000">0</span>;

        <span style="color:#008000">/// <summary></span>
        <span style="color:#008000">/// 逆时针旋转</span>
        <span style="color:#008000">/// </summary></span>
        <span style="color:#008000">/// <param name="sender"></param></span>
        <span style="color:#008000">/// <param name="e"></param></span>
        private <span style="color:#a31515">void</span> <span style="color:#a31515">pictureEdit3_Click</span>(object sender, EventArgs e)
        {
            <span style="color:#0000ff">if</span> (axAcroPDF1.Visible)
            {
                currentRotation -= <span style="color:#880000">90</span>; 

                PdfRotation pdfRotation = GetCounterClockwiseRotation(currentRotation);
                 
                var path = axAcroPDF1.src;
                <span style="color:#008000">//调用旋转PDF保存方法</span>
                SafeSavePdfWithRelease(axAcroPDF1, path,pdfRotation); 
                axAcroPDF1.LoadFile(path);
                axAcroPDF1.setView(<span style="color:#a31515">"Fit"</span>); <span style="color:#008000">//适应窗口大小</span>
            }
        }

        <span style="color:#008000">/// <summary></span>
        <span style="color:#008000">/// 顺时针旋转</span>
        <span style="color:#008000">/// </summary></span>
        <span style="color:#008000">/// <param name="sender"></param></span>
        <span style="color:#008000">/// <param name="e"></param></span>
        private <span style="color:#a31515">void</span> <span style="color:#a31515">pictureEdit2_Click</span>(object sender, EventArgs e)
        {
            <span style="color:#0000ff">if</span> (axAcroPDF1.Visible)
            {
                currentRotation += <span style="color:#880000">90</span>; 

                PdfRotation pdfRotation = GetCounterClockwiseRotation(currentRotation);

                var path = axAcroPDF1.src;
                <span style="color:#008000">//调用旋转PDF保存方法</span>
                SafeSavePdfWithRelease(axAcroPDF1, path, pdfRotation);

                axAcroPDF1.LoadFile(path);
                axAcroPDF1.setView(<span style="color:#a31515">"Fit"</span>); <span style="color:#008000">//适应窗口大小 </span>
            }
        }


        <span style="color:#008000">/// <summary></span>
        <span style="color:#008000">/// 通过旋转度数计算旋转的角度</span>
        <span style="color:#008000">/// </summary></span>
        <span style="color:#008000">/// <param name="counterClockwiseDegrees">当前旋转角度</param></span>
        public <span style="color:#a31515">static</span> PdfRotation <span style="color:#a31515">GetCounterClockwiseRotation</span>(<span style="color:#a31515">int</span> counterClockwiseDegrees)
        {
            <span style="color:#a31515">const</span> <span style="color:#a31515">int</span> fullCircle = <span style="color:#880000">360</span>;
            <span style="color:#a31515">int</span> effectiveDegrees = counterClockwiseDegrees % fullCircle;

            <span style="color:#0000ff">if</span> (effectiveDegrees < <span style="color:#880000">0</span>) effectiveDegrees += fullCircle; <span style="color:#008000">// 处理负角度</span>

            <span style="color:#0000ff">if</span> (currentRotation >= <span style="color:#880000">360</span>) 
            {
                currentRotation = <span style="color:#880000">0</span>;
            }
            <span style="color:#0000ff">if</span> (currentRotation <= <span style="color:#880000">-360</span>) 
            {
                currentRotation = <span style="color:#880000">0</span>;
            }

            <span style="color:#0000ff">switch</span> (effectiveDegrees)
            {
                <span style="color:#0000ff">case</span> <span style="color:#880000">90</span>:
                    <span style="color:#0000ff">return</span> PdfRotation.Rotate90; 
                <span style="color:#0000ff">case</span> <span style="color:#880000">180</span>:
                    <span style="color:#0000ff">return</span> PdfRotation.Rotate180;
                <span style="color:#0000ff">case</span> <span style="color:#880000">270</span>:
                    <span style="color:#0000ff">return</span> PdfRotation.Rotate270;
                <span style="color:#0000ff">case</span> <span style="color:#880000">0</span>:
                <span style="color:#0000ff">default</span>:
                    <span style="color:#0000ff">return</span> PdfRotation.Rotate0;
            }
        }
        <span style="color:#008000">/// <summary></span>
</code></span></span>