Create PDF and save to the sdcard in Android

Create PDF file and save it to sdcard in Android
Create PDF file and save it to sdcard in Android

In this article, we will create PDF file of the current view and then save it to the sdcard(internal storage) as pdf file. Here we use PdfDocument (https://developer.android.com/reference/android/graphics/pdf/PdfDocument) which added from API 19(Android KitKat). So you can use it above KitKat version of Android.

First of all we will create Bitmap of the view which we want to save on the PDF file. So for that we need to get the heigh and width of the view, so here we do all the operation in the ‘onWindowFocusChanged’ method instead of ‘onCreate’ method. So the basic structure of the file will look like this.

private RelativeLayout rlContainer;

private int width, height;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_pdf);

  rlContainer = findViewById(R.id.rl_container);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);

  width = rlContainer.getWidth();
  height = rlContainer.getHeight();
}

Here we have on RelativeLayout which contains all the elements we need to print on the PDF file. So we will initialise it in onCreate and then get its width and height in onWindowFocusChanged. If you try to get the width and height in onCreate, it will be 0 because at that time the view did not rendered on the screen. So we will use the above method for that.

Now we will create a Bitmap of that relative layout using following code. Later we will draw this Bitmap on PDF file using Canvas of the PDF file.

Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c1 = new Canvas(b);
rlContainer.draw(c1);

Now we initialise PdfDocument and create pages in that PDF file. For that we use PdfDocument.Page class. If we have 5 pages in our PDF file then we have to create 5 object of PdfDocument.Page and add them to PdfDocument. Then we fetch the canvas of the PDF page and draw our Bitmap on it. Please check the code below.

PdfDocument pd = new PdfDocument();

PdfDocument.PageInfo pi = new PdfDocument.PageInfo.Builder(width, height, 1).create();
PdfDocument.Page p = pd.startPage(pi);
Canvas c = p.getCanvas();
c.drawBitmap(b, 0, 0, new Paint());
pd.finishPage(p);

pd.close();

Here we can iterate the code from line 3 to line 7 to add more pages to the PDF files. After that we need to close the PdfDocument. This will draw our bitmap to the PDF file. Now to write this PdfDocument to the file on sdcard(internal storage), we need to create a File and write PdfDocument to its OutputStream. But make sure that you write this code before closing the PdfDocument pd.close(). Here is the code for that.

try {
  //make sure you have asked for storage permission before this
  File f = new File(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer-pdf-test.pdf");
  pd.writeTo(new FileOutputStream(f));
} catch (FileNotFoundException fnfe) {
  fnfe.printStackTrace();
} catch (IOException ioe) {
  ioe.printStackTrace();
}

pd.close();

Please make sure that before running above code, you have the storage permission granted by user otherwise you will get error. Please check for the runtime Android permission for that. Now your PDF file is saved at your provided location on your sdcard(internal storage).

Here is the full code.

private RelativeLayout rlContainer;

private int width, height;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_pdf);

  rlContainer = findViewById(R.id.rl_container);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);

  width = rlContainer.getWidth();
  height = rlContainer.getHeight();

  Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  Canvas c1 = new Canvas(b);
  rlContainer.draw(c1);

  PdfDocument pd = new PdfDocument();

  PdfDocument.PageInfo pi = new PdfDocument.PageInfo.Builder(width, height, 1).create();
  PdfDocument.Page p = pd.startPage(pi);
  Canvas c = p.getCanvas();
  c.drawBitmap(b, 0, 0, new Paint());
  pd.finishPage(p);

  try {
    //make sure you have asked for storage permission before this
    File f = new File(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer-pdf-test.pdf");
    pd.writeTo(new FileOutputStream(f));
  } catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
  } catch (IOException ioe) {
    ioe.printStackTrace();
  }

  pd.close();
}

If you find any problem or doubt, please mention in comments. Do not forget to share!

Download Source Code: https://github.com/dakshbhatt21/a-computer-engineer

Draw line using finger on Canvas in Android

Draw line on canvas using finger
Draw line on canvas using finger

Draw line on canvas using your finger is very much important task if you are going to develop any kind of drawing app or image editing app. So in this tutorial we are going to see how one can draw on canvas using touch methods.

(Those who want to just copy paste the whole class can scroll at the end!)

First of all we create a custom view which contains the canvas and all its touch methods which performs the draw activity.

This is the class which we are going to use in XML layout. You can use it in XML like this . Here we are going to add all the initialization in the second constructor as we are going to use it in the XML file. But it is advisable if you use a common method and add it to every constructor.

public class DrawLineCanvas extends View {

  public DrawLineCanvas(Context context) {
    super(context);
  }

  public DrawLineCanvas(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public DrawLineCanvas(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

}

Now we add few methods in the above class, each method has different purpose of its own.

This method will give us the width and height of the view that is inflated. So if we give “match_parent” in the XML file then we can get the full width and height of the view and create bitmap and canvas of that size so that we can draw on whole visible area of the screen except toolbar and status bar.

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
}

Now this method will identify the touch event and draw on the bitmap accordingly. We consider three different events here. One is when user touch the screen, second when the user move his/her finger on the screen and the last one when the user lift the finger from the screen.

@Override
public boolean onTouchEvent(MotionEvent event) {

  switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
      break;
    case MotionEvent.ACTION_MOVE:
      break;
    case MotionEvent.ACTION_UP:
      break;
    default:
      return false;
  }

  invalidate();
  return true;
}

Here “invalidate()” calls the below method so it will draw on the canvas.

The below method will draw the path(which we drew using finger) on the canvas. We will call this method every time we touch or drag finger on the screen.

@Override
protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
}

Now we initialise all the needed things to get started.

private Canvas c;

private Paint pLine, pBg;
private Path touchPath;

private Bitmap b;

public DrawLineCanvas(Context context, AttributeSet attrs) {
  super(context, attrs);

  pBg = new Paint();
  pBg.setColor(Color.WHITE);

  pLine = new Paint();
  pLine.setColor(Color.GREEN);
  pLine.setAntiAlias(true);
  pLine.setStyle(Paint.Style.STROKE);
  pLine.setStrokeWidth(12);

  touchPath = new Path();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  c = new Canvas(b);
}

Here Canvas is used to save the drawing when we lift the finger and started drawing again. Paint is for line that is being drawn and the background on which the line is being drawn. It includes the line size, color, stroke, etc. Path is for saving the path that we create by moving our finger on the screen. So when we lift the finger we will draw this path to our canvas and it will be drawn on the screen. And lastly the Bitmap is used to initialise our canvas with the size we are getting from the view inflated in the XML using method onSizeChanged().

Now comes the drawing part, now we will identify the touch event and save it to the “touchPath” we initialised so that we can draw it to the canvas once we lift the finger. On every action event, we also calls the “invalidate()” method so that your current moving path will be drawn on the canvas.

@Override
public boolean onTouchEvent(MotionEvent event) {

  float touchX = event.getX();
  float touchY = event.getY();
  switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
      touchPath.moveTo(touchX, touchY);
      break;
    case MotionEvent.ACTION_MOVE:
      touchPath.lineTo(touchX, touchY);
      break;
    case MotionEvent.ACTION_UP:
      touchPath.lineTo(touchX, touchY);
      c.drawPath(touchPath, pLine);
      touchPath = new Path();
      break;
    default:
      return false;
  }

  invalidate();
  return true;
}

Here in “ACTION_UP” event we draw the path “touchPath” to the Canvas c and reset the path so you can start new line when you again start drawing something.

Now finally the “onDraw()” method which will draw everything on canvas and you can see your drawing on screen!

@Override
protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);

  canvas.drawBitmap(b, 0, 0, pBg);
  canvas.drawPath(touchPath, pLine);
}

The complete class looks like this.

public class DrawLineCanvas extends View {

  private Canvas c;

  private Paint pLine, pBg;
  private Path touchPath;

  private Bitmap b;

  public DrawLineCanvas(Context context) {
    super(context);
  }

  public DrawLineCanvas(Context context, AttributeSet attrs) {
    super(context, attrs);

    pBg = new Paint();
    pBg.setColor(Color.WHITE);

    pLine = new Paint();
    pLine.setColor(Color.GREEN);
    pLine.setAntiAlias(true);
    pLine.setStyle(Paint.Style.STROKE);
    pLine.setStrokeWidth(12);

    touchPath = new Path();
  }

  public DrawLineCanvas(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    c = new Canvas(b);
  }
  
  @Override
  public boolean onTouchEvent(MotionEvent event) {

    float touchX = event.getX();
    float touchY = event.getY();
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        touchPath.moveTo(touchX, touchY);
        break;
      case MotionEvent.ACTION_MOVE:
        touchPath.lineTo(touchX, touchY);
        break;
      case MotionEvent.ACTION_UP:
        touchPath.lineTo(touchX, touchY);
        c.drawPath(touchPath, pLine);
        touchPath = new Path();
        break;
      default:
        return false;
    }

    invalidate();
    return true;
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.drawBitmap(b, 0, 0, pBg);
    canvas.drawPath(touchPath, pLine);
  }
}

To save this canvas as image on your sdcard, please check my other post here: https://acomputerengineer.wordpress.com/2015/01/13/how-to-draw-canvas-on-imageview-and-save-canvas-as-bitmap-and-store-in-sdcard-in-android/

For source code, please checkout my Github repo here: https://github.com/dakshbhatt21/a-computer-engineer

Draw circle shape in Canvas in Android

Draw circle shape in canvas is very easy thing to do if you are familiar with Canvas in Android. Please checkout this tutorial if you don’t know about how to create basic Canvas, display it to ImageView and save it as Bitmap.

Here we draw one canvas of 500×500 resolution and draw circle shape in the middle of the canvas.

The below line will draw circle shape in canvas.

canvas.drawCircle(x, y, radius, paint);

Here we pass x and y as the center coordinats of the circle. radius represents the radius of the circle being drawn. And paint is the Paint for drawing circle.

Now we will write the full code to draw circle in canvas.

Paint pBackground = new Paint();
pBackground.setColor(Color.WHITE);
canvas.drawRect(0, 0, 512, 512, pBackground);
Paint pCircle = new Paint();
pCircle.setColor(Color.BLACK);
canvas.drawCircle(256, 256, 256, pCircle);

Draw line on Canvas in Android

You can draw different type of line on Canvas in Android. You can change it’s color, stroke, effect, etc. Here we will see the basics of drawing line on Canvas.

Checkout the basics of Canvas if you are new to Canvas: Draw Canvas on ImageView

Here is the example of different parameters you can apply to draw a line on Canvas in Android.

device-2016-08-23-003351
Draw line on Canvas
//simple line
Paint p1 = new Paint();
p1.setAntiAlias(true);
p1.setColor(Color.BLACK);
p1.setStrokeWidth(10);
canvas.drawLine(10, 40, 400, 40, p1);

//line with color
Paint p2 = new Paint();
p2.setAntiAlias(true);
p2.setColor(Color.RED);
p2.setStrokeWidth(10);
canvas.drawLine(10, 80, 400, 80, p2);

//line with round ends
Paint p3 = new Paint();
p3.setAntiAlias(true);
p3.setColor(Color.BLUE);
p3.setStrokeWidth(10);
p3.setStrokeCap(Paint.Cap.ROUND);
canvas.drawLine(10, 120, 400, 120, p3);

//slanted line
Paint p4 = new Paint();
p4.setAntiAlias(true);
p4.setColor(Color.GREEN);
p4.setStrokeWidth(10);
canvas.drawLine(10, 160, 400, 200, p4);

Here we use AntiAlias flag to true to remove distortion in the lines.

How to draw Canvas on ImageView and save Canvas as Bitmap and store in sdcard in Android

In some of the application we draw image, text and line on Canvas object in Android. Canvas is the basic component in android for drawing objects on it.

#1 Create Canvas and display it in ImageView.
Now we create a Canvas and display its content in ImageView.

  public class MyCanvas extends View {
    public MyCanvas(Context context) {
      super(context);
      // TODO Auto-generated constructor stub
    }

    @Override
    protected void onDraw(Canvas canvas) {
      // TODO Auto-generated method stub
      super.onDraw(canvas);
      Paint pBackground = new Paint();
      pBackground.setColor(Color.WHITE);
      canvas.drawRect(0, 0, 512, 512, pBackground);
      Paint pText = new Paint();
      pText.setColor(Color.BLACK);
      pText.setTextSize(20);
      canvas.drawText("Sample Text", 100, 100, pText);
    }
  }

Now set this canvas to your ImageView using following code.

  View v = new MyCanvas(getApplicationContext());
  Bitmap bitmap = Bitmap.createBitmap(500/*width*/, 500/*height*/, Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(bitmap);
  v.draw(canvas);
  ImageView iv = (ImageView) findViewById(R.id.iv);
  iv.setImageBitmap(bitmap);

You can see “Sample Text” in ImageView.

#2 Save canvas bitmap to sdcard.
We will save this bitmap to sdcard so all your drawing on canvas will be stored as image.

Bitmap b = null;

//create directory if not exist
File dir = new File("/sdcard/tempfolder/");
if (!dir.exists()) {
  dir.mkdirs();
}

File output = new File(dir, "tempfile.jpg");
OutputStream os = null;

try {
  os = new FileOutputStream(output);
  b.compress(Bitmap.CompressFormat.JPEG, 100, os);
  os.flush();
  os.close();

  //this code will scan the image so that it will appear in your gallery when you open next time
  MediaScannerConnection.scanFile(this,	new String[] { output.toString() }, null,
    new MediaScannerConnection.OnScanCompletedListener() {
      public void onScanCompleted(String path, Uri uri) {
        Log.d("appname", "image is saved in gallery and gallery is refreshed.");
      }
    }
  );
} catch (Exception e) {
}

If you find any problem or doubt, please mention in comments. Do not forget to share!

Download Source Code: https://github.com/dakshbhatt21/a-computer-engineer