summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2022-01-20 15:45:01 +0100
committerTor Andersson <tor.andersson@artifex.com>2022-01-20 15:45:01 +0100
commitbde4c7bf4ab17f5dcb3421fdcd951abb44f5442c (patch)
tree0ed2b5d3656c89194074b5f1038a0f4d3832660c
parentc83b19432e4d49900ea4e844a4e22c388a691fed (diff)
Various file handling cleanups.
Add a class to wrap an InputStream into something seekable for MuPDF rather than pre-loading the entire file into a buffer. If the input stream is not seekable, we may end up pre-loading the entire file anyway, but by using a BufferedInputStream. Use the ContentProvider to get the mime-type, file name, and file size used as a hint to the input stream wrapper.
-rw-r--r--lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java52
-rw-r--r--lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java62
2 files changed, 89 insertions, 25 deletions
diff --git a/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java b/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java
index f12e205..6875028 100644
--- a/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java
+++ b/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java
@@ -11,9 +11,11 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.OpenableColumns;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
@@ -35,13 +37,9 @@ import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Stack;
-import java.io.InputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
public class DocumentActivity extends Activity
{
@@ -56,7 +54,7 @@ public class DocumentActivity extends Activity
protected String key;
protected String mimetype;
- protected byte[] buffer;
+ protected SeekableInputStream file;
protected boolean hasLoaded;
protected boolean isReflowable;
@@ -119,28 +117,32 @@ public class DocumentActivity extends Activity
Uri uri = getIntent().getData();
mimetype = getIntent().getType();
- if (mimetype == null || mimetype.equals("application/octet-stream"))
- mimetype = uri.getLastPathSegment();
-
key = uri.toString();
- title = uri.getLastPathSegment();
- Log.i(APP, "URI " + uri.toString());
- Log.i(APP, "MAGIC " + mimetype);
- Log.i(APP, "TITLE " + title);
+ Cursor cursor = getContentResolver().query(uri, null, null, null);
+ cursor.moveToFirst();
+ title = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ long size = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
+ if (size == 0)
+ size = -1;
+
+ Log.i(APP, "OPEN URI " + uri.toString());
+ Log.i(APP, " NAME " + title);
+ Log.i(APP, " SIZE " + size);
+
+ Log.i(APP, " MAGIC (Intent) " + mimetype);
+ if (mimetype == null || mimetype.equals("application/octet-stream")) {
+ mimetype = getContentResolver().getType(uri);
+ Log.i(APP, " MAGIC (Resolver) " + mimetype);
+ }
+ if (mimetype == null || mimetype.equals("application/octet-stream")) {
+ mimetype = title;
+ Log.i(APP, " MAGIC (Filename) " + mimetype);
+ }
try {
- InputStream stm = getContentResolver().openInputStream(uri);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buf = new byte[16384];
- int n;
- while ((n = stm.read(buf)) != -1)
- out.write(buf, 0, n);
- out.flush();
- buffer = out.toByteArray();
- key = toHex(MessageDigest.getInstance("MD5").digest(buffer));
- Log.i(APP, "BUFFER " + buffer.length + " " + key);
- } catch (IOException | NoSuchAlgorithmException x) {
+ file = new SeekableInputStreamWrapper(getContentResolver().openInputStream(uri), size);
+ } catch (IOException x) {
Log.e(APP, x.toString());
Toast.makeText(this, x.getMessage(), Toast.LENGTH_SHORT).show();
}
@@ -328,7 +330,7 @@ public class DocumentActivity extends Activity
boolean needsPassword;
public void work() {
Log.i(APP, "open document");
- doc = Document.openDocument(buffer, mimetype);
+ doc = Document.openDocument(file, mimetype);
needsPassword = doc.needsPassword();
}
public void run() {
diff --git a/lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java b/lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java
new file mode 100644
index 0000000..d19949a
--- /dev/null
+++ b/lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java
@@ -0,0 +1,62 @@
+package com.artifex.mupdf.mini;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+
+import com.artifex.mupdf.fitz.SeekableInputStream;
+
+public class SeekableInputStreamWrapper implements SeekableInputStream
+{
+ // Max file size supported if the size is unknown
+ protected final int MAX_SIZE = (128<<20);
+
+ protected InputStream is;
+ protected long p, length;
+
+ public SeekableInputStreamWrapper(InputStream raw, long n) throws IOException {
+ int size = n < 0 ? MAX_SIZE : (int)n + 1;
+ if (!raw.markSupported()) {
+ is = new BufferedInputStream(raw, size);
+ } else {
+ is = raw;
+ }
+ is.mark(size);
+ length = n;
+ p = 0;
+ }
+
+ public long seek(long offset, int whence) throws IOException {
+ switch (whence) {
+ case SEEK_SET:
+ p = offset;
+ break;
+ case SEEK_CUR:
+ p = p + offset;
+ break;
+ case SEEK_END:
+ if (length < 0) {
+ is.reset();
+ length = is.skip(MAX_SIZE);
+ if (length < 0)
+ throw new IOException("could not find length of stream");
+ }
+ p = length + offset;
+ break;
+ }
+ is.reset();
+ is.skip(p);
+ return p;
+ }
+
+ public long position() throws IOException {
+ return p;
+ }
+
+ public int read(byte[] buf) throws IOException {
+ int n = is.read(buf);
+ if (n > 0)
+ p += n;
+ return n;
+ }
+}