تبدیل صفحات یک فایل PDF به تصویر، با استفاده از Acrobat SDK
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: چهار دقیقه

با استفاده از اشیاء Com همراه با Acrobat SDK می‌توان تمام صفحات یک فایل PDF را تبدیل به تصویر کرد. این SDK به همراه نگارش کامل Adobe Acrobat نیز بر روی سیستم نصب می‌شود و یا می‌توان آن‌را به صورت جداگانه از سایت Adobe دریافت کرد.
پس از آن، برای تبدیل صفحات یک فایل PDF به تصویر، مراحل زیر باید طی شود:
الف) وهله سازی از شیء AcroExch.PDDoc
در صورتیکه SDK یاد شده بر روی سیستم نصب نباشد، این وهله سازی با شکست مواجه خواهد شد و همچنین باید دقت داشت که این SDK به همراه نگارش رایگان Adobe reader ارائه نمی‌شود.
ب) گشودن فایل PDF به کمک شیء Com وهله سازی شده (pdfDoc.Open)
ج) دریافت اطلاعات صفحه مورد نظر (pdfDoc.AcquirePage)
د) کپی این اطلاعات به درون clipboard ویندوز (pdfPage.CopyToClipboard)
به این ترتیب به یک تصویر Bmp قرار گرفته شده در clipboard ویندوز خواهیم رسید
ه) مرحله بعد تغییر ابعاد و ذخیره سازی این تصویر نهایی است.

کدهای زیر، روش انجام این مراحل را بیان می‌کنند:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using Acrobat; //Add a Com Object ref. to "Adobe Acrobat 10.0 Type Library" => Program Files\Adobe\Acrobat 10.0\Acrobat\acrobat.tlb
using Microsoft.Win32;

namespace PdfThumbnail.Lib
{
    public static class PdfToImage
    {
        const string AdobeObjectsErrorMessage = "Failed to create the PDF object.";
        const string BadFileErrorMessage = "Failed to open the PDF file.";
        const string ClipboardError = "Failed to get the image from clipboard.";
        const string SdkError = "This operation needs the Acrobat SDK(http://www.adobe.com/devnet/acrobat/downloads.html), which is combined with the full version of Adobe Acrobat.";

        public static byte[] PdfPageToPng(string pdfFilePath, int thumbWidth = 600, int thumbHeight = 750, int pageNumber = 0)
        {
            byte[] imageData = null;
            runJob((pdfDoc, pdfRect) =>
                {
                    imageData = pdfPageToPng(thumbWidth, thumbHeight, pageNumber, pdfDoc, pdfRect);
                }, pdfFilePath);
            return imageData;
        }

        public static void AllPdfPagesToPng(Action<byte[], int, int> dataCallback, string pdfFilePath, int thumbWidth = 600, int thumbHeight = 750)
        {
            runJob((pdfDoc, pdfRect) =>
                {
                    var numPages = pdfDoc.GetNumPages();
                    for (var pageNumber = 0; pageNumber < numPages; pageNumber++)
                    {
                        var imageData = pdfPageToPng(thumbWidth, thumbHeight, pageNumber, pdfDoc, pdfRect);
                        dataCallback(imageData, pageNumber + 1, numPages);
                    }
                }, pdfFilePath);
        }

        static void runJob(Action<CAcroPDDoc, CAcroRect> job, string pdfFilePath)
        {
            if (!File.Exists(pdfFilePath))
                throw new InvalidOperationException(BadFileErrorMessage);

            var acrobatPdfDocType = Type.GetTypeFromProgID("AcroExch.PDDoc");
            if (acrobatPdfDocType == null || !isAdobeSdkInstalled)
                throw new InvalidOperationException(SdkError);

            var pdfDoc = (CAcroPDDoc)Activator.CreateInstance(acrobatPdfDocType);
            if (pdfDoc == null)
                throw new InvalidOperationException(AdobeObjectsErrorMessage);

            var acrobatPdfRectType = Type.GetTypeFromProgID("AcroExch.Rect");
            var pdfRect = (CAcroRect)Activator.CreateInstance(acrobatPdfRectType);

            var result = pdfDoc.Open(pdfFilePath);
            if (!result)
                throw new InvalidOperationException(BadFileErrorMessage);

            job(pdfDoc, pdfRect);

            releaseComObjects(pdfDoc, pdfRect);
        }

        public static byte[] ResizeImage(this Image image, int thumbWidth, int thumbHeight)
        {
            var srcWidth = image.Width;
            var srcHeight = image.Height;
            using (var bmp = new Bitmap(thumbWidth, thumbHeight, PixelFormat.Format32bppArgb))
            {
                using (var gr = Graphics.FromImage(bmp))
                {
                    gr.SmoothingMode = SmoothingMode.HighQuality;
                    gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    gr.CompositingQuality = CompositingQuality.HighQuality;
                    gr.InterpolationMode = InterpolationMode.High;

                    var rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight);
                    gr.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel);

                    using (var memStream = new MemoryStream())
                    {
                        bmp.Save(memStream, ImageFormat.Png);
                        return memStream.ToArray();
                    }
                }
            }
        }

        static bool isAdobeSdkInstalled
        {
            get
            {
                return Registry.ClassesRoot.OpenSubKey("AcroExch.PDDoc", writable: false) != null;
            }
        }

        private static Bitmap pdfPageToBitmap(int pageNumber, CAcroPDDoc pdfDoc, CAcroRect pdfRect)
        {
            var pdfPage = (CAcroPDPage)pdfDoc.AcquirePage(pageNumber);
            if (pdfPage == null)
                throw new InvalidOperationException(BadFileErrorMessage);

            var pdfPoint = (CAcroPoint)pdfPage.GetSize();

            pdfRect.Left = 0;
            pdfRect.right = pdfPoint.x;
            pdfRect.Top = 0;
            pdfRect.bottom = pdfPoint.y;

            pdfPage.CopyToClipboard(pdfRect, 0, 0, 100);

            Bitmap pdfBitmap = null;
            var thread = new Thread(() =>
            {
                var data = Clipboard.GetDataObject();
                if (data != null && data.GetDataPresent(DataFormats.Bitmap))
                    pdfBitmap = (Bitmap)data.GetData(DataFormats.Bitmap);
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();

            Marshal.ReleaseComObject(pdfPage);

            return pdfBitmap;
        }

        private static byte[] pdfPageToPng(int thumbWidth, int thumbHeight, int pageNumber, CAcroPDDoc pdfDoc, CAcroRect pdfRect)
        {
            var pdfBitmap = pdfPageToBitmap(pageNumber, pdfDoc, pdfRect);
            if (pdfBitmap == null)
                throw new InvalidOperationException(ClipboardError);

            var pdfImage = pdfBitmap.GetThumbnailImage(thumbWidth, thumbHeight, null, IntPtr.Zero);
            // (+ 7 for template border)
            var imageData = pdfImage.ResizeImage(thumbWidth + 7, thumbHeight + 7);
            return imageData;
        }

        private static void releaseComObjects(CAcroPDDoc pdfDoc, CAcroRect pdfRect)
        {
            pdfDoc.Close();
            Marshal.ReleaseComObject(pdfRect);
            Marshal.ReleaseComObject(pdfDoc);
        }
    }
}
و برای استفاده از آن خواهیم داشت:
using System;
using System.IO;
using System.Windows.Forms;
using PdfThumbnail.Lib;

namespace PdfThumbnail
{
    class Program
    {
        static void Main(string[] args)
        {
            var pdfPath = Application.StartupPath + @"\test.pdf";
            PdfToImage.AllPdfPagesToPng((pageImageData, pageNumber, numPages) =>
                {
                    Console.WriteLine("Page {0}/{1}", pageNumber, numPages);
                    File.WriteAllBytes(string.Format("{0}\\page-{1}.png", Application.StartupPath, pageNumber), pageImageData);
                }, pdfPath);
        }
    }
}

کدهای این قسمت را از اینجا نیز می‌توانید دریافت کنید:
PdfThumbnail.zip
 
  • #
    ‫۱۱ سال و ۱۱ ماه قبل، جمعه ۱۲ آبان ۱۳۹۱، ساعت ۰۳:۵۰
    امکان دریافت SDK فوق با IP ایرانی نیست. آن‌را از اینجا هم می‌توانید دریافت کنید.

  • #
    ‫۱۱ سال و ۶ ماه قبل، پنجشنبه ۲۲ فروردین ۱۳۹۲، ساعت ۱۹:۱۷
    با سلام.
    می توان از این مثال در وب نیز استفاده کرد؟
    باتشکر.
    • #
      ‫۱۱ سال و ۶ ماه قبل، پنجشنبه ۲۲ فروردین ۱۳۹۲، ساعت ۱۹:۳۳
      - اگر سرور دار خودتون هستید و می‌تونید روی سرور وابستگی‌های مربوط به نگارش کامل Adobe Acrobat را نصب کنید و همچنین برنامه در حالت Full trust اجرا می‌شود؛ بله.
      - راه حل‌های دیگری هم هستند که در قسمت اشتراک‌های سایت مطرح شدند.
      • #
        ‫۱۱ سال و ۶ ماه قبل، پنجشنبه ۲۲ فروردین ۱۳۹۲، ساعت ۱۹:۴۰
        مهندس جان.
        کیفیت تصویر تولیدی را چگونه می‌توان بالا برد؟
        باتشکر.
        • #
          ‫۱۱ سال و ۶ ماه قبل، پنجشنبه ۲۲ فروردین ۱۳۹۲، ساعت ۲۰:۵۹
          - کیفیت بالایی داره.
          - حداکثر از ResizeImage استفاده نکنید (مستقیما از Bitmap تولیدی استفاده کنید) یا آن‌را تغییر دهید. در کل متد ResizeImage هم بر اساس تولید تصویر با کیفیت بالا تنظیم شده.
  • #
    ‫۱۰ سال و ۸ ماه قبل، جمعه ۱۱ بهمن ۱۳۹۲، ساعت ۲۰:۰۲
    با سلام . روش دیگری که نیاز به نصب کتابخانه جانبی دیگر نداشته باشد و بتوان از آن تحت وب نیز استفاده کرد (برای هر دو ورژن x64 و x86) را چه توصیه میکنید؟ با تشکر.
      • #
        ‫۶ سال و ۳ ماه قبل، سه‌شنبه ۱۵ خرداد ۱۳۹۷، ساعت ۰۹:۴۶
        کتابخانه فوق متاسفانه در NetCore قابل استفاده نمی‌باشد . آیا موارد مشابه جهت تبدیل صفحات Pdf به عکس که NetCore را نیز ساپورت کنند موجود هست ؟
        • #
          ‫۶ سال و ۳ ماه قبل، سه‌شنبه ۱۵ خرداد ۱۳۹۷، ساعت ۱۲:۳۰
          ghostscript روش چندسکویی تبدیل صفحات PDF به تصویر است. نسخه‌ی خط فرمان آن با ارسال چند پارامتر به آن در تمام سیستم عامل‌ها قابل استفاده‌است.
          gs -dNOPAUSE -q -sDEVICE=pnggray -r500 -dBATCH -dFirstPage=2 -dLastPage=2 -sOutputFile=test.png test.pdf
          • #
            ‫۶ سال و ۳ ماه قبل، سه‌شنبه ۱۵ خرداد ۱۳۹۷، ساعت ۱۳:۱۹
            مشکل این هست که فرایند اپلود فایل و تبدیل به عکس توسط سیستم باید انجام بشه و استفاده از نسخه خط فرمان مقدور نیست .
            کدی که در نسخه net 4.6 نوشته شده این هستش که بدون مشکل کار می‌کنه :
            string destinationFilePath = ("d:\\temp\\1.jpg");
                            GhostscriptWrapper.GenerateOutput("d:\\temp\\1.pdf", destinationFilePath,
                                new GhostscriptSettings
                                {
                                    Device = GhostscriptDevices.jpeg,
                                    Page = new GhostscriptPages
                                    {
                                        Start = 1,
                                        End = 1,
                                        AllPages = true,
                                    },
                                    Resolution = new Size
                                    {
                                        Height = 150,
                                        Width = 150
                                    },
                                    Size = new GhostscriptPageSize
                                    {
                                        Native = GhostscriptPageSizes.a4
                                    }
                                });
            اما وقتی همین کد با پکیج GhostScriptshart 1.3.1.4 استفاده می‌شه تنها نسخه 32 بیتی فعال هست و اگر تارگت پروژه به 32 عوض بشه باقی ماژول‌ها دچار سوعملکرد و خطا می‌شن .
            • #
              ‫۶ سال و ۳ ماه قبل، سه‌شنبه ۱۵ خرداد ۱۳۹۷، ساعت ۱۳:۳۲
              شما زمانیکه قرار هست با نسخه‌ی خط فرمان کار کنید نیازی به هیچ نوع محصور کننده‌ای ندارید.
              public static class Cmd
              {
                  public static int Execute(string filename, string arguments)
                  {
                      var startInfo = new ProcessStartInfo
                      {
                          CreateNoWindow = true,
                          FileName = filename,
                          Arguments = arguments,
                      };
                      using (var process = new Process { StartInfo = startInfo })
                      {
                          try
                          {
                              process.Start();
                              process.WaitForExit(30000);
                              return process.ExitCode;
                          }
                          catch (Exception exception)
                          {
                              if (!process.HasExited)
                              {
                                  process.Kill();
                              }
                              return (int)ExitCode.Exception;
                          }
                      }
                  }
              }