PDF generated using PDFBox is magnified when printing / saving via Safari - pdf

I generated a pdf using PDFBox which is then sent to the front-end. When I print / save this pdf in Chrome / Firefox, it looks good. However, when I try to do the same using Safari, the pdf is magnified.
Not sure if this is a browser issue or whether it has something to do with Safari not able to read the pdf properly. Any ideas?
This is the pdf generated from Chrome, and this is the one from Safari (I've redacted few details).
This is my code:
PDXObjectImage blankImg = (PDXObjectImage) object;
if(blankImg.getHeight() > 460){
BufferedImage img = ImageIO.read(new ByteArrayInputStream(shippingLabel));
BufferedImage resizedImage = Scalr.resize(img, Scalr.Method.BALANCED, Scalr.Mode.FIT_EXACT, img.getWidth(), img.getHeight());
// Convert images to jpg format
BufferedImage jpegImage = new BufferedImage(resizedImage.getWidth(),resizedImage.getHeight(), BufferedImage.TYPE_INT_RGB);
jpegImage.createGraphics().drawImage(resizedImage, 0, 0, Color.WHITE, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(jpegImage, "jpg", baos);
// Replace empty image in template with the image generated from shipping label byte array
PDXObjectImage carrierLabel = new PDJpeg(doc, new ByteArrayInputStream(baos.toByteArray()));
blankImg.getCOSStream().replaceWithStream(carrierLabel.getCOSStream());
break;
}
PDStream updatedStream = new PDStream(doc);
OutputStream out = updatedStream.createOutputStream();
ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
tokenWriter.writeTokens(tokens);
page.setContents(updatedStream);
// Convert PDDoc to byte[]
ByteArrayOutputStream outUpdated = new ByteArrayOutputStream();
doc.save(outUpdated);
return outUpdated.toByteArray();
This is what I'm doing. I have a pdf with a blank image. I read this image, and replace this with another existing image which is available as byte array, and finally return the updated pdf as a byte array as ResponseEntity from Spring boot server running in the backend.
Thanks.

Related

pdfbox - unable to capture modified values from pdf

I have a requirement to open PDF on JXBrowser and let the user modify values on PDF and upon saving, I should able to read the modified values and save to database.
My issue was, I am unable to fetch modified values from pdf, its always sending back original values from pdf (acroForm.getField(field name);). Could you help me if there is any other way to solve this problem.
I am using pdfbox 2.0.1
Appreciate your help.
Thanks,
Prasad
Update1:
Adding sample code that I have used in my application
PDDocument PDFDoc = PDDocument.load(complaintform.pdf);
LoggerProvider.setLevel(Level.OFF);
Base64Encoder b64 = new Base64Encoder();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PDFDoc.save(baos);
String pdfHTML = "<HTML><BODY style=\"width:100%; height:100%\" > <embed style=\"width:100%; height:100%\" src=\"data:application/pdf;base64,"+b64.encode(baos.toByteArray())+"\"type=\"application/pdf\"></BODY></HTML>";
Browser browser = new Browser();
BrowserView browserView = new BrowserView(browser);
this.add(browserView, BorderLayout.CENTER);
browser.loadHTML(pdfHTML);
save()
{
PDDocumentCatalog docCatalog = PDFDoc.getDocumentCatalog();
PDAcroForm acroForm = docCatalog.getAcroForm();
PDField field = acroForm.getField("last");
String modifiedValue = field.getValueAsString();
}

How to add an image to a pdf file in APPEND mode using PdfStamper?

I'm using the iTextSharp library version 5.5.6.0.
This file contains customizable text fields and it's necessary to keep an interactive text form fields: https://yadi.sk/i/yoUvDI9EmtVhc .
But I can't adding an image in PdfTemplate object.
The code in c# at this stage is:
string outpath = #"D:\pdf_\output.pdf";
string inpath = #"D:\pdf_\input.pdf";
string stamp = #"D:\pdf_\img.png";
This method does'nt add the image, but the text boxes are active.
public static void onlyInteractive()
{
using (MemoryStream os = new MemoryStream())
using (PdfReader pdfReader = new PdfReader(inpath))
//APPEND mode
using (PdfStamper stamper = new PdfStamper(pdfReader, os, '\0', true))
{
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(stamp);
image.SetAbsolutePosition(0, 0);
PdfTemplate template = PdfTemplate.CreateTemplate(stamper.Writer, image.Width, image.Height);
template.AddImage(image);
stamper.GetOverContent(1).AddTemplate(template, 150, 200, true);
os.WriteTo(new FileStream(outpath, FileMode.Create, FileAccess.ReadWrite));
}
}
The behavior of this method back to the first.
public static void onlyImage()
{
using (Stream output = new FileStream(outpath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
using (PdfReader reader = new PdfReader(inpath))
using (var stamper = new PdfStamper(reader, output, '\0', true))
{
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(stamp);
image.SetAbsolutePosition(0, 0);
PdfTemplate template = PdfTemplate.CreateTemplate(stamper.Writer, image.Width, image.Height);
template.AddImage(image);
stamper.GetOverContent(1).AddTemplate(template, 150, 200, true);
}
}
onlyInteractive
The issue with this code is that you grab the output PDF before it is finished:
using (PdfStamper stamper = new PdfStamper(pdfReader, os, '\0', true))
{
[...]
os.WriteTo(new FileStream(outpath, FileMode.Create, FileAccess.ReadWrite));
}
When the stamper is getting closed (here implicitly at the end of its using block), some not yet stored PDF objects are written and the internal cross references and the file trailer are written.
You write the os contents to file before that. Thus, your result document is incomplete. Adobe Reader upon opening it repairs it which results in essentially your original document.
onlyImage
This code by itself is correct, it stamps the image onto the document and stores it correctly.
Your problem here is that the document itself is Reader-enabled, i.e. it is signed with a so called usage rights signature. Such signatures tell Adobe Reader upon opening a file to make additional features available displaying editing the document in question.
But when checking the signature on the document with the image, Adobe Reader sees that the document has been changed in a way that is not compatible with the usage rights granted by the signature: An image has been added to the page content which is something not granted by the signature. Thus, Adobe Reader revokes the granted features, in your case form editing.
Removing the usage rights signature
One option in this situation is to remove that signature. In that case form editing is not granted anymore by means of that signature. But in newer Adobe Reader versions (since version XI if I recall correctly) form editing has been granted to all documents by default! In your case that feature is removed due to the invalidated signature!
This can be done as follows:
using (Stream output = new FileStream(outpath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
using (PdfReader reader = new PdfReader(inpath))
using (var stamper = new PdfStamper(reader, output))
{
reader.RemoveUsageRights();
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(stamp);
image.SetAbsolutePosition(0, 0);
PdfTemplate template = PdfTemplate.CreateTemplate(stamper.Writer, image.Width, image.Height);
template.AddImage(image);
stamper.GetOverContent(1).AddTemplate(template, 150, 200, true);
}
You can now edit the PDF with image in newer Adobe Readers.
Unfortunately, though, there is an error upon saving the document. I don't know whether they have to do with the fact that the source document is partially invalid (Adobe Preflight complains about a number of issues, foremost the use of an undefined encoding name Win1251Encoding) or whether something else gets broken.
Removing the usage rights signature in append mode
Working in append mode we have to manually remove the usage rights signature. Actually, we'll remove the whole Perms dictionary from the Catalog:
using (Stream output = new FileStream(outpath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
using (PdfReader reader = new PdfReader(inpath))
using (var stamper = new PdfStamper(reader, output, '\0', true))
{
reader.Catalog.Remove(PdfName.PERMS);
stamper.MarkUsed(reader.Catalog);
iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(stamp);
image.SetAbsolutePosition(0, 0);
PdfTemplate template = PdfTemplate.CreateTemplate(stamper.Writer, image.Width, image.Height);
template.AddImage(image);
stamper.GetOverContent(1).AddTemplate(template, 150, 200, true);
}
Now you can edit the form and save the file (at least I can in Adobe Reader DC).
PS: The correct coordinates
In a comment the OP shared another PDF and stated that it
for the other file is impossible to place a picture on a page with landscape orientation.
There indeed is an issue in the OP's code:
stamper.GetOverContent(1).AddTemplate(template, 150, 200, true);
The fixed coordinates 150, 200 are a sign that the OP assumes the lower left page corner to be the origin 0, 0 of the coordinate system. While this often is the case, this is not necessarily true. One always has to take the CropBox (which defaults to the MediaBox) into account, i.e. for the OP's code:
Rectangle cropBox = reader.GetCropBox(1);
stamper.GetOverContent(1).AddTemplate(template, cropBox.Left + 150, cropBox.Bottom + 200, true);
the library takes rotation not correctly, but gives 0 degrees.
But that is correct! Your sample PDF is somewhat special as it uses an unrotated rectangle for landscape and a rotated rectangle for portrait.

Opening generated PDF in a browser using iText

I am using PDFStamper to generate a PDF file and then I want to pass it to be opened in a Browser. My code is in a JSP file. My code to actually generate a PDF to Desktop works but not to route to a browser. Below is my code.
PdfReader reader = new PdfReader("/path/pdfs/raw.pdf");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, baos);
PdfContentByte canvas = stamper.getOverContent(1);
BaseFont font = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
canvas.setFontAndSize(font, 12);
canvas.beginText();
canvas.showTextAligned(Element.ALIGN_LEFT, "TEST! TEST! TEST! TEST! ", 80, 713, 0);
canvas.endText();
stamper.close();
reader.close();
String filename="test.pdf";
response.setContentType("application/pdf");
response.setHeader( "Content-Disposition", "filename=" + filename );
response.setContentType("application/pdf");
OutputStream os = response.getOutputStream();
baos.writeTo(os);
os.flush();
This currently opens a blank page - I am not sure what exactly I am doing wrong.
I can make this work using iText Document but since I am opening an existing document and adding stuff to it I have to use PDFStamper and that is where the issue comes. I've confirmed the PDF file in reader exists and can be accessed via a browser by directly going to the location.
Any help would be appreciated!
Using, Struts2, Tile2, Weblogic, Java, iText

itextsharp stamper vs smartcopy, show pdf in browser without toolbar or navpanes

My application is grabbing pdf bytes from our db and sending the pdf to an iframe, using the itextsharp library. When the pdf is displayed in the iframe, the toolbar and navigation pane show, but we'd like to hide those. When I load a pdf document by simply typing in the pdf's url with #toolbar=0&navpanes=0, I see the result I'm looking for.
The application logic is using PdfStamper to add some buttons and other data to the pdf. When I write the pdf to the Response.Outputstream, the pdf shows up with the added buttons, and all is good except that I can't get rid of the toolbar and navpanes. I've tried adding "toolbar=0&navpanes=0" to the url in the response header, but to no avail.
I've written a test application which shows that using PdfSmartCopy instead of the stamper works perfectly - the pdf is shown in the browser which hides the toolbar and navpane by default.
The problem is that I still need to add some buttons to the pdf via the stamper. I've written a test app which adds the buttons via the stamper, then the smart copy grabs each page from the stamper and writes all this out to the Response.Output. The pdf shows in the browser with no toolbar or navpanes, but the buttons are not there.
Here is the code which uses both the stamper and the smart copy - your help is greatly appreciated:
private void SendStamperToCopy()
{
try
{
String filePath = #"C:\debug\PerfIndicWithDefaults.pdf";
byte[] pdfBytes = ReadFile(filePath);
Document document = new Document();
PdfSmartCopy copy = new PdfSmartCopy(document, Response.OutputStream);
document.Open();
MemoryStream memStream = new MemoryStream();
PdfReader reader = new PdfReader(pdfBytes);
PdfStamper pdfStamper = new PdfStamper(reader, memStream);
// add a button with the stamper
iTextSharp.text.Rectangle rectCancel = new iTextSharp.text.Rectangle(50, 50, 20, 20);
PushbuttonField btnCancel = new PushbuttonField(pdfStamper.Writer, rectCancel, "Cancel");
btnCancel.Text = "Cancel";
iTextSharp.text.pdf.PdfAnnotation fieldCancel = btnCancel.Field;
pdfStamper.AddAnnotation(fieldCancel, 1);
int numOfPgs = reader.NumberOfPages;
for (int n = 1; n <= numOfPgs; n++)
{
copy.AddPage(pdfStamper.GetImportedPage(reader, n));
}
String headerStr = "inline; filename=PerfIndicWithDefaults.pdf";
Response.AppendHeader("content-disposition", headerStr);
Response.ContentType = "application/pdf";
Response.OutputStream.Flush();
document.Close();
Response.OutputStream.Close();
}
catch (Exception ex)
{
Console.Write(ex);
Response.OutputStream.Flush();
Response.OutputStream.Close();
}
}
If I understand your question correctly, you want to use PdfStamper to add a button and you want to change the viewer preferences. This can be done like this:
PdfReader reader = new PdfReader(source);
System.IO.MemoryStream m = new System.IO.MemoryStream();
PdfStamper stamper = new PdfStamper(reader, m);
PdfStamper.ViewerPreferences = PdfWriter.HideToolbar | PdfWriter.PageModeUseNone;
stamper.Close();
reader.Close();
The HideToolbar will hide the toolbar, whereas PageModeUseNone means that you don't show any panels (such as the bookmarks panel, etc...).
It is not clear why you would need PdfSmartCopy in this context. Maybe I'm missing something. Also: there are some strange errors in your code: you never close the stamper instance, yet you import a page from the stamper into the copy instance. I've never seen any one try that. It's certainly not what I had in mind when I wrote iText. Your code is very confusing to me.

How to exactly position an Image inside an existing PDF page using PDFBox?

I am able to insert an Image inside an existing pdf document, but the problem is,
The image is placed at the bottom of the page
The page becomes white with the newly added text showing on it.
I am using following code.
List<PDPage> pages = pdDoc.getDocumentCatalog().getAllPages();
if(pages.size() > 0){
PDJpeg img = new PDJpeg(pdDoc, in);
PDPageContentStream stream = new PDPageContentStream(pdDoc,pages.get(0));
stream.drawImage(img, 60, 60);
stream.close();
}
I want the image on the first page.
PDFBox is a low-level library to work with PDF files. You are responsible for more high-level features. So in this example, you are placing your image at (60, 60) starting from lower-left corner of your document. That is what stream.drawImage(img, 60, 60); does.
If you want to move your image somewhere else, you have to calculate and provide the wanted location (perhaps from dimensions obtained with page.findCropBox(), or manually input your location).
As for the text, PDF document elements are absolutely positioned. There are no low-level capabilities for re-flowing text, floating or something similar. If you write your text on top of your image, it will be written on top of your image.
Finally, for your page becoming white -- you are creating a new content stream and so overwriting the original one for your page. You should be appending to the already available stream.
The relevant line is:
PDPageContentStream stream = new PDPageContentStream( pdDoc, pages.get(0));
What you should do is call it like this:
PDPageContentStream stream = new PDPageContentStream( pdDoc, pages.get(0), true, true);
The first true is whether to append content, and the final true (not critical here) is whether to compress the stream.
Take a look at AddImageToPDF sample available from PDFBox sources.
Try this
doc = PDDocument.load( inputFileName );
PDXObjectImage ximage = null;
ximage = new PDJpeg(doc, new FileInputStream( image )
PDPage page = (PDPage)doc.getDocumentCatalog().getAllPages().get(0);
PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true);
contentStream.drawImage( ximage, 425, 675 );
contentStream.close();
This prints the image in first page. If u want to print in all pages just put on a for loop with a condition of number of pages as the limit.
This worked for me well!
So late answer but this is for who works on it in 2020 with Kotlin: drawImage() is getting float values inside itself so try this:
val file = File(getPdfFile(FILE_NAME))
val document = PDDocument.load(file)
val page = document.getPage(0)
val contentStream: PDPageContentStream
contentStream = PDPageContentStream(document, page, true, true)
// Define a content stream for adding to the PDF
val bitmap: Bitmap? = ImageSaver(this).setFileName("sign.png").setDirectoryName("signature").load()
val mediaBox: PDRectangle = page.mediaBox
val ximage: PDImageXObject = JPEGFactory.createFromImage(document, bitmap)
contentStream.drawImage(ximage, mediaBox.width - 4 * 65, 26f)
// Make sure that the content stream is closed:
contentStream.close()
// Save the final pdf document to a file
pdfSaveLocation = "$directoryPDF/$UPDATED_FILE_NAME"
val pathSave = pdfSaveLocation
document.save(pathSave)
document.close()
I am creating a new PDF and running below code in a loop - to add one image per page and below co-ordinates and height and width values work well for me.
where out is BufferedImage reference variable
PDPage page = new PDPage();
outputdocument.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(outputdocument, page, AppendMode.APPEND, true);
PDImageXObject pdImageXObject = JPEGFactory.createFromImage(outputdocument, out);
contentStream.drawImage(pdImageXObject, 5, 2, 600, 750);
contentStream.close();
This link gives you details about Class PrintImageLocations.
This PrintImageLocations will give you the x and y coordinates of the images.
Usage: java org.apache.pdfbox.examples.util.PrintImageLocations input-pdf