使用iTextSharp压缩PDF的大小

发布时间 2023-04-14 15:50:03作者: xuxuzhaozhao

将PDF中含有的图片压缩大小并转成灰度图,让PDF文件大幅下降
nuget 安装 iTextSharp

using iTextSharp.text.pdf;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace Program
{
    class Program
    {
        static void Main(string[] args)
        {
            string source = "C:\\Users\\xuxuzhaozhao\\Desktop\\PDFTest.pdf";
            string destination = "C:\\Users\\xuxuzhaozhao\\Desktop\\1OUTPDFTest.pdf";

            PdfReader reader = new PdfReader(source);
            using (FileStream fs = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                using (PdfStamper stamper = new PdfStamper(reader, fs))
                {
                    PdfDictionary page;
                    int pageCount = reader.NumberOfPages;
                    for (int i = 1; i <= pageCount; i++)
                    {
                        page = reader.GetPageN(i);
                        foreach (var obj in FindImageInPDFDictionary(page))
                        {
                            if (obj == null) continue;
                            iTextSharp.text.Image pdfImage = null;
                            int XrefIndex = Convert.ToInt32(((PRIndirectReference)obj).Number.ToString(System.Globalization.CultureInfo.InvariantCulture));
                            PdfObject pdfObj = reader.GetPdfObject(XrefIndex);
                            PdfStream pdfStrem = (PdfStream)pdfObj;
                            byte[] bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStrem);

                            if (bytes == null) continue;
                            try
                            {
                                using (var memStream = new MemoryStream(bytes))
                                {
                                    memStream.Position = 0;
                                    System.Drawing.Image img = null;
                                    if (i == 1 || i == pageCount)
                                    {
                                        img = System.Drawing.Image.FromStream(memStream);
                                    }
                                    else
                                    {
                                        System.Drawing.Image Image = System.Drawing.Image.FromStream(memStream);
                                        Bitmap bitmap = ImageHelper.ResizeImage(Image, .35M);
                                        img = BitmapToGrayscale(bitmap);
                                    }

                                    ImageFormat format = img.PixelFormat == PixelFormat.Format1bppIndexed
                                                         || img.PixelFormat == PixelFormat.Format4bppIndexed
                                                         || img.PixelFormat == PixelFormat.Format8bppIndexed
                                        ? ImageFormat.Tiff
                                        : ImageFormat.Jpeg;

                                    pdfImage = iTextSharp.text.Image.GetInstance(img, format);
                                }
                                PdfReader.KillIndirect(obj);
                            } catch  { }
                            if (pdfImage != null)
                            {
                                stamper.Writer.AddDirectImageSimple(pdfImage, (PRIndirectReference)obj);
                            }
                        }
                    }
                }
            }
        }

        private static List<PdfObject> FindImageInPDFDictionary(PdfDictionary pg)
        {
            List<PdfObject> resultList = new List<PdfObject>();
            PdfDictionary res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
            PdfDictionary xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
            if (xobj != null)
            {
                foreach (PdfName name in xobj.Keys)
                {
                    PdfObject obj = xobj.Get(name);
                    if (obj.IsIndirect())
                    {
                        PdfDictionary tg = (PdfDictionary)PdfReader.GetPdfObject(obj);

                        PdfName type = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));

                        //image at the root of the pdf
                        if (PdfName.IMAGE.Equals(type))
                        {
                            resultList.Add(obj);
                        }// image inside a form
                        else if (PdfName.FORM.Equals(type))
                        {
                            resultList.AddRange(FindImageInPDFDictionary(tg));
                        } //image inside a group
                        else if (PdfName.GROUP.Equals(type))
                        {
                            resultList.AddRange(FindImageInPDFDictionary(tg));
                        }
                    }
                }
            }

            return resultList;
        }

        public static class ImageHelper
        {
            /// <summary>
            /// Resize the image to the specified width and height.
            /// </summary>
            /// <param name="image">The image to resize.</param>
            /// <param name="width">The width to resize to.</param>
            /// <param name="height">The height to resize to.</param>
            /// <returns>The resized image.</returns>
            public static Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
            {
                var destRect = new System.Drawing.Rectangle(0, 0, width, height);
                var destImage = new System.Drawing.Bitmap(width, height);

                destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

                using (var graphics = Graphics.FromImage(destImage))
                {
                    graphics.CompositingMode = CompositingMode.SourceCopy;
                    graphics.CompositingQuality = CompositingQuality.HighQuality;
                    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    graphics.SmoothingMode = SmoothingMode.HighQuality;
                    graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

                    using (var wrapMode = new ImageAttributes())
                    {
                        wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                        graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
                    }
                }

                return destImage;
            }

            public static Bitmap ResizeImage(System.Drawing.Image image, decimal percentage)
            {
                int width = (int)Math.Round(image.Width * percentage, MidpointRounding.AwayFromZero);
                int height = (int)Math.Round(image.Height * percentage, MidpointRounding.AwayFromZero);
                return ResizeImage(image, width, height);
            }
        }

        public static Bitmap BitmapToGrayscale(Bitmap source)
        {
            // Create target image.
            int width = source.Width;
            int height = source.Height;
            Bitmap target = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            // Set the palette to discrete shades of gray
            ColorPalette palette = target.Palette;
            for (int i = 0; i < palette.Entries.Length; i++)
            {
                palette.Entries[i] = Color.FromArgb(0, i, i, i);
            }
            target.Palette = palette;

            // Lock bits so we have direct access to bitmap data
            BitmapData targetData = target.LockBits(new System.Drawing.Rectangle(0, 0, width, height),
                ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
            BitmapData sourceData = source.LockBits(new System.Drawing.Rectangle(0, 0, width, height),
                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            unsafe
            {
                for (int r = 0; r < height; r++)
                {
                    byte* pTarget = (byte*)(targetData.Scan0 + r * targetData.Stride);
                    byte* pSource = (byte*)(sourceData.Scan0 + r * sourceData.Stride);
                    for (int c = 0; c < width; c++)
                    {
                        byte colorIndex = (byte)(((*pSource) * 0.3 + *(pSource + 1) * 0.59 + *(pSource + 2) * 0.11));
                        *pTarget = colorIndex;
                        pTarget++;
                        pSource += 3;
                    }
                }
            }

            target.UnlockBits(targetData);
            source.UnlockBits(sourceData);
            return target;
        }
    }
}