PdfStamper always assuming A4 dimensions - c#

I'm attempting to write page numbers into my PDF document with itextsharp.
I've followed the example here. This answer points me in the direction of this implementation in C#.
Now, all works fine - assuming the page orientation is A4. In my case, it's not. I'm using a landscape A3 page. Because I want to nicely position the page number, I need the dimensions of the page I'm working on.
stamper.GetOverContent().PdfDocument.PageSize seems to always return the dimensions of an A4 page.
Here's a reproducible example:
using (var ms = new MemoryStream())
{
using (var doc = new Document(PageSize.A3.Rotate()))
{
Debug.WriteLine(doc.PageSize);
var writer = PdfWriter.GetInstance(doc, ms);
doc.Open();
doc.Add(new Paragraph("Hello!"));
}
byte[] firstPass = ms.ToArray();
PdfReader reader = new PdfReader(firstPass);
using (var fs = new FileStream("out2.pdf", FileMode.Create, FileAccess.Write, FileShare.None))
{
using (PdfStamper stamper = new PdfStamper(reader, fs))
{
int totalPages = reader.NumberOfPages;
for (var i = 1; i <= totalPages; i++)
{
var under = stamper.GetUnderContent(i);
var over = stamper.GetOverContent(i);
Debug.WriteLine(under.PdfDocument.PageSize);
Debug.WriteLine(over.PdfDocument.PageSize);
}
}
}
}
The output of which is:
Rectangle: 1191x842 (rot: 90 degrees)
RectangleReadOnly: 595x842 (rot: 0 degrees)
RectangleReadOnly: 595x842 (rot: 0 degrees)
How does one properly get the page size of documents with the PdfStamper?
Please note, this question is not about generating page numbers with iTextSharp. There are various workaround. This question is particularly about reading the correct dimensions of a document via PdfStamper.

I haven't got an explanation for why stamper.GetUnderContent(i).PdfDocument defaults to A4, however, the correct way to get the page size is:
var pageSize = reader.GetPageSizeWithRotation(i);
Note that this is the full page size, including margins.

Related

iTextSharp PDF only Rotates Once?

I have found code online to rotate a PDF, I then use the move command to overwrite the original file. Please let me know if there is a better way of doing this too. See code below:
string source = textBox1.Text;
string filename = Path.ChangeExtension(source, null);
string destination = filename + "-rot.pdf";
Debug.WriteLine (destination);
PdfReader reader = new PdfReader(source);
int pagesCount = reader.NumberOfPages;
for (int n = 1; n <= pagesCount; n++)
{
PdfDictionary page = reader.GetPageN(n);
PdfNumber rotate = page.GetAsNumber(PdfName.ROTATE);
//int rotation = rotate == null ? 90 : (rotate.intValue() + 90) % 360;
page.Put(PdfName.ROTATE, new PdfNumber(-90));
}
FileStream fs = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None);
PdfStamper stamper = new PdfStamper(reader, fs);
stamper.Close();
reader.Close();
File.Move(destination, source, true); //overwrite original with rotated and remove rotated file
However, I'm not sure I'm understanding the itextsharp pdf rotate function. If I then run the same thing again on the same pdf, it does not rotate it again. For example, if I had it set at 90, if I ran it twice I would expect the pdf to be at 180degrees to the original, but it does not work like that.
Is there a way to do this?
Thanks
Andrew
I’m guessing it’s rotating to 90 degrees and not by 90 degrees. So doing it twice (without saving and reloading perhaps) will just tell it to rotate to 90 not by 90.

Rotate PDF 90 Degrees in iTextSharp [duplicate]

I am trying to use a PDF for stamping and need to rotate it 90 degrees to lay it on correctly? Anyone know how to do this? Can't seem to find it online.
The Rotate90Degrees example uses PdfReader to get an instance of the document then changes the /Rotate value in every page dictionary. If there is no such entry, a /Rotate entry with value 90 is added:
final PdfReader reader = new PdfReader(source);
final int pagesCount = reader.getNumberOfPages();
for (int n = 1; n <= pagesCount; n++) {
final PdfDictionary page = reader.getPageN(n);
final PdfNumber rotate = page.getAsNumber(PdfName.ROTATE);
final int rotation =
rotate == null ? 90 : (rotate.intValue() + 90) % 360;
page.put(PdfName.ROTATE, new PdfNumber(rotation));
}
Once this is done, we use a PdfStamper to persist the change:
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
This is for iText Java. For iTextSharp, porting Java to C# is easy as the terminology is identical. Change some lower cases into upper cases like this:
PdfDictionary page = reader.GetPageN(1);
page.Put(PdfName.ROTATE, new PdfNumber(90));
There's a more or less identical code snippet in the question part of this post: How to rotate PDF page with iTextSharp without causing error in ghostscript?
For C# coders:
I replaced Bruno's answer with C# code:
and yes it's working like a charm, also you can change the rotation number to 180,270, etc
PdfReader reader = new PdfReader("Source.pdf");
int pagesCount = reader.NumberOfPages;
PdfDictionary page = reader.GetPageN(1);
PdfNumber rotate = page.GetAsNumber(PdfName.ROTATE);
page.Put(PdfName.ROTATE, new PdfNumber(90));
FileStream fs = new FileStream("created.pdf", FileMode.Create,
FileAccess.Write, FileShare.None);
PdfStamper stamper = new PdfStamper(reader, fs);

iTextSharp Resize each page to fit the pagesize

I have a .pdf document which has for example 7 pages. I split this document into 7 .pdf document, so it means that each document has only one page. But mainly I need to make fit the content of pages. So delete whitespaces, margins, resize. Have you got some simple advice? I add links for images and also code for split pdf document. Thank you for your response.
INPUT:
DESIRED OUTPUT:
CODE:
public void PdfSplitDocument(string filename)
{
String path = "C:/Doc/" + filename;
String result = "d:/output/result";
PdfCopy copy;
PdfReader reader = new PdfReader(path);
for (int i = 1; i <= reader.NumberOfPages; i++)
{
Document document = new Document(PageSize.A4, 0, 0, 0, 0);
copy = new PdfCopy(document, new FileStream(result + i + ".pdf", FileMode.Create));
document.Open();
copy.AddPage(copy.GetImportedPage(reader, i));
document.Close();
}
}
Take a look at the ShowTextMargins example. It uses the TextMarginFinder class to find the margins within which text is found. In this example, taken from my book "iText in Action - Second Edition", I use this class to draw a rectangle:
public void addMarginRectangle(String src, String dest)
throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));
TextMarginFinder finder;
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
finder = parser.processContent(i, new TextMarginFinder());
PdfContentByte cb = stamper.getOverContent(i);
cb.rectangle(finder.getLlx(), finder.getLly(),
finder.getWidth(), finder.getHeight());
cb.stroke();
}
stamper.close();
reader.close();
}
In your case, you want to crop the pages based on the rectangle. You have a finder object that allows you to get the coordinate of the lower-left corner (llx and lly) and the coordinate of the upper-right corner (urx and ury). You can use these coordinates to crop pages as is done in the CropPages example:
public void manipulatePdf(String src, String dest)
throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
int n = reader.getNumberOfPages();
PdfDictionary pageDict;
PdfRectangle rect = new PdfRectangle(llx, lly, urx, ury);
for (int i = 1; i <= n; i++) {
pageDict = reader.getPageN(i);
pageDict.put(PdfName.CROPBOX, rect);
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
Another option is to change the media box instead of the crop box:
pageDict.put(PdfName.MEDIABOX, rect);
The C# version of these examples can be found here:
ShowTextMargins
CropPages

Rotating PDF 90 degrees using iTextSharp in C#

I am trying to use a PDF for stamping and need to rotate it 90 degrees to lay it on correctly? Anyone know how to do this? Can't seem to find it online.
The Rotate90Degrees example uses PdfReader to get an instance of the document then changes the /Rotate value in every page dictionary. If there is no such entry, a /Rotate entry with value 90 is added:
final PdfReader reader = new PdfReader(source);
final int pagesCount = reader.getNumberOfPages();
for (int n = 1; n <= pagesCount; n++) {
final PdfDictionary page = reader.getPageN(n);
final PdfNumber rotate = page.getAsNumber(PdfName.ROTATE);
final int rotation =
rotate == null ? 90 : (rotate.intValue() + 90) % 360;
page.put(PdfName.ROTATE, new PdfNumber(rotation));
}
Once this is done, we use a PdfStamper to persist the change:
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
This is for iText Java. For iTextSharp, porting Java to C# is easy as the terminology is identical. Change some lower cases into upper cases like this:
PdfDictionary page = reader.GetPageN(1);
page.Put(PdfName.ROTATE, new PdfNumber(90));
There's a more or less identical code snippet in the question part of this post: How to rotate PDF page with iTextSharp without causing error in ghostscript?
For C# coders:
I replaced Bruno's answer with C# code:
and yes it's working like a charm, also you can change the rotation number to 180,270, etc
PdfReader reader = new PdfReader("Source.pdf");
int pagesCount = reader.NumberOfPages;
PdfDictionary page = reader.GetPageN(1);
PdfNumber rotate = page.GetAsNumber(PdfName.ROTATE);
page.Put(PdfName.ROTATE, new PdfNumber(90));
FileStream fs = new FileStream("created.pdf", FileMode.Create,
FileAccess.Write, FileShare.None);
PdfStamper stamper = new PdfStamper(reader, fs);

Itextsharp: Adjust 2 elements on exactly one page

So, I'm having this problem using C# (.NET 4.0 + WinForms) and iTextSharp 5.1.2.
I have some scanned images stored on a DB and need to build on the fly PDF with those images. Some files have just one page and other ones hundreds. That is working just fine using:
foreach (var page in pages)
{
Image pageImage = Image.GetInstance(page.Image);
pageImage.ScaleToFit(document.PageSize.Width,document.PageSize.Height);
pageImage.Alignment = Image.ALIGN_TOP | Image.ALIGN_CENTER;
document.Add(pageImage);
document.NewPage();
//...
}
The problem is:
I need to add an small table at the bottom of the last page.
I try:
foreach (var page in pages)
{
Image pageImage = Image.GetInstance(page.Image);
pageImage.ScaleToFit(document.PageSize.Width,document.PageSize.Height);
pageImage.Alignment = Image.ALIGN_TOP | Image.ALIGN_CENTER;
document.Add(pageImage);
document.NewPage();
//...
}
Table t = new table....
document.Add(t);
The table is successfully added but IF the size of the image fits the page size of the document then the table is added on the next page.
I need to resize the last image of the document (if it has multiple ones, or the first if has only 1) in order to put the table directly on that page (with the image) and that both ocuppy just one page.
I try to scale the image by percent BUT given that the image size of the image that'll be on the last page is unknow and that it must FILL the biggest portion of the page I need to do that dinamically.
Any idea?
Let me give you a couple of things that might help you and then I'll give you a full working example that you should be able to customize.
The first thing is that the PdfPTable has a special method called WriteSelectedRows() that allows you to draw a table at an exact x,y coordinate. It has six overloads but the most commonly used one is probably:
PdfPTable.WriteSelectedRows(int rowStart,int rowEnd, float xPos, float yPos, PdfContentByte canvas)
To place a table with the upper left corner positioned at 400,400 you would call:
t.WriteSelectedRows(0, t.Rows.Count, 400, 400, writer.DirectContent);
Before calling this method you are required to set the table's width using SetTotalWidth() first:
//Set these to your absolute column width(s), whatever they are.
t.SetTotalWidth(new float[] { 200, 300 });
The second thing is that the height of the table isn't known until the entire table is rendered. This means that you can't know exactly where to place a table so that it truly is at the bottom. The solution to this is to render the table to a temporary document first and then calculate the height. Below is a method that I use to do this:
public static float CalculatePdfPTableHeight(PdfPTable table)
{
using (MemoryStream ms = new MemoryStream())
{
using (Document doc = new Document(PageSize.TABLOID))
{
using (PdfWriter w = PdfWriter.GetInstance(doc, ms))
{
doc.Open();
table.WriteSelectedRows(0, table.Rows.Count, 0, 0, w.DirectContent);
doc.Close();
return table.TotalHeight;
}
}
}
}
This can be called like this:
PdfPTable t = new PdfPTable(2);
//In order to use WriteSelectedRows you need to set the width of the table
t.SetTotalWidth(new float[] { 200, 300 });
t.AddCell("Hello");
t.AddCell("World");
t.AddCell("Test");
t.AddCell("Test");
float tableHeight = CalculatePdfPTableHeight(t);
So with all of that here's a full working WinForms example targetting iTextSharp 5.1.1.0 (I know you said 5.1.2 but this should work just the same). This sample looks for all JPEGs in a folder on the desktop called "Test". It then adds them to an 8.5"x11" PDF. Then on the last page of the PDF, or if there's only 1 JPEG to start with on the only page, it expands the height of the PDF by however tall the table that we're adding is and then places the table at the bottom left corner. See the comments in the code itself for further explanation.
using System;
using System.Text;
using System.Windows.Forms;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
namespace Full_Profile1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public static float CalculatePdfPTableHeight(PdfPTable table)
{
//Create a temporary PDF to calculate the height
using (MemoryStream ms = new MemoryStream())
{
using (Document doc = new Document(PageSize.TABLOID))
{
using (PdfWriter w = PdfWriter.GetInstance(doc, ms))
{
doc.Open();
table.WriteSelectedRows(0, table.Rows.Count, 0, 0, w.DirectContent);
doc.Close();
return table.TotalHeight;
}
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
//Create our table
PdfPTable t = new PdfPTable(2);
//In order to use WriteSelectedRows you need to set the width of the table
t.SetTotalWidth(new float[] { 200, 300 });
t.AddCell("Hello");
t.AddCell("World");
t.AddCell("Test");
t.AddCell("Test");
//Calculate true height of the table so we can position it at the document's bottom
float tableHeight = CalculatePdfPTableHeight(t);
//Folder that we are working in
string workingFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Test");
//PDF that we are creating
string outputFile = Path.Combine(workingFolder, "Output.pdf");
//Get an array of all JPEGs in the folder
String[] AllImages = Directory.GetFiles(workingFolder, "*.jpg", SearchOption.TopDirectoryOnly);
//Standard iTextSharp PDF init
using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (Document document = new Document(PageSize.LETTER))
{
using (PdfWriter writer = PdfWriter.GetInstance(document, fs))
{
//Open our document for writing
document.Open();
//We do not want any margins in the document probably
document.SetMargins(0, 0, 0, 0);
//Declare here, init in loop below
iTextSharp.text.Image pageImage;
//Loop through each image
for (int i = 0; i < AllImages.Length; i++)
{
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Increase the size of the page by the height of the table
document.SetPageSize(new iTextSharp.text.Rectangle(0, 0, document.PageSize.Width, document.PageSize.Height + tableHeight));
}
//Add a new page to the PDF
document.NewPage();
//Create our image instance
pageImage = iTextSharp.text.Image.GetInstance(AllImages[i]);
pageImage.ScaleToFit(document.PageSize.Width, document.PageSize.Height);
pageImage.Alignment = iTextSharp.text.Image.ALIGN_TOP | iTextSharp.text.Image.ALIGN_CENTER;
document.Add(pageImage);
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Draw the table to the bottom left corner of the document
t.WriteSelectedRows(0, t.Rows.Count, 0, tableHeight, writer.DirectContent);
}
}
//Close document for writing
document.Close();
}
}
}
this.Close();
}
}
}
EDIT
Below is an edit based on your comments. I'm only posting the contents of the for loop which is the only part that changed. When calling ScaleToFit you just need to take tableHeight into account.
//Loop through each image
for (int i = 0; i < AllImages.Length; i++)
{
//Add a new page to the PDF
document.NewPage();
//Create our image instance
pageImage = iTextSharp.text.Image.GetInstance(AllImages[i]);
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Scale based on the height of document minus the table height
pageImage.ScaleToFit(document.PageSize.Width, document.PageSize.Height - tableHeight);
}
else
{
//Scale normally
pageImage.ScaleToFit(document.PageSize.Width, document.PageSize.Height);
}
pageImage.Alignment = iTextSharp.text.Image.ALIGN_TOP | iTextSharp.text.Image.ALIGN_CENTER;
document.Add(pageImage);
//If we only have one image or we are on the second to last one
if ((AllImages.Length == 1) | (i == (AllImages.Length - 1)))
{
//Draw the table to the bottom left corner of the document
t.WriteSelectedRows(0, t.Rows.Count, 0, tableHeight, writer.DirectContent);
}
}
Just use a method table.CalculateHeights() if you want to know the height of table.

Categories