iTextSharp Add Watermark Only If it Doesn't Already Exist - c#

Does anyone know if there is a way to check for a watermark on a PDF document using iTextSharp?
I want to do this before adding a new one. In my case, I have to add a new watermark if it wasn't already added by someone, but I don't know how to check this using iTextSharp's PdfReader class.
Something like this:
var reader = new PdfReader(bytes);
var stamper = new PdfStamper(reader, ms);
var dc = stamper.GetOverContent(pageNumber);
bool alreadyStamped = cd.CheckIfTextOrImageExists();

After some investigation thanks to the #ChrisHaas comment I was able to achieve that verification. So, if text is present on the particular page, I can find it using SimpleTextExtractionStrategy, even if it's in the WaterMark collection.
PdfReader pdfReader = new PdfReader(bytes);
for (int page = 1; page <= pdfReader.NumberOfPages; page++)
{
ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
string currentPageText = PdfTextExtractor.GetTextFromPage(pdfReader, page, strategy);
if (currentPageText.Contains(searthText))
{
// adding new WaterMark here
Console.WriteLine("text was found on page "+i);
}
}
pdfReader.Close();
Hopefully, this approach helps someone, who got a similar issue.

Related

iTextSharp Overlay two pdf files

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

Footer not showing in iTextSharp v4

I'm trying to create a PDF from HTML, and am using iTextSharp for that. From my reading, the license that covers the newer versions of iTextSharp would require me to make the source code available. We can't do that, so we're using version 4, which is under the LGPL.
I'm trying to get a footer to appear along with the HTML, but it's not working for some reason. I've tried removing the HTML and just using text. Just putting a chunk in the footer. Multiple pages vs single pages. Hopefully I'm just missing something easy, but from the examples I've seen it should be super easy.
using (MemoryStream ms = new MemoryStream())
{
Document doc = new Document(PageSize.LETTER, 35,35,35,70);
var font = FontFactory.GetFont("arial", 8f);
font.Color = Color.BLACK;
var chunk = new Chunk("Footer", font);
var phrase = new Phrase(chunk);
var footer = new HeaderFooter(phrase, true);
footer.Alignment = 1;
footer.Border = Rectangle.NO_BORDER;
doc.Footer = footer;
//doc.Footer = new HeaderFooter(new Phrase("Footer"),false);
var writer = PdfWriter.GetInstance(doc, ms);
var htmlWorker = new HTMLWorker(doc);
using (var sr = new StringReader(html))
{
doc.Open();
doc.Add(new Chunk("Text"));
//htmlWorker.Parse(sr);
doc.Close();
}
return ms.ToArray();
}
Watch your HeaderFooter ctor. The signature your are using might lead to setting the header text only.
Anyway maybe use the PdfWriter.PageEvent and some class deriving from PdfPageEventHelper to implement header and footer (instead of HeaderFooter())

How to add in reply to annotation using iTextSharp

I am trying to add a sticky note reply to in pdf using iTextSharp. I am able to create a new annotation in the pdf. But i cannot link it as child of an already existing annotation. I copied most of the properties in parent to its child. I copied it by analyzing the properties of a reply, by manually adding a reply from Adobe Reader. What I am missing is the property /IRT. It needs a reference to the parent popup. Like /IRT 16 0 R.
Below is the code i am trying.
private void annotateReplyPdf()
{
string outputFile = #"D:\temp\temp.pdf";
// Creating iTextSharp.text.pdf.PdfReader object to read the Existing PDF Document
using (PdfReader reader = new PdfReader(FILE_NAME))
{
using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
// Creating iTextSharp.text.pdf.PdfStamper object to write Data from iTextSharp.text.pdf.PdfReader object to FileStream object
using (PdfStamper stamper = new PdfStamper(reader, fs))
{
//get page 1
PdfDictionary pageDic = reader.GetPageN(1);
//get annotations in page 1
PdfArray pageAnnotsArray = pageDic.GetAsArray(PdfName.ANNOTS);
if (pageAnnotsArray != null)
{
PdfDictionary curAnnotDic = pageAnnotsArray.GetAsDict(0);
PdfArray rect = curAnnotDic.GetAsArray(PdfName.RECT);
Rectangle rectangle = new Rectangle(float.Parse(rect[0].ToString()), float.Parse(rect[1].ToString()), float.Parse(rect[2].ToString()), float.Parse(rect[3].ToString()));
PdfAnnotation newAnnot = new PdfAnnotation(stamper.Writer, rectangle);
newAnnot.Title = "john.conor";
var dtNow = DateTime.Now;
newAnnot.Put(PdfName.C, curAnnotDic.Get(PdfName.C));
newAnnot.Put(PdfName.CONTENTS, new PdfString("Reply using prog"));
newAnnot.Put(PdfName.CREATIONDATE, new PdfDate(dtNow));
// newAnnot.Put(PdfName.IRT, curAnnotDic.); stuck here
newAnnot.Put(PdfName.M, new PdfDate(dtNow));
newAnnot.Put(PdfName.NAME, curAnnotDic.Get(PdfName.NAME));
newAnnot.Put(PdfName.RC, curAnnotDic.Get(PdfName.RC));
newAnnot.Put(PdfName.SUBTYPE, PdfName.TEXT);
newAnnot.Put(PdfName.SUBJECT, curAnnotDic.Get(PdfName.SUBJECT));
stamper.AddAnnotation(newAnnot, 1);
}
}
}
}
}
The methods I have used might not be accurate or efficient, as most of the code were found by trial and error and checking other similar examples(also checking the pdf specification).
Can somebody please fill that code, which does the magic.
note: SO question doesn't provide a code for the answer.
Please take a look at the AddInReplyTo example.
We have a file named hello_sticky_note.pdf that looks like this:
I am going to skip the method to detect the annotation of the sticky note (in your question, you already have this code). In my example, I know that this annotation is the first entry in the /Annots array (the annotation with index 0).
This is how I'm going to add an "in reply to" annotation:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary page = reader.getPageN(1);
PdfArray annots = page.getAsArray(PdfName.ANNOTS);
PdfDictionary sticky = annots.getAsDict(0);
PdfArray stickyRect = sticky.getAsArray(PdfName.RECT);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
PdfWriter writer = stamper.getWriter();
Rectangle stickyRectangle = new Rectangle(
stickyRect.getAsNumber(0).floatValue(), stickyRect.getAsNumber(1).floatValue(),
stickyRect.getAsNumber(2).floatValue(), stickyRect.getAsNumber(3).floatValue()
);
PdfAnnotation replySticky = PdfAnnotation.createText(
writer, stickyRectangle, "Reply", "Hello PDF", true, "Comment");
replySticky.put(PdfName.IRT, annots.getAsIndirectObject(0));
stamper.addAnnotation(replySticky, 1);
stamper.close();
}
Just like you, I get the original annotation (in my code, it's named sticky) and I get the position of that annotation (stickyRect). I create a stickyRectangle object in a slightly different way than you do (my way is better, but that doesn't matter too much) and I use that stickyRectangle to create a new PdfAnnotation named replySticky.
That's what you already have. Now I add the missing part:
replySticky.Put(PdfName.IRT, annots.GetAsIndirectObject(0));
In your code, you add the annotation dictionary, but what you actually need is the reference to that dictionary.
The resulting PDF looks like hello_in_reply_to.pdf:

iTextSharp - Change order of Optional content groups

I have a PDF file with a hierarchy of layers (aka OCG). Using the following code snippet
var ocProps = reader.Catalog.GetAsDict(PdfName.OCPROPERTIES);
var occd = ocProps.GetAsDict(PdfName.D);
var order = occd.GetAsArray(PdfName.ORDER);
I can query the current order from the source file. But I have no idea how to modify this data in order to copy it into a new file with the following snippet.
var reader = new PdfReader(input);
var document = new Document(reader.GetPageSizeWithRotation(1));
var pdfCopyProvider = new PdfCopy(document,
new System.IO.FileStream(output, System.IO.FileMode.Create));
document.Open();
// TBD do OCG modification ...
var importedPage = pdfCopyProvider.GetImportedPage(reader, 1);
pdfCopyProvider.AddPage(importedPage);
document.Close();
Nonetheless, the ocg information is copied to the new pdf file by default. I saw a comment from Bruno Lowagie several weeks ago concerning merging of ocgs https://stackoverflow.com/questions/21573892/itextsharp-merge-impose-pdfs-while-maintaining-layers-optional-content-groups but I'm not sure whether this includes simple copying also.
Any hint on this is welcome. Merging of ocgs might be a topic in future so hints on that topic are welcome, too
Regards,
Holger
Added: I'm using most recent version 5.5.0.0
Added:
In addition to Bruno's answer, I publish the C# version of the manipulatePdf method
public void ManipulatePdf(string source, string destination)
{
var reader = new PdfReader(source);
var ocProps = reader.Catalog.GetAsDict(PdfName.OCPROPERTIES);
var occd = ocProps.GetAsDict(PdfName.D);
var order = occd.GetAsArray(PdfName.ORDER);
var nestedLayers = (PdfObject)order[0];
var nestedLayerArray = (PdfObject)order[1];
var groupedLayers = (PdfObject)order[2];
var radiogroup = (PdfObject)order[3];
order[0] = radiogroup;
order[1] = nestedLayers;
order[2] = nestedLayerArray;
order[3] = groupedLayers;
var stamper = new PdfStamper(reader, new System.IO.FileStream(destination, System.IO.FileMode.Create));
stamper.Close();
reader.Close();
}
You're already very close to the solution. See the ChangeOCGOrder to find out how to change ocg.pdf into ocg_reordered.pdf. (The code is in Java, but you shouldn't have any trouble porting it to... VB.NET?)
You already had something like this:
PdfDictionary catalog = reader.getCatalog();
PdfDictionary ocProps = catalog.getAsDict(PdfName.OCPROPERTIES);
PdfDictionary occd = ocProps.getAsDict(PdfName.D);
PdfArray order = occd.getAsArray(PdfName.ORDER);
This is good: you're looking at the right place!
Now you need something like this:
PdfObject nestedLayers = order.getPdfObject(0);
PdfObject nestedLayerArray = order.getPdfObject(1);
PdfObject groupedLayers = order.getPdfObject(2);
PdfObject radiogroup = order.getPdfObject(3);
order.set(0, radiogroup);
order.set(1, nestedLayers);
order.set(2, nestedLayerArray);
order.set(3, groupedLayers);
In my example, the ORDER array contains 4 elements. I get these four elements, and I change the order of the entries in the original array.
Note that I could also have done something like:
order.addFirst(order.remove(3));
That would have the same effect as the 8 lines of code above, but the 8 lines help you understand the mechanism.

How to add new page in pdf for multiple record iTextsharp

I have an xml and mapping to pdf form field using iTextsharp. It works well for single record. But when multiple records are there, it does not add 2nd record in the output pdf. Here is a code
public static void GeneratePdf(string sin, List<XElement> elements)
{
var pdfTemplate = HttpContext.Current.Server.MapPath("~/input.pdf");
var newFile = HttpContext.Current.Server.MapPath("~/output.pdf");
var pdfReader = new PdfReader(pdfTemplate);
var pdfStamper = new PdfStamper(pdfReader, new FileStream(
newFile, FileMode.Create));
foreach (var element in elements)
{
foreach (var elem in elements.Elements())
{
pdfStamper.AcroFields.SetField(elem.Name.ToString(), (string)elem);
}
}
pdfStamper.FormFlattening = false;
pdfStamper.Close();
}
Your question is unclear because you're telling us nothing about the nature of your form.
IF YOU'RE FORM IS BASED ON ACROFORM TECHNOLOGY
Please take a look at this video tutorial: http://itextpdf.com/codenvy_webapp
You can find the examples used in this tutorial here: https://github.com/blowagie/itextsamples
You can also find the standalone examples here: http://itextpdf.com/sandbox/acroforms/reporting
Note that there's an example "HOW NOT TO DO IT". Make sure you use the correct example: FillFlattenMerge2
Document document = new Document();
PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
document.open();
ByteArrayOutputStream baos;
PdfReader reader;
PdfStamper stamper;
AcroFields fields;
while (myApp.hasMoreRecords()) {
baos = new ByteArrayOutputStream();
reader = new PdfReader(SRC);
stamper = new PdfStamper(reader, baos);
fields = stamper.getAcroFields();
myApp.processNextRecord(fields);
stamper.setFormFlattening(true);
stamper.close();
reader.close();
// add the PDF to PdfCopy
reader = new PdfReader(baos.toByteArray());
copy.addDocument(reader);
reader.close();
}
document.close();
Note that myApp is an instance of a custom class you could write to loop over a record set. You'd implement a method hasMoreRecords() and processNextRecord() to loop over the records and process them one by one.
IF YOUR FORM IS BASED ON THE XML FORMS ARCHITECTURE
In this case, you're using the wrong methods. You can't fill out a dynamic form using the setField() method. The fact that it works for one record is a sign that your form is either an AcroForm or a hybrid XFA form. In both cases you can NOT achieve what you want without changing your form into a pure, dynamic XFA form.
As soon as you have a pure, dynamic XFA form, you need to use this code:
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader,
new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
XfaForm xfa = form.getXfa();
xfa.fillXfaForm(new FileInputStream(xml));
stamper.close();
reader.close();
I repeat: this code snippet will only work if you have a correct form. Elements in your question indicate that this is not the case. If your form isn't a dynamic XFA form, you won't find any software that can achieve what you want!!!
Watch these movies for more info: http://itextpdf.com/product/xfa_worker

Categories

Resources