Introduction
Many custom workflows which are in use in small and big companies implement incoming PDF documents processing and stamping for tracking, archiving, and later use. These docs can be of any origin: received by emails, scanned by the personnel from hard copies etc.
Manual processing takes significant amount of time, and in this article we’ll show how to speed up this process by creating an app for automatic PDF page resizing and stamping.
The app
Consider the following simple program:
namespace ResizeAndStampPDF
{
classProgram
{
staticvoid Main(string[] args)
{
if (args != null&& args.Length > 0)
{
string destinationPath =
System.IO.Path.GetFileNameWithoutExtension(args[0]) + "_stamped.pdf";
PageStamper.Stamp(args[0],destinationPath,"FF6173","949124","Approved");
Process.Start(destinationPath);
}
}
}
}
It uses the PageStamper.Stamp() method to perform the stamping task by passing doc’s attributes. This class uses Apitron PDF Kit .NET component and also demonstrates its Fixed and Flow layout APIs in action (see this blog post to read about the difference between them). See the next section for the details.
PDF Stamping implementation
Class PageStamper listed below transforms the page by expanding it down and adding a stamp onto this area.
///<summary>
/// Resizes PDF page and adds custom stamp at the bottom.
///</summary>
staticclassPageStamper
{
privateconstint stampHeight = 36;
private constint sectionMargin = 40;
publicstaticvoid Stamp(string sourcePath, string destinationPath, string branchNo,
string incomingNo, string documentStatus)
{
// open file
using (Stream stream = File.Open(sourcePath, FileMode.Open, FileAccess.ReadWrite))
{
// load pdf document from stream
using (FixedDocument originalDoc = newFixedDocument(stream))
{
int pageNumber = 1;
// process pages
foreach (Page page in originalDoc.Pages)
{
// append the required stamp
Section stampSection = CreateStampSection(page, branchNo, incomingNo,
documentStatus, pageNumber++);
// resize the page
ResizePage(page);
// save state to avoid any problems with possible graphics state change
page.Content.SaveGraphicsState();
// apply transformations to properly position the stamp
ApplyTransformations(page, stampSection);
// add section object to the page
page.Content.AppendContentElement(stampSection,
stampSection.Width.Value, stampSection.Height.Value);
// restore the original state
page.Content.RestoreGraphicsState();
}
// save changes to PDF
using (Stream outputStream = File.Create(destinationPath))
{
originalDoc.Save(outputStream);
}
}
}
}
///<summary>
/// Resizes PDF page, taking into account its rotation property.
///</summary>
///<param name="page"></param>
privatestaticvoid ResizePage(Page page)
{
Boundary mediaBox = ResizeBoundary(page.Boundary.MediaBox, page.Rotate);
page.Resize(newPageBoundary(mediaBox));
}
privatestaticSection CreateStampSection(Page page, string branchNo, string incomingNo,
string documentStatus, int pageNumber)
{
// create section content element and set its size
Section stampSection = newSection();
stampSection.Height = stampHeight;
if(page.Rotate == PageRotate.Rotate0 || page.Rotate == PageRotate.Rotate180)
{
stampSection.Width = page.Boundary.MediaBox.Width - sectionMargin;
}
else
{
stampSection.Width = page.Boundary.MediaBox.Height - sectionMargin;
}
// create grid element
Grid grid = newGrid(Length.Auto,Length.Auto,Length.Auto,Length.Auto,Length.Auto);
grid.Font = newFont(StandardFonts.HelveticaBold, 12);
grid.Color = RgbColors.Red;
grid.InnerBorderColor = RgbColors.Red;
grid.InnerBorder = newBorder(1);
grid.Width = Length.FromPercentage(100);
// add header row
grid.Add(newGridRow( newTextBlock("Page #"),
newTextBlock("Branch #"),
newTextBlock("Incoming Doc #"),
newTextBlock("Document Status#"),
newTextBlock("Date"))
{ Align = Align.Center });
// add data row
grid.Add(newGridRow( newTextBlock(pageNumber.ToString()),
newTextBlock(branchNo),
newTextBlock(incomingNo),
newTextBlock(documentStatus),
newTextBlock(DateTime.Now.ToString("dd/MM/yyyy")))
{ Align = Align.Center });
stampSection.Add(grid);
return stampSection;
}
///<summary>
/// Applies transformations to generated content considering initial page rotation.
///</summary>
///<param name="page">The page to transform.</param>
///<param name="section">Section used </param>
privatestaticvoid ApplyTransformations(Page page, Section section)
{
switch (page.Rotate)
{
casePageRotate.Rotate90:
{
page.Content.SetRotate(Math.PI/2.0f);
// set current position on page
page.Content.Translate((page.Boundary.MediaBox.Height
-section.Width.Value)/2.0,
-page.Boundary.MediaBox.Right);
break;
}
casePageRotate.Rotate180:
{
page.Content.SetRotate(Math.PI);
// set current position on page
page.Content.Translate(
-page.Boundary.MediaBox.Width +
((page.Boundary.MediaBox.Width - section.Width.Value)/2.0),
-page.Boundary.MediaBox.Height);
break;
}
casePageRotate.Rotate270:
{
page.Content.SetRotate(-Math.PI/2.0f);
// set current position on page
page.Content.Translate(
-page.Boundary.MediaBox.Height +
(page.Boundary.MediaBox.Height - section.Width.Value)/2.0,
page.Boundary.MediaBox.Left);
break;
}
casePageRotate.Rotate0:
default:
{
// set current position on page
page.Content.Translate((page.Boundary.MediaBox.Width
- section.Width.Value)/2.0,
page.Boundary.MediaBox.Bottom);
break;
}
}
}
///<summary>
/// Resizes passed boundary if it's not null, otherwise returns null.
///</summary>
///<param name="boundary">Boundary to resize.</param>
///<param name="rotation">Page rotation.</param>
///<param name="heightDelta">The height delta, defaut is 36.</param>
///<returns>New boundary the <paramref name="boundary"/> is not null, otherwise null.
</returns>
privatestaticBoundary ResizeBoundary(Boundary boundary, PageRotate rotation,
double heightDelta = stampHeight)
{
if (boundary == null)
{
returnnull;
}
switch (rotation)
{
casePageRotate.Rotate90:
returnnewBoundary(boundary.Left, boundary.Bottom, boundary.Right +
heightDelta,
boundary.Top);
casePageRotate.Rotate180:
returnnewBoundary(boundary.Left, boundary.Bottom, boundary.Right,
boundary.Top + heightDelta);
casePageRotate.Rotate270:
returnnewBoundary(boundary.Left - heightDelta, boundary.Bottom,
boundary.Right + heightDelta, boundary.Top);
casePageRotate.Rotate0:
default:
returnnewBoundary(boundary.Left, boundary.Bottom - heightDelta,
boundary.Right,
boundary.Top);
}
}
}
The resulting document, transformed by the code above, looks as follows:
![]() |
Pic. 1 Transformed PDF document with stamp |
Conclusion
This sample shows how to combine fixed and flow layout API together and get the best form both. We open the document using Fixed layout API and add a Section content element from Flow layout API after resizing. It helps us to quickly generate the stamp grid, and avoid many manual drawing operations.
The actual resizing magic happens in Page.Resize() method which repositions the content according to its initial state.
This project is a small demo of what Apitron PDF Kit for .NETcan do with PDF and if you’d like to know more, then our free book is at your service. The complete code for this example can be found in our GitHub repo.
