diff --git a/plugin.xml b/plugin.xml
index 5146917..a267ea6 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -107,6 +107,9 @@
         
 
+        
+
         
 
diff --git a/src/android/PrintAdapter.java b/src/android/PrintAdapter.java
index 187f1ff..4c46df9 100644
--- a/src/android/PrintAdapter.java
+++ b/src/android/PrintAdapter.java
@@ -41,8 +41,8 @@ import static android.print.PrintDocumentInfo.CONTENT_TYPE_DOCUMENT;
 /**
  * Document adapter to render and print PDF files.
  */
-class PrintAdapter extends PrintDocumentAdapter {
-
+class PrintAdapter extends PrintDocumentAdapter
+{
     // The name of the print job
     private final @NonNull String jobName;
 
@@ -107,7 +107,7 @@ class PrintAdapter extends PrintDocumentAdapter {
         OutputStream output = new FileOutputStream(dest.getFileDescriptor());
 
         try {
-            PrintContent.copy(input, output);
+            PrintIO.copy(input, output);
         } catch (IOException e) {
             callback.onWriteFailed(e.getMessage());
             return;
@@ -124,7 +124,7 @@ class PrintAdapter extends PrintDocumentAdapter {
     {
         super.onFinish();
 
-        PrintContent.close(input);
+        PrintIO.close(input);
 
         callback.onFinish();
     }
diff --git a/src/android/PrintContent.java b/src/android/PrintContent.java
index 41ab594..ccf0ec4 100644
--- a/src/android/PrintContent.java
+++ b/src/android/PrintContent.java
@@ -22,30 +22,26 @@
 package de.appplant.cordova.plugin.printer;
 
 import android.content.Context;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.util.Base64;
 
-import java.io.ByteArrayInputStream;
-import java.io.Closeable;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.net.URLConnection;
 
-class PrintContent {
-
+/**
+ * Knows how to convert a resource URL into an io stream.
+ */
+class PrintContent
+{
     // List of supported content types
     enum ContentType { PLAIN, HTML, IMAGE, PDF, UNSUPPORTED }
 
-    // Application context
-    private final @NonNull Context context;
+    // Helper class to deal with io operations
+    private final @NonNull PrintIO io;
 
     /**
      * Initializes the asset utils.
@@ -53,7 +49,7 @@ class PrintContent {
      * @param ctx The application context.
      */
     private PrintContent (@NonNull Context ctx) {
-        context = ctx;
+        io = new PrintIO(ctx);
     }
 
     /**
@@ -90,13 +86,19 @@ class PrintContent {
         {
             String mime;
 
-            if (path.startsWith("base64:")) {
-                try {
-                    mime = URLConnection.guessContentTypeFromStream(openBase64(path));
-                } catch (IOException e) {
+            if (path.startsWith("base64:"))
+            {
+                try
+                {
+                    mime = URLConnection.guessContentTypeFromStream(io.openBase64(path));
+                }
+                catch (IOException e)
+                {
                     return ContentType.UNSUPPORTED;
                 }
-            } else {
+            }
+            else
+            {
                 mime = URLConnection.guessContentTypeFromName(path);
             }
 
@@ -131,7 +133,8 @@ class PrintContent {
      * @return An open IO stream or null if the file does not exist.
      */
     @Nullable
-    static InputStream open (@NonNull String path, @NonNull Context context)
+    static BufferedInputStream open (@NonNull String path,
+                                     @NonNull Context context)
     {
         return new PrintContent(context).open(path);
     }
@@ -144,28 +147,28 @@ class PrintContent {
      * @return An open IO stream or null if the file does not exist.
      */
     @Nullable
-    private InputStream open (@NonNull String path)
+    private BufferedInputStream open (@NonNull String path)
     {
         InputStream stream = null;
 
         if (path.startsWith("res:"))
         {
-            stream = openResource(path);
+            stream = io.openResource(path);
         }
         else if (path.startsWith("file:///"))
         {
-            stream = openFile(path);
+            stream = io.openFile(path);
         }
         else if (path.startsWith("file://"))
         {
-            stream = openAsset(path);
+            stream = io.openAsset(path);
         }
         else if (path.startsWith("base64:"))
         {
-            stream = openBase64(path);
+            stream = io.openBase64(path);
         }
 
-        return stream;
+        return stream != null ? new BufferedInputStream(stream) : null;
     }
 
     /**
@@ -196,19 +199,19 @@ class PrintContent {
 
         if (path.startsWith("res:"))
         {
-            bitmap = decodeResource(path);
+            bitmap = io.decodeResource(path);
         }
         else if (path.startsWith("file:///"))
         {
-            bitmap = decodeFile(path);
+            bitmap = io.decodeFile(path);
         }
         else if (path.startsWith("file://"))
         {
-            bitmap = decodeAsset(path);
+            bitmap = io.decodeAsset(path);
         }
         else if (path.startsWith("base64:"))
         {
-            bitmap = decodeBase64(path);
+            bitmap = io.decodeBase64(path);
         }
         else {
             bitmap = BitmapFactory.decodeFile(path);
@@ -216,226 +219,4 @@ class PrintContent {
 
         return bitmap;
     }
-
-    /**
-     * Copies content of input stream to output stream.
-     *
-     * @param input  The readable input stream.
-     * @param output The writable output stream.
-     *
-     * @throws IOException If the input stream is not readable,
-     *                     or the output stream is not writable.
-     */
-    static void copy (@NonNull InputStream input,
-                      @NonNull OutputStream output) throws IOException
-    {
-        byte[] buf = new byte[1024];
-        int bytesRead;
-
-        while ((bytesRead = input.read(buf)) > 0) {
-            output.write(buf, 0, bytesRead);
-        }
-
-        input.reset();
-        close(output);
-    }
-
-    /**
-     * Closes the stream.
-     *
-     * @param stream The stream to close.
-     */
-    static void close (@NonNull Closeable stream)
-    {
-        try {
-            stream.close();
-        } catch (IOException e) {
-            // ignore
-        }
-    }
-
-    /**
-     * Opens an file given as a file:/// path.
-     *
-     * @param path The path to the file.
-     *
-     * @return An open IO stream or null if the file does not exist.
-     */
-    @Nullable
-    private InputStream openFile (@NonNull String path)
-    {
-        String absPath = path.substring(7);
-
-        try {
-            return new FileInputStream(absPath);
-        } catch (FileNotFoundException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Decodes an file given as a file:/// path to a bitmap.
-     *
-     * @param path The path to the file.
-     *
-     * @return A bitmap or null if the path is not valid
-     */
-    @Nullable
-    private Bitmap decodeFile (@NonNull String path)
-    {
-        String absPath = path.substring(7);
-
-        return BitmapFactory.decodeFile(absPath);
-    }
-
-    /**
-     * Opens an asset file given as a file:// path.
-     *
-     * @param path The path to the asset.
-     *
-     * @return An open IO stream or null if the file does not exist.
-     */
-    @Nullable
-    private InputStream openAsset (@NonNull String path)
-    {
-        String resPath = path.replaceFirst("file:/", "www");
-
-        try {
-            return getAssets().open(resPath);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    /**
-     * Decodes an asset file given as a file:// path to a bitmap.
-     *
-     * @param path The path to the asset.
-     *
-     * @return A bitmap or null if the path is not valid
-     */
-    @Nullable
-    private Bitmap decodeAsset (@NonNull String path)
-    {
-        InputStream stream  = openAsset(path);
-        Bitmap bitmap;
-
-        if (stream == null)
-            return null;
-
-        bitmap = BitmapFactory.decodeStream(stream);
-
-        close(stream);
-
-        return bitmap;
-    }
-
-    /**
-     * Opens a resource file given as a res:// path.
-     *
-     * @param path The path to the resource.
-     *
-     * @return An open IO stream or null if the file does not exist.
-     */
-    @NonNull
-    private InputStream openResource (@NonNull String path)
-    {
-        String resPath = path.substring(6);
-        int resId      = getResId(resPath);
-
-        return getResources().openRawResource(resId);
-    }
-
-    /**
-     * Decodes a resource given as a res:// path to a bitmap.
-     *
-     * @param path The path to the resource.
-     *
-     * @return A bitmap or null if the path is not valid
-     */
-    @Nullable
-    private Bitmap decodeResource (@NonNull String path)
-    {
-        String data  = path.substring(9);
-        byte[] bytes = Base64.decode(data, 0);
-
-        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
-    }
-
-    /**
-     * Opens a resource file given as a res:// path.
-     *
-     * @param path The path to the resource.
-     *
-     * @return An open IO stream or null if the file does not exist.
-     */
-    @NonNull
-    private InputStream openBase64 (@NonNull String path)
-    {
-        String data  = path.substring(9);
-        byte[] bytes = Base64.decode(data, 0);
-
-        return new ByteArrayInputStream(bytes);
-    }
-
-    /**
-     * Decodes a resource given as a base64:// string to a bitmap.
-     *
-     * @param path The given relative path.
-     *
-     * @return A bitmap or null if the path is not valid
-     */
-    @Nullable
-    private Bitmap decodeBase64 (@NonNull String path)
-    {
-        String data  = path.substring(9);
-        byte[] bytes = Base64.decode(data, 0);
-
-        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
-    }
-
-    /**
-     * Returns the resource ID for the given resource path.
-     *
-     * @return The resource ID for the given resource.
-     */
-    private int getResId (@NonNull String resPath)
-    {
-        Resources res   = getResources();
-        String pkgName  = context.getPackageName();
-        String dirName  = "drawable";
-        String fileName = resPath;
-
-        if (resPath.contains("/")) {
-            dirName  = resPath.substring(0, resPath.lastIndexOf('/'));
-            fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
-        }
-
-        String resName = fileName.substring(0, fileName.lastIndexOf('.'));
-        int resId      = res.getIdentifier(resName, dirName, pkgName);
-
-        if (resId == 0) {
-            resId = res.getIdentifier(resName, "mipmap", pkgName);
-        }
-
-        if (resId == 0) {
-            resId = res.getIdentifier(resName, "drawable", pkgName);
-        }
-
-        return resId;
-    }
-
-    /**
-     * Returns the asset manager for the app.
-     */
-    private AssetManager getAssets() {
-        return context.getAssets();
-    }
-
-    /**
-     * Returns the resource bundle for the app.
-     */
-    private Resources getResources() {
-        return context.getResources();
-    }
 }
diff --git a/src/android/PrintIO.java b/src/android/PrintIO.java
new file mode 100644
index 0000000..7c2fa25
--- /dev/null
+++ b/src/android/PrintIO.java
@@ -0,0 +1,283 @@
+/*
+ Copyright 2013 Sebastián Katzer
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+package de.appplant.cordova.plugin.printer;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Base64;
+
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Provides IO utility functions to deal with the resources.
+ */
+class PrintIO
+{
+    // Application context
+    private final @NonNull Context context;
+
+    /**
+     * Initializes the asset utils.
+     *
+     * @param ctx The application context.
+     */
+    PrintIO (@NonNull Context ctx)
+    {
+        context = ctx;
+    }
+
+    /**
+     * Copies content of input stream to output stream.
+     *
+     * @param input  The readable input stream.
+     * @param output The writable output stream.
+     *
+     * @throws IOException If the input stream is not readable,
+     *                     or the output stream is not writable.
+     */
+    static void copy (@NonNull InputStream input,
+                      @NonNull OutputStream output) throws IOException
+    {
+        byte[] buf = new byte[input.available()];
+        int bytesRead;
+
+        input.mark(Integer.MAX_VALUE);
+
+        while ((bytesRead = input.read(buf)) > 0)
+        {
+            output.write(buf, 0, bytesRead);
+        }
+
+        input.reset();
+        close(output);
+    }
+
+    /**
+     * Closes the stream.
+     *
+     * @param stream The stream to close.
+     */
+    static void close (@NonNull Closeable stream)
+    {
+        try {
+            stream.close();
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * Opens an file given as a file:/// path.
+     *
+     * @param path The path to the file.
+     *
+     * @return An open IO stream or null if the file does not exist.
+     */
+    @Nullable
+    InputStream openFile (@NonNull String path)
+    {
+        String absPath = path.substring(7);
+
+        try {
+            return new FileInputStream(absPath);
+        } catch (FileNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Decodes an file given as a file:/// path to a bitmap.
+     *
+     * @param path The path to the file.
+     *
+     * @return A bitmap or null if the path is not valid
+     */
+    @Nullable
+    Bitmap decodeFile (@NonNull String path)
+    {
+        String absPath = path.substring(7);
+
+        return BitmapFactory.decodeFile(absPath);
+    }
+
+    /**
+     * Opens an asset file given as a file:// path.
+     *
+     * @param path The path to the asset.
+     *
+     * @return An open IO stream or null if the file does not exist.
+     */
+    @Nullable
+    InputStream openAsset (@NonNull String path)
+    {
+        String resPath = path.replaceFirst("file:/", "www");
+
+        try {
+            return getAssets().open(resPath);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * Decodes an asset file given as a file:// path to a bitmap.
+     *
+     * @param path The path to the asset.
+     *
+     * @return A bitmap or null if the path is not valid
+     */
+    @Nullable
+    Bitmap decodeAsset (@NonNull String path)
+    {
+        InputStream stream  = openAsset(path);
+        Bitmap bitmap;
+
+        if (stream == null)
+            return null;
+
+        bitmap = BitmapFactory.decodeStream(stream);
+
+        close(stream);
+
+        return bitmap;
+    }
+
+    /**
+     * Opens a resource file given as a res:// path.
+     *
+     * @param path The path to the resource.
+     *
+     * @return An open IO stream or null if the file does not exist.
+     */
+    @NonNull
+    InputStream openResource (@NonNull String path)
+    {
+        String resPath = path.substring(6);
+        int resId      = getResId(resPath);
+
+        return getResources().openRawResource(resId);
+    }
+
+    /**
+     * Decodes a resource given as a res:// path to a bitmap.
+     *
+     * @param path The path to the resource.
+     *
+     * @return A bitmap or null if the path is not valid
+     */
+    @Nullable
+    Bitmap decodeResource (@NonNull String path)
+    {
+        String data  = path.substring(9);
+        byte[] bytes = Base64.decode(data, 0);
+
+        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+    }
+
+    /**
+     * Opens a resource file given as a res:// path.
+     *
+     * @param path The path to the resource.
+     *
+     * @return An open IO stream or null if the file does not exist.
+     */
+    @NonNull
+    InputStream openBase64 (@NonNull String path)
+    {
+        String data  = path.substring(9);
+        byte[] bytes = Base64.decode(data, 0);
+
+        return new ByteArrayInputStream(bytes);
+    }
+
+    /**
+     * Decodes a resource given as a base64:// string to a bitmap.
+     *
+     * @param path The given relative path.
+     *
+     * @return A bitmap or null if the path is not valid
+     */
+    @Nullable
+    Bitmap decodeBase64 (@NonNull String path)
+    {
+        String data  = path.substring(9);
+        byte[] bytes = Base64.decode(data, 0);
+
+        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+    }
+
+    /**
+     * Returns the resource ID for the given resource path.
+     *
+     * @return The resource ID for the given resource.
+     */
+    private int getResId (@NonNull String resPath)
+    {
+        Resources res   = getResources();
+        String pkgName  = context.getPackageName();
+        String dirName  = "drawable";
+        String fileName = resPath;
+
+        if (resPath.contains("/")) {
+            dirName  = resPath.substring(0, resPath.lastIndexOf('/'));
+            fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
+        }
+
+        String resName = fileName.substring(0, fileName.lastIndexOf('.'));
+        int resId      = res.getIdentifier(resName, dirName, pkgName);
+
+        if (resId == 0) {
+            resId = res.getIdentifier(resName, "mipmap", pkgName);
+        }
+
+        if (resId == 0) {
+            resId = res.getIdentifier(resName, "drawable", pkgName);
+        }
+
+        return resId;
+    }
+
+    /**
+     * Returns the asset manager for the app.
+     */
+    private AssetManager getAssets() {
+        return context.getAssets();
+    }
+
+    /**
+     * Returns the resource bundle for the app.
+     */
+    private Resources getResources() {
+        return context.getResources();
+    }
+}
diff --git a/src/android/PrintManager.java b/src/android/PrintManager.java
index 4e090a2..e0f7838 100644
--- a/src/android/PrintManager.java
+++ b/src/android/PrintManager.java
@@ -45,12 +45,11 @@ import static android.os.Build.VERSION.SDK_INT;
 import static android.print.PrintJobInfo.STATE_COMPLETED;
 import static de.appplant.cordova.plugin.printer.PrintContent.ContentType.UNSUPPORTED;
 
-class PrintManager {
-
-    interface OnPrintFinishCallback {
-        void onFinish (boolean completed);
-    }
-
+/**
+ * Provides high level methods for printing.
+ */
+class PrintManager
+{
     // The application context
     private final @NonNull Context context;
 
@@ -363,4 +362,9 @@ class PrintManager {
     {
         return (android.print.PrintManager) context.getSystemService(PRINT_SERVICE);
     }
+
+    interface OnPrintFinishCallback
+    {
+        void onFinish (boolean completed);
+    }
 }
diff --git a/src/android/PrintOptions.java b/src/android/PrintOptions.java
index e752e41..e5e19db 100644
--- a/src/android/PrintOptions.java
+++ b/src/android/PrintOptions.java
@@ -43,8 +43,8 @@ import static android.support.v4.print.PrintHelper.SCALE_MODE_FIT;
 /**
  * Wrapper for the print job settings.
  */
-class PrintOptions {
-
+class PrintOptions
+{
     // The print job settings
     private final @NonNull JSONObject spec;
 
diff --git a/src/android/PrintProxy.java b/src/android/PrintProxy.java
index b217622..f258c51 100644
--- a/src/android/PrintProxy.java
+++ b/src/android/PrintProxy.java
@@ -33,8 +33,8 @@ import android.support.v4.print.PrintHelper;
 /**
  * Simple delegate class to have access to the onFinish method.
  */
-class PrintProxy extends PrintDocumentAdapter {
-
+class PrintProxy extends PrintDocumentAdapter
+{
     // Holds the delegate object
     private final @NonNull PrintDocumentAdapter delegate;
 
diff --git a/src/android/Printer.java b/src/android/Printer.java
index afdf666..afb49bb 100644
--- a/src/android/Printer.java
+++ b/src/android/Printer.java
@@ -37,8 +37,8 @@ import org.json.JSONObject;
  * that loads the markup data. Once the page has been fully rendered it takes
  * the print adapter of that web view and initializes a print job.
  */
-public class Printer extends CordovaPlugin {
-
+public final class Printer extends CordovaPlugin
+{
     /**
      * Executes the request.
      *