At the moment I'm trying to print some pdfs in java. The API used is Apache PDF Box in Version 2.0. After converting the images I write them to disk to save memory. In the next step I read them again and write the title in the head of the image. After that I write them again. To use them I read them again separately. When I read them for the last time I get the following exception every 200-300 images:
java.lang.IndexOutOfBoundsException: off < 0 || len < 0 || off+len > b.length || off+len < 0!
at javax.imageio.stream.MemoryCacheImageInputStream.read(MemoryCacheImageInputStream.java:100)
at com.sun.imageio.plugins.common.SubImageInputStream.read(SubImageInputStream.java:61)
at com.sun.imageio.plugins.common.InputStreamAdapter.read(InputStreamAdapter.java:47)
at java.io.SequenceInputStream.read(SequenceInputStream.java:207)
at java.util.zip.InflaterInputStream.fill(InflaterInputStream.java:238)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at com.sun.imageio.plugins.png.PNGImageReader.decodePass(PNGImageReader.java:1104)
at com.sun.imageio.plugins.png.PNGImageReader.decodeImage(PNGImageReader.java:1215)
at com.sun.imageio.plugins.png.PNGImageReader.readImage(PNGImageReader.java:1330)
at com.sun.imageio.plugins.png.PNGImageReader.read(PNGImageReader.java:1606)
at javax.imageio.ImageIO.read(ImageIO.java:1448)
at javax.imageio.ImageIO.read(ImageIO.java:1400)
at my.code.Class.method()
I use the following code to convert the PDFs:
final HashMap<Integer, File> images = new HashMap<>();
PDDocument document = PDDocument.load(sourceFile);
PDFRenderer pdfRenderer = new PDFRenderer(document);
final ExecutorService service = Executors.newFixedThreadPool(4);
for (int page = 0; page < document.getNumberOfPages(); ++page)
{
final int finalPageNumber = page;
Runnable r = () -> {
try
{
//Java could only print with 72 dpi so I'll use it
BufferedImage image = pdfRenderer.renderImageWithDPI(finalPageNumber, 72);
File imageFile = new File(sourceFile.getAbsolutePath() + "-" + (finalPageNumber + 1) + ".png");
ImageIO.write(image, "png", imageFile);
image.flush();
images.put(finalPageNumber, imageFile);
}
catch (final IOException e)
{
e.printStackTrace();
}
};
Thread t = new Thread(r, page + "");
t.setName("" + page);
service.submit(t);
}
And for reading is the following code used:
// example url: /tmp/example.pdf-17.png
Image i = ImageIO.read(url);
What about to solve this problem
edit
What I've forgotten to say I add a Title manually to the Image:
BufferedImage bimage = new BufferedImage( simple.getValue().getWidth(null),
simple.getValue().getHeight(null) + 50,
BufferedImage.TYPE_INT_ARGB);
Graphics bGr = bimage.createGraphics();
bGr.setColor(Color.WHITE);
bGr.fillRect(0, 0, bimage.getWidth(), bimage.getHeight());
bGr.setFont(new Font("Arial", Font.PLAIN, 15));
bGr.setColor(Color.BLACK);
bGr.drawImage(simple.getValue(), 0, 50, null);
bGr.drawString(entry.getValue(), 20, 20);
try
{
ImageIO.write(bimage, "PNG", new File(simple.getKey().toURI()));
}
catch (IOException | URISyntaxException e)
{
e.printStackTrace();
}
bGr.dispose();
And here are the links to the files (It could occur with every page):
Image which throws the exception
PDF file which is used (generated by Word2007)
I have solved my Problem. I hadn't wait for the writing of the Image. Now I wait for the finish of the ExecutorService and everything works fine.
I think there was a problem to free resources, since I've seen that at MemoryCacheImageInputStream line 100 every thing was fine when the exception was thrown.
Related
**I'm using the below code to fetch the multiple failure screenshots from the folder to Bugzilla tool, while uploading the pictures in bugzilla, color of the picture is disorted. [enter image description here][1]. Can any one help me to rectify this issue. ? **
try {
BugzillaConnector conn = new BugzillaConnector();
conn.connectTo("bugzilla.com");
LogIn logIn = new LogIn("username", "password");
conn.executeMethod(logIn);
Bug bug = new BugFactory()
.newBug()
.setProduct("SeleniumFramework")
.setComponent("CoreJavaTestNG")
.setVersion("1.0").setPlatform("PC")
.setOperatingSystem("Windows")
.setDescription("Bug posted from Java Source Code")
.setSummary("Bug posted from Java Source Code")
.createBug();
ReportBug report = new ReportBug(bug);
conn.executeMethod(report);
int bugID = report.getID();
System.out.println("Bug posted and its ID is " + bugID);
GetBug get = new GetBug(bugID);
conn.executeMethod(get);
System.out.println(get.getBug().getID());
System.out.println(get.getBug().getSummary());
System.out.println(get.getBug().getProduct());
System.out.println(get.getBug().getComponent());
System.out.println(get.getBug().getVersion());
System.out.println(get.getBug().getPlatform());
System.out.println(get.getBug().getOperatingSystem());
// Passing txtFileFilter to listFiles() method to retrieve only file start with fail files
File[] files = folder.listFiles(txtFileFilter);
int Count = 0;
for (File file : files) {
BufferedImage bImage = ImageIO.read(new File(FilePath + file.getName()));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(bImage, "jpg", bos );
byte [] data = bos.toByteArray();
AttachmentFactory attachmentFactory = new AttachmentFactory();
Attachment attachment = attachmentFactory.newAttachment()
. setData(data)
. setMime("image/jpg") //Set the appropriate MIME type for the image format
. setSummary(file.toString()) //Description
. setName(file.toString())//Name of the Screenshot in Bugzilla
. setBugID(bugID)
. createAttachment();
AddAttachment add2 = new AddAttachment(attachment, bugID);
add2.getID();
conn.executeMethod(add2);
Count++;
}
System.out.println(Count + " File Uploded");
}
catch (Exception e) {
e.printStackTrace();
} ```
[1]: https://i.stack.imgur.com/qrIaq.jpg
The pinkish/readish ting your seeing is because the source image contains a alpha channel.
There is a known bug in ImageIO which will include the alpha channel into the output of the JPEG image (or some such thing, you can google it if you're really interested).
The basic solution to your problem is to apply the original image to a BufferedImage using a TYPE_INT_RGB, which will remove the alpha channel, for example see Removing transparency in PNG BufferedImage.
I used the code but am getting blue color background on the image
So, starting with this transparent PNG
And using the below code...
BufferedImage original = ImageIO.read(new File("/Users/shanew/Downloads/transparent.png"));
BufferedImage copy = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = copy.createGraphics();
g2d.setColor(Color.WHITE); // Or what ever fill color you want...
g2d.fillRect(0, 0, copy.getWidth(), copy.getHeight());
g2d.drawImage(original, 0, 0, null);
g2d.dispose();
File dest = new File("Test.jpg");
ImageIO.write(copy, "jpg", dest);
BufferedImage test = ImageIO.read(dest);
JPanel panel = new JPanel();
panel.add(new JLabel(new ImageIcon(original)));
panel.add(new JLabel(new ImageIcon(test)));
JOptionPane.showMessageDialog(null, panel);
I can produce...
If you're still having issues, then you need to do two things:
Update your original question with the code you are using
Provide a sample of the image you are trying to convert
It's not helpful to keep posting code in the comments
Fist of all, I'm relatively new to java, as well to iText.
Shortly, I have a program which copies every 2 pages from big source pdf document and creates a new doc for each pair of pages. Additionally, the program removes some text information from the first page and protects new documents with owner password.
Here is my code. I use iText 5.5.13.
//...
final Rectangle RECT_TOP= new Rectangle(25f, 788f, 288f, 812.5f);
final Rectangle RECT_BOT= new Rectangle(103.5f, 36.5f, 331f, 40f);
//...
PdfDocument document = new Document(reader.getPageSizeWithRotation(1));
File tempFile = File.createTempFile("temp", ".pdf");
PdfCopy writer = new PdfCopy(
document, //PdfDocument
new FileOutputStream(tempFile.getAbsolutePath()));
document.open();
writer.addPage( writer.getImportedPage(reader, i) );
writer.addPage( writer.getImportedPage(reader, i + 1) );
writer.freeReader(reader);
writer.close();
document.close();
PdfReader tmpReader = new PdfReader(tempFile.getAbsolutePath());
PdfStamper st = new PdfStamper(tmpReader, new FileOutputStream(outFile));
List<PdfCleanUpLocation> locations = new ArrayList<PdfCleanUpLocation>();
locations.add(new PdfCleanUpLocation(1, RECT_TOP, BaseColor.WHITE));
locations.add(new PdfCleanUpLocation(1, RECT_BOT, BaseColor.WHITE));
new PdfCleanUpProcessor(locations, st).cleanUp();
st.setEncryption(
"".getBytes(),
OWNER_PASSWORD.getBytes(),
PdfWriter.ALLOW_COPY | PdfWriter.ALLOW_PRINTING,
PdfWriter.ENCRYPTION_AES_256 | PdfWriter.DO_NOT_ENCRYPT_METADATA);
st.getWriter().freeReader(tmpReader);
st.close();
tmpReader.close();
tempFile.delete();
Source PDFs have a QR code as image on every page that I have to cleanUp(). Regions RECT_TOP and RECT_BOT don't include the image in any way.
I tested my code on two pdf with the same data inside. One of them was created with BullZip PDF printer (v PDF-1.5) and the other one with Foxit PDF printer (v PDF-1.7). The thing is the cleanUp method removes the QR code and data from rectangle locations passed to PdfCleanUpProcessor in BullZip-created docs, but for foxit PDFs it works as it should and I really need it working with Bullzip docs.
I tried to manipulate the version of the temp pdf file and cleanUp() through redacted annotations, but with no use.
I want to understand where to look at and what to change (maybe somewhere in PdfCleanUpProcessor class?) to make it work properly.
Does anybody know why this happens?
UPD. I managed to create some PDFs similar to ones I need to process and I found out another interesting thing: Bullzip is able to create both "bad" and "good" files itself. I checked different printer settings, including permissions, but it is still hard to say what it depends on. Among the noticable differences there is only a slightly smaller file size and a little different page margins.
Anyway, here my test files
You have indeed found a bug in the iText 5 PdfCleanUpProcessor: It drops all inline images which are not partially subject to redaction.
The bug in detail
The bug is located in the PdfCleanUpRenderListener method renderImage:
public void renderImage(ImageRenderInfo renderInfo) {
List<Rectangle> areasToBeCleaned = getImageAreasToBeCleaned(renderInfo);
if (areasToBeCleaned == null) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} else if ( areasToBeCleaned.size() > 0) {
try {
PdfImageObject pdfImage = renderInfo.getImage();
byte[] imageBytes = processImage(pdfImage.getImageAsBytes(), areasToBeCleaned);
if (renderInfo.getRef() == null && pdfImage != null) { // true => inline image
PdfDictionary dict = pdfImage.getDictionary();
PdfObject imageMask = dict.get(PdfName.IMAGEMASK);
Image image = Image.getInstance(imageBytes);
if (imageMask == null) {
imageMask = dict.get(PdfName.IM);
}
if (imageMask != null && imageMask.equals(PdfBoolean.PDFTRUE)) {
image.makeMask();
}
PdfContentByte canvas = getContext().getCanvas();
canvas.addImage(image, 1, 0, 0, 1, 0, 0, true);
} else if (pdfImage != null && imageBytes != pdfImage.getImageAsBytes()) {
chunks.add(new PdfCleanUpContentChunk.Image(true, imageBytes));
}
} catch (UnsupportedPdfException pdfException) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Inline images require special treatment here: For non-inlined images the actual instruction for drawing the image XObject is handled elsewhere and renderImage only needs to check whether the image has to be redacted and provide a redacted image version. For inline images, though, this method also has to add the image to the result content stream (unless it is completely redacted away).
As you can see there is a special treatment of inline images in the block for images partially covered by redaction areas (areasToBeCleaned.size() > 0) but none for images not covered by redaction areas (areasToBeCleaned != null and areasToBeCleaned.size() == 0).
How to fix it
You can fix this by adding a similar special treatment in a new else clause:
public void renderImage(ImageRenderInfo renderInfo) {
List<Rectangle> areasToBeCleaned = getImageAreasToBeCleaned(renderInfo);
if (areasToBeCleaned == null) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} else if ( areasToBeCleaned.size() > 0) {
try {
PdfImageObject pdfImage = renderInfo.getImage();
byte[] imageBytes = processImage(pdfImage.getImageAsBytes(), areasToBeCleaned);
if (renderInfo.getRef() == null && pdfImage != null) { // true => inline image
PdfDictionary dict = pdfImage.getDictionary();
PdfObject imageMask = dict.get(PdfName.IMAGEMASK);
Image image = Image.getInstance(imageBytes);
if (imageMask == null) {
imageMask = dict.get(PdfName.IM);
}
if (imageMask != null && imageMask.equals(PdfBoolean.PDFTRUE)) {
image.makeMask();
}
PdfContentByte canvas = getContext().getCanvas();
canvas.addImage(image, 1, 0, 0, 1, 0, 0, true);
} else if (pdfImage != null && imageBytes != pdfImage.getImageAsBytes()) {
chunks.add(new PdfCleanUpContentChunk.Image(true, imageBytes));
}
} catch (UnsupportedPdfException pdfException) {
chunks.add(new PdfCleanUpContentChunk.Image(false, null));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else { // add inline images not subject to redaction to the content
try {
PdfImageObject pdfImage = renderInfo.getImage();
if (renderInfo.getRef() == null && pdfImage != null) { // true => inline image
PdfDictionary dict = pdfImage.getDictionary();
PdfObject imageMask = dict.get(PdfName.IMAGEMASK);
Image image = Image.getInstance(pdfImage.getImageAsBytes());
if (imageMask == null) {
imageMask = dict.get(PdfName.IM);
}
if (imageMask != null && imageMask.equals(PdfBoolean.PDFTRUE)) {
image.makeMask();
}
PdfContentByte canvas = getContext().getCanvas();
canvas.addImage(image, 1, 0, 0, 1, 0, 0, true);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Example
To illustrate the issue and the effect of the fix, I created a simple PDF with five inline images:
Document document = new Document(new Rectangle(500, 500));
PdfWriter writer = PdfWriter.getInstance(document, baos);
document.open();
PdfContentByte canvas = writer.getDirectContent();
for (int i = 0; i < 5; i++) {
canvas.addImage(image, 50, 0, 0, 50, i * 100 + 25, i * 100 + 25, true);
}
document.close();
(RedactWithImageIssue helper method createPdfWithInlineImages)
looking like this:
Applying redaction like this
PdfReader reader = new PdfReader(pdf);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));
List<com.itextpdf.text.pdf.pdfcleanup.PdfCleanUpLocation> locations = new ArrayList<>();
locations.add(new com.itextpdf.text.pdf.pdfcleanup.PdfCleanUpLocation(1, new Rectangle(150, 150, 350, 350), BaseColor.RED));
new PdfCleanUpProcessor(locations, stamper).cleanUp();
stamper.close();
(RedactWithImageIssue test testRedactPdfWithInlineImages)
without and with the patch respectively results in
As you can see, originally only the partially redacted inline images remained but with the patch inline images completely outside the redaction area remain, too.
I am writing a test process which will act as duplicate to original processors
My class will take a video file as input then generates frame related information
Java2DFrameConverter bufferedImgConverter = new Java2DFrameConverter();
String filePath = "Palivala.mp4";
File tsFile = new File(filePath);
FFmpegFrameGrabber ffmpegFrameGrabber = new FFmpegFrameGrabber(tsFile);
OpenCVFrameConverter matConverter = new OpenCVFrameConverter.ToMat();
ffmpegFrameGrabber.start();
try {
while ((frame = ffmpegFrameGrabber.grabFrame(false, true, true, false)) != null) {
BufferedImage bufferedImage = bufferedImgConverter.convert(frame);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
ImageIO.write(bufferedImage, "jpeg", buffer);
} catch (IOException e) {
e.printStackTrace();
}
byte frameAsBytes[] = buffer.toByteArray();
start = System.currentTimeMillis();
BufferedImage bufferedImage2 = ImageIO.read(new ByteArrayInputStream(frameAsBytes));
Mat mat = matConverter.convertToMat(bufferedImgConverter.convert(bufferedImage)); //problem is here, I explained problem at the bottom
if (mat.empty())
continue;
if (prevMat != null) {
// here I am generating required data and sending to the method which I want to test
}
prevMat = mat.clone();
numProcessedFrames++;
}
} catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
System.out.println("Exception while grabbing frames for segment " + e);
}
ffmpegFrameGrabber.stop();
If I use initial BufferedImage object(bufferedImage) its working as expected but when I use converted BufferedImage object(bufferedImage2) its not working as expected i.e actually my process finds info about frozen frames, If the video is frozen for 20seconds
Its giving final output as 20seconds (if I use bufferedImage object)
Its giving final output as 14seconds (if I use bufferedImage2 object)
I am generating standard-deviation using mat object which is getting from
Mat mat = matConverter.convertToMat(bufferedImgConverter.convert(bufferedImage)); //it works fine
Mat mat = matConverter.convertToMat(bufferedImgConverter.convert(bufferedImage2)); its the problem
I am getting frozen-frame info using standard-deviation and my standard-deviation-threshold is 90
standard-deviation is continuously below 90 If I use bufferedImage object
standard-deviation is continuously below 90 till 6 seconds then for 1 frame I am getting standard-deviation value as [118.0, 118.0, 119.0] then next frame again below 90 If I use bufferedImage2 object
It seems to me there are two ways to store an attachment in a NotesDocument.
Either as a RichTextField or as a "MIME Part".
If they are stored as RichText you can do stuff like:
document.getAttachment(fileName)
That does not seem to work for an attachment stored as a MIME Part. See screenshot
I have thousands of documents like this in the backend. This is NOT a UI issue where I need to use the file Download control of XPages.
Each document as only 1 attachment. An Image. A JPG file. I have 3 databases for different sizes. Original, Large, and Small. Originally I created everything from documents that had the attachment stored as RichText. But my code saved them as MIME Part. that's just what it did. Not really my intent.
What happened is I lost some of my "Small" pictures so I need to rebuild them from the Original pictures that are now stored as MIME Part. So my ultimate goal is to get it from the NotesDocument into a Java Buffered Image.
I think I have the code to do what I want but I just "simply" can't figure out how to get the attachment off the document and then into a Java Buffered Image.
Below is some rough code I'm working with. My goal is to pass in the document with the original picture. I already have the fileName because I stored that out in metaData. But I don't know how to get that from the document itself. And I'm passing in "Small" to create the Small image.
I think I just don't know how to work with attachments stored in this manner.
Any ideas/advice would be appreciated! Thanks!!!
public Document processImage(Document inputDoc, String fileName, String size) throws IOException {
// fileName is the name of the attachment on the document
// The goal is to return a NEW BLANK document with the image on it
// The Calling code can then deal with keys and meta data.
// size is "Original", "Large" or "Small"
System.out.println("Processing Image, Size = " + size);
//System.out.println("Filename = " + fileName);
boolean result = false;
Session session = Factory.getSession();
Database db = session.getCurrentDatabase();
session.setConvertMime(true);
BufferedImage img;
BufferedImage convertedImage = null; // the output image
EmbeddedObject image = null;
InputStream imageStream = null;
int currentSize = 0;
int newWidth = 0;
String currentName = "";
try {
// Get the Embedded Object
image = inputDoc.getAttachment(fileName);
System.out.println("Input Form : " + inputDoc.getItemValueString("form"));
if (null == image) {
System.out.println("ALERT - IMAGE IS NULL");
}
currentSize = image.getFileSize();
currentName = image.getName();
// Get a Stream of the Imahe
imageStream = image.getInputStream();
img = ImageIO.read(imageStream); // this is the buffered image we'll work with
imageStream.close();
Document newDoc = db.createDocument();
// Remember this is a BLANK document. The calling code needs to set the form
if ("original".equalsIgnoreCase(size)) {
this.attachImage(newDoc, img, fileName, "JPG");
return newDoc;
}
if ("Large".equalsIgnoreCase(size)) {
// Now we need to convert the LARGE image
// We're assuming FIXED HEIGHT of 600px
newWidth = this.getNewWidth(img.getHeight(), img.getWidth(), 600);
convertedImage = this.getScaledInstance(img, newWidth, 600, false);
this.attachImage(newDoc, img, fileName, "JPG");
return newDoc;
}
if ("Small".equalsIgnoreCase(size)) {
System.out.println("converting Small");
newWidth = this.getNewWidth(img.getHeight(), img.getWidth(), 240);
convertedImage = this.getScaledInstance(img, newWidth, 240, false);
this.attachImage(newDoc, img, fileName, "JPG");
System.out.println("End Converting Small");
return newDoc;
}
return newDoc;
} catch (Exception e) {
// HANDLE EXCEPTION HERE
// SAMLPLE WRITE TO LOG.NSF
System.out.println("****************");
System.out.println("EXCEPTION IN processImage()");
System.out.println("****************");
System.out.println("picName: " + fileName);
e.printStackTrace();
return null;
} finally {
if (null != imageStream) {
imageStream.close();
}
if (null != image) {
LibraryUtils.incinerate(image);
}
}
}
I believe it will be some variation of the following code snippet. You might have to change which mimeentity has the content so it might be in the parent or another child depending.
Stream stream = session.createStream();
doc.getMIMEEntity().getFirstChildEntity().getContentAsBytes(stream);
ByteArrayInputStream bais = new ByteArrayInputStream(stream.read());
return ImageIO.read(bais);
EDIT:
session.setConvertMime(false);
Stream stream = session.createStream();
Item itm = doc.getFirstItem("ParentEntity");
MIMEEntity me = itm.getMIMEEntity();
MIMEEntity childEntity = me.getFirstChildEntity();
childEntity.getContentAsBytes(stream);
ByteArrayOutputStream bo = new ByteArrayOutputStream();
stream.getContents(bo);
byte[] mybytearray = bo.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(mybytearray);
return ImageIO.read(bais);
David have a look at DominoDocument,http://public.dhe.ibm.com/software/dw/lotus/Domino-Designer/JavaDocs/XPagesExtAPI/8.5.2/com/ibm/xsp/model/domino/wrapped/DominoDocument.html
There you can wrap every Notes document
In the DominoDocument, there such as DominoDocument.AttachmentValueHolder where you can access the attachments.
I have explained it at Engage. It very powerful
http://www.slideshare.net/flinden68/engage-use-notes-objects-in-memory-and-other-useful-java-tips-for-x-pages-development
I'm using PDFBox to generate PDF files, however when I try to draw an image which I receive from an array of bytes I get the following error:
Insufficient data for an image
This is the basic structure of my code:
public ByteArrayOutputStream generatePDF() {
.. Variable Declaration
// Creating Document
document = new PDDocument();
// Creating Pages
for(int i = 0; i < arrayVar.length; i++) {
// Adding page to document
page = new PDPage();
// Creating FONT Attributes
fontNormal = PDType1Font.HELVETICA;
fontBold = PDType1Font.HELVETICA_BOLD;
// Building Front & Back Invoice Images
singleImageMap = // Getting Map With Array Of Bytes from Web Service Call;
if(singleImageMap != null && !singleImageMap.isEmpty()) {
arrayFront = Utils.readImage((byte[]) singleImageMap.get(Constants.WS_IMAGE_FRONT));
arrayBack = Utils.readImage((byte[]) singleImageMap.get(Constants.WS_IMAGE_BACK));
fileFront = new ByteArrayInputStream(arrayFront);
fileBack = new ByteArrayInputStream(arrayBack);
bufferedImageFront = ImageIO.read(fileFront);
bufferedImageBack = ImageIO.read(fileBack);
rescaledFrontImg = Scalr.resize(bufferedImageFront, 500);
rescaledBackImg = Scalr.resize(bufferedImageBack, 500);
front = new PDJpeg(document, rescaledFrontImg);
back = new PDJpeg(document, rescaledBackImg);
}
// Next we start a new content stream which will "hold" the to be created content.
contentStream = new PDPageContentStream(document, page);
// Let's define the content stream
contentStream.beginText();
contentStream.setFont(fontNormal, 8);
contentStream.moveTextPositionByAmount(200, 740);
contentStream.drawString("NAME: " + arrayVar[i].getParameter(Constants.NAME));
contentStream.endText();
if(front != null && back != null) {
contentStream.drawImage(front, 55, 500);
contentStream.drawImage(back, 55, 260);
}
// Add Page
document.addPage(page);
// Let's close the content stream
contentStream.close();
}
// Let's create OutputStream object
output = new ByteArrayOutputStream();
// Finally Let's save the PDF
document.save(output);
document.close();
return output;
}
Since I receive a PNG file from the Web Service I do the conversion to JPG with the following method:
public static byte[] readImage(byte[] file) throws Exception {
ImageInputStream is = ImageIO.createImageInputStream(new ByteArrayInputStream(file));
BufferedImage originalImage = ImageIO.read(is);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpg", baos );
byte[] imageInByte = baos.toByteArray();
return imageInByte;
}
As per this link:
https://issues.apache.org/jira/browse/PDFBOX-849
It points out that the error is because the PDJepg object should be created before the creation of the contentStream, but that's what I do in my code.
I'm not sure if there is a problem with the structure of my code, or that maybe there is an error in the way I'm handling the image bytes I'm getting from the Web Service call.
Does anyone has an idea of what could be the problem?
UPDATE
I did what Zelter Ady and indeed the image that I'm getting from the Web Service is valid since I was able to generate a physical file with it, so the problem should be somewhere around the manipulation of the image, the thing is I don't know what I'm missing.
I've got the same problem. With some images, Acrobat failed to display pages with this message:
Insufficient data for an image
My problem came from the colorModel in some jpeg images.
To track which images weren't ok, i log the BufferedImage colorModel by log.warn(img.getColorModel());
[VisualLocatorServlet.doGet:142] ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace#4b7fce transparency = 1 has alpha = false isAlphaPre = false
[VisualLocatorServlet.doGet:142] ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace#4b7fce transparency = 1 has alpha = false isAlphaPre = false
[VisualLocatorServlet.doGet:142] ColorModel: #pixelBits = 8 numComponents = 1 color space = java.awt.color.ICC_ColorSpace#19ef899 transparency = 1 has alpha = false isAlphaPre = false
Obviously, failing images are 8-bits encoded.
To fix that, i did the following:
byte[] buffer = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedImage img = ImageIO.read(new URL(visual));
/* resample 8-bits to 24-bits if necessary to fix pdf corruption */
if(img.getColorModel().getNumColorComponents()==1){
log.warn("components #1"+img.getColorModel());
BufferedImage out = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g2 = out.createGraphics();
g2.setBackground(Color.WHITE);
g2.drawImage(i, 0, 0, null);
g2.dispose();
log.warn("redrawn image "+img.getColorModel());
}
ImageIO.write(img, "jpeg", out);
...
The main point is to recreate a BufferedImage in 24bits. (BufferedImage.TYPE_3BYTE_BGR).
This may be an issue on the Adobe viewer side rather than at creation time. There's a known issue with the latest Acrobat versions: “Insufficient data for an image” error after updating to 10.1.4 or 9.5.2:
http://blogs.adobe.com/dmcmahon/2012/08/21/acrobat-insufficient-data-for-an-image-error-after-updating-to-10-1-4-or-9-5-2/
Before the build of the pdf try to save the image in a file, just to see the image is complete and can be saved.
You may use something like this to test the received image:
System.IO.File.WriteAllBytes("c:\\tmp.png", (byte[]) singleImageMap.get(Constants.FRONT));
and then open the image in a imageviewer. If the image cannot be open, then u have an error here. If the image is ok.... at least you know that this part is ok!
Well after a lot of debugging I found that the problem was here:
front = new PDJpeg(document, rescaledFrontImg);
back = new PDJpeg(document, rescaledBackImg);
The PDJpeg class has two constructors:
PDJpeg(PDDocument doc, BufferedImage bi)
PDJpeg(PDDocument doc, InputStream is)
I was passing a BufferedImage and at some point that I still can't figure out, I assume all the bytes were not being completely sent thus I got the message "Insufficient Data For An Image".
Solution: I passed an InputStream instead of a BufferedImage.
I still don't know why I got that error using a BufferedImage maybe I needed to do some sort of .push()?
This code worked for me.
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import org.apache.commons.imaging.Imaging;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
public void generatePdfFromTifPbox(File sourceFile, String destinationPath) throws Exception {
//sourceFile is tiff file, destinationPath is pdf destination path with pdf file name
PDDocument doc = new PDDocument();
List<BufferedImage> bimages = Imaging.getAllBufferedImages(sourceFile);
for (BufferedImage bi : bimages) {
PDPage page = new PDPage();
doc.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(doc, page);
try {
// the .08F can be tweaked. Go up for better quality,
// but the size of the PDF will increase
PDImageXObject image = JPEGFactory.createFromImage(doc, bi, 0.08f);
Dimension scaledDim = getScaledDimension(new Dimension(image.getWidth(), image.getHeight()),
new Dimension((int) page.getMediaBox().getWidth(), (int) page.getMediaBox().getHeight()));
contentStream.drawImage(image, 1, 1, scaledDim.width, scaledDim.height);
} finally {
contentStream.close();
}
}
doc.save(destinationPath);
}
private Dimension getScaledDimension(Dimension imgSize, Dimension boundary) {
int original_width = imgSize.width;
int original_height = imgSize.height;
int bound_width = boundary.width;
int bound_height = boundary.height;
int new_width = original_width;
int new_height = original_height;
// first check if we need to scale width
if (original_width > bound_width) {
// scale width to fit
new_width = bound_width;
// scale height to maintain aspect ratio
new_height = (new_width * original_height) / original_width;
}
// then check if we need to scale even with the new height
if (new_height > bound_height) {
// scale height to fit instead
new_height = bound_height;
// scale width to maintain aspect ratio
new_width = (new_height * original_width) / original_height;
}
return new Dimension(new_width, new_height);
}
Reference/Courtesy: http://www.paulzepernick.com/java/java-apache-pdfbox-convert-multipage-tiff-to-pdf/
Maven dependency:
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-imaging</artifactId>
<version>1.0-alpha1</version>
</dependency>