We have an interface that allows users to send back copies of the receipts to our customers. The user feeds in up to a five or so PDFs and they all get merged using itextsharp's (v5.5.13.1) PdfReader.
Unfortunately some users somehow password-protect a file or two here and there... I want to avoid the issue of needing to trust the user to supply us with unprotected files.
Relevant code
PdfReader.unethicalreading = true;
PdfReader reader = null;
foreach(string pdf in pdfs)
{
reader = new PdfReader(pdf);//iTextSharp.text.exceptions.BadPasswordException : 'Bad user password'
}
If there are no password-protected files, the merge goes off without a hitch. Otherwise when it comes to the offending file, the above will throw at the line with the thrown exception.
From what I understood unethicalreading was all that was needed. I even tried to assign it immediately before the line that throws but it gives me the same result.
Does this flag work on C# since most of the help I see online for this library is written for Java (normal, I know, but I wonder if this part of the library has been properly ported)?
Related
I am using iTextSharp version 4.1.6.0 and my code throws the "PdfReader not opened with owner password" exception. The strange thing is that I can open all single PDF files, but on merging them it fails. Following other threads I was trying:
PdfReader.unethicalreading = true;
but this parameter is not accepted and I get:
'PDFReader' does not contain a definition for 'unethicalreading'
Martin
Taking a look at the source of PdfReader here, the static property unethicalreading is available after v5.0.2.
but on merging them it fails
Yes, because copying and merging are operations that require special permissions which require the user/owner password.
My only requirement is to find a selected pdf in a folder is Reader enabled or not, more specifically if usage rights are defined in a way that allows people to add annotations (e.g. comments).
I am doing this in windows application. If I click a button, an event is triggered searching a folder for PDF files. This event needs to check whether or not the PDFs in the folder are Reader enabled for comments. If they are, I need to remove the comment usage rights or revert the PDF back to its original version.
My code can only find PDF files in the folder. I don`t know how to check if the selected PDF is comment enabled or not. Please be gentle and suggest solution.
Here's my code:
private void button1_Click(object sender, EventArgs e)
{
{
string[] filePaths = Directory.GetFiles("D:\\myfolder\\pdffolder");
List<ListViewItem> files = new List<ListViewItem>();
foreach (string filePath in filePaths)
{
---need to check comment enabled or not---
}
}
}
You want to know if a PDF is Reader enabled or not. Reader enabling is established by adding a digital signature known as a Usage Rights (UR) signature. If you have an instance of PdfReader, you can check whether or not a PDF is Reader enabled by using the hasUsageRights() method:
PdfReader reader = new PdfReader(path_to_file);
boolean isReaderEnabled = reader.hasUsageRights();
Usage rights can encompass many different things, such as allowing people to comment, allowing people to save a filled out form, allowing people to sign a document,...
To find out which rights are enabled, you have to inspect either the UR or the UR3 dictionary (note that UR is deprecated, but there may still be PDFs out there that have a UR dictionary):
PdfDictionary catalog = reader.getCatalog();
PdfDictionary perms = catalog.getAsDict(PdfName.PERMS);
PdfDictionary ur = null;
if (perms != null) {
PdfDictionary ur = perms.getAsDict(PdfName.UR);
if (ur == null)
ur = perms.getAsDict(PdfName.UR3);
}
}
If ur remains null, there are no usage rights. If you only want to check if commenting is enabled, you'll have to inspect the entries of the ur dictionary. There will be an /Annots entry with as value an array with values such as Create, Delete, Modify, Copy, Import, Export, Online and SummaryView. FOr the full overview of possible entries, see Table 255 "Entries in the UR transform parameters dictionary" of ISO-32000-1.
You can remove all usage rights like this:
PdfReader reader = new PdfReader(path_to_file);
if (reader.hasUsageRights()) {
reader.removeUsageRights();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(path_to_new_file));
stamper.close();
}
It is impossible to remove only the usage rights for commenting while preserving other usage rights (if present). Just removing the /Annots entry from the /UR or /UR3 dictionary will break the digital signature that enables usage rights. This digital signature is created with a private key owned by Adobe and no third party tool (other than an Adobe product) is allowed to use that key.
Final note:
all code snippet were written in Java, but iTextSharp has corresponding methods or properties in C#. It shouldn't be a problem to port the snippets to C#.
In many cases, it's sufficient to change a lower case into an upper case:
Java: object.add(something);
C#: object.Add(something);
Or you have to remove the set/get:
Java: object.setSomething(something);
C#: object.Something = something;
Thanks for all who takes effect to my question. I finally found the answer in a similar way by reading the PDF and check for a particular string (particular string presented if Comment enabled on the PDF).
The particular string starts with /Annot ....., First I read the PDF thru System.IO, then store in a string and looking for the particular string, If the searching string available then the PDF is comment enabled else not.
We're having trouble setting a BaseUrl using iTextSharp. We have used Adobes Implementation for this in the past, but we got some severe performance issues. So we switched to iTextSharp, which is aprox 10 times faster.
Adobe enabled us to set a base url for each document. We really need this in order to deploy our documents on different servers. But we cant seem to find the right code to do this.
This code is what we used with Adobe:
public bool SetBaseUrl(object jso, string baseUrl)
{
try
{
object result = jso.GetType().InvokeMember("baseURL", BindingFlags.SetProperty, null, jso, new Object[] {baseUrl });
return result != null;
}
catch
{
return false;
}
}
A lot of solutions describe how you can insert links in new or empty documents. But our documents already exist and do contain more than just text. We want to overlay specific words with a link that leads to one or more other documents. Therefore, its really important to us that we can insert a link without accessing the text itself. Maybe lay a box ontop of these words and set its position (since we know where the words are located in the document)
We have tried different implementations, using the setAction method, but it doesnt seem to work properly. The result was in most cases, that we saw out box, but there was no link inside or associated with it. (the cursor didn't change and nothing happend, when i clicked inside the box)
Any help is appreciated.
I've made you a couple of examples.
First, let's take a look at BaseURL1. In your comment, you referred to JavaScript, so I created a document to which I added a snippet of document-level JavaScript:
writer.addJavaScript("this.baseURL = \"http://itextpdf.com/\";");
This works perfectly in Adobe Acrobat, but when you try this in Adobe Reader, you get the following error:
NotAllowedError: Security settings prevent access to this property or
method. Doc.baseURL:1:Document-Level:0000000000000000
This is consistent with the JavaScript reference for Acrobat where it is clearly indicated that special permissions are needed to change the base URL.
So instead of following your suggested path, I consulted ISO-32000-1 (which was what I asked you to do, but... I've beaten you in speed).
I discovered that you can add a URI dictionary to the catalog with a Base entry. So I wrote a second example, BaseURL2, where I add this dictionary to the root dictionary of the PDF:
PdfDictionary uri = new PdfDictionary(PdfName.URI);
uri.put(new PdfName("Base"), new PdfString("http://itextpdf.com/"));
writer.getExtraCatalog().put(PdfName.URI, uri);
Now the BaseURL works in both Acrobat and Reader.
Assuming that you want to add a BaseURL to existing documents, I wrote BaseURL3. In this example, we add the same dictionary to the root dictionary of an existing PDF:
PdfReader reader = new PdfReader(src);
PdfDictionary uri = new PdfDictionary(PdfName.URI);
uri.put(new PdfName("Base"), new PdfString("http://itextpdf.com/"));
reader.getCatalog().put(PdfName.URI, uri);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
Using this code, you can change a link that points to "index.php" (base_url.pdf) into a link that points to "http://itextpdf.com/index.php" (base_url_3.pdf).
Now you can replace your Adobe license with a less expensive iTextSharp license ;-)
I'm writing a program where I can send mails (using domino.dll) from three different department mailboxes (each using its own mail server and nsf-file).
All three department mailboxes have a predefined mail signature such as
Regards
Department X
Since those can change anytime I don't want to hardcode the signature in my program but extract them from the mailbox/nsf-file instead and append it to the mail body (or something else if there are better approaches).
I've been looking around all day without finding a solution to this problem, so my question is: How is this achieved?
So far my code is similar to this:
public Boolean sendNotesMail(String messageText)
{
//Create new notes session
NotesSession _notesSession = new NotesSession();
//Initialize Notes Database to null;
NotesDatabase _notesDataBase = null;
//Initialize Notes Document to null;
NotesDocument _notesDocument = null;
string mailServer = #"Path/DDB";
string mailFile = #"Deparmentmail\number.nsf";
//required for send, since its byRef and not byVal, gets set later.
object oItemValue = null;
// Start the connection to Notes. Otherwise log the error and return false
try
{
//Initialize Notes Session
_notesSession.Initialize("");
}
catch
{
//Log
}
// Set database from the mailServer and mailFile
_notesDataBase = _notesSession.GetDatabase(mailServer, mailFile, false);
//If the database is not already open then open it.
if (!_notesDataBase.IsOpen)
{
_notesDataBase.Open();
}
//Create the notes document
_notesDocument = _notesDataBase.CreateDocument();
//Set document type
_notesDocument.ReplaceItemValue("Form", "Memo");
//sent notes memo fields (To and Subject)
_notesDocument.ReplaceItemValue("SendTo", emailAddress);
_notesDocument.ReplaceItemValue("Subject", subjectText);
// Needed in order to send from a departmental mailbox
_notesDocument.ReplaceItemValue("Principal", _notesDataBase.Title);
//Set the body of the email. This allows you to use the appendtext
NotesRichTextItem _richTextItem = _notesDocument.CreateRichTextItem("Body");
// Insert the text to the body
_richTextItem.AppendText(messageText);
try
{
_notesDocument.Send(false, ref oItemValue);
}
}
EDIT:
Thanks to Richard Schwartz my solution is:
object signature = _notesDataBase.GetProfileDocument("calendarprofile", "").GetItemValue("Signature");
String[] stringArray = ((IEnumerable)signature).Cast<object>().Select(x => x.ToString()).ToArray();
_richTextItem.AppendText(stringArray[0]);
The signature is stored in a profile document in the NSF file. You can use the method NotesDatabase.getProfileDocument() to access it. This method takes two arguments:
ProfileName: The profile document name that you need to find the signature is "calendarprofile". (Yes, that's right. It's actually a common profile for many functions, but the calendar developers got there first and named it. ;-))
UniqueKey: Leave this as an empty string. (It is traditionally used to store a username in profile documents in shared databases, but not used in the calendarprofile doc in the mail file.)
You access data in the profile document the same way that you access them in regular documents, e.g., using getItem(), getItemValue(), etc. For a simple text signature, the NotesItem that you are looking for is called "Signature". I notice, however, that there are also items called "Signature_1" and "Signature_2", and "SignatureOption".
If you look at the Preferences UI for setting signatures in Notes mail, you will see that there is a choice between simple text and HTML or graphic files. No doubt this choice will be reflected in the SignatureOption item, so you will probably want to check that first. I have not explored where the data goes if you use imported HTML or graphic files, so I can't say for sure whether it goes into Signature, Signature_1, Signature_2, or somewhere else. But you can explore that on your own by using NotesPeek. You can download it here. It presents a tree-style view of the NSF file. There's a branch of the tree for Profiles, and you can find the calendarprofile there. Then just play around with different settings in the Notes mail preferences and see what changes. (NotesPeek doesn't pick up changes on the fly. You have to close and re-open the profile in NotesPeek after saving changes in the Notes mail preferences dialog in order to see the changes.)
If this gets too difficult, and you want a standard solution for all mails, you might consider this product or a similar one.
I have a fillable, saveable PDF file that has an owner password (that I don't have access to). I can fill it out in Adobe reader, export the FDF file, modify the FDF file, and then import it.
Then I tried to do it with iText for .NET. I can't create a PdfStamper from my PdfReader because I didn't provide the owner password to the reader. Is there any way to do this programmatically or must I recreate the document?
Even using FdfReader requires a PdfStamper. Am I missing anything? Anything legal that is - I'm pretty sure I could hack the document, but I can't. Ironically, recreating it would probably be ok.
This line will bypass edit password checking in iTextSharp:
PdfReader.unethicalreading = true;
[I found this question several months after it was posted and I'm posting this solution now for anyone who comes across this question in a search.]
I was in the exact same situation: my customer had a PDF with fillable fields that I needed to programmatically access. Unfortunately the PDF was password protected and they didn't have the password so I found couldn't work with their file.
What I discovered was that iTextSharp version 4.0.4 (and later) enforces password restrictions, earlier versions did not.
So I downloaded version 4.0.3 and sure enough it worked. In my case I didn't even have to change my code to use this older version.
You can download 4.0.3 (and all other versions) at SourceForge.
Two important things
Set PdfReader.unethicalreading = true to prevent BadPasswordException.
Set append mode in PdfStamper's constructor, otherwise the Adobe Reader Extensions signature becomes broken and Adobe Reader will display following message: "This document contained certain rights to enable special features in Adobe Reader. The document has been changed since it was created and these rights are no longer valid. Please contact the author for the original version of this document."
So all you need to do is this:
PdfReader.unethicalreading = true;
using (var pdfReader = new PdfReader("form.pdf"))
{
using (var outputStream = new FileStream("filled.pdf", FileMode.Create, FileAccess.Write))
{
using (var stamper = new iTextSharp.text.pdf.PdfStamper(pdfReader, outputStream, '\0', true))
{
stamper.AcroFields.Xfa.FillXfaForm("data.xml");
}
}
}
See How to fill XFA form using iText?
Unless someone else chimes in, I'll assume the answer is "No"
I wound up regenerating the PDF in an unencrypted form.