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();
}
Related
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
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
I am using iText 2.1.7 to merge some document PDFs into a single PDF. The code below seems to work just fine, however it appears in some instances the rendered PDF is scaled slightly smaller, like at 90% of the PDF if printed directly before processing.
Is there a way to keep the current size?
private void doMerge(List<InputStream> list, OutputStream outputStream) throws DocumentException, IOException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
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
PdfImportedPage page = writer.getImportedPage(reader, i);
//add the page to the destination pdf
cb.addTemplate(page, 0, 0);
}
}
outputStream.flush();
document.close();
outputStream.close();
}
The API was already available with that version. I would even suggest to use PdfSmartCopy instead of PdfCopy (or PdfCopyFields if form fields are inside).
private PdfSmartCopy copier;
public void SomeMainMethod(){
Document finalPdf = new Document();
copier = new PdfSmartCopy(finalPdf, outputstream);
//Start adding pdfs
finalPdf.open();
//add n documents
addDocuments(...);
finalPdf.close();
formCopier.close();
}
public void addDocument(InputStream pdfDocument, int startPage, int endPage){
PdfReader reader= new PdfReader(pdfDocument);
int startPage = 1;
int endPage = reader.getNumberOfPages();
for (int i = startPage; i <= endPage; i++) {
copier.addPage(this.copier.getImportedPage(reader,i));
}
if(copier!=null){
//Important: Free Memory!
copier.flush();
copier.freeReader(reader);
}
if (reader!=null) {
reader.close();
reader=null;
}
pdfDocument.close();
}
We are trying to upgrade from iText 5 to iText 7 and saw few issues. I am getting an exception as "com.itextpdf.io.IOException: PDF startxref not found." inside PdfReader#readPdf() and finally in the caller method getting an exception as "com.itextpdf.kernel.PdfException: Trailer not found.".
My use case is creating a PdfReader instance using inputSream and then creating PdfDocument from the reader and passing PdfWriter as a constructor parameter. We are trying to modify existing PDF, and the sample code is as below
PdfReader pdfReader = new PdfReader(inputStream);
pdfReader.setUnethicalReading(true);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PdfDocument pdfDocument = new PdfDocument(pdfReader, new PdfWriter(os));
What am I doing wrong and how can we fix this issue?
We have a utility method that writes the output stream and creates a new PDF attachment.
Copying the answer from the comments:
I got this issue fixed, I need to close pdfDocument before I am
writing attachments from the output stream.
I was not closing the stream properly, I created the pdfDocument
instance and reading from output stream before closing the
pdfDocument. So I need to close pdfDocument stream first and then read
from output stream to create attachments.
I had this exact same problem after add some elements to document (itextpdf 7.2.1), but the instance of PdfDocument was being closed (using a "try with resources"):
public static byte[] stampDocument(byte[] fileBytes, Integer startPageNumber) throws IOException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfDocument pdf = new PdfDocument(new PdfReader(bais), new PdfWriter(baos, new WriterProperties().setFullCompressionMode(true)))) {
addStamps(pdf, startPageNumber);
baos.flush();
return baos.toByteArray();
}
}
private static void addStamps(PdfDocument pdf, Integer startPageNumber) throws IOException {
int iStartPageNumber = (startPageNumber == null ? 0 : startPageNumber - 1);
for (int i = 1; i <= pdf.getNumberOfPages(); i++) {
Rectangle rect = pdf.getPage(i).getPageSize();
PdfCanvas canvas = new PdfCanvas(pdf.getPage(i).newContentStreamBefore(), pdf.getPage(i).getResources(), pdf);
stampPage(canvas, iStartPageNumber + i, rect.getHeight(), rect.getWidth());
}
}
private static void stampPage(PdfCanvas canvas, int pageNumber, float height, float width) throws IOException {
float x = width - 85;
float y = height - 100;
Rectangle recta = new Rectangle(x, y, 75, 75);
try (Canvas ca = new Canvas(canvas, recta)) {
ca.showTextAligned(new Paragraph(new Text(String.valueOf(pageNumber)).setFontSize(FONT_SIZE)), x + (recta.getWidth()/2), recta.getHeight()/2 - FONT_SIZE/2 + y, TextAlignment.CENTER);
}
canvas.rectangle(recta);
}
The solution was add a Document instance to be closed after add the new elements:
public static byte[] stampDocument(byte[] fileBytes, Integer startPageNumber) throws IOException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(fileBytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfDocument pdf = new PdfDocument(new PdfReader(bais), new PdfWriter(baos, new WriterProperties().setFullCompressionMode(true)))) {
Document doc = new Document(pdf);
addStamps(pdf, startPageNumber);
doc.close();
baos.flush();
return baos.toByteArray();
}
}
So Weird, I am generating a PDF using FlyingSaucer, then adding a footer to the PDF. The footer works in Chrome and FF, but not IE. But if I download the PDF, even from Chrome, where the text is there, open it locally with Adobe, it is not... I dont get it.
Code:
ByteArrayOutputStream os = originalPDF.getOutputStream();
ByteArrayOutputStream newos = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(os.toByteArray());
PdfStamper stamper = new PdfStamper(reader, newos);
for(int i=1; i<= reader.getNumberOfPages(); i++){
Rectangle pageSize = reader.getPageSize(i);
PdfContentByte content = stamper.getUnderContent(i);
BaseFont baseFont = BaseFont.createFont(BaseFont.HELVETICA, "utf8", false);
content.setFontAndSize(baseFont, 15);
content.setRGBColorFill(0, 0, 0);
content.showTextAligned(PdfContentByte.ALIGN_LEFT, "Page "+i, 3, 5, 0);
content.showTextAligned(PdfContentByte.ALIGN_RIGHT, " - Printed: "+DateUtil.date2str_MM_dd_yyyy(new Date()), pageSize.getWidth()-3, 5, 0);
}
stamper.close();
request.getSession().setAttribute("file", newos.toByteArray());
Any Ideas?
#mkl
EDIT:
I cannot post the PDF, sensitive information and all that BUT, if I change the code to:
PdfContentByte content = stamper.getUnderContent(i);
String msg = "Page "+i+" - Printed: "+DateUtil.datetime2str_MM_dd_yyyy_h_mm_a(new Timestamp(new Date().getTime()));
ColumnText.showTextAligned(content, PdfContentByte.ALIGN_CENTER, new Phrase(msg), center, 5, 0);
It works on all browsers and Adobe. So clearly my font and/or encoding is wrong. Any suggestions for proper encoding?