Down-scale images in a PDF

Images, Shapes, Manipulate PDF
6/27/2014

This code sample shows how to down-scale images in a PDF file. This is done by extracting all graphics as a collection of shapes. Next, each ImageShape in this collection is replaced with a downscaled copy. Finally, the result is written back to a new PDF file.

downscale-images-pdf.png

C# code sample

1 using (FileStream fileIn = new FileStream(@"..\..\..\inputDocuments\BlueWater.pdf", FileMode.Open, FileAccess.Read)) 2 { 3 Document pdfIn = new Document(fileIn); 4 5 //downscale all images in the document to 18 dots per inch 6 int dpi = 10; 7 Document pdfOut = new Document(); 8 9 foreach (Page page in pdfIn.Pages) 10 { 11 ShapeCollection shapes = page.CreateShapes(); 12 //downscale all imageshapes on this page 13 downScaleImages(shapes, dpi); 14 15 //add all shapes to the new document 16 Page newPage = new Page(page.Width, page.Height); 17 newPage.Overlay.Add(shapes); 18 pdfOut.Pages.Add(newPage); 19 } 20 21 using (FileStream fileOut = new FileStream(@"..\..\out.pdf", FileMode.Create, FileAccess.Write)) 22 { 23 pdfOut.Write(fileOut); 24 } 25 } 26 } 27 28 static void downScaleImages(ShapeCollection shapes, int dpi) 29 { 30 for (int i = 0; i < shapes.Count; i++) 31 { 32 Shape shape = shapes[i]; 33 34 if (shape is ShapeCollection) 35 { 36 // recurse 37 downScaleImages(shape as ShapeCollection, dpi); 38 } 39 else if (shape is ImageShape) 40 { 41 shapes.RemoveAt(i); 42 ImageShape downScaled = downScale(shape as ImageShape, dpi); 43 shapes.Insert(i, downScaled); 44 } 45 } 46 } 47 48 static ImageShape downScale(ImageShape image, int dpi) 49 { 50 Matrix matrix = image.Transform.CreateGdiMatrix(); 51 PointF[] points = new PointF[] { 52 new PointF(0, 0), 53 new PointF((float)image.Width, 0), 54 new PointF(0, (float)image.Height) 55 }; 56 matrix.TransformPoints(points); 57 58 // real dimensions of the image in points as it appears on the page 59 float realWidth = distance(points[0], points[1]); 60 float realHeight = distance(points[0], points[2]); 61 62 // given the desired resolution, these are the desired number of cols/rows of the optimized image 63 int desiredColumns = (int)(realWidth * ((float)dpi / 72f)); 64 int desiredRows = (int)(realHeight * ((float)dpi / 72f)); 65 66 // create the new image and copy the source image to it (resampling happens here) 67 using (Bitmap bitmap = image.CreateBitmap()) 68 { 69 if (desiredColumns > bitmap.Width) return image; // prevent upscale 70 if (desiredRows > bitmap.Width) return image; // prevent upscale 71 72 Bitmap optimizedBitmap = new Bitmap(desiredColumns, desiredRows, PixelFormat.Format32bppArgb); 73 74 //draw the image so the pixels can be resampled 75 using (Graphics graphics = Graphics.FromImage(optimizedBitmap)) 76 { 77 graphics.DrawImage(bitmap, 0, 0, desiredColumns, desiredRows); 78 } 79 80 //create new imageshape and keep all of the settings the same 81 ImageShape optimized = new ImageShape(optimizedBitmap, true); 82 optimized.Compression = Compression.Jpeg; 83 optimized.Width = image.Width; 84 optimized.Height = image.Height; 85 optimized.Transform = image.Transform; 86 87 optimized.Opacity = image.Opacity; 88 optimized.BlendMode = image.BlendMode; 89 optimized.Transform = image.Transform; 90 91 return optimized; 92 } 93 } 94 95 static float distance(PointF a, PointF b) 96 { 97 return (float)Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y)); 98 }

VB.NET code sample

1 Shared Sub Main() 2 Using fileIn As New FileStream("..\..\..\inputDocuments\BlueWater.pdf", FileMode.Open, FileAccess.Read) 3 Dim pdfIn As New Document(fileIn) 4 5 'downscale all images in the document to 18 dots per inch 6 Dim dpi As Integer = 10 7 Dim pdfOut As New Document() 8 9 For Each page As Page In pdfIn.Pages 10 Dim shapes As ShapeCollection = page.CreateShapes() 11 'downscale all imageshapes on this page 12 downScaleImages(shapes, dpi) 13 14 'add all shapes to the new document 15 Dim newPage As New Page(page.Width, page.Height) 16 newPage.Overlay.Add(shapes) 17 pdfOut.Pages.Add(newPage) 18 Next 19 20 Using fileOut As New FileStream("..\..\out.pdf", FileMode.Create, FileAccess.Write) 21 pdfOut.Write(fileOut) 22 End Using 23 End Using 24 End Sub 25 26 Private Shared Sub downScaleImages(shapes As ShapeCollection, dpi As Integer) 27 For i As Integer = 0 To shapes.Count - 1 28 Dim shape As Shape = shapes(i) 29 30 If TypeOf shape Is ShapeCollection Then 31 ' recurse 32 downScaleImages(TryCast(shape, ShapeCollection), dpi) 33 ElseIf TypeOf shape Is ImageShape Then 34 shapes.RemoveAt(i) 35 Dim downScaled As ImageShape = downScale(TryCast(shape, ImageShape), dpi) 36 shapes.Insert(i, downScaled) 37 End If 38 Next 39 End Sub 40 41 Private Shared Function downScale(image As ImageShape, dpi As Integer) As ImageShape 42 Dim matrix As Matrix = image.Transform.CreateGdiMatrix() 43 Dim points As PointF() = New PointF() {New PointF(0, 0), New PointF(CSng(image.Width), 0), New PointF(0, CSng(image.Height))} 44 matrix.TransformPoints(points) 45 46 ' real dimensions of the image in points as it appears on the page 47 Dim realWidth As Single = distance(points(0), points(1)) 48 Dim realHeight As Single = distance(points(0), points(2)) 49 50 ' given the desired resolution, these are the desired number of cols/rows of the optimized image 51 Dim desiredColumns As Integer = CInt(realWidth * (CSng(dpi) / 72.0F)) 52 Dim desiredRows As Integer = CInt(realHeight * (CSng(dpi) / 72.0F)) 53 54 ' create the new image and copy the source image to it (resampling happens here) 55 Using bitmap As Bitmap = image.CreateBitmap() 56 If desiredColumns > bitmap.Width Then 57 Return image 58 End If 59 ' prevent upscale 60 If desiredRows > bitmap.Width Then 61 Return image 62 End If 63 ' prevent upscale 64 Dim optimizedBitmap As New Bitmap(desiredColumns, desiredRows, PixelFormat.Format32bppArgb) 65 66 'draw the image so the pixels can be resampled 67 Using graphics__1 As Graphics = Graphics.FromImage(optimizedBitmap) 68 graphics__1.DrawImage(bitmap, 0, 0, desiredColumns, desiredRows) 69 End Using 70 71 'create new imageshape and keep all of the settings the same 72 Dim optimized As New ImageShape(optimizedBitmap, True) 73 optimized.Compression = Compression.Jpeg 74 optimized.Width = image.Width 75 optimized.Height = image.Height 76 optimized.Transform = image.Transform 77 78 optimized.Opacity = image.Opacity 79 optimized.BlendMode = image.BlendMode 80 optimized.Transform = image.Transform 81 82 Return optimized 83 End Using 84 End Function 85 86 Private Shared Function distance(a As PointF, b As PointF) As Single 87 Return CSng(Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y))) 88 End Function