How to downscale all images in a PDF

Shapes, Images, Manipulate PDF
1/23/2014

Downloads

This code sample shows how to downscale images in a PDF file. In order to downscale the images, we extract all graphical content as a collection of shapes. We then replace each ImageShape in this collection with a downscaled copy. Next, the result is written back to a new PDF file.

1 static void Main(string[] args) 2 { 3 using (FileStream fileIn = new FileStream(@"..\..\..\inputDocuments\BlueWater.pdf", FileMode.Open, FileAccess.Read)) 4 { 5 Document pdfIn = new Document(fileIn); 6 7 //downscale all images in the document to 18 dots per inch 8 int dpi = 10; 9 Document pdfOut = new Document(); 10 11 foreach (Page page in pdfIn.Pages) 12 { 13 ShapeCollection shapes = page.CreateShapes(); 14 //downscale all imageshapes on this page 15 downScaleImages(shapes, dpi); 16 17 //add all shapes to the new document 18 Page newPage = new Page(page.Width, page.Height); 19 newPage.Overlay.Add(shapes); 20 pdfOut.Pages.Add(newPage); 21 } 22 23 using (FileStream fileOut = new FileStream(@"..\..\out.pdf", FileMode.Create, FileAccess.Write)) 24 { 25 pdfOut.Write(fileOut); 26 } 27 } 28 } 29 30 static void downScaleImages(ShapeCollection shapes, int dpi) 31 { 32 for (int i = 0; i < shapes.Count; i++) 33 { 34 Shape shape = shapes[i]; 35 36 if (shape is ShapeCollection) 37 { 38 // recurse 39 downScaleImages(shape as ShapeCollection, dpi); 40 } 41 else if (shape is ImageShape) 42 { 43 shapes.RemoveAt(i); 44 ImageShape downScaled = downScale(shape as ImageShape, dpi); 45 shapes.Insert(i, downScaled); 46 } 47 } 48 }

The following code snippet shows how we resample a given ImageShape.

1 static ImageShape downScale(ImageShape image, int dpi) 2 { 3 Matrix matrix = image.Transform.CreateGdiMatrix(); 4 PointF[] points = new PointF[] 5 { 6 new PointF(0, 0), 7 new PointF((float)image.Width, 0), 8 new PointF(0, (float)image.Height) 9 }; 10 matrix.TransformPoints(points); 11 12 // real dimensions of the image in points as it appears on the page 13 float realWidth = distance(points[0], points[1]); 14 float realHeight = distance(points[0], points[2]); 15 16 // given the desired resolution, these are the desired number of cols/rows of the optimized image 17 int desiredColumns = (int)(realWidth * ((float)dpi / 72f)); 18 int desiredRows = (int)(realHeight * ((float)dpi / 72f)); 19 20 // create the new image and copy the source image to it (resampling happens here) 21 using (Bitmap bitmap = image.CreateBitmap()) 22 { 23 if (desiredColumns > bitmap.Width) return image; // prevent upscale 24 if (desiredRows > bitmap.Width) return image; // prevent upscale 25 26 Bitmap optimizedBitmap = new Bitmap(desiredColumns, desiredRows, PixelFormat.Format32bppArgb); 27 28 //draw the image so the pixels can be resampled 29 using (Graphics graphics = Graphics.FromImage(optimizedBitmap)) 30 { 31 graphics.DrawImage(bitmap, 0, 0, desiredColumns, desiredRows); 32 } 33 34 //create new imageshape and keep all of the settings the same 35 ImageShape optimized = new ImageShape(optimizedBitmap, true); 36 optimized.Compression = Compression.Jpeg; 37 optimized.Width = image.Width; 38 optimized.Height = image.Height; 39 optimized.Transform = image.Transform; 40 41 optimized.Opacity = image.Opacity; 42 optimized.BlendMode = image.BlendMode; 43 optimized.Transform = image.Transform; 44 45 return optimized; 46 } 47 } 48 49 static float distance(PointF a, PointF b) 50 { 51 return (float)Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y)); 52 }