I have created an Android Invoice app. The generated invoice is standard Android layout with nested views. I am looking for a library that I can use to convert this view to an pdf document.
I am surprised there is no straight forward option coming up in my search or Perhaps I have the done the first thing last. Or perhaps what I am looking for is not possible.
Would someone please help point me to a tool that will help me convert or generate a PDF from an Android view. I am open to free and modest paid option. Or let me know is if what I am looking for is not possible.
Take a screen at your device:
Bitmap screen;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
screen= Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
If you're having ScrollView as root view then:
LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
RelativeLayout root = (RelativeLayout) inflater.inflate(R.layout.activity_main, null); //RelativeLayout is root view of my UI(xml) file.
root.setDrawingCacheEnabled(true);
Bitmap screen= getBitmapFromView(this.getWindow().findViewById(R.id.relativelayout)); // here give id of our root layout (here its my RelativeLayout's id)
Here is the getBitmapFromView() method:
public static Bitmap getBitmapFromView(View view) {
//Define a bitmap with the same size as the view
Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
//Bind a canvas to it
Canvas canvas = new Canvas(returnedBitmap);
//Get the view's background
Drawable bgDrawable =view.getBackground();
if (bgDrawable!=null)
//has background drawable, then draw it on the canvas
bgDrawable.draw(canvas);
else
//does not have background drawable, then draw white background on the canvas
canvas.drawColor(Color.WHITE);
// draw the view on the canvas
view.draw(canvas);
//return the bitmap
return returnedBitmap;
}
It will display entire screen including content hidden in your ScrollView.
Now that we have our bitmap screen let's save it to pdf (you have to download itextpdf-5.3.2.jar file and attach in your project..)
private static String FILE = "mnt/sdcard/invoice.pdf"; // add permission in your manifest...
try
{
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(FILE));
document.open();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
screen.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
addImage(document,byteArray);
document.close();
}
catch (Exception e)
{
e.printStackTrace();
}
private static void addImage(Document document,byte[] byteArray)
{
try
{
image = Image.getInstance(byteArray);
}
catch (BadElementException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (MalformedURLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// image.scaleAbsolute(150f, 150f);
try
{
document.add(image);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Haven't tested anything. Here are all the sources I used: source1, source2, source3.
You can use a custom library such as https://github.com/HendrixString/Android-PdfMyXml. I prefer this library than any other method. Just Go through these.
but there is another way - How to convert Android View to PDF - that generate a pdf that contains bitmap of your layout
I made a library to achieve this objective (Getting PDF from Java View objects).
The main code snippet is -
PdfGenerator.getBuilder()
.setContext(context)
.fromViewSource()
.fromView(targetView) /* "targetView" is the view ,you want to convert PDF */
/* "fromLayoutXML()" takes array of layout resources.
* You can also invoke "fromLayoutXMLList()" method here which takes list of layout resources instead of array. */
.setDefaultPageSize(PdfGenerator.PageSize.A4)
/* It takes default page size like A4,A5. You can also set custom page size in pixel
* by calling ".setCustomPageSize(int widthInPX, int heightInPX)" here. */
.setFileName("Test-PDF")
/* It is file name */
.setFolderName("FolderA/FolderB/FolderC")
/* It is folder name. If you set the folder name like this pattern (FolderA/FolderB/FolderC), then
* FolderA creates first.Then FolderB inside FolderB and also FolderC inside the FolderB and finally
* the pdf file named "Test-PDF.pdf" will be store inside the FolderB. */
.openPDFafterGeneration(true)
/* It true then the generated pdf will be shown after generated. */
.build(new PdfGeneratorListener() {
#Override
public void onFailure(FailureResponse failureResponse) {
super.onFailure(failureResponse);
/* If pdf is not generated by an error then you will findout the reason behind it
* from this FailureResponse. */
}
#Override
public void showLog(String log) {
super.showLog(log);
/*It shows logs of events inside the pdf generation process*/
}
#Override
public void onSuccess(SuccessResponse response) {
super.onSuccess(response);
/* If PDF is generated successfully then you will find SuccessResponse
* which holds the PdfDocument,File and path (where generated pdf is stored)*/
}
});
Without using a third-party library you can use PdfDocument which was introduced in Android API 19. However, keep in mind that the dimension of the pdf file will be in the postscript point(1/72 inch). Therefore, you have to convert your view's dimension to match the requirement before drawing to the canvas.
Related
I have a problem with making a pdf file in my android app using ITextPdf. I'm trying to convert a MPAndroidChart linechart to a bitmap and save it to a pdf file.
Here's the code for saveToPdf() method:
private void saveToPdf() {
Bitmap bitmap = saveChartToBitmap();
Document doc = new Document();
File pdfCreated = new File(getBaseContext().getFilesDir() , "PDFCreated.pdf");
try {
PdfWriter.getInstance(doc, new FileOutputStream(pdfCreated));
doc.open();
Image image = Image.getInstance(bitmap);
doc.newPage();
doc.add(image);
Toast.makeText(getBaseContext(), "Pdf created", Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException | DocumentException e) {
e.printStackTrace();
} finally {
doc.close();
}
And my saveChartToBitmap() method:
private Bitmap saveChartToBitmap() {
if (lineChart.getMeasuredHeight() <= 0) {
lineChart.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
Bitmap b = Bitmap.createBitmap(lineChart.getMeasuredWidth(), lineChart.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
lineChart.layout(0, 0, lineChart.getMeasuredWidth(), lineChart.getMeasuredHeight());
lineChart.draw(c);
return b;
} else {
return null;
}
}
Don't know if something with my conversion to bitmat or creating an Image object is wrong but can't figure it out. I get: The document has no pages error. So I need advice.
Thanks in advance.
Immediately after opening document, always add an empty chunk to document so that you can avoid this exception.
doc.open;
doc.add(new Chunk(''));
Check if your saveChartToBitmap() method returns null. Also did you see the toast message.
I solved my problem. Instead of passing the bitmap to the Image.getInstance() method as an argument I converted it to a byte array and now It's working. Thanks
I'm trying to implement Bitmap memeCanvas = BitmapFactory.decodeResource(getResources(), xxx).copy(Bitmap.Config.ARGB_8888, true); to draw on the images that I upload from my phone to my ImageView. Because I don't have a specific drawable path like R.drawable.hypotheticalImage, I don't know how to pass the same information for a drawable path that is dynamic as the 2nd parameter of BitmapFactory.decodeResource().
I can supply code per request.
This is my solution. I had to call getContext() to be able to access getContentResolver() and wrap the entire thing in a try-catch block.
Bitmap memeCanvas = null;
try {
memeCanvas = BitmapFactory
.decodeStream(getContext()
.getContentResolver()
.openInputStream(imageUri))
.copy(Bitmap.Config.ARGB_8888, true);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
i have designed an app to show images in a view Pager, now the thing is that user saves this images to its internal memory and the app gives a random no. to the image as name, upon clicking "View Favorite " button the user gets to view all the images in a view pager one by one, can any1 help me as to how i go about it??
private void loadImageFromStorage(String path)
{
try {
File f=new File(path, "image.png");
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));
Context context =getApplicationContext();
final ImageView imageView = new ImageView(getApplicationContext());
int padding = context.getResources().getDimensionPixelSize(
R.dimen.padding_medium);
imageView.setPadding(padding, padding, padding, padding);
imageView.setImageBitmap(b);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
this is the code to load the image but the thing is i need to load all the images not just one image.png but everything.png ;) how do i do it??
and also i wanna load with Picasso but i cant load the bitmap it says something like load is not for bitmap etc. etc. please help
I don't know if you have already solved this issue. But if you don't give your files a random number when you save them, you could give them sequential numbers like 1.jpg, 2.jpg .... then you could do a while loop to load each image. For example:
int counter = 0;
boolean imageExists = true;
while(imageExists)
{
File imageFile = new File (filePath + counter + ".jpg");
if(imageFile.exists())
{
Picasso.with(getBaseContext()).load(imgFile).fit().centerInside().into(imageView);
}
else
{
imageExists = false;
}
}
Hopefully this helps.
I am using edmodo/cropper library to crop the image after the user has taken the image from camera.
Link: https://github.com/edmodo/cropper/wiki
I got this issue on device GT-N7000 and some other android phones.
java.lang.OutOfMemoryError
1 at android.graphics.Bitmap.nativeCreate(Native Method)
2 at android.graphics.Bitmap.createBitmap(Bitmap.java:669)
3 at android.graphics.Bitmap.createBitmap(Bitmap.java:604)
4 at android.graphics.Bitmap.createBitmap(Bitmap.java:530)
5 at com.edmodo.cropper.CropImageView.getCroppedImage(CropImageView.java:357)
Does anyone know how to solve this issue. Please help me ,the device keep getting crashes.
I solved it by subsampling the captured image before storing it in cropView using BitmapFactory.Options.
Here is the code:
// setting path to the clicked image and cropped image
path_click = "sdcard/Pictures/Candice/Clicked.jpg";
path_crop = "sdcard/Pictures/Candice/Cropped.jpg";
final BitmapFactory.Options options = new BitmapFactory.Options();
//If set to a value > 1,requests the decoder to subsample the
//original image, returning a smaller image to save memory.
options.inSampleSize = 2;
clickedImage = BitmapFactory.decodeFile(path_click, options);
cropImageView.setImageBitmap(clickedImage);
// Sets initial aspect ratio to 10/10, for demonstration purposes
cropImageView.setAspectRatio(DEFAULT_ASPECT_RATIO_VALUES,
DEFAULT_ASPECT_RATIO_VALUES);
cropButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Here we save the cropped image and then call the next
// activity
// To retrieve the image contained within the Cropper window,
// use the provided method, getCroppedImage() to retrieve a
// Bitmap of the cropped image.
croppedImageBitmap = cropImageView.getCroppedImage();
/** Save cropped image to SD card using output streams **/
// An output stream that writes bytes to a file.
// If it does not exist, a new file will be created.
FileOutputStream out = null;
try {
out = new FileOutputStream(path_crop);
// Writing a compressed version of bitmap to outputstream.
croppedImageBitmap.compress(Bitmap.CompressFormat.JPEG, 90,
out);
// Just after compression,add
croppedImageBitmap.recycle();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
startActivity(chooseIntent);
}
});
I don't know how big is your image, but have you tried adding android:largeHeap="true" to your <application> tag in the AndroidManifest.xml?
My application is some kind of "mini paint" and I would like to save my current view to the device memory... I would like to do the the opposite process too (Load an image from the device memory and set it as my currentview)
MiniPaint
yeah that's suppose to be a flamingo, I'm an artist!
Haven't tried it myself, but this answer shows taking a screenshot programmatically by getting the root view and saving off its drawing cache. That may be all you need to save your painting.
EDIT: Fixed link
First off I am assuming you are performing this drawing by overriding the onDraw() method on a View object, which passes in a Canvas object that you then perform some drawing operations on.
Here's a very basic way to approach this problem. There are probably lots of additional considerations to take into account, such as the file format(s) you read from and write to, and some extra error handling in the I/O code. But this should get you going.
To save what drawing you currently have, write out your View's drawingCache to a Picture object, then use the Picture's writeToStream method.
To load a pre-existing picture, you can use the Picture.readFromStream method, then in your onDraw call, draw the loaded picture to your Canvas.
To Wit:
/**
* Saves the current drawing cache of this View object to external storage.
* #param filename a file to be created in the device's Picture directory on the SD card
*/
public void SaveImage(String filename) {
// Grab a bitmap of what you've drawn to this View object so far
Bitmap b = this.getDrawingCache();
// It's easy to save a Picture object to disk, so we copy the contents
// of the Bitmap into a Picture
Picture pictureToSave = new Picture();
// To copy the Bitmap into the Picture, we have to use a Canvas
Canvas c = pictureToSave.beginRecording(b.getWidth(), b.getHeight());
c.drawBitmap(b, 0, 0, new Paint());
pictureToSave.endRecording();
// Create a File object where we are going to write the Picture to
File file = new File(this.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), filename);
try {
file.createNewFile();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
// Write the contents of the Picture object to disk
try {
OutputStream os = new FileOutputStream(file);
pictureToSave.writeToStream(os);
os.close();
}
catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
}
/**
* Returns a Picture object loaded from external storage
* #param filename the name of the file in the Pictures directory on the SD card
* #return null if the file is not found, or a Picture object.
*/
public Picture LoadImage(String filename) {
// Load a File object where we are going to read the Picture from
File file = new File(this.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), filename);
Picture pictureToLoad = null;
try {
InputStream is = new FileInputStream(file);
pictureToLoad = Picture.createFromStream(is);
is.close();
}
catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
// Return the picture we just loaded. Draw the picture to canvas using the
// Canvas.draw(Picture) method in your View.onDraw(Canvas) method
return pictureToLoad;
}
Useful links I read to figure this out:
Reference on generating file paths in the device's external storage
Picture
Canvas
Bitmap