package co.smartreceipts.android.workers.reports.pdf.renderer.imagex;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.pdf.PdfRenderer;
import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
import com.google.common.base.Preconditions;
import com.tom_roush.pdfbox.pdmodel.PDDocument;
import com.tom_roush.pdfbox.pdmodel.graphics.image.JPEGFactory;
import com.tom_roush.pdfbox.pdmodel.graphics.image.LosslessFactory;
import com.tom_roush.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.IOException;
import co.smartreceipts.android.utils.log.Logger;
import co.smartreceipts.android.workers.reports.pdf.renderer.imagex.compat.PdfRendererCompat;
import wb.android.image.ImageUtils;
/**
* Used to load pdf files via the native Android {@link PdfRenderer} stack
*/
public class CompatPdfPDImageXFactory implements PdfPDImageXFactory {
private static final float IMAGE_QUALITY_SCALING_FACTOR = 2.75f;
private final Context context;
private final PDDocument pdDocument;
private final File file;
private ParcelFileDescriptor parcelFileDescriptor;
private PdfRendererCompat pdfRenderer;
private int currentPage = -1;
public CompatPdfPDImageXFactory(@NonNull Context context, @NonNull PDDocument pdDocument, @NonNull File file) {
this.context = Preconditions.checkNotNull(context);
this.pdDocument = Preconditions.checkNotNull(pdDocument);
this.file = Preconditions.checkNotNull(file);
}
@Override
public void open() throws IOException, SecurityException {
parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
pdfRenderer = new PdfRendererCompat(context, parcelFileDescriptor);
}
@NonNull
@Override
public PDImageXObject get() throws IOException {
Preconditions.checkNotNull(pdfRenderer, "The Pdf file must first be opened");
Preconditions.checkNotNull(parcelFileDescriptor, "The Pdf file must first be opened");
PdfRendererCompat.Page page = null;
Bitmap bitmap = null;
try {
Logger.debug(this, "Beginning the render of PDF page {} at {}", currentPage, System.currentTimeMillis());
page = pdfRenderer.openPage(currentPage);
final int scaledHeight = (int) IMAGE_QUALITY_SCALING_FACTOR * page.getHeight();
final int scaledWidth = (int) IMAGE_QUALITY_SCALING_FACTOR * page.getWidth();
final Rect destClip = new Rect(0, 0, scaledWidth, scaledHeight);
bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
page.render(bitmap, destClip, null, PdfRendererCompat.Page.RENDER_MODE_FOR_DISPLAY);
try {
bitmap = ImageUtils.applyWhiteBackground(bitmap);
bitmap = ImageUtils.changeCodec(bitmap, Bitmap.CompressFormat.JPEG, 100);
Logger.debug(this, "Creating pdf image from converted JPEG to speed up processing time");
return JPEGFactory.createFromImage(pdDocument, bitmap);
} catch (IOException e) {
// For some reason, the Lossless factory takes 15-20s per page whereas JPGs are vastly quicker
Logger.warn(this, "Failed to convert to JPG to speed up our processing. Creating the bitmap from our lossless factory of PDF page {} at {}", currentPage, System.currentTimeMillis());
return LosslessFactory.createFromImage(pdDocument, bitmap);
}
} finally {
Logger.debug(this, "Completing the render of PDF page {} at {}", currentPage, System.currentTimeMillis());
if (page != null) {
page.close();
}
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
}
/**
* Bumps us to use the next page
*
* @return {@code false} if we're on the last page. {@code true} otherwise
*/
@Override
public boolean nextPage() {
return ++currentPage < pdfRenderer.getPageCount();
}
@Override
public void close() throws IOException {
if (pdfRenderer != null) {
pdfRenderer.close();
}
IOUtils.closeQuietly(parcelFileDescriptor);
}
}