I need to add some text content to the bottom of an existing .pdf document with iText. I have a working method, but the new content is displayed in the top-left corner, overlapping the existing content:
public PdfDocument GetDocumentWithAppendedContent(string path, string content)
{
var stream = new MemoryStream();
var writer = new PdfWriter(stream);
writer.SetCloseStream(false); // so I can reuse stream to create a readonly document later
var pdfResult = new PdfDocument(new PdfReader(path), writer);
var document = new Document(pdfResult);
var div = new Div().SetMargin(0).SetPadding(0).SetKeepTogether(true);
div.Add(new Paragraph(content));
document.Add(div);
document.Close();
pdfResult.Close();
stream.Position = 0;
return new PdfDocument(new PdfReader(stream)); // return a readonly version
}
How to "move" the content at the bottom of the last page, instead of the beginning of the first? I am new to iText and, surprisingly, can't find a solution online.
You can do it using the code like this.
PdfPage page = doc.GetPage(doc.GetNumberOfPages());
//Create canvas fro the last page
Canvas canvas = new Canvas(page, page.GetPageSize());
//Set fixed position to put the div at the left bottom corner of the canvas
div.SetFixedPosition(0, 0, page.getPageSize().getWidth());
canvas.Add(p);
doc.Close();
Related
I have 2 pdf documents. One with the actual content pdf, the second one is a letterhead pdf. I'm trying to overlay one on the other one but the issue is once overlapped content is over letterhead as well. Here is the code that I have recently. My question is how can I put the content between header and footer? Thanks in advance.
string originalPdfPath = #"C:\Users\BiberEmr\Downloads\testPdf\ThreePageContent.pdf";
string letterHeadPath = #"C:\Users\BiberEmr\Downloads\testPdf\letterhead_lorenzo.pdf";
// Opening reader on content PDF and creating stamper
PdfReader contentReader = new PdfReader(originalPdfPath);
PdfStamper stamper = new PdfStamper(contentReader, new FileStream(#"C:\Users\BiberEmr\Downloads\testPdf\result2.pdf", FileMode.Create));
// Opening template pdf
PdfReader templateReader = new PdfReader(letterHeadPath);
PdfImportedPage templatePage = stamper.GetImportedPage(templateReader, 1);
// Looping through pages
for (int i = 1; i <= contentReader.NumberOfPages; i++)
{
// Retrieve content page where to apply template
PdfContentByte contentPage = stamper.GetUnderContent(i);
// Apply template to PDF content
contentPage.AddTemplate(templatePage, 0, 50);
}
stamper.Close();
templateReader.Close();
contentReader.Close();
Here is the current output
Page 1
Page2
I have a PDF file with some images, which I want to replace with some other PDF. The code goes through the pdf and gets the image references:
PdfDocument pdf = new PdfDocument(new PdfReader(args[0]), new PdfWriter(args[1]));
for(int i=1; i<=pdf.GetNumberOfPages(); ++i)
{
PdfDictionary pageDict = pdf.GetPage(i).GetPdfObject();
PdfDictionary resources = pageDict.GetAsDictionary(PdfName.Resources);
PdfDictionary xObjects = resources.GetAsDictionary(PdfName.XObject);
foreach (PdfName imgRef in xObjects.KeySet())
{
// image reference
}
}
For all my images I have a corresponding PDF which I would like to replace the image with. What I tried is to Put the other PDF (which is always a single page) as object by:
PdfDocument other = new PdfDocument(new PdfReader("replacement.pdf"));
xObjects.Put(imgRef, other.GetFirstPage().GetPdfObject().Clone());
But while closing the PdfDocument an exception is thrown:
iText.Kernel.PdfException: 'Pdf indirect object belongs to other PDF document. Copy object to current pdf document.'
How can I achieve to replace the image with (the content of) another PDF?
Update
I also tried a few other approaches, which maybe improved results. To overcome the previous error message, I copy the page to the original pdf by:
var page = other.GetFirstPage().CopyTo(pdf);
However, replacing the xObject doesn't work:
xObjects.Put(imgRef, page.GetPdfObject());
Results in a corrupted PDF.
To just copy the original page into another document to be used as an image replacement, you can use PdfPage#CopyAsFormXObject.
So let's assume we have this PDF as a template and we want to replace the image of a desert with the contents of another PDF:
Let's also assume the PDF that we want to use as a replacement looks as follows:
The issue is that if we blindly replace the original image with the contents of the PDF, chances are we will get something like this:
So we will get a feeling that everything worked well while we still have a bad visual result. The issue is that coordinates work a bit differently for plain raster images and vector XObjects (PDF replacements). So we also need to adjust the transformation matrix (/Matrix key) of our newly created XObject.
So the code could look like this:
PdfDocument pdf = new PdfDocument(new PdfReader(#"template.pdf"), new PdfWriter(#"out.pdf"));
for(int i=1; i<=pdf.GetNumberOfPages(); ++i) {
PdfDictionary pageDict = pdf.GetPage(i).GetPdfObject();
PdfDictionary resources = pageDict.GetAsDictionary(PdfName.Resources);
PdfDictionary xObjects = resources.GetAsDictionary(PdfName.XObject);
IDictionary<PdfName, PdfStream> toReplace = new Dictionary<PdfName, PdfStream>();
foreach (PdfName imgRef in xObjects.KeySet()) {
PdfStream previousXobject = xObjects.GetAsStream(imgRef);
PdfDocument imageReplacementDoc =
new PdfDocument(new PdfReader(#"insert.pdf"));
PdfXObject imageReplacement = imageReplacementDoc.GetPage(1).CopyAsFormXObject(pdf);
toReplace[imgRef] = imageReplacement.GetPdfObject();
adjustXObjectSize(imageReplacement);
imageReplacementDoc.Close();
}
foreach (var x in toReplace) {
xObjects.Put(x.Key, x.Value);
}
}
pdf.Close();
UPD: Implementation of adjustXObjectSize(thanks mkl):
private void adjustXObjectSize(PdfXObject pageXObject) {
float scaleXobject = 1 / Math.Max(pageXObject.GetWidth(), pageXObject.GetHeight());
AffineTransform transform = new AffineTransform();
transform.Scale(scaleXobject, scaleXobject);
float[] matrix = new float[6];
transform.GetMatrix(matrix);
pageXObject.GetPdfObject().Put(PdfName.Matrix, new PdfArray(matrix));
}
And the visual result after running the above code on the samples I described would look like this:
i want to save pdf page as image but no success. i am able to create iText.Layout.Element.Image from pdf with itext7 but stuck here
using var pdfreader = new PdfReader("../../../documents/valid.pdf");
PdfDocument origPdf = new PdfDocument(pdfreader);
PdfPage origPage = origPdf.GetPage(1);
using var stream = new MemoryStream();
using var pdfwriter = new PdfWriter(stream);
PdfDocument pdf = new PdfDocument(pdfwriter);
Document document = new Document(pdf);
PdfFormXObject pageCopy = origPage.CopyAsFormXObject(pdf);
Image image = new Image(pageCopy);
// want to save this image
The iText 7 Image class is (according to JavaDocs) a layout element that represents an image for inclusion in the document model. It essentially can wrap arbitrary contents to be added to the contents of some page (or form XObject, ...) in an image like manner. It is not, however, an arbitrary-content-to-bitmap converter.
If you want to render a page as a bitmap using iText 7 components, consider using the iText 7 Core add-on pdfRender.
I am currently designing a report for a customer and I have to place a text at the bottom of the last page. I have to do it while generating each quarter for 16k pdfs.
iText 7.1.5 is used, but will be upgraded to the latest version with the next release.
Doing it with a Footer on every page is not an option because the paragraph can have up to 14 lines of text. Adding a normal paragraph at the end of the document is also no solution because my client requested that the text is on top of the footer.
The expected result:
current generation of PDFs
PdfADocument pdf = new PdfADocument(...)
...
// handler for adding header and footer on every page
pdf.AddEventHandler(PdfDocumentEvent.END_PAGE, headerFooterHandler);
Document doc = new Document(pdf);
doc.SetTopMargin(ConversionUtility.MillimeterToPoint(48));
doc.SetLeftMargin(ConversionUtility.MillimeterToPoint(26));
doc.SetRightMargin(ConversionUtility.MillimeterToPoint(18));
doc.SetBottomMargin(ConversionUtility.MillimeterToPoint(26));
... Some customer specific code
// paragraphs and data table is added
foreach(var feeLine in feeList.Values) {
switch (feeLine.Type) {
case "U":
case "T1":
case "T2":
case "BS":
doc.Add(GenerateTextBlock(feeLine, CheckSameType(feeLine.Type, feeList, i)));
break;
case "U3":
doc.Add(GenerateTextBlock(feeLine, CheckSameType(feeLine.Type, feeList, i)));
GenerateTableBlockStart(GetColumnCount(feeList[i + 1]));
break;
default:
if (CheckEndOfTable(feeList, i)) {
var table = GenerateTableBlock(feeLine, ColumnCount, true);
doc.Add(table);
table.Complete();
} else {
GenerateTableBlock(feeLine, ColumnCount, false);
}
break;
}
}
headerFooterHandler.WritePageTotal(pdf);
doc.Close();
I would need some advise / piece of code how to find the remaining space on the last page. Placing and writing the text is no problem.
You can use the absolute positioning to position the text right where you want for the last page. All you need to know is to find the position where you want to place the paragraph. This can be done by opening any pdf in a PDF Reader such as Adobe / Foxit Reader and changing the ruler to points. Now all you need is to zoom in and find the position where you want to place the text. For example
`string dest = "destination pdf's path"
//Initialize PDF Writer
writer = new PdfWriter(dest);
//Initialize PDF Document
pdf = new PdfDocument(writer);
// Initialize document
document = new Document(pdf, PageSize.A4);
//You page text here
Paragraph p = new Paragraph("bla bla bla bla ");
document.Add(p);
//Write what ever you want to write on the page...
.
.
Paragraph footer = new Paragraph("some text")
footer.SetFixedPosition(72f, 50f, 500f);
footer.SetFontSize(6f);
document.Add(footer);
document.Close();`
As you fill the main page area only using a Document with a default DocumentRenderer, you can simply query the current area of the document renderer.
E.g. this piece of code writes into each line the bounding box of available space before drawing the line in question:
using (PdfWriter pdfWriter = new PdfWriter(#"DetermineRemainingSpace.pdf"))
using (PdfDocument pdfDocument = new PdfDocument(pdfWriter))
using (Document document = new Document(pdfDocument))
{
for (int i = 0; i < 30; i++)
{
Rectangle currentBox = document.GetRenderer().GetCurrentArea().GetBBox();
string current = string.Format(CultureInfo.InvariantCulture, "{0:F}×{1:F} from ({2:F}, {3:F}) to ({4:F}, {5:F})", currentBox.GetWidth(), currentBox.GetHeight(),
currentBox.GetLeft(), currentBox.GetBottom(), currentBox.GetRight(), currentBox.GetTop());
document.Add(new Paragraph(string.Format("{0:D2}, previously available {1}", i, current)));
}
}
The output:
Thus,
I would need some advise / piece of code how to find the remaining space on the last page. Placing and writing the text is no problem.
simply query the document renderer current area as above after all regular content has been added to the document.
I have an application that uses itextsharp to fill PDF form fields.
I got new requirement from the customer to allow underlining the fields values.
I have read many posts including answers to questions in this site but I could't figure out a way to do it.
Current my code does the following:
Creates a PDFStamper instance
Get the form fields using stamper.AcroFields property
Set the field value using the AcroFields.SetFieldRichValue() method.
But when I am opening the PDF the field is empty.
I verified that the field is set as rich text in the PDF itself.
Any idea what I am doing wrong ?
Here is a snnipest of my code:
FileStream stream = File.Open(targetFile, FileMode.Create);
var pdfStamper = new PdfStamper(new PdfReader(sourceFile), stream);
// Iterate the fields in the PDF
foreach (var fieldName in pdfStamper.AcroFields.Fields.Keys)
{
// Get the field value of the current field
var fieldValue = "<?xml version=\"1.0\"?><body xmlns=\"http://www.w3.org/1999/xtml\" xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\" xfa:contentType=\"text/html\" xfa:APIVersion=\"Acrobat:8.0.0\" xfa:spec=\"2.4\"><p style=\"text-align:left\"><b><i>Here is some bold italic text</i></b></p><p style= \"font-size:16pt\">This text uses default text state parameters but changes the font size to 16.</p></body>"
// Set the field value
if (String.IsNullOrEmpty(fieldValue) == false)
{
pdfStamper.AcroFields.SetFieldRichValue(key, fieldValue);
}
}
Edit:
I have revised my code based on Mark Storer's post to a question (http://stackoverflow.com/questions/1454701/adding-rich-text-to-an-acrofield-in-itextsharp). The new code is:
// Create reader to read the source file
var reader = new PdfReader(sourceFile);
// Create a stream for the generated file
var stream = File.Open(targetFile, FileMode.Create);
// Create stamper to generate the new file
var pdfStamper = new PdfStamper(reader, stream);
// Field name and value
var fieldName = "myfield";
var fieldValue = "<?xml version=\"1.0\"?><body xfa:APIVersion=\"Acroform:2.7.0.0\" xfa:spec=\"2.1\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\"><p dir=\"ltr\" style=\"margin-top:0pt;margin-bottom:0pt;font-family:Helvetica;font-size:12pt\"><b>write line1 bold</b></p</body>";
// Output stream for the temporary file that should contain the apearance of the field
var msOutput = new FileStream(#"d:\temp.pdf", FileMode.Create);
// string reader to read the field value
var textReader = new StringReader(fieldValue);
// Create new document
var document = new Document(pdfStamper.AcroFields.GetFieldPositions(fieldName)[0].position);
// writer for the new doucment
var writer = PdfWriter.GetInstance(document, msOutput);
// Open the document
document.Open();
// Get elements to append to the doucment
var list = HTMLWorker.ParseToList(textReader, null);
// Append elements to the doucment
foreach (var element in list)
{
document.Add(element);
}
// close the documnet
document.Close();
// Append push button that contains the generated content as its apearance
// this approach is based on the suggestion from Mark storer that can be found in:
// http://stackoverflow.com/questions/1454701/adding-rich-text-to-an-acrofield-in-itextsharp
var button = new PushbuttonField(pdfStamper.Writer, pdfStamper.AcroFields.GetFieldPositions(fieldName)[0].position, fieldName + "_")
{
Layout = PushbuttonField.LAYOUT_ICON_ONLY,
BackgroundColor = null,
Template = pdfStamper.Writer.GetImportedPage(new PdfReader(targetFile, 1)
};
pdfStamper.AddAnnotation(button.Field, 1);
pdfStamper.FormFlattening = true;
pdfStamper.Close();
pdfStamper.Dispose();
But the problem now is that the temporary document contains no content....
Any ideas ?
There's a lot of code above, almost 400 lines. In the future, try to distill everything down to a very simple and reproducible test case.
When using SetFieldRichValue you also need to set the PdfStamper's AcroFields.GenerateAppearances property to false:
stamper.AcroFields.GenerateAppearances = false;
You can read more about this here along with some caveats and other workarounds.
I managed to resolve it with little trick - I used Anchor (hyperling) element inside ColumnText element and position it above the form field. The anchor is displayed with underline by default. In order to avoid the hand marker when the user hover the mouse on the anchor, I set the "Reference" property of the anchor to null.
Here is the code I used:
// Create reader
var reader = new PdfReader(sourceFilePathAndName);
// Create stream for the target file
var stream = File.Open(targetFilePathAndName, FileMode.Create);
// Create the stamper
var pdfStamper = new PdfStamper(reader, stream);
const string fieldName = "MyField";
// Get the position of the field
var targetPosition = pdfStamper.AcroFields.GetFieldPositions(fieldName)[0].position;
// Set the font
var fontNormal = FontFactory.GetFont("Arial", 16, Font.UNDERLINE, BaseColor.BLACK);
// Create the anchor
var url = new Anchor("some default text", fontNormal) { Reference = null };
// Create the element that will contain the anchor and allow to position it anywhere on the document
var data = new ColumnText(pdfStamper.GetOverContent(1));
// Add the anchor to its container
data.SetSimpleColumn(url, targetPosition.Left, targetPosition.Bottom, targetPosition.Right, targetPosition.Top, 0, 0);
// Write the content to the document
data.Go();
pdfStamper.FormFlattening = true;
pdfStamper.Close();
pdfStamper.Dispose();