iText7. How to flatten an existing pdf document - java

I have masked an existing pdf document with images, as described into this question: iText7 Image Transparency
My issue is that somebody using Acrobat Reader DC Pro can still edit the document and remove the images, making the masking ineffective.
I have been thinking of flattening the pdDocument, but it seems the API applies to form, and not to the entire document.
I have tried the code below, but it is still possible to edit the pdf and remove the masking images.
Do you have any advice for this?
// Read the pdf input
PdfReader pdfReader = new PdfReader(value);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PdfWriter pdfWriter = new PdfWriter(outputStream);
PdfDocument pdfDoc = new PdfDocument(pdfReader, pdfWriter);
Document document = new Document(pdfDoc);
// Creating an ImageData object
ImageData data = ImageDataFactory.create(fileName);
for (int x = 1; x < 800; ) {
for (int y = 1; y < 1000; ) {
Image image = new Image(data);
image.setFixedPosition(x , y);
document.add(image);
y = y + y1 + 40;
}
x = x + x1 + 40;
}
PdfAcroForm.getAcroForm(pdfDoc, true).flattenFields();
// The content has now been modified, return it as a stream
document.close();
I expect: the image cannot be removed, or the document cannot be edited

Related

Itext 7 PDF - Adding image to document is not flushed to disk but stays in memory unlike Itext 5

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);
}

Convert pdfReader to byte[] - Itext Java [duplicate]

How to get byte array from Itext PDFReader.
float width = 8.5f * 72;
float height = 11f * 72;
float tolerance = 1f;
PdfReader reader = new PdfReader("source.pdf");
for (int i = 1; i <= reader.getNumberOfPages(); i++)
{
Rectangle cropBox = reader.getCropBox(i);
float widthToAdd = width - cropBox.getWidth();
float heightToAdd = height - cropBox.getHeight();
if (Math.abs(widthToAdd) > tolerance || Math.abs(heightToAdd) > tolerance)
{
float[] newBoxValues = new float[] {
cropBox.getLeft() - widthToAdd / 2,
cropBox.getBottom() - heightToAdd / 2,
cropBox.getRight() + widthToAdd / 2,
cropBox.getTop() + heightToAdd / 2
};
PdfArray newBox = new PdfArray(newBoxValues);
PdfDictionary pageDict = reader.getPageN(i);
pageDict.put(PdfName.CROPBOX, newBox);
pageDict.put(PdfName.MEDIABOX, newBox);
}
}
From above code I need to get byte array from reader object. How?
1) Not working, getting empty byteArray.
OutputStream out = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, out);
stamper.close();
byte byteArray[] = (((ByteArrayOutputStream)out).toByteArray());
2) Not working, getting java.io.IOException: Error: Header doesn't contain versioninfo
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
for (int i = 1; i <= reader.getNumberOfPages(); i++)
{
outputStream.write(reader.getPageContent(i));
}
PDDocument pdDocument = new PDDocument().load(outputStream.toByteArray( );)
Is there any other way to get byte array from PDFReader.
Let's take a the question from a different angle. It seems to me that you want to render a PDF page by page. If so, then your question is all wrong. Extracting the page content stream will not be sufficient as I already indicated: not a single renderer will be able to render such a stream because you don't pass any resources such as fonts, Form and Image XObjects,...
If you want to render separate pages from a PDF, you need to burst the document into separate single page full-blown PDF documents. These single page documents need to contain all the necessary information to render the page. This isn't memory friendly: suppose that you have a 100 KByte document of 10 pages where every page shows an 80 KByte logo, you'll end up with 10 documents that are each at least 80 KByte (times 10 makes already 800 KByte which is much more than the 10-page document where a single Image XObject is shared by the 10 pages).
You'd need to do something like this:
PdfReader reader = new PdfReader("source.pdf");
int n = reader.getNumberOfPages();
reader close();
ByteArrayOutputStream boas;
PdfStamper stamper;
for (int i = 0; i < n; ) {
reader = new PdfReader("source.pdf");
reader.selectPages(String.valueOf(++i));
baos = new ByteArrayOutputStream();
stamper = new PdfStamper(reader, baos);
stamper.close();
doSomethingWithBytes(baos.toByteArray);
}
In this case, baos.toByteArray() will contain the bytes of a valid PDF file. This wasn't the case in any of your attempts.
PdfReader reader = new PdfReader("source.pdf");
byte byteArray[] = reader.getPageContent(1); // page 1
Also have a look at this link

Non removable watermark on PDF file using iText in Java

We have a requirement where we need to add text watermark on magazines which has multiple rich images on each page. I tried com.itextpdf.jar version 5.0.6 to add the watermark but eventually I am able to remove it using Adobe Acrobat Pro.
I tried below option also but that too didn't work.
stamper.setFreeTextFlattening(true);
Is it possible with iText to add a watermark which can not be removed without much effort.
Below is my implementation.
public static void addWaterMark() throws IOException, DocumentException {
PdfReader reader = new PdfReader("C:/Trade-catalog/Catalog2017.pdf");
ByteArrayOutputStream outputPdf = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, outputPdf);
String bodyWatermarkText = "12345 - John Smith";
String bodyWatermarkRotation = "35";
String footerWatermarkText = "Richard Parker";
BaseFont font = BaseFont.createFont("/fonts/micross.ttf", "Cp1250", BaseFont.EMBEDDED);
PdfGState state = new PdfGState();
state.setFillOpacity(0.3f);
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
Rectangle thisPageSize = reader.getPageSize(i);
PdfPatternPainter bodyPainter = stamper.getOverContent(i).createPattern(thisPageSize.getWidth(),
thisPageSize.getHeight());
bodyPainter.setColorFill(new BaseColor(0, 0, 0));
bodyPainter.beginText();
bodyPainter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_FILL);
bodyPainter.setFontAndSize(font, 60);
bodyPainter.showTextAlignedKerned(Element.ALIGN_CENTER, bodyWatermarkText, thisPageSize.getWidth() / 2,
thisPageSize.getHeight() / 2, Integer.valueOf(bodyWatermarkRotation));
bodyPainter.showTextAlignedKerned(Element.ALIGN_RIGHT, footerWatermarkText, thisPageSize.getWidth() * 0.97f,
thisPageSize.getHeight() * 0.015f, 0);
bodyPainter.endText();
PdfContentByte overContent = stamper.getOverContent(i);
overContent.setGState(state);
overContent.setColorFill(new PatternColor(bodyPainter));
overContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(),
thisPageSize.getHeight());
overContent.fill();
overContent.setFlatness(100);
}
stamper.close();
FileOutputStream outputStream = new FileOutputStream(
"C:/Trade-catalog/output/TradeCatalog2017Watermarked_bodyPainter.pdf");
outputPdf.writeTo(outputStream);
outputPdf.close();
reader.close();
}

Changing opacity of transparent image / Changing value of extgstate dictionary

I'm trying to implement a invisible watermarking function using itext 7 in java. So far I've managed to implement the embedding of watermark to all pages using the following code:
PdfDocument pdfdoc = new PdfDocument(new PdfReader(source),new PdfWriter(dest));
Document doc = new Document(pdfdoc);
PdfCanvas canvas;
Rectangle pagesize;
PdfExtGState qrcode = new PdfExtGState();
qrcode.setFillOpacity(0); // sets opacity of watermark.
byte[] bytearray = convertBI(watermark);
ImageData imgd = ImageDataFactory.create(bytearray);
float w = imgd.getWidth() , h = imgd.getHeight();
float x,y;
for(int i = 1;i<=pdfdoc.getNumberOfPages();i++)
{
PdfPage page = pdfdoc.getPage(i);
pagesize = page.getPageSizeWithRotation();
page.setIgnorePageRotationForContent(true);
x = (pagesize.getLeft() + pagesize.getRight())/ 2;
y = (pagesize.getTop() + pagesize.getBottom())/ 2;
canvas = new PdfCanvas(pdfdoc.getPage(i));
canvas.saveState();
canvas.setExtGState(qrcode);
canvas.addImage(imgd,w,0,0,h,x-(w/2),y-(h/2),true);
canvas.restoreState();
}
doc.close();
However I'm having troubles retrieving the watermark. So far I've tried redrawing the page on another canvas and setting the fill opacity but to no avail. The only way I've managed to make the watermark visible through the use of itext-rups and manually changing the value of the extGS as seen here
Would anyone be able to advise me on whether it is possible to change the value of the extGState dictionary from code or any alternative methods to achieving the same result?
Update: So I've tried to access the dictionary in code but it just return nulls.
PdfDocument pdfdoc = new PdfDocument(new PdfReader(source),new PdfWriter(dest));
Document doc = new Document(pdfdoc);
for(int pageNo = 1; pageNo<= pdfdoc.getNumberOfPages();pageNo++)
{
PdfPage pdfpage = pdfdoc.getPage(pageNo);
PdfResources rsrc = pdfpage.getResources();
PdfDictionary pExtGSD = rsrc.getResource(PdfName.ExtGState);
if(!pExtGSD.isEmpty())
{
System.out.println(pExtGSD.getAsFloat(new PdfName("/Gs1")));
}
}
doc.close();

iText PDFDocument page size inaccurate

I am trying to add a header to existing pdf documents in Java with iText. I can add the header at a fixed place on the document, but all the documents are different page sizes, so it is not always at the top of the page. I have tried getting the page size so that I could calculate the position of the header, but it seems as if the page size is not actually what I want. On some documents, calling reader.getPageSize(i).getTop(20) will place the text in the right place at the top of the page, however, on some different documents it will place it half way down the page. Most of the pages have been scanned be a Xerox copier, if that makes a difference. Here is the code I am using:
PdfReader reader = new PdfReader(readFilePath);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(writeFilePath));
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
PdfContentByte cb = stamper.getOverContent(i);
cb.beginText();
cb.setFontAndSize(bf, 14);
float x = reader.getPageSize(i).getWidth() / 2;
float y = reader.getPageSize(i).getTop(20);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, "Copy", x, y, 0);
cb.endText();
}
stamper.close();
PDF that works correctly
PDF that works incorrectly
Take a look at the StampHeader1 example. I adapted your code, introducing ColumnText.showTextAligned() and using a Phrase for the sake of simplicity (maybe you can change that part of your code too):
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Phrase header = new Phrase("Copy", new Font(FontFamily.HELVETICA, 14));
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
float x = reader.getPageSize(i).getWidth() / 2;
float y = reader.getPageSize(i).getTop(20);
ColumnText.showTextAligned(
stamper.getOverContent(i), Element.ALIGN_CENTER,
header, x, y, 0);
}
stamper.close();
reader.close();
}
As you have found out, this code assumes that no rotation was defined.
Now take a look at the StampHeader2 example. I'm using your "Wrong" file and I've added one extra line:
stamper.setRotateContents(false);
By telling the stamper not to rotate the content I'm adding, I'm adding the content using the coordinates as if the page isn't rotated. Please take a look at the result: stamped_header2.pdf. We added "Copy" at the top of the page, but as the page is rotated, we see the word appear on the side. The word is rotated because the page is rotated.
Maybe that's what you want, maybe it isn't. If it isn't, please take a look at StampHeader3 in which I calculate x and y differently, based on the rotation of the page:
if (reader.getPageRotation(i) % 180 == 0) {
x = reader.getPageSize(i).getWidth() / 2;
y = reader.getPageSize(i).getTop(20);
}
else {
x = reader.getPageSize(i).getHeight() / 2;
y = reader.getPageSize(i).getRight(20);
}
Now the word "Copy" appears on what is perceived as the "top of the page" (but in reality, it could be the side of the page): stamped_header3.pdf

Categories