iTextSharp PDF only Rotates Once? - c#

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.

Related

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);

PdfStamper always assuming A4 dimensions

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.

Something is Leaving my Files Open

I have a web api that creates 2 temporary PDFs, merges them, then deletes them. Problem is, in my code, when it goes to clean out the temp files, it tells me they're being used by another process. Something in the below code is keeping the files open so that, after this function is called, they can't be deleted. For the life of me, I can't figure out what. I've tried disposing whatever resources I can find and deleting the files in all different places trying to figure out WHAT is leaving them open, and I think I've narrowed it down to the if statement after the f counter is being incremented. I think it doesn't like the way the reader is being reassigned.
public static bool MergeFiles(string destinationFile, string[] sourceFiles)
{
int f = 0;
var reader = new PdfReader(sourceFiles[f]);
int n = reader.NumberOfPages;
using (var fileStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
{
var document = new Document(reader.GetPageSizeWithRotation(1));
PdfWriter writer = PdfWriter.GetInstance(document, fileStream);
document.Open();
PdfContentByte cb = writer.DirectContent;
while (f < sourceFiles.Length)
{
int i = 0;
while (i < n)
{
i++;
document.SetPageSize(reader.GetPageSizeWithRotation(i));
document.NewPage();
PdfImportedPage page = writer.GetImportedPage(reader, i);
int rotation = reader.GetPageRotation(i);
if (rotation == 90 || rotation == 270)
{
cb.AddTemplate(page, 0, -1f, 1f, 0, 0,reader.GetPageSizeWithRotation(i).Height);
}
else
{
cb.AddTemplate(page, 1f, 0, 0, 1f, 0, 0);
}
}
f++;
if (f < sourceFiles.Length)
{
reader = new PdfReader(sourceFiles[f]);
n = reader.NumberOfPages;
}
}
document.Close();
writer.Dispose();
}
reader.Close();
reader.Dispose();
Log.Info(string.Format("Documents merged into: {0}", destinationFile));
return true;
}
Later in the code, the files are being removed using the following command:
File.Delete(tempCoverLetterFile);
FWIW: I've found that, if you put this code above the using statement, it will delete the one file, no problem:
reader.Dispose();
File.Delete(sourceFiles[0]);
I also found that, if you add a reader.Dispose() in the if statement before you reassign the reader to the next pdf, it works. But, the end pdf that gets merged is corrupted and won't open.
Where you have if f < sourcefiles.length you open a new reader. However, you havent closed the old. This would leave you with open files.
I would expect before the new reader is opened you need to add reader.close and reader.dispose (this latter maybe optional)

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 - Overwrite Text

In the code below, I am attempting to place a layer of text over existing text (the original existing text is "hidden" by writing it in white font; it is serving as a placeholder for this replacement process). The code below is very heavily reliant on code provided by Chris Haas in this post:
Getting Coordinates of string using ITextExtractionStrategy and LocationTextExtractionStrategy in Itextsharp
However, the "left" position for my text placement is fine, but the "bottom" is lower than the text I'm trying to overwrite (even though the font is the same). How can I alter this to get the correct coordinates of the text in the original document? The original document was created using tables for layout, so does that impact this "overwrite" process?
byte[] content;
string tmppath = #"C:\Junk\MSE_1.pdf";
using (MemoryStream output = new MemoryStream())
{
PdfReader pdf_rdr = new PdfReader(tmppath);
PdfStamper stamper = new PdfStamper(pdf_rdr, output);
for (int i = 1; i < pdf_rdr.NumberOfPages; i++)
{
//Create an instance of our strategy
var t = new MyLocationTextExtractionStrategy("stuff");
if (t != null)
{
//Parse the page of the document above
//(if the text was found)
var ex = PdfTextExtractor.GetTextFromPage(pdf_rdr, i, t);
//Loop through each chunk found
foreach (var p in t.myPoints)
{
Console.WriteLine(string.Format("Found text {0} at {1} x {2} on page {3}", p.Text, p.Rect.Left, p.Rect.Bottom, i.ToString()));
PdfContentByte pcb = stamper.GetOverContent(i);
pcb.BeginText();
try
{
BaseFont bf = BaseFont.CreateFont(#"C:\Junk\FontFiles\georgia.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
pcb.SetFontAndSize(bf, 10);
pcb.SetTextMatrix(p.Rect.Left, p.Rect.Bottom);
//pcb.SetTextRise(3);
pcb.ShowText("STUFF");
}
finally
{
pcb.EndText();
}
}
}
}
// Set the flattening flag to true, as the editing is done
stamper.FormFlattening = true;
// close the pdf stamper
stamper.Close();
//close the PDF reader
pdf_rdr.Close();
//put the output into the byte array
content = output.ToArray();
}
//write the content to a PDF file
using (FileStream fs = File.Create(#"C:\Junk\MSE_REPLACED.pdf"))
{
fs.Write(content, 0, (int)content.Length);
fs.Flush();
}
Chris' MyLocationTextExtractionStrategy only considers the descent line and the ascent line of the text pieces because it is interested in the area used by the string.
For your task, though, you need the base line because text drawing operations use the current position as base line start.
Thus, you should create a variant of Chris' class which manages the base line of the text instead of / in addition to its ascent and descent line.
PS: The OP asked in a comment
I'm afraid I'm not familiar with working with the base line. Do you have any examples you could share?
If you look at Chris' code you referred to, you'll see this RenderText implementation:
public override void RenderText(TextRenderInfo renderInfo) {
base.RenderText(renderInfo);
//Get the bounding box for the chunk of text
var bottomLeft = renderInfo.GetDescentLine().GetStartPoint();
var topRight = renderInfo.GetAscentLine().GetEndPoint();
//Create a rectangle from it
var rect = new iTextSharp.text.Rectangle(
bottomLeft[Vector.I1],
bottomLeft[Vector.I2],
topRight[Vector.I1],
topRight[Vector.I2]
);
//Add this to our main collection
this.myPoints.Add(new RectAndText(rect, renderInfo.GetText()));
}
As you see he stores the rectangle with the lower left corner at the start of the descent line and the upper right corner being the end of the ascent line.
If you replace
//Get the bounding box for the chunk of text
var bottomLeft = renderInfo.GetDescentLine().GetStartPoint();
var topRight = renderInfo.GetAscentLine().GetEndPoint();
by
//Get the bounding box for the chunk of text above the baseline
var bottomLeft = renderInfo.GetBaseline().GetStartPoint();
var topRight = renderInfo.GetAscentLine().GetEndPoint();
your overwriting code should work just fine.

Categories