Adding multiple images to a PDF document with itext in android - java

I am developing an android application in which i want to add multiple high resolution images to the pdf file with itext jar. I am getting an out of memory exception. When I set the smaller values for width and height it works fine. This is implemented inside a async background task class. The source code is as follows
fos = new FileOutputStream(pdfFilePath);
//Rectangle pagesize = new Rectangle(595.44f, 841.68f);
document = new Document(PageSize.A4, 0f, 0f, 0f, 0f);
PdfWriter.getInstance(document,new FileOutputStream(pdfFilePath));
fileArray = imageDirectory.listFiles();
Log.d("Files", "Size: "+ fileArray.length);
for (i=0; i < fileArray.length; i++){
document.open();
bMap= BitmapFactory.decodeFile(fileArray[i].getPath(), null);
bMap = Bitmap.createScaledBitmap(bMap, 2339, 1654, false);
matrix = new Matrix();
matrix.postRotate(90);
bMap = Bitmap.createBitmap(bMap, 0, 0, bMap.getWidth(), bMap.getHeight(), matrix, false);
bMap = toGrayscale(bMap);
stream = new ByteArrayOutputStream();
bMap.compress(Bitmap.CompressFormat.JPEG, 25, stream);
byteArray = stream.toByteArray();
document.add(Image.getInstance(byteArray));
Log.e("Files", "FileName:" + fileArray[i].getName());
bMap = null;
matrix = null;
byteArray = null;
stream = null;
document.close();
}
fos.close();

See: Managing Bitmap Memory.
You probably want to call bMap.recycle() before setting it to null.

Related

Java : PDF page preview error, expected xref

I am trying to create a preview for PDF files which are created by Ballasamic Mockups. Around 50% of the time, I am not getting a preview and getting xref missing error. What am I doing wrong?
Error log :
com.sun.pdfview.PDFParseException: Expected 'xref' at start of table
at com.sun.pdfview.PDFFile.readTrailer(PDFFile.java:974)
at com.sun.pdfview.PDFFile.parseFile(PDFFile.java:1175)
at com.sun.pdfview.PDFFile.<init>(PDFFile.java:126)
at com.sun.pdfview.PDFFile.<init>(PDFFile.java:102)
Code :
private byte[] onlyCreatePdfPreview(String path, int attachId) {
try {
File file = new File(path);
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel();
ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
PDFFile pdffile = new com.sun.pdfview.PDFFile(buf);
PDFPage page = pdffile.getPage(0);
Rectangle rect = new Rectangle(0, 0,
(int) page.getBBox().getWidth(),
(int) page.getBBox().getHeight());
java.awt.Image img = page.getImage(
rect.width, rect.height, //width & height
rect, // clip rect
null, // null for the ImageObserver
true, // fill background with white
true // block until drawing is done
);
BufferedImage buffered = toBufferedImage(img);
buffered = Scalr.resize(buffered, Scalr.Method.ULTRA_QUALITY, 400, 250);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(buffered, "png", baos);
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
}
What am I doing wrong? Thank you.
Final Working code
try {
String pdfPath = zipLocation + String.valueOf(new BigInteger(130, random).toString(32));
PdfReader reader = new PdfReader(path);
PdfStamper pdfStamper = new PdfStamper(reader,new FileOutputStream(pdfPath));
pdfStamper.getWriter().setPdfVersion(PdfWriter.PDF_VERSION_1_4);
pdfStamper.close();
reader.close();
RandomAccessFile raf = new RandomAccessFile(pdfPath, "r");
FileChannel channel = raf.getChannel();
ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
PDFFile pdffile = new com.sun.pdfview.PDFFile(buf);
PDFPage page = pdffile.getPage(0);
Rectangle rect = new Rectangle(0, 0,
(int) page.getBBox().getWidth(),
(int) page.getBBox().getHeight());
java.awt.Image img = page.getImage(
rect.width, rect.height, //width & height
rect, // clip rect
null, // null for the ImageObserver
true, // fill background with white
true // block until drawing is done
);
BufferedImage buffered = toBufferedImage(img);
buffered = Scalr.resize(buffered, Scalr.Method.ULTRA_QUALITY, 400, 250);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(buffered, "png", baos);
baos.flush();
return baos.toByteArray();
}//catch block
(This answer collects information from comments to the question which eventually led to a solution.)
Apparently com.sun.pdfview.PDFFile expects the cross references to start with xref. But this expectation only makes sense for PDFs following a PDF Reference up to revision 3 (version 1.4) published November 2001; PDFs following a later Reference or even the ISO 32000 standard (part 1 or 2) have the choice of using a cross reference stream (starting with an object number) instead of a cross reference table (starting with xref).
Thus, one should switch to using software following newer specifications than some more than 15 years old version or one has to convert one's PDFs to follow the old specifications, at least on the surface.
One can convert manually (e.g. using Adobe Acrobat) or automatized (e.g. using iText). (These examples software products really are only examples, other products can also be used for this task.)
If using a current iText 5 version, the conversion looks like this:
PdfReader reader = new PdfReader(SOURCE);
PdfStamper stamper = new PdfStamper(reader, DEST);
stamper.getWriter().setPdfVersion(PdfWriter.PDF_VERSION_1_4);
stamper.close();
reader.close();
And one has to take care that if SOURCE is a file name or a random access file, DEST must not be a file output stream to the same file. Otherwise the original file is truncated before the PdfStamper had a chance to copy it all to its output.

Retrieve multiple images from a bytearray in Java

I am working with a bytearray which contains multiple images. How can I retrieve the images from the bytearray in Java.
PDDocument document = null;
int pageCounter = 0;
BufferedImage bim = null;
document = PDDocument.load(pdfBytes);
PDFRenderer pdfRenderer = new PDFRenderer(document);
bim = pdfRenderer.renderImageWithDPI(pageCounter, 300, ImageType.RGB);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(rotate(bim), imageFormat, bos);
output = bos.toByteArray();
rotate is a method to rotate the image. I want to rotate only one Image. The other I do not want to rotate. Also I want to output it again in Bytes

Android scaled bitmap signature into pdf

i couldn't find anything for my problem.
My problem is this. I have to create a pdf with a signature. I don't have problems to create pdf or to create signature but when i put them together, i resize the signature to be smaller but the quality of the signature is been reduced.
Here I show you the signature and the scaled signature.
create signature
scaled signature
This is how i create the bitmap of the signature.
mSignaturePad = (SignaturePad) dialog.findViewById(R.id.signature_pad);
Bitmap signaturePad = mSignaturePad.getSignatureBitmap();
createPDF(signaturePad);
This is how i create the canvas of the pdf page.
PdfDocument.Page page = document.startPage(pageInfo);
Canvas canvas = page.getCanvas();
And finally i draw the bitmap after i obtain the scaled bitmap.
int width = source.getWidth();
int height = source.getHeight();
float scaleWidth = ((float) 100) / width;
float scaleHeight = ((float) 140) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
matrix.postRotate(-90);
Bitmap scaledBitmap = Bitmap.createBitmap(source, 0, 0, width, height, matrix, true);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawBitmap(scaledBitmap, 387, 717, paint3);
This is not the only way that i have tested to scale bitmap. I tested many ways but nothing worked. How can i scale this bitmap?
I hope you can help me. Thank you.
psd: Sorry for my english.
The below explains how to add a image from assets folder. If you have the bitmap already, then no issue just follow it from the bitmap process. you can also follow the Example.
File myFile1 = new File("/sdcard/ourdata/" + pdfname + ".pdf");
myFile1.createNewFile();
Document pdfDoc = new Document();
PdfWriter docWriter = PdfWriter.getInstance(document,
new FileOutputStream(myFile1));
document.open();
PdfContentByte cb = docWriter.getDirectContent();
InputStream inputStream = getAssets().open("signature.png");
Bitmap bmp = BitmapFactory.decodeStream(inputStream);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
Image signature = Image.getInstance(stream.toByteArray());
signature.setAbsolutePosition(400f, 150f);
signature.scalePercent(25f);
document.add(signature);

Insufficient Data For An Image (PDF File Generation)

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>

Get bytes from iText's Barcode39 image

how can I get the bytes from an image generated using itext's barcode39 class ? I have:
Document document = new Document(new Rectangle(340, 842));
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
PdfContentByte cb = writer.getDirectContent();
Barcode39 code39ext = new Barcode39();
code39ext.setCode("Testing Text");
code39ext.setStartStopText(false);
code39ext.setExtended(true);
Image img = code39ext.createImageWithBarcode(cb, null, null);
Now I need help to get the bytes from img in order to send it via email and save it to a file.
Thanks in advance.
Assuming that you actually do not need the PDF file but only the barcode image, then you might try:
Barcode39 code39ext = new Barcode39();
code39ext.setCode("Testing Text");
code39ext.setStartStopText(false);
code39ext.setExtended(true);
java.awt.Image rawImage = code39ext.createAwtImage(Color.BLACK, Color.WHITE);
BufferedImage outImage = new BufferedImage(rawImage.getWidth(null), rawImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
outImage.getGraphics().drawImage(rawImage, 0, 0, null);
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ImageIO.write(outImage, "png", bytesOut);
bytesOut.flush();
byte[] pngImageData = bytesOut.toByteArray();
This should just create the barcode image, render it to memory and save it to a stream / byte[] for further usage.

Categories