How to convert Image to PDF File Using Apache PDFBox in Android

If you have been thinking about how to convert an image file to a PDF file, you are in the right place.

At the end of this tutorial you will learn how it can be done in android.

In our previous PDF tutorials, we have covered how to create new PDF file in android and also how to load or write a PDF file in android so we will focus on how to convert image to PDF file.

Below is the screenshot of the app we will create

1. CREATE A NEW ANDROID PROJECT

  • Open Android Studio
  • Go to file menu
  • Select new option
  • Enter project name
  • Enter activity name
  • Keep other default settings
  • Click on finish button to create a new android project

2. ADD PERMISSION

Since accessing android storage API needs permission, we are going to add this code in our project manifest file.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

3. ADD ANDROID THIRD-PARTY LIBRARIES

We are going to add few android libraries that will help us solve some problems rather than reinventing the wheels.

Open your project build.gradle and add the libraries below.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.0"

    defaultConfig {
        applicationId "com.inducesmile.apachepdfbox"
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.material:material:1.3.0-alpha02'

    //Adapter
    implementation 'io.github.manneohlund:smart-recycler-adapter:3.0.0'

    //Dialog
    implementation "com.yarolegovich:lovely-dialog:1.1.0"

    //Gson
    implementation 'com.google.code.gson:gson:2.8.6'

    //loading
    implementation 'com.github.d-max:spots-dialog:1.1@aar'

    //RxAndroid
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
    implementation "io.reactivex.rxjava2:rxjava:2.2.10"

    //Pdf viewer
    implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'

    //PDFBox android
    implementation 'com.tom_roush:pdfbox-android:1.8.10.1'
    //File picker
    implementation 'com.github.jaiselrahman:FilePicker:1.3.2'

    implementation 'com.jakewharton:butterknife:10.2.1'
    annotationProcessor "com.jakewharton:butterknife-compiler:10.2.1"

    implementation 'com.karumi:dexter:5.0.0'

    //Lombok
    compileOnly 'org.projectlombok:lombok:1.18.8'
    annotationProcessor 'org.projectlombok:lombok:1.18.8'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

4. CREATE A NEW ACTIVITY CLASS

Create a new activity and name it AddImageToPdfActivity or any name of your choice.

Open the xml layout file of the activity class and paste the code below.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="12dp"
    tools:context=".AddImageToPdfActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/selected_images"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"/>

 <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:backgroundTint="@color/colorAccent"
        android:src="@drawable/ic_baseline_add_24" />

</FrameLayout>

We have added a FloatingActionButton view which will be used to open FilePickerActivity.

The RecyclerView will hold the details of images selected from the File Picker.

5. CREATE HELPER CLASSES

Right click on your project package folder, choose new and select package option. Add the name of the folder as utils.

Follow the same steps and create two more folders. Name them model and adapter.

i). Let create a new adapter class for the RecyclerView. We have add this library SmartRecyclerAdapter to make our work easier.

Create a new Java class inside the adapter folder and name it ImageViewHolder.

Open the Java class and paste the code below.

public class ImageViewHolder extends SmartViewHolder<Document> {

    private static final String TAG = ImageViewHolder.class.getSimpleName();

    @BindView(R.id.file_icon)
    AppCompatImageView fileIcon;

    @BindView(R.id.filename)
    AppCompatTextView filename;

    @BindView(R.id.file_size)
    AppCompatTextView fileSize;

    public ImageViewHolder(ViewGroup parentView) {
        super(LayoutInflater.from(parentView.getContext()).inflate(R.layout.image_item_layout, parentView, false));
        ButterKnife.bind(this, itemView);
    }

    @Override
    public void bind(Document item) {
        if (item != null){
            loadImage(item.getDocumentPath(), fileIcon);
            filename.setText(item.getDocumentName());
            fileSize.setText(item.getDocumentSize());
        }
    }

    private void loadImage(String filePath, AppCompatImageView imageView){
        File imgFile = new  File(filePath);
        if(imgFile.exists()){

            Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
            imageView.setImageBitmap(bitmap);
        }
    }
}

ii). Create a new Java class inside the model folder and name it Document.

Open the class and add the code below.

@Setter
@Getter
public class Document {

    private String documentName;
    private String documentSize;
    private String createdAt;
    private String documentPath;
    private int icon;

    public Document(String documentName, String documentSize, String createdAt, String documentPath) {
        this.documentName = documentName;
        this.documentSize = documentSize;
        this.createdAt = createdAt;
        this.documentPath = documentPath;
    }
}

iii). Inside the Utils folder, we are going to create some helper classes.

Create three Java classes and name them as follows: AddImageToPdf.java, DimensionUtils.java and CommonUtils.java.

Open AddImageToPdf.java and paste the code below.

This class contains a method that converts image(s) to Pdf file.

public class ImageToPdfHelper {
    private Context context;
    private List<String> inputImage;
    private File outputFile;

    public ImageToPdfHelper(Context context, List<String> inputImage, File outputFile) {
        this.context = context;
        this.inputImage = inputImage;
        this.outputFile = outputFile;
        PDFBoxResourceLoader.init(context);
    }

    public void convertImageToPdf() throws IOException {
        PDDocument document = new PDDocument();

        for (String imageUri : inputImage) {
            PDPage pdPage = new PDPage(PDRectangle.A5);
            document.addPage(pdPage);
            PDPageContentStream pdPageContentStream = new PDPageContentStream(document, pdPage);

            PDImageXObject pdImageXObject = PDImageXObject.createFromFile(imageUri, document);

            DimensionUtil pdfPageDim = new DimensionUtil((int) PDRectangle.A5.getWidth(), (int) PDRectangle.A5.getHeight());
            DimensionUtil imagePageDim = new DimensionUtil(pdImageXObject.getWidth(), pdImageXObject.getHeight());

            DimensionUtil newDim = CommonUtils.getScaledDimension(imagePageDim, pdfPageDim);

            int imagewidth = newDim.getWidth();
            int imageheight = newDim.getHeight();

             float x = (pdPage.getMediaBox().getWidth() - imagewidth) / 2;
             float y = (pdPage.getMediaBox().getHeight() - imageheight) / 2;

            pdPageContentStream.drawImage(pdImageXObject, x, y, imagewidth, imageheight);

            pdPageContentStream.close();
        }
        document.save(outputFile);
        document.close();
    }
}

Open DimensionUtil.java and paste the code below.

@Setter
@Getter
public class DimensionUtil {
    private int width;
    private int height;

    public DimensionUtil(int width, int height){
        this.width = width;
        this.height = height;
    }
}

Open CommonUtils.java and paste the code below.

public class CommonUtils {

    public static DimensionUtil getScaledDimension(DimensionUtil imgSize, DimensionUtil boundary){
        int original_width = imgSize.getWidth();
        int original_height = imgSize.getHeight();
        int bound_width = boundary.getWidth();
        int bound_height = boundary.getHeight();
        int new_width = original_width;
        int new_height = original_height;

        if(original_width > bound_width){
            new_width = bound_width;
            new_height = (new_width * original_height) / original_width;
        }

        if(new_height > bound_height){
            new_height = bound_height;
            new_width = (new_height * original_width) / original_height;
        }

        return new DimensionUtil(new_width, new_height);
    }
}

6. Open AddImageToPdfActivity class

Finally, we are going to connect all the code, classes and methods together.

We obtained the View references and attached an event listener to Fab button.

When the Fab button is clicked, it opens File Picker for the user to select image files.

Thereafter, the image details will be store and used to create a new PDF file.

Open the AddImageToPdfActivity class and paste the code below.

public class AddImageToPdfActivity extends AppCompatActivity {

    private static final String TAG = AddImageToPdfActivity.class.getSimpleName();

    private static final int PDF_REQUEST_CODE = 2121;

    private Document document;

    @BindView(R.id.selected_images)
    RecyclerView recyclerView;

    private List<Document> documents;

    private AddImageToPDF addImageToPDF;

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

        ButterKnife.bind(this);

        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null){
            actionBar.setTitle("Add Image to PDF");
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.create_pdf_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        int menuId = item.getItemId();
        if (menuId == android.R.id.home){
            onBackPressed();
        }
        if (menuId == R.id.action_create_pdf){
            //Add image(s) to pdf
            addImageToPDF.requestFilename();
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }

    private void setupList(List<Document> documents){
        SmartRecyclerAdapter
                .items(documents)
                .map(Document.class, ImageViewHolder.class)
                .setLayoutManager(new LinearLayoutManager(AddImageToPdfActivity.this))
                .into(recyclerView);
    }


    @OnClick(R.id.fab)
    public void onOpenFilePicker(View view){
        Intent intent = PdfFilePicker.openPdf(this, 3);
        startActivityForResult(intent, PDF_REQUEST_CODE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && requestCode == PDF_REQUEST_CODE) {
            getFileInStorage(data);
        }
    }

    private void getFileInStorage(@Nullable Intent data) {
        documents = new ArrayList<>();
        documents.clear();
        if (data != null) {
            ArrayList<MediaFile> mediaFileArrayList = data.getParcelableArrayListExtra(FilePickerActivity.MEDIA_FILES);
            if (mediaFileArrayList != null) {
                for (MediaFile mediaFile : mediaFileArrayList){

                    String filePath = Helper.getPath(this, mediaFile.getUri());

                    if (!TextUtils.isEmpty(filePath) || filePath != null){
                        File file = new File(filePath);
                        document = new Document(file.getName(), Helper.formatSize(file.length()), Helper.formatDate(file.lastModified()), file.getAbsolutePath());
                        documents.add(document);
                        Log.d(TAG, "Selected Filename =  " + document.getDocumentName());
                    }
                }
                //show selected image
                setupList(documents);
                List<String> paths = selectImagePaths();
                addImageToPDF = new AddImageToPDF(AddImageToPdfActivity.this, paths);
            }
        }
    }

    private List<String> selectImagePaths(){
        List<String> paths = new ArrayList<>();
        if (documents != null){
            for (Document document : documents){
                paths.add(document.getDocumentPath());
            }
        }
        return paths;
    }
}

7. Create menu layout file

We are going to add a menu item which will serve as a button to convert images to pdf file.

Inside the res folder, create a new resource folder and name it menu.

Inside the menu folder, create a new menu layout file and name it create_pdf_menu.xml.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_create_pdf"
        android:orderInCategory="100"
        android:title="Create PDF"
        app:showAsAction="always" />
</menu>

In subsequent tutorials, we are going to cover how to manipulate PDF in android like merge multiple PDFs, compress PDF and so on.

If you have any questions about working with PDF file in Android kindly use the comment box below and drop your questions.

Add a Comment