Inserting a "linked rectangle" with itext - itext

I want to insert a hyperlink into an existing PDF at a position I know in advance: I already have the coordinates of a rectangle on a given page. I want to link this rectangle to another page of the same PDF (which I also know in advance).
How do I achieve this?

Please take a look at the AddLinkAnnotation example.
As you (should) already know (but you didn't show what you've already tried, which is kind of mandatory on StackOverflow), you can use PdfStamper to manipulate an existing PDF. Adding a rectangular link on one page to another page, is as simple as adding a link annotation to that page:
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Rectangle linkLocation = new Rectangle(523, 770, 559, 806);
PdfDestination destination = new PdfDestination(PdfDestination.FIT);
PdfAnnotation link = PdfAnnotation.createLink(stamper.getWriter(),
linkLocation, PdfAnnotation.HIGHLIGHT_INVERT,
3, destination);
link.setBorder(new PdfBorderArray(0, 0, 0));
stamper.addAnnotation(link, 1);
stamper.close();
The link object is created using:
the writer instance tied to the stamper,
the rectangle (the position you say you know in advance,
a highlighting option (pick one: HIGHLIGHT_NONE, HIGHLIGHT_INVERT, HIGHLIGHT_OUTLINE, HIGHLIGHT_PUSH, HIGHLIGHT_TOGGLE),
the page you want to link to,
a destination (different options are possible, see The ABC of PDF).
Once you have an instance of PdfAnnotation, you can add it to a specific page using the addAnnotation() method.

Related

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

iTextSharp - Continuing ordered list on second page with a number other than '1'

I am fairly new to iTextSharp. I create PDFs by adding variable data (text/barcodes/images) to existing PDF documents/templates (think boiler plate). Most commonly, I have to place various sections of text in specific places. I know how to create an ordered list, but I have come across a situation where the list begins with #1 on the first page and then #2-4 on the top of the second page. I use two different templates for p1 and p2.
I am currently creating the document by creating ColumnTexts, placing SimpleColumns with specific coordinates, and then placing phrases inside. I am not sure if this is the best way or not, so I am open for alternative solutions.
I have checked out several places including http://www.mikesdotnetting.com/article/83/lists-with-itextsharp but I see nothing that describes how to start a list at a number other than '1'. None of the 6 overloads provide a parameter for starting number.
Thanks!
There are two answers to your question. The first one is to point you to the official documentation. There is a method setFirst() that (I quote) sets the number that has to come first in the list.
You are using the C# port of iText, so if you want the list to start counting at 10, you need to do something like:
list.First = 10;
The second answer takes more time, but it is probably the better one.You don't need two List objects, one for the first page and one for the second page. It's better to add the List to a ColumnText object and then distribute the column over two pages.
Take a look at the ListInColumn example. It takes an existing PDF (with the text "Hello World Hello People") and it adds a list using ColumnText: list_in_column.pdf
This is how it's done:
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
List list = new List(List.ORDERED);
for (int i = 0; i < 10; i++) {
list.add("...");
}
ColumnText ct = new ColumnText(stamper.getOverContent(1));
ct.addElement(list);
Rectangle rect = new Rectangle(250, 400, 500, 806);
ct.setSimpleColumn(rect);
int status = ct.go();
if (ColumnText.hasMoreText(status)) {
ct.setCanvas(stamper.getOverContent(2));
ct.setSimpleColumn(rect);
ct.go();
}
stamper.close();
To add the content on the first page, I use:
ColumnText ct = new ColumnText(stamper.getOverContent(1));
You are probably using similar code.
The content is added using the line:
int status = ct.go();
If not all the content was added, I change the canvas to add the rest of the content on the second page:
ct.setCanvas(stamper.getOverContent(2));
The rest of the code is pretty standard.
I think the setCanvas() method is the missing piece in your puzzle, although in your case, you'll need:
ct.Canvas = stamper.GetOverContent(2);

Creating a PDF image in iText

I'm trying to add an image that I have on the filesystem as a pdf into a pdf that I'm creating on the fly.
I tried to use the Image class, but it seems that it does not work with PDFs (only JPEG, PNG or GIF). How can I create an element from an existing PDF, so that I can place it in my new PDF?
Please download chapter 6 of my book and read all about the class PdfImportedPage.
In the most basic example, you'd create a PdfReader instance and import the page into the PdfWriter instance, from which point on you can use the PdfImportedPage instance, either directly, or wrapped inside an Image object:
PdfReader reader = new PdfReader(existingPdf);
PdfImportedPage page = writer.getImportedPage(reader, i);
Image img = Image.getInstance(page);
reader.close();

Second page for PDFContentByte

I'm pretty sure I'm missing something simple, but since I've been breaking my head on this for a while, I'm just going to ask.
I'm using JavaScript to access the iText (Java) library to take a filable PDF and serve it up via a browser. The process has worked for my first one, and now I'm doing one where the original fillable PDF has 2 pages. I've been trying to get the second page for a while now. I'm using the PdfContentByte to get it to the browser, and it works except I can't seem to get the PdfContentByte to have a second page. My relevant code is below. When I add the second template (page2) they way I do, it moves what I'm writing, but I'm still just getting one (US letter) page.
This may not be the most efficient code, but like I said, I've been trying a few things on this. If someone has a pointer, I would be very grateful.
var cb:com.itextpdf.text.pdf.PdfContentByte = writer.getDirectContent();
var cb2:com.itextpdf.text.pdf.PdfContentByte = writer.getDirectContent();
var reader2:com.itextpdf.text.pdf.PdfReader = new com.itextpdf.text.pdf.PdfReader(os.toByteArray());
var page:com.itextpdf.text.pdf.PdfImportedPage = writer.getImportedPage(reader2, 1);
cb.addTemplate(page, 0, 0); //this works as expected
var page2:com.itextpdf.text.pdf.PdfImportedPage = writer.getImportedPage(reader2, 2);
// this will add, and with the 100 do an offset, but the
// "physical size" of the paper is the same
cb2.addTemplate(page2, 0, 100);
Have a look at chapter 6 of iText in Action, 2nd edition, especially at subsection 6.4.1: Concatenating and splitting PDF documents.
Listing 6.22, ConcatenateStamp.java, shows you how you should create a PDF from copies of pages of multiple other PDFs; the sample actually additionally adds a new "Page X of Y" footer which you may keep or remove from the sample.

Crop/resize a page after adding content?

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!