Products
PDFKit.NET 4.0
Created
6/27/2014
Tags
Images Manipulate PDF Shapes

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.

C# code sample using (FileStream fileIn = new FileStream(@"..\..\..\inputDocuments\BlueWater.pdf", FileMode.Open, FileAccess.Read))

{ Document pdfIn = new Document(fileIn);

    //downscale all images in the document to 18 dots per inch
    int dpi = 10;
    Document pdfOut = new Document();

    foreach (Page page in pdfIn.Pages)
    {
        ShapeCollection shapes = page.CreateShapes();
        //downscale all imageshapes on this page
        downScaleImages(shapes, dpi);

        //add all shapes to the new document
        Page newPage = new Page(page.Width, page.Height);
        newPage.Overlay.Add(shapes);
        pdfOut.Pages.Add(newPage);
    }

    using (FileStream fileOut = new FileStream(@"..\..\out.pdf", FileMode.Create, FileAccess.Write))
    {
        pdfOut.Write(fileOut);
    }
}

}

static void downScaleImages(ShapeCollection shapes, int dpi) { for (int i = 0; i < shapes.Count; i++) { Shape shape = shapes[i];

    if (shape is ShapeCollection)
    {
        // recurse
        downScaleImages(shape as ShapeCollection, dpi);
    }
    else if (shape is ImageShape)
    {
        shapes.RemoveAt(i);
        ImageShape downScaled = downScale(shape as ImageShape, dpi);
        shapes.Insert(i, downScaled);
    }
}

}

static ImageShape downScale(ImageShape image, int dpi) { Matrix matrix = image.Transform.CreateGdiMatrix(); PointF[] points = new PointF[] { new PointF(0, 0), new PointF((float)image.Width, 0), new PointF(0, (float)image.Height) }; matrix.TransformPoints(points);

// real dimensions of the image in points as it appears on the page
float realWidth = distance(points[0], points[1]);
float realHeight = distance(points[0], points[2]);

// given the desired resolution, these are the desired number of cols/rows of the optimized image
int desiredColumns = (int)(realWidth * ((float)dpi / 72f));
int desiredRows = (int)(realHeight * ((float)dpi / 72f));

// create the new image and copy the source image to it (resampling happens here)
using (Bitmap bitmap = image.CreateBitmap())
{
    if (desiredColumns > bitmap.Width) return image; // prevent upscale
    if (desiredRows > bitmap.Width) return image; // prevent upscale

    Bitmap optimizedBitmap = new Bitmap(desiredColumns, desiredRows, PixelFormat.Format32bppArgb);

    //draw the image so the pixels can be resampled
    using (Graphics graphics = Graphics.FromImage(optimizedBitmap))
    {
        graphics.DrawImage(bitmap, 0, 0, desiredColumns, desiredRows);
    }

    //create new imageshape and keep all of the settings the same
    ImageShape optimized = new ImageShape(optimizedBitmap, true);
    optimized.Compression = Compression.Jpeg;
    optimized.Width = image.Width;
    optimized.Height = image.Height;
    optimized.Transform = image.Transform;

    optimized.Opacity = image.Opacity;
    optimized.BlendMode = image.BlendMode;
    optimized.Transform = image.Transform;

    return optimized;
}

}

static float distance(PointF a, PointF b) { return (float)Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y)); } ]]>

VB.NET code sample 'downscale all images in the document to 18 dots per inch Dim dpi As Integer = 10 Dim pdfOut As New Document() For Each page As Page In pdfIn.Pages Dim shapes As ShapeCollection = page.CreateShapes() 'downscale all imageshapes on this page downScaleImages(shapes, dpi) 'add all shapes to the new document Dim newPage As New Page(page.Width, page.Height) newPage.Overlay.Add(shapes) pdfOut.Pages.Add(newPage) Next Using fileOut As New FileStream("..\..\out.pdf", FileMode.Create, FileAccess.Write) pdfOut.Write(fileOut) End Using End Using

End Sub

Private Shared Sub downScaleImages(shapes As ShapeCollection, dpi As Integer) For i As Integer = 0 To shapes.Count - 1 Dim shape As Shape = shapes(i)

    If TypeOf shape Is ShapeCollection Then
        ' recurse
        downScaleImages(TryCast(shape, ShapeCollection), dpi)
    ElseIf TypeOf shape Is ImageShape Then
        shapes.RemoveAt(i)
        Dim downScaled As ImageShape = downScale(TryCast(shape, ImageShape), dpi)
        shapes.Insert(i, downScaled)
    End If
Next

End Sub

Private Shared Function downScale(image As ImageShape, dpi As Integer) As ImageShape Dim matrix As Matrix = image.Transform.CreateGdiMatrix() Dim points As PointF() = New PointF() {New PointF(0, 0), New PointF(CSng(image.Width), 0), New PointF(0, CSng(image.Height))} matrix.TransformPoints(points)

' real dimensions of the image in points as it appears on the page
Dim realWidth As Single = distance(points(0), points(1))
Dim realHeight As Single = distance(points(0), points(2))

' given the desired resolution, these are the desired number of cols/rows of the optimized image
Dim desiredColumns As Integer = CInt(realWidth * (CSng(dpi) / 72.0F))
Dim desiredRows As Integer = CInt(realHeight * (CSng(dpi) / 72.0F))

' create the new image and copy the source image to it (resampling happens here)
Using bitmap As Bitmap = image.CreateBitmap()
    If desiredColumns > bitmap.Width Then
        Return image
    End If
    ' prevent upscale
    If desiredRows > bitmap.Width Then
        Return image
    End If
    ' prevent upscale
    Dim optimizedBitmap As New Bitmap(desiredColumns, desiredRows, PixelFormat.Format32bppArgb)

    'draw the image so the pixels can be resampled
    Using graphics__1 As Graphics = Graphics.FromImage(optimizedBitmap)
        graphics__1.DrawImage(bitmap, 0, 0, desiredColumns, desiredRows)
    End Using

    'create new imageshape and keep all of the settings the same
    Dim optimized As New ImageShape(optimizedBitmap, True)
    optimized.Compression = Compression.Jpeg
    optimized.Width = image.Width
    optimized.Height = image.Height
    optimized.Transform = image.Transform

    optimized.Opacity = image.Opacity
    optimized.BlendMode = image.BlendMode
    optimized.Transform = image.Transform

    Return optimized
End Using

End Function

Private Shared Function distance(a As PointF, b As PointF) As Single Return CSng(Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y))) End Function ]]>