Is there way creating automatically calculating fields with iTextsharp? I have tried doing this with javascript but the problem is that field values only get updated during certain events (ex. mouseover, mouseup). If I use events the field values only update when I move mouse cursor. They don't get updated if I write a value to field, then move mouse cursor somewhere else and then press enter. They get updated when I move cursor back to the field. Afaik there is no event like "field value changed" or something similiar?
There is no "on changed" event like there is in HTML, however there are "on focus" and "on blur" events so you can pretty easily write your own. The code below shows this off. It first creates a global JavaScript variable (which is not needed, you can discard that line, it just helps me think). Then it creates a standard text field and sets two actions, the Fo (focus) event and the Bl (blur) event. You can find these events and others in the PDF standard section 12.6.3 table 194.
In the focus event I'm just storing the current text field's value. In the blur event I'm comparing the store value with the new value and then just alerting if they are the same or different. If you have a bunch of fields you'll probably want to use a global array instead of individual variables, too. See the code comments for more information. This was tested against iTextSharp 5.4.2.0.
//Our test file
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Test.pdf");
//Standard PDF creation, nothing special
using (var fs = new FileStream(testFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, fs)) {
doc.Open();
//Add a global variable. This line is 100% not needed but it helps me think more clearly
writer.AddJavaScript(PdfAction.JavaScript("var first_name = '';", writer));
//Create a text field
var tf = new TextField(writer, new iTextSharp.text.Rectangle(50, 50, 300, 100), "first_name");
//Give it some style and default text
tf.BorderStyle = PdfBorderDictionary.STYLE_INSET;
tf.BorderColor = BaseColor.BLACK;
tf.Text = "First Name";
//Get the underlying form field object
var tfa = tf.GetTextField();
//On focus (Fo) store the value in our global variable
tfa.SetAdditionalActions(PdfName.FO, PdfAction.JavaScript("first_name = this.getField('first_name').value;", writer));
//On blur (Bl) compare the old value with the entered value and do something if they are the same/different
tfa.SetAdditionalActions(PdfName.BL, PdfAction.JavaScript("var old_value = first_name; var new_value = this.getField('first_name').value; if(old_value != new_value){app.alert('Different');}else{app.alert('Same');}", writer));
//Add our form field to the document
writer.AddAnnotation(tfa);
doc.Close();
}
}
}
Related
I'm creating a simple PDF file with some text and an hyperlink attached to the that text:
Document pdfDocument = new Document();
Page pdfPage = pdfDocument.Pages.Add();
TextFragment textFragment = new TextFragment("My Text");
Table table = new Table();
Row row = table.Rows.Add();
Cell cell = row.Cells.Add();
cell.Paragraphs.Add(textFragment);
pdfPage.Paragraphs.Add(table);
LinkAnnotation link = new LinkAnnotation(pdfPage, textFragment.Rectangle); //[Before Save]textFragment.Rectangle: 0,0,35.56,10
link.Action = new GoToURIAction("Link1 before save");
pdfPage.Annotations.Add(link);
pdfDocument.Save(dataDir + "SimplePDFWithLink.pdf");
The problem is that the link annotation is being assign to the before save rectangle [0,0,33.56,10] at the bottom of the screen where's the textFragment is being added to a different rectangle (I can't set here the Position property because I don't know it, it is relative to the cell's table).
In order to solve this I've tried saving the page and only then searching the textFragment using TextFragmentAbsorber
pdfDocument.Save(dataDir + "SimplePDFWithLink.pdf");
//[After Save]textFragment.Rectangle: 0,0,90,770
TextFragmentAbsorber textFragmentAbsorber = new TextFragmentAbsorber();
pdfPage.Accept(textFragmentAbsorber);
foreach (TextFragment absorbedTextFragment in textFragmentAbsorber.TextFragments)
{
link = new LinkAnnotation(pdfPage, absorbedTextFragment.Rectangle);
link.Action = new GoToURIAction("Link 2 after save");
pdfPage.Annotations.Add(link);
}
pdfDocument.Save(dataDir + "SimplePDFWithLink.pdf");
My Question:
Is is possible to add a simple link to a TextFragment (which is BaseParagraph not StructureElement) without saving the document first?
Here is a simple demo of the outcome, you can see that before saving the document the link is added to the left bottom of the document instead of the text rectangle:
Update:
If I specify the TextFragment's Position value with some arbitrary values, the link is then added exactly to the text, but I don't know what will be the Position value of the element because it being built dynamically using a Table.
Working with TextFragment and TextSegment does work and adds the link without pre-saving the file:
TextFragment textFragment = new TextFragment("My Text");
TextSegment textSegment = new TextSegment("Link to File");
textSegment.Hyperlink = new Aspose.Pdf.WebHyperlink("www.google.com");
textFragment.Segments.Add(textSegment);
It is worth to mention it is works well when linking to a file on the user's file-system like:
textSegment.Hyperlink = new Aspose.Pdf.WebHyperlink("Files\foo.png");
I've created 2 form-fillable pdf's, one to be used as a customer order form and the other to be used in-house as a production sheet. Each of the pdf's has identical fields (same name and type of field for each). I've written an app that (among several other things) uses iTextSharp to read all of the fields in a given customer order form, creates a new production sheet, and fills in all of the data from the order form. This all works smoothly for the text and date fields (string data). However, there is one image field on each pdf and I need to take the image from the image field on the order form and copy it to the image field on the production sheet. This is where I'm getting hung up.
I can use pr.Acrofields.GetFieldItem("imageFieldName"); to get the image as an Acrofields.item object, but I can't seem to get iTextSharp to let me put that into an image field using something like the PdfStamper.Acrofields.SetField() method, since it will only take a string.
Is there perhaps a way to take that image data and store it as a temporary .jpg or .bmp file, then insert that into the production sheet's image field? Or am I going about this all wrong?
As already said in a comment, the pdf format does not have any image fields. Some pdf designers allow to emulate them using e.g. a button plus some javascript. But as the field is merely emulated, there is no image value. This is indeed the case for your two documents.
To retrieve the image from the source form button, therefore, we cannot take the button value but instead have to extract the image from the button appearance. We do this using the itext parser namespace classes with a custom ImageRenderListener render listener class collecting bitmap images.
To set the image to the target form button, furthermore, we also cannot simply set the button value but have to set the button appearance. We do this using the iText AcroFields methods GetNewPushbuttonFromField and ReplacePushbuttonField.
The ImageRenderListener render listener class
All this render listener does is collect bitmap images:
public class ImageRenderListener : IRenderListener
{
public List<System.Drawing.Image> Images = new List<System.Drawing.Image>();
public void BeginTextBlock()
{ }
public void EndTextBlock()
{ }
public void RenderText(TextRenderInfo renderInfo)
{ }
public void RenderImage(ImageRenderInfo renderInfo)
{
PdfImageObject imageObject = renderInfo.GetImage();
if (imageObject == null)
{
Console.WriteLine("Image {0} could not be read.", renderInfo.GetRef().Number);
}
else
{
Images.Add(imageObject.GetDrawingImage());
}
}
}
A Copy method for the image
This method retrieves the first image from the source reader form element and adds it to the target stamper form element:
void Copy(PdfReader source, string sourceButton, PdfStamper target, string targetButton)
{
PdfStream xObject = (PdfStream) PdfReader.GetPdfObjectRelease(source.AcroFields.GetNormalAppearance(sourceButton));
PdfDictionary resources = xObject.GetAsDict(PdfName.RESOURCES);
ImageRenderListener strategy = new ImageRenderListener();
PdfContentStreamProcessor processor = new PdfContentStreamProcessor(strategy);
processor.ProcessContent(ContentByteUtils.GetContentBytesFromContentObject(xObject), resources);
System.Drawing.Image drawingImage = strategy.Images.First();
Image image = Image.GetInstance(drawingImage, drawingImage.RawFormat);
PushbuttonField button = target.AcroFields.GetNewPushbuttonFromField(targetButton);
button.Image = image;
target.AcroFields.ReplacePushbuttonField(targetButton, button.Field);
}
An example
I filled an image into the source document using Adobe Acrobat Reader
and saved this document as Customer Order Form-Willi.pdf.
Then I applied the above copy method:
String source = #"Customer Order Form-Willi.pdf";
String dest = #"Production Sheet.pdf";
String target = #"Production Sheet-withImage.pdf";
using (PdfReader sourceReader = new PdfReader(source))
using (PdfReader destReader = new PdfReader(dest))
using (PdfStamper targetStamper = new PdfStamper(destReader, File.Create(target), (char)0, true))
{
Copy(sourceReader, "proofImage", targetStamper, "proofImage");
}
The result in Production Sheet-withImage.pdf:
Some words of warning
The code above is very optimistic and contains no plausibility checks. For production you should definitively make it more defensive and check for null values, empty lists, etc.
I have an application that uses itextsharp to fill PDF form fields.
One of these fields has some text with tags. For example:
<U>This text should be underlined</>.
I'd like that the text closed in .. has to be underlined.
How could I do that?
How could I approch it with HTMLWorker for example?
Here's the portion of code where I write my description:
for (int i = 0; i < linesDescription.Count; i++)
{
int count = linesDescription[i].Count();
int countTrim = linesDescription[i].Trim().Count();
Chunk cnk = new Chunk(linesDescription[i] + GeneralPurpose.ReturnChar, TextStyle);
if (firstOpe && i > MaxLinePerPage - 1)
LongDescWrapped_dt_extra.Add(cnk);
else
LongDescWrapped_dt.Add(cnk);
}
Ordinary text fields do not support rich text. If you want the fields to remain interactive, you will need RichText fields. These are fields that are flagged in a way that they accept an RV value. This is explained here: Set different parts of a form field to have different fonts using iTextSharp (Note that I didn't succeed in getting this to work, but you may have better luck.)
If it is OK for you to flatten the form (i.e. remove all interactivity), please take a look at the FillWithUnderline example:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.setFormFlattening(true);
AcroFields form = stamper.getAcroFields();
FieldPosition pos = form.getFieldPositions("Name").get(0);
ColumnText ct = new ColumnText(stamper.getOverContent(pos.page));
ct.setSimpleColumn(pos.position);
ElementList elements = XMLWorkerHelper.parseToElementList("<div>Bruno <u>Lowagie</u></div>", null);
for (Element element : elements) {
ct.addElement(element);
}
ct.go();
stamper.close();
}
In this example, we don't fill out the field, but we get the fields position (a page number and a rectangle). We then use ColumnText to add content at this position. As we are inputting HTML, we use XML Worker to parse the HTML into iText objects that we can add to the ColumnText object.
This is a Java example, but it should be easy to port this to C# if you know how to code in C# (which I don't).
You can trythis
Chunk chunk = new Chunk("Underlined TExt", FontFactory.GetFont(FontFactory.TIMES_ROMAN, 12.0f, iTextSharp.text.Font.BOLD | iTextSharp.text.Font.UNDERLINE));
Paragraph reportHeadline = new Paragraph(chunk);
reportHeadline.SpacingBefore = 12.0f;
pdfDoc.Add(reportHeadline);
I would like to know if there is a way to check if the "New Page" happened because of exceeded table or programmatically (by using doc.NewPage();)?
If the new page caused because of exceeded table or text, I need to hide the header table and show a text instead, else if the new page caused programmatically I need to display the header table normally.
I tried to find a flag or something like this in the "OnStartPage" event that show me if the page exceeded or not, but I found nothing.
I hope that some one can help me here.
Thanks!
I would look at the IPdfPTableEventSplit interface that you can implement and assign to a PdfPTable.TableEvent property. It has two methods, SplitTable and TableLayout. The first method is called whenever a table split happens, the second is called whenever the table actually gets written to the canvas. In the first method you could set a flag and disable the header rows if a split happened and in the second method you could write your content out.
The SplitTable method is fired before the new page is added so you need to keep track of a trinary state, "no split", "draw on next page" and "draw on this page". I've packaged these up as an enum:
[Flags]
public enum SplitState {
None = 0,
DrawOnNextPage = 1,
DrawOnThisPage = 2
}
The implemented interface would look like this:
public class SplitTableWatcher : IPdfPTableEventSplit {
/// <summary>
/// The current table split state
/// </summary>
private SplitState currentSplitState = SplitState.None;
public void SplitTable(PdfPTable table) {
//Disable header rows for automatic splitting (per OP's request)
table.HeaderRows = 0;
//We now need to split on the next page, so append the flag
this.currentSplitState |= SplitState.DrawOnNextPage;
}
public void TableLayout(PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart, PdfContentByte[] canvases) {
//If a split happened and we're on the next page
if (this.currentSplitState.HasFlag(SplitState.DrawOnThisPage)) {
//Draw something, nothing too special here
var cb = canvases[PdfPTable.TEXTCANVAS];
cb.BeginText();
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, false), 18);
//Use the table's widths and heights to find a spot, this probably could use some tweaking
cb.SetTextMatrix(widths[0][0], heights[0]);
cb.ShowText("A Split Happened!");
cb.EndText();
//Unset the draw on this page flag, it will be reset below if needed
this.currentSplitState ^= SplitState.DrawOnThisPage;
}
//If we previously had the next page flag set change it to this page
if (currentSplitState.HasFlag(SplitState.DrawOnNextPage)) {
this.currentSplitState = SplitState.DrawOnThisPage;
}
}
}
And finally the actual implementation of that class with some simple test data:
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
using (var fs = new FileStream(testFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, fs)) {
doc.Open();
var t = new PdfPTable(1);
//Implement our class
t.TableEvent = new SplitTableWatcher();
//Add a single header row
t.HeaderRows = 1;
t.AddCell("Header");
//Create 100 test cells
for (var i = 1; i < 100; i++) {
t.AddCell(i.ToString());
}
doc.Add(t);
doc.Close();
}
}
}
I want to add and pop-up window in C# project to display an image and text by clicking on itextsharp annotation.
iTextSharp.text.pdf.PdfAnnotation annot = iTextSharp.text.pdf.PdfAnnotation.CreateLink(stamper.Writer, c.rect, PdfAnnotation.HIGHLIGHT_INVERT, PdfAction.JavaScript("app.alert('action!')", stamper.Writer));
above code is used to display the alert and i want to customize it to my needs can someone please give me an option.i'm not familiar with javascript. or can i use any other options ?
You need a couple of annotations to achieve what you want.
Let me start with a simple text annotation:
Suppose that:
writer is your PdfWriter instance,
rect1 and rect2 are rectangles that define coordinates,
title and contents are string objects with the content you want to show in the text annotation,
Then you need this code snippet to add the popup annotations:
// Create the text annotation
PdfAnnotation text = PdfAnnotation.CreateText(writer, rect1, title, contents, false, "Comment");
text.Name = "text";
text.Flags = PdfAnnotation.FLAGS_READONLY | PdfAnnotation.FLAGS_NOVIEW;
// Create the popup annotation
PdfAnnotation popup = PdfAnnotation.CreatePopup(writer, rect2, null, false);
// Add the text annotation to the popup
popup.Put(PdfName.PARENT, text.IndirectReference);
// Declare the popup annotation as popup for the text
text.Put(PdfName.POPUP, popup.IndirectReference);
// Add both annotations
writer.AddAnnotation(text);
writer.AddAnnotation(popup);
// Create a button field
PushbuttonField field = new PushbuttonField(wWriter, rect1, "button");
PdfAnnotation widget = field.Field;
// Show the popup onMouseEnter
PdfAction enter = PdfAction.JavaScript(JS1, writer);
widget.SetAdditionalActions(PdfName.E, enter);
// Hide the popup onMouseExit
PdfAction exit = PdfAction.JavaScript(JS2, writer);
widget.SetAdditionalActions(PdfName.X, exit);
// Add the button annotation
writer.AddAnnotation(widget);
Two constants aren't explained yet:
JS1:
"var t = this.getAnnot(this.pageNum, 'text'); t.popupOpen = true; var w = this.getField('button'); w.setFocus();"
JS2:
"var t = this.getAnnot(this.pageNum, 'text'); t.popupOpen = false;"
This is, of course, explained in my book, more specifically in chapter 7. You can find a full example here. If you need a C# example, please look for the corresponding example here.
If you also want an image, please take a look at this example: advertisement.pdf
Here you have an advertisement that closes when you click "close this advertisement". This is also done using JavaScript. You need to combine the previous snippet with the code of the Advertisement example.
The key JavaScript methods you'll need are: getField() and getAnnot(). You'll have to change the properties to show or hide the content.
I solved this problem by using an attachment instead of a popup. Below code will place an image in your pdf, and on click of image, it will open image with full resolution.
PdfReader reader = new PdfReader(ClassLoader.getSystemClassLoader().getResourceAsStream("source.pdf"));
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("target.pdf"));
PdfAnnotation imgAttach = PdfAnnotation.createFileAttachment(stamper.getWriter(), new Rectangle(100, 100, 200, 200), "", null, "PATH/TO/image.jpeg", "image.jpeg");
PdfAppearance app = stamper.getOverContent(1).createAppearance(100, 100);
Image img = Image.getInstance("PATH/TO/image.jpeg");
img.scaleAbsolute(100, 100);
img.setAbsolutePosition(100, 100);
app.addImage(img);
imgAttach.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, app);
stamper.addAnnotation(imgAttach, 1);
stamper.getOverContent(1).addImage(img);
stamper.close();