Crop/resize a page after adding content? - itext

I'm wondering if the dimensions of a page can be altered after content has been added to it?
I'm creating a PDF document in code using iTextSharp, and placing some content on a page. I'll only know the height of the content after drawing it, then I need to basically "crop" the page, so that it's only as tall as the content.
I know I can do this by writing the content to a pdfTemplate, then doing SetPageSize() and NewPage(), then adding the template to the new page. However, this document must only have 1 page. That's the catch - I can't set the size of page 1 after the fact, only subsequent pages, but the doc must only contain one page.
Unless there's a way of deleting page 1 after adding the properly-sized second page, I can't think of how to achieve this: A one-page PDF whose page size I have to change after having written content to it.

I ended up doing the following:
Create document as a memorystream.
Create a pdfTemplate, add content to it and remember how big the content is.
Add a new (2nd) page, the same size as the content, and add template to it.
Reseek to start of memorystream, open a PdfReader on it.
Create a new PDF file with PdfCopy, copying the 2nd page from memory to the file.
After a day searching for a more direct method, this seemed the most expedient. Basically:
dim ms As New IO.MemoryStream
dim doc As New Document
dim pw As PdfWriter = PdfWriter.GetInstance(doc, ms)
doc.Open
Dim cb As PdfContentByte = pw.DirectContent
Dim tpl = cb.CreateTemplate(doc.PageSize.Width, doc.PageSize.Height)
... add content to template ...
' Add template to a new page of the right dimensions
doc.Add(New Paragraph(" ")) ' page 1 content required for NewPage to work
doc.SetPageSize(New Rectangle(width, height)) ' size of content we added
doc.NewPage()
cb.AddTemplate(tpl, 0, 0)
' Close our in-memory doc but leave stream open.
pw.CloseStream = False
pw.Close()
doc.Close()
' Now create actual file and write only second page of doc.
ms.Seek(0, IO.SeekOrigin.Begin) ' Go back to start of memorystream
Dim pr As New PdfReader(ms)
doc = New Document(pr.GetPageSizeWithRotation(2)) ' New doc, size of page 2
Dim copier As New PdfCopy(doc, New IO.FileStream(<filename>, IO.FileMode.Create))
doc.Open()
copier.AddPage(copier.GetImportedPage(pr, 2)) ' Add page 2 of our in-memory document.
copier.Close()
doc.Close()
pr.Close()
Now I have a PDF with a single page custom sized to the content which was added.
Hope it helps someone else!

Related

PdfCopy breaks the string replacement of PDF using iTextSharp

I have two pdfs, lets say content and header.
Now I want to add this header to the top of content.
So first, I have used Document, PdfWriter then took the PdfContentByte (canvas) to place the header.pdf inside content with desired location.
This addTemplate method breaks the format of PDF.
I already have string replacement method after this merging, which is not working now after this merge method.
After the suggestions from iText, I tried to use PdfCopy or PdfSmartCopy.
But how can we place this header in desired location using PdfCopy ?
If not, then how to merge without breaking the format of PDF using writer / pdfContentByte ?
Note:
I have tried to merge the Pdfs by using PdfCopy, but again its breaking.
PdfObject pdfObject = dict.GetDirectObject(PdfName.CONTENTS);
(pdfObject is PRStream)
Here the pdfObject is not a PRStream instead it becomes PdfArray.
Please help me to resolve this issue.

Updating the page number with OpenXML inside the page content (not in header)

I have a Word document with the page number inside of the header. I update this document through OpenXML by adding new content. After that, when I open this document the page number is correctly (automatically) updated.
But now, I need to have this page number not inside the header but inside the page content. In this case the page number is not correctly (automatically) updated.
How to have this page number automatically updated ? Any ideas ?
PS: I already try this code without luck:
DocumentSettingsPart settingsPart = mainPart.GetPartsOfType<DocumentSettingsPart>().First();
//Create object to update fields on open
UpdateFieldsOnOpen updateFields = new UpdateFieldsOnOpen();
updateFields.Val = new DocumentFormat.OpenXml.OnOffValue(true);
// Insert object into settings part.
settingsPart.Settings.PrependChild<UpdateFieldsOnOpen>(updateFields);
settingsPart.Settings.Save();

itext7 pdf to image

I am using iText7(java) and am looking for a way to convert a pdf page to image.
In older iText versions you could do this :
PdfImportedPage page = writer.getImportedPage(reader, 1);
Image image = Image.getInstance(page);
But iText7 does not have PdfImportedPage .
My use case, I have a one page pdf file. I need to add a table and resize the pdf contents to fit a single page. In old iText I would create a page , add table, convert existing pdf page to image, resize image and add that resized image to new page. Is there a new way to do this in iText7.
Thanks to Bruno's answer I got this working with following code :
PdfPage origPage = readerDoc.getPage(1);
Rectangle rect = origPage.getPageSize();
Document document = new Document(writerDoc);
Table wrapperTable = new Table(1);
Table containerTable = new Table(new float[]{0.5f,0.5f});
containerTable.setWidthPercent(100);
containerTable.addCell( "col1");
containerTable.addCell("col2");
PdfFormXObject pageCopy = origPage.copyAsFormXObject(writerDoc);
Image image = new Image(pageCopy);
image.setBorder(Border.NO_BORDER);
image.setAutoScale(true);
image.setHeight(rect.getHeight()-250);
wrapperTable.addCell(new Cell().add(containerTable).setBorder(Border.NO_BORDER));
wrapperTable.addCell(new Cell().add(image).setBorder(Border.NO_BORDER));
document.add(wrapperTable);
document.close();
readerDoc.close();
Please read the official documentation for iText 7, more specifically Chapter 6: Reusing existing PDF documents
In PDF, there's the concept of Form XObjects. A Form XObject is a piece of PDF content that is stored outside the content stream of a page, hence XObject which stands for eXternal Object. The use of the word Form in Form XObject could be confusing, because people might be thinking of a form as in a fillable form with fields. To avoid that confusing, we introduced the term PdfTemplate in iText 5.
The class PdfImportedPage you refer to was a subclass of PdfTemplate: it was a piece of PDF syntax that could be reused in another page. Over the years, we noticed that people also got confused by the word PdfTemplate.
In iText 7, we returned to the basics. When talking about a Form XObject, we use the class PdfFormXObject. When talking about a page in a PDF file, we use the class PdfPage.
This is how we get a PdfPage from an existing document:
PdfDocument origPdf = new PdfDocument(new PdfReader(src));
PdfPage origPage = origPdf.getPage(1);
This is how we use that page in a new document:
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
PdfFormXObject pageCopy = origPage.copyAsFormXObject(pdf);
If you want to use that pageCopy as an Image, just create it like this:
Image image = new Image(pageCopy);

is it posible to include page numbers with onendpage events

I am working through some more examples from the new itextpdf website...(good job by the way) I normally add page numbers in headers and footers as a second pass over the document once it is completed.
Is there a way to add the page number dynamically in a header or footer as an onendpage event?
Clearly this could be done by using a counter with document.addPage(), but text may create new pages all by itself when given a large text block so this would then not work.
Thank you very much for your comment on the new web site!
You can indeed get the current page number in the onEndPage() method and add it to the document. Please take a look at the MovieHistory2 example, or better yet: the MovieCountries1 example.
Allow me to simplify the onEndPage() method of these examples:
public void onEndPage(PdfWriter writer, Document document) {
Rectangle rect = writer.getPageSize();
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_CENTER, new Phrase(
String.format("page %d", writer.getPageNumber())),
(rect.getLeft() + rect.getRight()) / 2, rect.getBottom() - 18, 0);
}
In this snippet, writer.getPageNumber() will give you the current page number. I add it to the page at the bottom-middle.

I use itext.dll for generating a PDF using ASP.NET and I want a footer in my document?

I use itext.dll for genrating a PDF using ASP.NET and I want a footer in my document in the form of:
Page 1 of 6
HeaderFooter footer = new HeaderFooter(new Phrase("Page"), new Phrase(" of 6"));
footer.setBorder(Rectangle.NO_BORDER);
footer.setAlignment(Element.ALIGN_CENTER);
document.setFooter(footer);
Is this possible without hardcoding the total number of pages? I.e. is there a method to get the total number pages in a document?
I have found that there are (at least) 2 ways of doing this.
One is to create the document without the footer and after that use PdfStamper to stamp the page numbers with total on it. But that raised some problems with me when I output the stampers product to MemoryStream and there seems to be no way of closing the stamper without closing the stream at the same time.
The other way is to create one instance of PdfTemplate that will represent the total page count and add that to every page to footer or where ever you want it.
Next you can use your own PdfPageEventHelper class and implement OnCloseDocument method where you can fill the template with total page count:
public override void OnCloseDocument(PdfWriter writer, Document document)
{
PageCountTemplate.BeginText();
PageCountTemplate.SetFontAndSize(HeaderFont.BaseFont, HeaderFont.Size);
PageCountTemplate.ShowText((writer.CurrentPageNumber - 1).ToString());
PageCountTemplate.EndText();
}
I personally also use OnOpenDocument to create the template and OnEndPage to write it on each page.
EDIT:
To answer Jan's question, OnCloseDocument is called only once when the whole doc has been written. When Doc.Close() is called I mean.