diff --git a/app/src/main/java/org/grapheneos/pdfviewer/PdfDocumentAdapter.java b/app/src/main/java/org/grapheneos/pdfviewer/PdfDocumentAdapter.java
new file mode 100644
index 000000000..05c8c7745
--- /dev/null
+++ b/app/src/main/java/org/grapheneos/pdfviewer/PdfDocumentAdapter.java
@@ -0,0 +1,77 @@
+package org.grapheneos.pdfviewer;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentInfo;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class PdfDocumentAdapter extends PrintDocumentAdapter {
+
+ private Context mContext;
+ private String mPrintJobName;
+ private Uri mUri;
+
+ public PdfDocumentAdapter(Context ctx, String jobName, Uri uri) {
+ this.mContext = ctx;
+ this.mPrintJobName = jobName;
+ this.mUri = uri;
+ }
+
+ @Override
+ public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback layoutResultCallback, Bundle bundle) {
+ if (cancellationSignal.isCanceled()) {
+ layoutResultCallback.onLayoutCancelled();
+ } else {
+ PrintDocumentInfo.Builder pdinfoBuilder = new PrintDocumentInfo.Builder(mPrintJobName);
+ pdinfoBuilder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT);
+ pdinfoBuilder.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN);
+ PrintDocumentInfo pdInfo = pdinfoBuilder.build();
+ layoutResultCallback.onLayoutFinished(pdInfo, !newAttributes.equals(oldAttributes));
+ }
+ }
+
+ @Override
+ public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor, CancellationSignal cancellationSignal, WriteResultCallback writeResultCallback) {
+ InputStream inputStream = null;
+ OutputStream outputStream = null;
+
+ try {
+ inputStream = mContext.getContentResolver().openInputStream(mUri);;
+ outputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
+ byte[] buffer = new byte[mContext.getResources().getInteger(R.integer.print_adapter_buffer_size)];
+
+ int readSize = inputStream.read(buffer);
+ while (readSize >= 0 && !cancellationSignal.isCanceled()) {
+ outputStream.write(buffer, 0, readSize);
+ readSize = inputStream.read(buffer);
+ }
+
+ if (cancellationSignal.isCanceled()) {
+ writeResultCallback.onWriteCancelled();
+ } else {
+ writeResultCallback.onWriteFinished(new PageRange[] { PageRange.ALL_PAGES });
+ }
+ } catch (IOException ex) {
+ writeResultCallback.onWriteFailed(ex.getMessage());
+ } finally {
+ try {
+ if (outputStream != null)
+ outputStream.close();
+ if (inputStream != null)
+ inputStream.close();
+ } catch (IOException ignored) {
+
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java b/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java
index 606a1da79..78bad5cf4 100644
--- a/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java
+++ b/app/src/main/java/org/grapheneos/pdfviewer/PdfViewer.java
@@ -7,6 +7,9 @@
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintManager;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
@@ -22,6 +25,7 @@
import android.webkit.WebViewClient;
import android.widget.TextView;
import android.widget.Toast;
+import android.content.Context;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.app.LoaderManager;
@@ -345,6 +349,14 @@ private void renderPage(final int zoom) {
mWebView.evaluateJavascript("onRenderPage(" + zoom + ")", null);
}
+ private void printDocument() {
+ PrintManager printManager = (PrintManager)getSystemService(Context.PRINT_SERVICE);
+ String jobName = getString(R.string.print_job_base_name);
+ PrintDocumentAdapter printDocumentAdapter = new PdfDocumentAdapter(this, jobName, mUri);
+ PrintAttributes printAttributes = new PrintAttributes.Builder().build();
+ printManager.print(jobName, printDocumentAdapter, printAttributes);
+ }
+
private void documentOrientationChanged(final int orientationDegreesOffset) {
mDocumentOrientationDegrees = (mDocumentOrientationDegrees + orientationDegreesOffset) % 360;
if (mDocumentOrientationDegrees < 0) {
@@ -465,6 +477,7 @@ public boolean onPrepareOptionsMenu(Menu menu) {
final int ids[] = { R.id.action_zoom_in, R.id.action_zoom_out, R.id.action_jump_to_page,
R.id.action_next, R.id.action_previous, R.id.action_first, R.id.action_last,
R.id.action_rotate_clockwise, R.id.action_rotate_counterclockwise,
+ R.id.action_print,
R.id.action_view_document_properties };
if (mDocumentState < STATE_LOADED) {
for (final int id : ids) {
@@ -541,6 +554,10 @@ public boolean onOptionsItemSelected(MenuItem item) {
.show(getSupportFragmentManager(), JumpToPageFragment.TAG);
return true;
+ case R.id.action_print:
+ printDocument();
+ return true;
+
default:
return super.onOptionsItemSelected(item);
}
diff --git a/app/src/main/res/menu/pdf_viewer.xml b/app/src/main/res/menu/pdf_viewer.xml
index e1cc94d2f..5faa41cd5 100644
--- a/app/src/main/res/menu/pdf_viewer.xml
+++ b/app/src/main/res/menu/pdf_viewer.xml
@@ -67,6 +67,11 @@
android:title="@string/action_rotate_counterclockwise"
app:showAsAction="ifRoom" />
+
+
-
+
+ 16384
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 345df9b00..047fe2ab1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,6 +13,11 @@
Rotate counterclockwise
Jump to page
Properties
+ Print
+
+ PDF Document
+ Print job completed successfully
+ Print job failed
Invalid date
Failed to obtain document metadata