Reputation: 13
I am currently trying to add a link to the header of footer of a pdf document however the library gives the following error System.IndexOutOfRangeException: 'Requested page number 0 is out of bounds.' when adding the link to the header using the IText7 library.
Adding the same object to the body of the page works fine.
Surrounding the code with a try catch results in the following:
I couldn't find any code examples online regarding this problem in IText7, the solutions in ITextSharp are not applicable anymore.
My question is how do I add a link to an external website to the header of the pdf? Is the current behavior a bug in the library or intended?
I am using the following code:
The main method, loading the html, initializing the document and adding the object to the header and the main page.
public void Convert()
{
// Initialize template
IList<IElement> templateElements = HtmlConverter.ConvertToElements(File.ReadAllText("FooterTest.html"));
// Initialize document
PdfWriter pdfWriter = new PdfWriter("Output.pdf");
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument);
document.SetTopMargin(100);
// Adding the header object to the header and the main body
pdfDocument.AddEventHandler(PdfDocumentEvent.START_PAGE, new PdfHeader((IBlockElement)templateElements[0], document));
document.Add((IBlockElement)templateElements[0]);
document.Close();
}
Event handler class responsible for adding the object to the header. The code gives the error mentioned above within the try-catch
public class PdfHeader : IEventHandler
{
private readonly IBlockElement footer;
private readonly Document doc;
public PdfHeader(IBlockElement footer, Document doc)
{
this.doc = doc;
this.footer = footer;
}
public void HandleEvent(Event headerEvent)
{
PdfDocumentEvent docEvent = (PdfDocumentEvent)headerEvent;
PdfDocument pdf = docEvent.GetDocument();
PdfPage page = docEvent.GetPage();
Rectangle pageSize = page.GetPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(page.GetLastContentStream(), page.GetResources(), pdf);
Rectangle rectangle = new Rectangle(
pdf.GetDefaultPageSize().GetX() + doc.GetLeftMargin(),
pdf.GetDefaultPageSize().GetTop() - 80,
page.GetPageSize().GetWidth() - doc.GetLeftMargin() - doc.GetRightMargin(),
50);
//Below is the code where the error is produced.
try
{
new Canvas(pdfCanvas, pdf, rectangle).Add(footer);
}
catch { }
}
}
The html file containing the header object (FooterTest.html loaded in the Convert() method)
<html>
<body>
<table>
<tr>
<td>
This is a some text not containing a link.
</td>
</tr>
<tr>
<td>
This text contains a link to <a href="https://www.google.com">Google</a> to demonstrate the issue.
</td>
</tr>
</table>
</body>
</html>
This is my first question on stack overflow so any feedback on the question itself is also appreciated.
Upvotes: 1
Views: 2632
Reputation: 588
What you've encountered is not entirely a bug, however iText should definitely fail more gracefully and explanatory in this case.
The problem here, is that for the Canvas
class it's actually not known what is the page on which the drawing performs. In general case Canvas
is just a high level wrapper for the content drawing operations, which can be placed on any content stream (e.g. in form XObject, page content stream etc). However the link is something that is defined specifically on a page level (via link annotation).
It's reasonable easy to work around the issue. I can suggest you two approaches.
First approach is to fix the issue by overriding the CanvasRenderer:
// set the custom renderer:
Canvas canvas = new Canvas(pdfCanvas, pdf, rectangle);
canvas.setRenderer(new PageCanvasRenderer(canvas, page));
canvas.add(footer);
...
private static class PageCanvasRenderer extends CanvasRenderer {
private final PdfPage page;
public PageCanvasRenderer(Canvas canvas, PdfPage page) {
super(canvas);
this.page = page;
}
@Override
protected LayoutArea updateCurrentArea(LayoutResult overflowResult) {
if (currentArea == null) {
currentArea = new RootLayoutArea(canvas.getPdfDocument().getPageNumber(page), canvas.getRootArea().clone());
}
return currentArea;
}
}
Second approach is to use Document
instance instead of the Canvas
. The Document
works with the pages content always, so the explained issue doesn't exist here. You can use fixed positioning to place the content in PdfHeader:
Replace the
new Canvas(pdfCanvas, pdf, rectangle).Add(footer);
with
Document document = new Document(pdf);
Div canvas = new Div().setFixedPosition(pdf.getPageNumber(page), rectangle.getLeft(), rectangle.getBottom(), rectangle.getWidth());
canvas.add(footer);
document.add(canvas);
// Don't close document itself! It would close the PdfDocument!
document.getRenderer().close();
Upvotes: 2