Skip to main content

Resizing Pages in a PDF File Using ImageGear .NET

Portable Document Format (PDF) files have become the ubiquitous way to store documents for sharing with a broad audience. While popular, PDF documents have several drawbacks. One large drawback is the fact that PDF documents are intended to be immutable. In other words, PDF documents lack the internal information necessary to reorganize its contents, unlike, for instance, a word processor document.

There are however ways to reclaim portions of PDF documents for use in new or updated PDF documents. One of the most common is to reuse pages from existing PDF documents. This does lead to one particularly vexing issue – reusing pages that were created for different media sizes.

When a PDF document has pages that are all the same size, PDF viewers can scale and scroll the document consistently, and the document appears aesthetically pleasing. The document is also likely to print well. The problem is that PDF pages from image scanners, PDF pages produced by word processors, and PDF pages generated from images are likely to be produced with different media sizes.

This is where ImageGear .NET can help. ImageGear .NET can resize PDF pages using the following code.

First, we need to determine what size we want the pages to be. We will use this size to define the “MediaBox”, which specifies the outermost boundaries of the page. For this example, we will use 8.5 inches by 11 inches, which is letter size.


ImGearPDFDocument igPDFDocument;
using (Stream stream = new FileStream(@"PDFDocumentIn.pdf", FileMode.Open, FileAccess.Read))
    igPDFDocument = (ImGearPDFDocument)ImGearFileFormats.LoadDocument(stream, 0, -1);

    double newMediaBoxWidth = 8.5 * 72.0; // Convert inches to Points
    double newMediaBoxHeight = 11 * 72.0;

 

Next, we iterate through the pages in the PDF document, and resize each page. To resize a page, we first need to determine how much to scale and translate the page.


foreach (ImGearPDFPage page in igPDFDocument.Pages)
{
    using (ImGearPDFBasDict pageDict = page.GetDictionary())
    {
	     // Get the existing MediaBox to determine how much to scale and translate the page
	     ImGearPDFAtom mediaBoxKey = new ImGearPDFAtom("MediaBox");
 	     ImGearPDFBasArray mediaBox = (ImGearPDFBasArray)pageDict.Get(mediaBoxKey);
	     double mediaBoxLowerLeftX = (((ImGearPDFBasInt)(mediaBox.Get(0))).Value);
	     double mediaBoxLowerLeftY = (((ImGearPDFBasInt)(mediaBox.Get(1))).Value);
	     double mediaBoxUpperRightX = (((ImGearPDFBasInt)(mediaBox.Get(2))).Value);
	     double mediaBoxUpperRightY = (((ImGearPDFBasInt)(mediaBox.Get(3))).Value);
	     // Calculate how much to scale each axis to fill the page
	     double scaleX = newMediaBoxWidth / (mediaBoxUpperRightX - mediaBoxLowerLeftX);
	     double scaleY = newMediaBoxHeight / (mediaBoxUpperRightY - mediaBoxLowerLeftY);
	     // Determine which axis needs the least scaling to fill the page
	     double scale = scaleX;
	     if(scaleY < scaleX)
	         scale = scaleY;
	     // Determine how much to shift the content to center the page
	     double translateX = mediaBoxLowerLeftX + (newMediaBoxWidth - (mediaBoxUpperRightX - mediaBoxLowerLeftX) * scale) / 2.0;
	     double translateY = mediaBoxLowerLeftY + (newMediaBoxHeight - (mediaBoxUpperRightY - mediaBoxLowerLeftY) * scale) / 2.0;
 

 

Next, create an Affine matrix to scale and translate the page.


        // Create an Affine matrix to scale and translate the page
        ImGearPDFFixedMatrix scaleMatrix = new ImGearPDFFixedMatrix
	    {
	        A = ImGearPDF.DoubleToFixed(scale),
	        D = ImGearPDF.DoubleToFixed(scale),
	        H = ImGearPDF.DoubleToFixed(translateX),
	        V = ImGearPDF.DoubleToFixed(translateY)
	    };

 

Using the Affine matrix, transform the contents of the page. Since we will be transforming individual elements on the page, we need to keep track of each transformed element (using transformedIDs) so we don’t transform any element more than once.


        try
        {
             // Transform all the elements on each page. Keep track of transformed elements
             // so that no element is transformed more than once.
             using (ImGearPDEContent content = page.GetContent())
             {
                  List<int> transformedIDs = new List<int>();
                  TransformContent(content, scaleMatrix, transformedIDs);
                  page.SetContent();
             }
         }
         finally
         {
             page.ReleaseContent();
         }

 

Now that the page has been transformed, set the new MediaBox.


         using (ImGearPDFBasArray newMediaBox = new ImGearPDFBasArray((ImGearPDFDocument)page.Document, false, 4))
         {
   	          // Update the MediaBox
              newMediaBox.PutFixed(0, false, ImGearPDF.DoubleToFixed(0.0));
              newMediaBox.PutFixed(1, false, ImGearPDF.DoubleToFixed(0.0));
              newMediaBox.PutFixed(2, false, ImGearPDF.DoubleToFixed(newMediaBoxWidth));
              newMediaBox.PutFixed(3, false, ImGearPDF.DoubleToFixed(newMediaBoxHeight));
              pageDict.Put(mediaBoxKey, newMediaBox);
   	          // Remove any existing CropBox
              ImGearPDFAtom cropBoxKey = new ImGearPDFAtom("CropBox");
              if(pageDict.Known(cropBoxKey))
                  pageDict.Remove(cropBoxKey);
          }

 

Now we need the function TransformContent() to transform the content of a PDF page. This will take each element on a page and individually transform it to its new location on the page.


private void TransformContent(ImGearPDEContent content, ImGearPDFFixedMatrix scaleMatrix, List<int> transformedIDs)
{
   // If there is a matrix in the content attributes, transform it.
   ImGearPDEContentAttrs contentAttributes = content.GetAttributes();
   contentAttributes.Matrix = Concat(contentAttributes.Matrix, scaleMatrix);
   // Transform each element in the content
   for (int i = content.ElementCount - 1; i >= 0; i--)
   	using (ImGearPDEElement pdeElement = content.GetElement(i))
   	     TransformElement(pdeElement, scaleMatrix, transformedIDs);
}

 

Now we need the function TransformElement() to transform individual elements on a PDF page. Note that some elements contain elements and even content. These elements and content will be transformed recursively (hence the need for the transformedIDs list).


private void TransformElement(ImGearPDEElement pdeElement, ImGearPDFFixedMatrix scaleMatrix, List<int> transformedIDs)
{
   if (!transformedIDs.Contains(pdeElement.UniqueId))
   {
   	transformedIDs.Add(pdeElement.UniqueId);
   	switch(pdeElement.Type)
   	{
   	    case ImGearPDEType.CONTAINER:
   		ImGearPDEContainer pdeContainer = (ImGearPDEContainer)pdeElement;
   		using (ImGearPDEContent moreContent = pdeContainer.GetContent())
   		    TransformContent(moreContent, scaleMatrix, transformedIDs);
   		break;
   	    case ImGearPDEType.CLIP:
   		 ImGearPDEClip pdeClip = (ImGearPDEClip)pdeElement;
   		 for (int i = pdeClip.ElementCount - 1; i >= 0; --i)
   		     using (ImGearPDEElement anotherElement = pdeClip.GetElement(i))
   			 TransformElement(anotherElement, scaleMatrix, transformedIDs);
   		 break;
   	    case ImGearPDEType.GROUP:
   		 ImGearPDEGroup pdeGroup = (ImGearPDEGroup)pdeElement;
   		 using (ImGearPDEContent moreContent = pdeGroup.GetContent())
   		     TransformContent(moreContent, scaleMatrix, transformedIDs);
   		  break;
   	    case ImGearPDEType.TEXT:
   		 ImGearPDEText pdeText = (ImGearPDEText)pdeElement;
   		 for (int i = 0; i < pdeText.RunsCount; ++i)
   		     pdeText.RunSetMatrix(i, Concat(pdeText.GetMatrix(ImGearPDETextFlags.RUN, i), scaleMatrix));
   		 break;
   	    case ImGearPDEType.FORM:
   		 ImGearPDEForm pdeForm = (ImGearPDEForm)pdeElement;
   		 pdeForm.SetMatrix(Concat(pdeForm.GetMatrix(), scaleMatrix));
   		 using (ImGearPDEContent moreContent = pdeForm.GetContent())
   		     TransformContent(moreContent, scaleMatrix, transformedIDs);
   		 break;
   		 default:
   		     pdeElement.SetMatrix(Concat(pdeElement.GetMatrix(), scaleMatrix));
   		     break;
   	 }
   	 if (pdeElement.Type != ImGearPDEType.CLIP)
   	    using (ImGearPDEElement pdeClip = pdeElement.GetClip())
   		if (pdeClip != null && pdeClip.Type == ImGearPDEType.CLIP)
   		    TransformElement(pdeClip, scaleMatrix, transformedIDs);
   }
}

 

The last part we need is a function to concatenate (multiply) two Affine matrices together.


private ImGearPDFFixedMatrix Concat(ImGearPDFFixedMatrix matrix1, ImGearPDFFixedMatrix matrix2)
{
    // Multiply two Affine transformation matrices together to produce one matrix
    // that will perform the same transformation as the two matrices performed in series
    double matrix1A = ImGearPDF.FixedToDouble(matrix1.A);
    double matrix1B = ImGearPDF.FixedToDouble(matrix1.B);
    double matrix1C = ImGearPDF.FixedToDouble(matrix1.C);
    double matrix1D = ImGearPDF.FixedToDouble(matrix1.D);
    double matrix1H = ImGearPDF.FixedToDouble(matrix1.H);
    double matrix1V = ImGearPDF.FixedToDouble(matrix1.V);
    double matrix2A = ImGearPDF.FixedToDouble(matrix2.A);
    double matrix2B = ImGearPDF.FixedToDouble(matrix2.B);
    double matrix2C = ImGearPDF.FixedToDouble(matrix2.C);
    double matrix2D = ImGearPDF.FixedToDouble(matrix2.D);
    double matrix2H = ImGearPDF.FixedToDouble(matrix2.H);
    double matrix2V = ImGearPDF.FixedToDouble(matrix2.V);
    ImGearPDFFixedMatrix result = new ImGearPDFFixedMatrix
    {
        A = ImGearPDF.DoubleToFixed(matrix1A * matrix2A + matrix1B * matrix2C),
        B = ImGearPDF.DoubleToFixed(matrix1A * matrix2B + matrix1B * matrix2D),
        C = ImGearPDF.DoubleToFixed(matrix1C * matrix2A + matrix1D * matrix2C),
        D = ImGearPDF.DoubleToFixed(matrix1C * matrix2B + matrix1D * matrix2D),
        H = ImGearPDF.DoubleToFixed(matrix1H * matrix2A + matrix1V * matrix2C + matrix2H),
        V = ImGearPDF.DoubleToFixed(matrix1H * matrix2B + matrix1V * matrix2D + matrix2V)
     };
     return result;
}

 

After modifying the PDF document, save it.


using (Stream stream = new FileStream(@"PDFDocumentOut.pdf", FileMode.Create, FileAccess.Write))
    igPDFDocument.Save(stream, ImGearSavingFormats.PDF, 0, 0, -1, ImGearSavingModes.OVERWRITE);

 

Using this code, you should be able to resize any PDF document page. 

To learn more about ImageGear and all of its capabilities, check out the ImageGear .NET product page and dive into the developer resources section.

David Hilsabeck, Senior Software Engineer I

David Hilsabeck started with TMS, Inc. in 1989 after receiving a Bachelor’s of Science in Computer Science from Cameron University and spending two years in the defense industry. Within TMS, David worked on the company’s text retrieval and imaging products, such as Prizm Viewer. In 2004, TMS was acquired by Pegasus Imaging, and the two company’s product lines were merged. Since that time and through the transition from Pegasus Imaging to Accusoft, David has worked with and contributed to Accusoft’s imaging SDK product line. Outside work, David enjoys building large pyrotechnic devices, and is an active member of the Florida Pyrotechnic Arts Guild.