I am trying to make a program that multiple users can share one PDF document , and every one can put his comments on the PDF using add sticky note without changing/modifying other notes.
For example ,
The program will transfer that PDF file to another person for review and checking , the reviewer will put his comments on the PDF and send it to Approver person . The approver can't edit reviewer comments and change it at all , he can add new sticky note to it for his comments.
if I use the security on the PDF ( ~ PdfWriter.AllowModifyAnnotations ) , I will disable entering new sticky note.
is there any solution for that ?
please help me and thanks in advance
PDF annotation objects can have flags; one of these flags is a read-only flag. Thus, you only have to iterate over all annotations on all pages and set their respective read-only flag.
In iText 5.5.x this can be done like this:
PdfReader reader = new PdfReader(resourceStream);
PdfStamper stamper = new PdfStamper(reader, outputStream);
for (int page = 1; page <= reader.getNumberOfPages(); page++)
{
PdfDictionary pageDictionary = reader.getPageN(page);
PdfArray annotationArray = pageDictionary.getAsArray(PdfName.ANNOTS);
if (annotationArray == null)
continue;
for (PdfObject object : annotationArray)
{
PdfObject directObject = PdfReader.getPdfObject(object);
if (directObject instanceof PdfDictionary)
{
PdfDictionary annotationDictionary = (PdfDictionary) directObject;
PdfNumber flagsNumber = annotationDictionary.getAsNumber(PdfName.F);
int flags = flagsNumber != null ? flagsNumber.intValue() : 0;
flags |= PdfAnnotation.FLAGS_READONLY;
annotationDictionary.put(PdfName.F, new PdfNumber(flags));
}
}
}
stamper.close();
(iText 5 MarkAnnotationReadOnly.java)
In iText 7.0.x it can be done like this
try ( PdfReader pdfReader = new PdfReader(resourceStream);
PdfWriter pdfWriter = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
for (int page = 1; page <= pdfDocument.getNumberOfPages(); page++)
{
PdfPage pdfPage = pdfDocument.getPage(page);
for (PdfAnnotation pdfAnnotation : pdfPage.getAnnotations())
{
pdfAnnotation.setFlag(PdfAnnotation.READ_ONLY);
}
}
}
(iText 7 MarkAnnotationReadOnly.java)
Only the kernel iText 7 artifact is required.
Related
I need to generate large size PDF by adding multiple images to it. In itext 5 if I add a image to the document it is immediately flushed to disk. But in itext 7 it stays in memory and is written to disk only after closing the document.
Itext 7 docs says about using large tables concept https://kb.itextpdf.com/home/it7kb/examples/large-tables , which I tried but it also doesnt flush the images to disk.
Anyone know why ? Thanks in advance for help.
itext 5 code (java)
Document document = new Document(PageSize.A4, 36, 36, 36, 72);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("test.pdf"));
document.open();
Image image1 = null;
for (int i = 0; i < 1000; i++) {
File f = new File("big_image.png");
InputStream is = new FileInputStream(f);
image1 = Image.getInstance(IOUtils.toByteArray(is));
document.add(image1);
}
document.close();
writer.close();
itext 7 code (java)
PdfDocument pdfDoc = new PdfDocument(new PdfWriter("test.pdf"));
Document doc = new Document(pdfDoc);
for (int i = 0; i < 1000; i++) {
File f = new File("big_image.png");
doc.add(new Image(ImageDataFactory.create(f.getPath())));
}
doc.close();
It seems this is not working as intended in iText 7. We'll need to look into it further (disclosure: I'm an iText Software employee).
In the meantime, as a simple workaround, you can flush the images explicitly:
for (int i = 0; i < 1000; i++) {
File f = new File("big_image.png");
Image image = new Image(ImageDataFactory.create(f.getPath()));
image.getXObject().makeIndirect(pdfDoc).flush();
doc.add(image);
}
We have created PDF files via a template and we fill the fields with values from the database. "Name field" = "value".
Now we want to read the value of one of the fields in the PDF file we created earlier.
CODE
reader = new PdfReader("C:\\temp\\letter.pdf");
baos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, baos);
form = stamper.getAcroFields();
System.out.println(form.getField("CUSTOMER-NAME"));
But when I do that returns "null" and it does not recognize any field. It is as if the fields does not exist.
Any help would appreciated. Thanks.
Hi,
I have 2 file types. One has been flattened :-( and another one no.
For the last file, i am using this code and it work:
PdfReader reader = new PdfReader(FILE);
PdfDictionary root = reader.getCatalog();
PdfDictionary form1 = root.getAsDict(PdfName.ACROFORM);
PdfArray fields = form1.getAsArray(PdfName.FIELDS);
PdfDictionary page;
PdfArray annots;
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
page = reader.getPageN(i);
annots = page.getAsArray(PdfName.ANNOTS);
for (int j = 0; j < annots.size(); j++) {
fields.add(annots.getAsIndirectObject(j));
}
}
AcroFields form2 = reader.getAcroFields();
Thanks a lot!!!
Regards,
Muni
As bruno suggested - if you only want to read, only use the PdfReader...
Try the following code and see whether something is printed out:
PdfReader reader = new PdfReader("C:\\temp\\letter.pdf");
AcroFields acroFields = reader.getAcroFields();
Map<String, AcroFields.Item> fields = acroFields.getFields();
Iterator<Entry<String, Item>> it = fields.entrySet().iterator();
while (it.hasNext()) {
Entry<String, Item> entry = it.next();
String value = acroFields.getField(entry.getKey());
System.out.println(entry.getKey()+":"+value);
}
There could be many reasons why your field is not found:
the PDF doesn't contain PDF form fields at all (pdf has been flattened)
the field name is a different one (e.g. Customer-Name)
you field has a parent (so the name is parent.CUSTOMER-NAME)
the form fields were not correctly inserted
...
Can you see the fields in e.g. adobe reader?
i didn't found anything specific to that topic, therefore i'm using this board and hope to get a question/help.
What i have: i have a pdf-document, which is very big (adobe reader says: 21.399,4 x 15.123,7 mm, zoom-factory, when opened is ~1,64% screen-filling!). It's a construction drawing, but this doesn't matter. When i'm using sublime to analyze the structure, i can find the following:
pdf-Version is: 1.6
CropBox[0.0 0.0 14400.0 10177.0]
/Rotate 0/Type/Page/UserUnit 4.2125
What i need: a smaller document, because of the big size, i can't go on with processing the file
What i tried: using iText to reduce the UserUnit to default "1" for the first step. This should make some things easier. My code in the java-programm looks like this:
PdfReader reader = new PdfReader(inFile.getAbsolutePath());
try (FileOutputStream outStream = new FileOutputStream(outFile)) {
PdfStamper pdfStamper = new PdfStamper(reader, outStream);
PdfWriter writer = pdfStamper.getWriter();
writer.setUserunit(1f);
pdfStamper.close();
}
another thing, which i tried, was:
PdfDictionary pageDict;
pageDict = reader.getCatalog();
pageDict.put(PdfName.USERUNIT, new PdfNumber(1f));
Both things didn't work, so my questions are:
Is it possible to change UserUnits of an existing file? Or do i need to create a new one with the properties i want to have and then writing the content of the existing pdf in my new one?
if it's possible: what else do i need to do to change the UserUnits?
With greetings from Heidelberg,
sincerly
D. Pfizenmaier
Please take a look at the ScaleRotate example. It was written in answer to the question Rotating in Itextsharp while preserving comment location & orientation where you'll find more info about the user unit.
This is the code that is relevant:
PdfReader reader = new PdfReader(src);
int n = reader.getNumberOfPages();
PdfDictionary page;
for (int p = 1; p <= n; p++) {
page = reader.getPageN(p);
page.put(PdfName.USERUNIT, new PdfNumber(1f));
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
Note that you are getting the root of the document using getCatalog() but the name you give to the variable sounds as if you assume that it is a page dictionary. That won't work...
I am having problem while merging documents of different width using iText.
Below is the code I'm using to merge.
public static void doMerge(List<InputStream> list, OutputStream outputStream) throws Exception {
Rectangle pagesize = new Rectangle(1700f, 20f);
com.itextpdf.text.Document document = new com.itextpdf.text.Document(pagesize);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
document.setPageSize(pagesize);
com.itextpdf.text.pdf.PdfContentByte cb = writer.getDirectContent();
for (InputStream in : list){
PdfReader reader = new PdfReader(in);
for (int i = 1; i <= reader.getNumberOfPages(); i++){
document.newPage();
//import the page from source pdf
com.itextpdf.text.pdf.PdfImportedPage page = writer.getImportedPage(reader, i);
//calculate the y for merging it from top
float y = document.getPageSize().getHeight() - page.getHeight();
//add the page to the destination pdf
cb.addTemplate(page, 0, y);
}
reader.close();
in.close();
}
outputStream.flush();
document.close();
outputStream.close();
}
First page of pdf will be of 14 inch of width and 13 inch of height. All the other pages on document will be always smaller than it.
I want to merge all the documents altogether in a single document.
I don't know how to set a width and height of a single merged document.
I think Rectangle pagesize = new Rectangle(1700f, 20f); should do it but it's not working means if change it to Rectangle pagesize = new Rectangle(1700f, 200f);, document has no effect.
Please guide me further.
Using the PdfWriter class to merge documents goes against all the recommendations given in the official documentation, though there are unofficial examples that may have lured you into writing bad code. I hope that you understand that I find these bad examples even more annoying than you do.
Please take a look at Table 6.1 in chapter 6 of my book. It gives you an overview showing when to use which class. In this case, you should use PdfCopy:
String[] files = { MovieLinks1.RESULT, MovieHistory.RESULT };
// step 1
Document document = new Document();
// step 2
PdfCopy copy = new PdfCopy(document, new FileOutputStream(RESULT));
// step 3
document.open();
// step 4
PdfReader reader;
int n;
// loop over the documents you want to concatenate
for (int i = 0; i < files.length; i++) {
reader = new PdfReader(files[i]);
// loop over the pages in that document
n = reader.getNumberOfPages();
for (int page = 0; page < n; ) {
copy.addPage(copy.getImportedPage(reader, ++page));
}
copy.freeReader(reader);
reader.close();
}
// step 5
document.close();
If you are using a recent version of iText, you can even use the addDocument() method in which case you don't need to loop over all the pages. You also need to take special care if forms are involved. There are several examples demonstrating the new functionality (dating from after the book was written) in the Sandbox.
with the itext version 5.5 we can merge pdf more easly using the method PdfCopy.addDocument :
package tn.com.sf.za.rd.controller;
import java.io.FileOutputStream;
import java.io.IOException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
public class ReportMerging {
public static void main(String[] args) throws DocumentException, IOException {
String DOC_ONE_PATH = "D:/s.zaghdoudi/tmp/one.pdf";
String DOC_TWO_PATH = "D:/s.zaghdoudi/tmp/two.pdf";
String DOC_THREE_PATH = "D:/s.zaghdoudi/tmp/three.pdf";
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(DOC_THREE_PATH));
document.open();
PdfReader readerOne = new PdfReader(DOC_ONE_PATH);
PdfReader readerTwo = new PdfReader(DOC_TWO_PATH);
copy.addDocument(readerOne);
copy.addDocument(readerTwo);
document.close();
}
}
We use xhtml to pdf with good success, but a new requirement came up to have headers and page count on every page. We are using newset release of Flying Saucer.
I followed example here: http://today.java.net/pub/a/today/2007/06/26/generating-pdfs-with-flying-saucer-and-itext.html#page-specific-features
...but this would not work. The header would be top left on first page.
If I use the r7 version, headers and page numbering works perfectly, but none of the passed in html is rendered, whilst in r8 the headers\ page numbers are ignored, but the html is rendered perfectly. xHTML used for tests is copied from url above.
I know I must be missing something very simple, if anyone has any ideas\ comments, I would be very grateful to hear.
I think they changed this functionality in r8.... try this method instead:
https://gist.github.com/626264
We use the same method and everything works perfectly, I have however decided not to use flying-saucer's built in headers/footers and use a PdfStamper to add them after the PDF is generated, it works quite well, here is an example.
public void modifyPdf(PdfStamper stamper) {
this.reader = stamper.getReader();
PdfContentByte under = null;
PdfPTable header = null;
PdfPTable footer = null;
final int total = this.reader.getNumberOfPages();
for (int page = 1; page <= total; page++) {
under = stamper.getUnderContent(page);
final PdfDocument doc = under.getPdfDocument();
final Rectangle rect = this.reader.getPageSizeWithRotation(page);
header = ... //build your header
footer = ... // build your footer
final float x = 0;
//write header to PDF
if (header != null) {
float y = (rect.getTop() - 0);
header.writeSelectedRows(0, -1, x, y, under);
}
//write footer to PDF
if (footer != null) {
float y = (rect.getBottom() + 20);
footer.writeSelectedRows(0, -1, x, y, under);
}
}
}
you can build your stamper like this:
final PdfReader reader = new PdfReader(/*your pdf file*/);
final PdfStamper stamper = new PdfStamper(reader, /* output */);
Hope you find this helpful.