|
|
@@ -1,1472 +0,0 @@
|
|
|
-/**
|
|
|
- * Copyright 2016 Bartosz Schiller
|
|
|
- * <p/>
|
|
|
- * Licensed 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
|
|
|
- * <p/>
|
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
- * <p/>
|
|
|
- * 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 com.github.barteksc.pdfviewer;
|
|
|
-
|
|
|
-import android.content.Context;
|
|
|
-import android.graphics.Bitmap;
|
|
|
-import android.graphics.Canvas;
|
|
|
-import android.graphics.Color;
|
|
|
-import android.graphics.Paint;
|
|
|
-import android.graphics.Paint.Style;
|
|
|
-import android.graphics.PaintFlagsDrawFilter;
|
|
|
-import android.graphics.PointF;
|
|
|
-import android.graphics.Rect;
|
|
|
-import android.graphics.RectF;
|
|
|
-import android.graphics.drawable.Drawable;
|
|
|
-import android.net.Uri;
|
|
|
-import android.os.AsyncTask;
|
|
|
-import android.os.HandlerThread;
|
|
|
-import android.util.AttributeSet;
|
|
|
-import android.util.Log;
|
|
|
-import android.widget.RelativeLayout;
|
|
|
-
|
|
|
-import com.github.barteksc.pdfviewer.listener.OnDrawListener;
|
|
|
-import com.github.barteksc.pdfviewer.listener.OnErrorListener;
|
|
|
-import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener;
|
|
|
-import com.github.barteksc.pdfviewer.listener.OnPageChangeListener;
|
|
|
-import com.github.barteksc.pdfviewer.listener.OnPageScrollListener;
|
|
|
-import com.github.barteksc.pdfviewer.listener.OnRenderListener;
|
|
|
-import com.github.barteksc.pdfviewer.model.PagePart;
|
|
|
-import com.github.barteksc.pdfviewer.scroll.ScrollHandle;
|
|
|
-import com.github.barteksc.pdfviewer.source.AssetSource;
|
|
|
-import com.github.barteksc.pdfviewer.source.ByteArraySource;
|
|
|
-import com.github.barteksc.pdfviewer.source.DocumentSource;
|
|
|
-import com.github.barteksc.pdfviewer.source.FileSource;
|
|
|
-import com.github.barteksc.pdfviewer.source.InputStreamSource;
|
|
|
-import com.github.barteksc.pdfviewer.source.UriSource;
|
|
|
-import com.github.barteksc.pdfviewer.util.ArrayUtils;
|
|
|
-import com.github.barteksc.pdfviewer.util.Constants;
|
|
|
-import com.github.barteksc.pdfviewer.util.MathUtils;
|
|
|
-import com.github.barteksc.pdfviewer.util.Util;
|
|
|
-import com.shockwave.pdfium.PdfDocument;
|
|
|
-import com.shockwave.pdfium.PdfiumCore;
|
|
|
-
|
|
|
-import java.io.File;
|
|
|
-import java.io.InputStream;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.List;
|
|
|
-
|
|
|
-/**
|
|
|
- * It supports animations, zoom, cache, and swipe.
|
|
|
- * <p>
|
|
|
- * To fully understand this class you must know its principles :
|
|
|
- * - The PDF document is seen as if we always want to draw all the pages.
|
|
|
- * - The thing is that we only draw the visible parts.
|
|
|
- * - All parts are the same size, this is because we can't interrupt a native page rendering,
|
|
|
- * so we need these renderings to be as fast as possible, and be able to interrupt them
|
|
|
- * as soon as we can.
|
|
|
- * - The parts are loaded when the current offset or the current zoom level changes
|
|
|
- * <p>
|
|
|
- * Important :
|
|
|
- * - DocumentPage = A page of the PDF document.
|
|
|
- * - UserPage = A page as defined by the user.
|
|
|
- * By default, they're the same. But the user can change the pages order
|
|
|
- * using {@link #load(DocumentSource, String, OnLoadCompleteListener, OnErrorListener, int[])}. In this
|
|
|
- * particular case, a userPage of 5 can refer to a documentPage of 17.
|
|
|
- */
|
|
|
-public class PDFView extends RelativeLayout {
|
|
|
-
|
|
|
- private static final String TAG = PDFView.class.getSimpleName();
|
|
|
-
|
|
|
- public static final float DEFAULT_MAX_SCALE = 3.0f;
|
|
|
- public static final float DEFAULT_MID_SCALE = 1.75f;
|
|
|
- public static final float DEFAULT_MIN_SCALE = 1.0f;
|
|
|
-
|
|
|
- private float minZoom = DEFAULT_MIN_SCALE;
|
|
|
- private float midZoom = DEFAULT_MID_SCALE;
|
|
|
- private float maxZoom = DEFAULT_MAX_SCALE;
|
|
|
-
|
|
|
- /**
|
|
|
- * START - scrolling in first page direction
|
|
|
- * END - scrolling in last page direction
|
|
|
- * NONE - not scrolling
|
|
|
- */
|
|
|
- enum ScrollDir {
|
|
|
- NONE, START, END
|
|
|
- }
|
|
|
-
|
|
|
- private ScrollDir scrollDir = ScrollDir.NONE;
|
|
|
-
|
|
|
- /**
|
|
|
- * Rendered parts go to the cache manager
|
|
|
- */
|
|
|
- CacheManager cacheManager;
|
|
|
-
|
|
|
- /**
|
|
|
- * Animation manager manage all offset and zoom animation
|
|
|
- */
|
|
|
- private AnimationManager animationManager;
|
|
|
-
|
|
|
- /**
|
|
|
- * Drag manager manage all touch events
|
|
|
- */
|
|
|
- private DragPinchManager dragPinchManager;
|
|
|
-
|
|
|
- /**
|
|
|
- * The pages the user want to display in order
|
|
|
- * (ex: 0, 2, 2, 8, 8, 1, 1, 1)
|
|
|
- */
|
|
|
- private int[] originalUserPages;
|
|
|
-
|
|
|
- /**
|
|
|
- * The same pages but with a filter to avoid repetition
|
|
|
- * (ex: 0, 2, 8, 1)
|
|
|
- */
|
|
|
- private int[] filteredUserPages;
|
|
|
-
|
|
|
- /**
|
|
|
- * The same pages but with a filter to avoid repetition
|
|
|
- * (ex: 0, 1, 1, 2, 2, 3, 3, 3)
|
|
|
- */
|
|
|
- private int[] filteredUserPageIndexes;
|
|
|
-
|
|
|
- /**
|
|
|
- * Number of pages in the loaded PDF document
|
|
|
- */
|
|
|
- private int documentPageCount;
|
|
|
-
|
|
|
- /**
|
|
|
- * The index of the current sequence
|
|
|
- */
|
|
|
- private int currentPage;
|
|
|
-
|
|
|
- /**
|
|
|
- * The index of the current sequence
|
|
|
- */
|
|
|
- private int currentFilteredPage;
|
|
|
-
|
|
|
- /**
|
|
|
- * The actual width and height of the pages in the PDF document
|
|
|
- */
|
|
|
- private int pageWidth, pageHeight;
|
|
|
-
|
|
|
- /**
|
|
|
- * The optimal width and height of the pages to fit the component size
|
|
|
- */
|
|
|
- private float optimalPageWidth, optimalPageHeight;
|
|
|
-
|
|
|
- /**
|
|
|
- * If you picture all the pages side by side in their optimal width,
|
|
|
- * and taking into account the zoom level, the current offset is the
|
|
|
- * position of the left border of the screen in this big picture
|
|
|
- */
|
|
|
- private float currentXOffset = 0;
|
|
|
-
|
|
|
- /**
|
|
|
- * If you picture all the pages side by side in their optimal width,
|
|
|
- * and taking into account the zoom level, the current offset is the
|
|
|
- * position of the left border of the screen in this big picture
|
|
|
- */
|
|
|
- private float currentYOffset = 0;
|
|
|
-
|
|
|
- /**
|
|
|
- * The zoom level, always >= 1
|
|
|
- */
|
|
|
- private float zoom = 1f;
|
|
|
-
|
|
|
- /**
|
|
|
- * True if the PDFView has been recycled
|
|
|
- */
|
|
|
- private boolean recycled = true;
|
|
|
-
|
|
|
- /**
|
|
|
- * Current state of the view
|
|
|
- */
|
|
|
- private State state = State.DEFAULT;
|
|
|
-
|
|
|
- /**
|
|
|
- * Async task used during the loading phase to decode a PDF document
|
|
|
- */
|
|
|
- private DecodingAsyncTask decodingAsyncTask;
|
|
|
-
|
|
|
- /**
|
|
|
- * The thread {@link #renderingHandler} will run on
|
|
|
- */
|
|
|
- private final HandlerThread renderingHandlerThread;
|
|
|
- /**
|
|
|
- * Handler always waiting in the background and rendering tasks
|
|
|
- */
|
|
|
- RenderingHandler renderingHandler;
|
|
|
-
|
|
|
- private PagesLoader pagesLoader;
|
|
|
-
|
|
|
- /**
|
|
|
- * Call back object to call when the PDF is loaded
|
|
|
- */
|
|
|
- private OnLoadCompleteListener onLoadCompleteListener;
|
|
|
-
|
|
|
- private OnErrorListener onErrorListener;
|
|
|
-
|
|
|
- /**
|
|
|
- * Call back object to call when the page has changed
|
|
|
- */
|
|
|
- private OnPageChangeListener onPageChangeListener;
|
|
|
-
|
|
|
- /**
|
|
|
- * Call back object to call when the page is scrolled
|
|
|
- */
|
|
|
- private OnPageScrollListener onPageScrollListener;
|
|
|
-
|
|
|
- /**
|
|
|
- * Call back object to call when the above layer is to drawn
|
|
|
- */
|
|
|
- private OnDrawListener onDrawListener;
|
|
|
-
|
|
|
- private OnDrawListener onDrawAllListener;
|
|
|
-
|
|
|
- /**
|
|
|
- * Call back object to call when the document is initially rendered
|
|
|
- */
|
|
|
- private OnRenderListener onRenderListener;
|
|
|
-
|
|
|
- /**
|
|
|
- * Paint object for drawing
|
|
|
- */
|
|
|
- private Paint paint;
|
|
|
-
|
|
|
- /**
|
|
|
- * Paint object for drawing debug stuff
|
|
|
- */
|
|
|
- private Paint debugPaint;
|
|
|
-
|
|
|
- private int defaultPage = 0;
|
|
|
-
|
|
|
- /**
|
|
|
- * True if should scroll through pages vertically instead of horizontally
|
|
|
- */
|
|
|
- private boolean swipeVertical = true;
|
|
|
-
|
|
|
- /**
|
|
|
- * Pdfium core for loading and rendering PDFs
|
|
|
- */
|
|
|
- private PdfiumCore pdfiumCore;
|
|
|
-
|
|
|
- private PdfDocument pdfDocument;
|
|
|
-
|
|
|
- private ScrollHandle scrollHandle;
|
|
|
-
|
|
|
- private boolean isScrollHandleInit = false;
|
|
|
-
|
|
|
- ScrollHandle getScrollHandle() {
|
|
|
- return scrollHandle;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * True if bitmap should use ARGB_8888 format and take more memory
|
|
|
- * False if bitmap should be compressed by using RGB_565 format and take less memory
|
|
|
- */
|
|
|
- private boolean bestQuality = false;
|
|
|
-
|
|
|
- /**
|
|
|
- * True if annotations should be rendered
|
|
|
- * False otherwise
|
|
|
- */
|
|
|
- private boolean annotationRendering = false;
|
|
|
-
|
|
|
- /**
|
|
|
- * True if the view should render during scaling<br/>
|
|
|
- * Can not be forced on older API versions (< Build.VERSION_CODES.KITKAT) as the GestureDetector does
|
|
|
- * not detect scrolling while scaling.<br/>
|
|
|
- * False otherwise
|
|
|
- */
|
|
|
- private boolean renderDuringScale = false;
|
|
|
-
|
|
|
- /**
|
|
|
- * Antialiasing and bitmap filtering
|
|
|
- */
|
|
|
- private boolean enableAntialiasing = true;
|
|
|
- private PaintFlagsDrawFilter antialiasFilter =
|
|
|
- new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
|
|
-
|
|
|
- /**
|
|
|
- * Spacing between pages, in DP
|
|
|
- */
|
|
|
- private int spacingPx = 0;
|
|
|
-
|
|
|
- /**
|
|
|
- * pages numbers used when calling onDrawAllListener
|
|
|
- */
|
|
|
- private List<Integer> onDrawPagesNums = new ArrayList<>(10);
|
|
|
-
|
|
|
- /**
|
|
|
- * Construct the initial view
|
|
|
- */
|
|
|
- public PDFView(Context context, AttributeSet set) {
|
|
|
- super(context, set);
|
|
|
-
|
|
|
- renderingHandlerThread = new HandlerThread("PDF renderer");
|
|
|
-
|
|
|
- if (isInEditMode()) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- cacheManager = new CacheManager();
|
|
|
- animationManager = new AnimationManager(this);
|
|
|
- dragPinchManager = new DragPinchManager(this, animationManager);
|
|
|
-
|
|
|
- paint = new Paint();
|
|
|
- debugPaint = new Paint();
|
|
|
- debugPaint.setStyle(Style.STROKE);
|
|
|
-
|
|
|
- pdfiumCore = new PdfiumCore(context);
|
|
|
- setWillNotDraw(false);
|
|
|
- }
|
|
|
-
|
|
|
- private void load(DocumentSource docSource, String password, OnLoadCompleteListener listener, OnErrorListener onErrorListener) {
|
|
|
- load(docSource, password, listener, onErrorListener, null);
|
|
|
- }
|
|
|
-
|
|
|
- private void load(DocumentSource docSource, String password, OnLoadCompleteListener onLoadCompleteListener, OnErrorListener onErrorListener, int[] userPages) {
|
|
|
-
|
|
|
- if (!recycled) {
|
|
|
- throw new IllegalStateException("Don't call load on a PDF View without recycling it first.");
|
|
|
- }
|
|
|
-
|
|
|
- // Manage UserPages if not null
|
|
|
- if (userPages != null) {
|
|
|
- this.originalUserPages = userPages;
|
|
|
- this.filteredUserPages = ArrayUtils.deleteDuplicatedPages(originalUserPages);
|
|
|
- this.filteredUserPageIndexes = ArrayUtils.calculateIndexesInDuplicateArray(originalUserPages);
|
|
|
- }
|
|
|
-
|
|
|
- this.onLoadCompleteListener = onLoadCompleteListener;
|
|
|
- this.onErrorListener = onErrorListener;
|
|
|
-
|
|
|
- int firstPageIdx = 0;
|
|
|
- if (originalUserPages != null) {
|
|
|
- firstPageIdx = originalUserPages[0];
|
|
|
- }
|
|
|
-
|
|
|
- recycled = false;
|
|
|
- // Start decoding document
|
|
|
- decodingAsyncTask = new DecodingAsyncTask(docSource, password, this, pdfiumCore, firstPageIdx);
|
|
|
- decodingAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Go to the given page.
|
|
|
- *
|
|
|
- * @param page Page index.
|
|
|
- */
|
|
|
- public void jumpTo(int page, boolean withAnimation) {
|
|
|
- float offset = -calculatePageOffset(page);
|
|
|
- if (swipeVertical) {
|
|
|
- if (withAnimation) {
|
|
|
- animationManager.startYAnimation(currentYOffset, offset);
|
|
|
- } else {
|
|
|
- moveTo(currentXOffset, offset);
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (withAnimation) {
|
|
|
- animationManager.startXAnimation(currentXOffset, offset);
|
|
|
- } else {
|
|
|
- moveTo(offset, currentYOffset);
|
|
|
- }
|
|
|
- }
|
|
|
- showPage(page);
|
|
|
- }
|
|
|
-
|
|
|
- public void jumpTo(int page) {
|
|
|
- jumpTo(page, false);
|
|
|
- }
|
|
|
-
|
|
|
- void showPage(int pageNb) {
|
|
|
- if (recycled) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Check the page number and makes the
|
|
|
- // difference between UserPages and DocumentPages
|
|
|
- pageNb = determineValidPageNumberFrom(pageNb);
|
|
|
- currentPage = pageNb;
|
|
|
- currentFilteredPage = pageNb;
|
|
|
- if (filteredUserPageIndexes != null) {
|
|
|
- if (pageNb >= 0 && pageNb < filteredUserPageIndexes.length) {
|
|
|
- pageNb = filteredUserPageIndexes[pageNb];
|
|
|
- currentFilteredPage = pageNb;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- loadPages();
|
|
|
-
|
|
|
- if (scrollHandle != null && !documentFitsView()) {
|
|
|
- scrollHandle.setPageNum(currentPage + 1);
|
|
|
- }
|
|
|
-
|
|
|
- if (onPageChangeListener != null) {
|
|
|
- onPageChangeListener.onPageChanged(currentPage, getPageCount());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get current position as ratio of document length to visible area.
|
|
|
- * 0 means that document start is visible, 1 that document end is visible
|
|
|
- *
|
|
|
- * @return offset between 0 and 1
|
|
|
- */
|
|
|
- public float getPositionOffset() {
|
|
|
- float offset;
|
|
|
- if (swipeVertical) {
|
|
|
- offset = -currentYOffset / (calculateDocLength() - getHeight());
|
|
|
- } else {
|
|
|
- offset = -currentXOffset / (calculateDocLength() - getWidth());
|
|
|
- }
|
|
|
- return MathUtils.limit(offset, 0, 1);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @param progress must be between 0 and 1
|
|
|
- * @param moveHandle whether to move scroll handle
|
|
|
- * @see PDFView#getPositionOffset()
|
|
|
- */
|
|
|
- public void setPositionOffset(float progress, boolean moveHandle) {
|
|
|
- if (swipeVertical) {
|
|
|
- moveTo(currentXOffset, (-calculateDocLength() + getHeight()) * progress, moveHandle);
|
|
|
- } else {
|
|
|
- moveTo((-calculateDocLength() + getWidth()) * progress, currentYOffset, moveHandle);
|
|
|
- }
|
|
|
- loadPageByOffset();
|
|
|
- }
|
|
|
-
|
|
|
- public void setPositionOffset(float progress) {
|
|
|
- setPositionOffset(progress, true);
|
|
|
- }
|
|
|
-
|
|
|
- private float calculatePageOffset(int page) {
|
|
|
- if (swipeVertical) {
|
|
|
- return toCurrentScale(page * optimalPageHeight + page * spacingPx);
|
|
|
- } else {
|
|
|
- return toCurrentScale(page * optimalPageWidth + page * spacingPx);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- float calculateDocLength() {
|
|
|
- int pageCount = getPageCount();
|
|
|
- if (swipeVertical) {
|
|
|
- return toCurrentScale(pageCount * optimalPageHeight + (pageCount - 1) * spacingPx);
|
|
|
- } else {
|
|
|
- return toCurrentScale(pageCount * optimalPageWidth + (pageCount - 1) * spacingPx);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void stopFling() {
|
|
|
- animationManager.stopFling();
|
|
|
- }
|
|
|
-
|
|
|
- public int getPageCount() {
|
|
|
- if (originalUserPages != null) {
|
|
|
- return originalUserPages.length;
|
|
|
- }
|
|
|
- return documentPageCount;
|
|
|
- }
|
|
|
-
|
|
|
- public void enableSwipe(boolean enableSwipe) {
|
|
|
- dragPinchManager.setSwipeEnabled(enableSwipe);
|
|
|
- }
|
|
|
-
|
|
|
- public void enableDoubletap(boolean enableDoubletap) {
|
|
|
- this.dragPinchManager.enableDoubletap(enableDoubletap);
|
|
|
- }
|
|
|
-
|
|
|
- private void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
|
|
|
- this.onPageChangeListener = onPageChangeListener;
|
|
|
- }
|
|
|
-
|
|
|
- OnPageChangeListener getOnPageChangeListener() {
|
|
|
- return this.onPageChangeListener;
|
|
|
- }
|
|
|
-
|
|
|
- private void setOnPageScrollListener(OnPageScrollListener onPageScrollListener) {
|
|
|
- this.onPageScrollListener = onPageScrollListener;
|
|
|
- }
|
|
|
-
|
|
|
- OnPageScrollListener getOnPageScrollListener() {
|
|
|
- return this.onPageScrollListener;
|
|
|
- }
|
|
|
-
|
|
|
- private void setOnRenderListener(OnRenderListener onRenderListener) {
|
|
|
- this.onRenderListener = onRenderListener;
|
|
|
- }
|
|
|
-
|
|
|
- OnRenderListener getOnRenderListener() {
|
|
|
- return this.onRenderListener;
|
|
|
- }
|
|
|
-
|
|
|
- private void setOnDrawListener(OnDrawListener onDrawListener) {
|
|
|
- this.onDrawListener = onDrawListener;
|
|
|
- }
|
|
|
-
|
|
|
- private void setOnDrawAllListener(OnDrawListener onDrawAllListener) {
|
|
|
- this.onDrawAllListener = onDrawAllListener;
|
|
|
- }
|
|
|
-
|
|
|
- public void recycle() {
|
|
|
-
|
|
|
- animationManager.stopAll();
|
|
|
-
|
|
|
- // Stop tasks
|
|
|
- if (renderingHandler != null) {
|
|
|
- renderingHandler.stop();
|
|
|
- renderingHandler.removeMessages(RenderingHandler.MSG_RENDER_TASK);
|
|
|
- }
|
|
|
- if (decodingAsyncTask != null) {
|
|
|
- decodingAsyncTask.cancel(true);
|
|
|
- }
|
|
|
-
|
|
|
- // Clear caches
|
|
|
- cacheManager.recycle();
|
|
|
-
|
|
|
- if (scrollHandle != null && isScrollHandleInit) {
|
|
|
- scrollHandle.destroyLayout();
|
|
|
- }
|
|
|
-
|
|
|
- if (pdfiumCore != null && pdfDocument != null) {
|
|
|
- pdfiumCore.closeDocument(pdfDocument);
|
|
|
- }
|
|
|
-
|
|
|
- renderingHandler = null;
|
|
|
- originalUserPages = null;
|
|
|
- filteredUserPages = null;
|
|
|
- filteredUserPageIndexes = null;
|
|
|
- pdfDocument = null;
|
|
|
- scrollHandle = null;
|
|
|
- isScrollHandleInit = false;
|
|
|
- currentXOffset = currentYOffset = 0;
|
|
|
- zoom = 1f;
|
|
|
- recycled = true;
|
|
|
- state = State.DEFAULT;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isRecycled() {
|
|
|
- return recycled;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Handle fling animation
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void computeScroll() {
|
|
|
- super.computeScroll();
|
|
|
- animationManager.computeFling();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onDetachedFromWindow() {
|
|
|
- recycle();
|
|
|
- super.onDetachedFromWindow();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
|
- if (isInEditMode() || state != State.SHOWN) {
|
|
|
- return;
|
|
|
- }
|
|
|
- animationManager.stopAll();
|
|
|
- calculateOptimalWidthAndHeight();
|
|
|
- if (swipeVertical) {
|
|
|
- moveTo(currentXOffset, -calculatePageOffset(currentPage));
|
|
|
- } else {
|
|
|
- moveTo(-calculatePageOffset(currentPage), currentYOffset);
|
|
|
- }
|
|
|
- loadPageByOffset();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onDraw(Canvas canvas) {
|
|
|
- if (isInEditMode()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- // As I said in this class javadoc, we can think of this canvas as a huge
|
|
|
- // strip on which we draw all the images. We actually only draw the rendered
|
|
|
- // parts, of course, but we render them in the place they belong in this huge
|
|
|
- // strip.
|
|
|
-
|
|
|
- // That's where Canvas.translate(x, y) becomes very helpful.
|
|
|
- // This is the situation :
|
|
|
- // _______________________________________________
|
|
|
- // | | |
|
|
|
- // | the actual | The big strip |
|
|
|
- // | canvas | |
|
|
|
- // |_____________| |
|
|
|
- // |_______________________________________________|
|
|
|
- //
|
|
|
- // If the rendered part is on the bottom right corner of the strip
|
|
|
- // we can draw it but we won't see it because the canvas is not big enough.
|
|
|
-
|
|
|
- // But if we call translate(-X, -Y) on the canvas just before drawing the object :
|
|
|
- // _______________________________________________
|
|
|
- // | _____________|
|
|
|
- // | The big strip | |
|
|
|
- // | | the actual |
|
|
|
- // | | canvas |
|
|
|
- // |_________________________________|_____________|
|
|
|
- //
|
|
|
- // The object will be on the canvas.
|
|
|
- // This technique is massively used in this method, and allows
|
|
|
- // abstraction of the screen position when rendering the parts.
|
|
|
-
|
|
|
- // Draws background
|
|
|
-
|
|
|
- if (enableAntialiasing) {
|
|
|
- canvas.setDrawFilter(antialiasFilter);
|
|
|
- }
|
|
|
-
|
|
|
- Drawable bg = getBackground();
|
|
|
- if (bg == null) {
|
|
|
- canvas.drawColor(Color.WHITE);
|
|
|
- } else {
|
|
|
- bg.draw(canvas);
|
|
|
- }
|
|
|
-
|
|
|
- if (recycled) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (state != State.SHOWN) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Moves the canvas before drawing any element
|
|
|
- float currentXOffset = this.currentXOffset;
|
|
|
- float currentYOffset = this.currentYOffset;
|
|
|
- canvas.translate(currentXOffset, currentYOffset);
|
|
|
-
|
|
|
- // Draws thumbnails
|
|
|
- for (PagePart part : cacheManager.getThumbnails()) {
|
|
|
- drawPart(canvas, part);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // Draws parts
|
|
|
- for (PagePart part : cacheManager.getPageParts()) {
|
|
|
- drawPart(canvas, part);
|
|
|
- if (onDrawAllListener != null && !onDrawPagesNums.contains(part.getUserPage())) {
|
|
|
- onDrawPagesNums.add(part.getUserPage());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (Integer page : onDrawPagesNums) {
|
|
|
- drawWithListener(canvas, page, onDrawAllListener);
|
|
|
- }
|
|
|
- onDrawPagesNums.clear();
|
|
|
-
|
|
|
- drawWithListener(canvas, currentPage, onDrawListener);
|
|
|
-
|
|
|
- // Restores the canvas position
|
|
|
- canvas.translate(-currentXOffset, -currentYOffset);
|
|
|
- }
|
|
|
-
|
|
|
- private void drawWithListener(Canvas canvas, int page, OnDrawListener listener) {
|
|
|
- if (listener != null) {
|
|
|
- float translateX, translateY;
|
|
|
- if (swipeVertical) {
|
|
|
- translateX = 0;
|
|
|
- translateY = calculatePageOffset(page);
|
|
|
- } else {
|
|
|
- translateY = 0;
|
|
|
- translateX = calculatePageOffset(page);
|
|
|
- }
|
|
|
-
|
|
|
- canvas.translate(translateX, translateY);
|
|
|
- listener.onLayerDrawn(canvas,
|
|
|
- toCurrentScale(optimalPageWidth),
|
|
|
- toCurrentScale(optimalPageHeight),
|
|
|
- page);
|
|
|
-
|
|
|
- canvas.translate(-translateX, -translateY);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Draw a given PagePart on the canvas
|
|
|
- */
|
|
|
- private void drawPart(Canvas canvas, PagePart part) {
|
|
|
- // Can seem strange, but avoid lot of calls
|
|
|
- RectF pageRelativeBounds = part.getPageRelativeBounds();
|
|
|
- Bitmap renderedBitmap = part.getRenderedBitmap();
|
|
|
-
|
|
|
- if (renderedBitmap.isRecycled()) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Move to the target page
|
|
|
- float localTranslationX = 0;
|
|
|
- float localTranslationY = 0;
|
|
|
- if (swipeVertical) {
|
|
|
- localTranslationY = calculatePageOffset(part.getUserPage());
|
|
|
- } else {
|
|
|
- localTranslationX = calculatePageOffset(part.getUserPage());
|
|
|
- }
|
|
|
- canvas.translate(localTranslationX, localTranslationY);
|
|
|
-
|
|
|
- Rect srcRect = new Rect(0, 0, renderedBitmap.getWidth(),
|
|
|
- renderedBitmap.getHeight());
|
|
|
-
|
|
|
- float offsetX = toCurrentScale(pageRelativeBounds.left * optimalPageWidth);
|
|
|
- float offsetY = toCurrentScale(pageRelativeBounds.top * optimalPageHeight);
|
|
|
- float width = toCurrentScale(pageRelativeBounds.width() * optimalPageWidth);
|
|
|
- float height = toCurrentScale(pageRelativeBounds.height() * optimalPageHeight);
|
|
|
-
|
|
|
- // If we use float values for this rectangle, there will be
|
|
|
- // a possible gap between page parts, especially when
|
|
|
- // the zoom level is high.
|
|
|
- RectF dstRect = new RectF((int) offsetX, (int) offsetY,
|
|
|
- (int) (offsetX + width),
|
|
|
- (int) (offsetY + height));
|
|
|
-
|
|
|
- // Check if bitmap is in the screen
|
|
|
- float translationX = currentXOffset + localTranslationX;
|
|
|
- float translationY = currentYOffset + localTranslationY;
|
|
|
- if (translationX + dstRect.left >= getWidth() || translationX + dstRect.right <= 0 ||
|
|
|
- translationY + dstRect.top >= getHeight() || translationY + dstRect.bottom <= 0) {
|
|
|
- canvas.translate(-localTranslationX, -localTranslationY);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- canvas.drawBitmap(renderedBitmap, srcRect, dstRect, paint);
|
|
|
-
|
|
|
- if (Constants.DEBUG_MODE) {
|
|
|
- debugPaint.setColor(part.getUserPage() % 2 == 0 ? Color.RED : Color.BLUE);
|
|
|
- canvas.drawRect(dstRect, debugPaint);
|
|
|
- }
|
|
|
-
|
|
|
- // Restore the canvas position
|
|
|
- canvas.translate(-localTranslationX, -localTranslationY);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Load all the parts around the center of the screen,
|
|
|
- * taking into account X and Y offsets, zoom level, and
|
|
|
- * the current page displayed
|
|
|
- */
|
|
|
- public void loadPages() {
|
|
|
- if (optimalPageWidth == 0 || optimalPageHeight == 0 || renderingHandler == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Cancel all current tasks
|
|
|
- renderingHandler.removeMessages(RenderingHandler.MSG_RENDER_TASK);
|
|
|
- cacheManager.makeANewSet();
|
|
|
-
|
|
|
- pagesLoader.loadPages();
|
|
|
- redraw();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Called when the PDF is loaded
|
|
|
- */
|
|
|
- void loadComplete(PdfDocument pdfDocument, int pageWidth, int pageHeight) {
|
|
|
- state = State.LOADED;
|
|
|
- this.documentPageCount = pdfiumCore.getPageCount(pdfDocument);
|
|
|
-
|
|
|
- this.pdfDocument = pdfDocument;
|
|
|
-
|
|
|
- this.pageWidth = pageWidth;
|
|
|
- this.pageHeight = pageHeight;
|
|
|
- calculateOptimalWidthAndHeight();
|
|
|
-
|
|
|
- pagesLoader = new PagesLoader(this);
|
|
|
-
|
|
|
- if (!renderingHandlerThread.isAlive()) {
|
|
|
- renderingHandlerThread.start();
|
|
|
- }
|
|
|
- renderingHandler = new RenderingHandler(renderingHandlerThread.getLooper(),
|
|
|
- this, pdfiumCore, pdfDocument);
|
|
|
- renderingHandler.start();
|
|
|
-
|
|
|
- if (scrollHandle != null) {
|
|
|
- scrollHandle.setupLayout(this);
|
|
|
- isScrollHandleInit = true;
|
|
|
- }
|
|
|
-
|
|
|
- if (onLoadCompleteListener != null) {
|
|
|
- onLoadCompleteListener.loadComplete(documentPageCount);
|
|
|
- }
|
|
|
-
|
|
|
- jumpTo(defaultPage, false);
|
|
|
- }
|
|
|
-
|
|
|
- void loadError(Throwable t) {
|
|
|
- state = State.ERROR;
|
|
|
- recycle();
|
|
|
- invalidate();
|
|
|
- if (this.onErrorListener != null) {
|
|
|
- this.onErrorListener.onError(t);
|
|
|
- } else {
|
|
|
- Log.e("PDFView", "load pdf error", t);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void redraw() {
|
|
|
- invalidate();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Called when a rendering task is over and
|
|
|
- * a PagePart has been freshly created.
|
|
|
- *
|
|
|
- * @param part The created PagePart.
|
|
|
- */
|
|
|
- public void onBitmapRendered(PagePart part) {
|
|
|
- // when it is first rendered part
|
|
|
- if (state == State.LOADED) {
|
|
|
- state = State.SHOWN;
|
|
|
- if (onRenderListener != null) {
|
|
|
- onRenderListener.onInitiallyRendered(getPageCount(), optimalPageWidth, optimalPageHeight);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (part.isThumbnail()) {
|
|
|
- cacheManager.cacheThumbnail(part);
|
|
|
- } else {
|
|
|
- cacheManager.cachePart(part);
|
|
|
- }
|
|
|
- redraw();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Given the UserPage number, this method restrict it
|
|
|
- * to be sure it's an existing page. It takes care of
|
|
|
- * using the user defined pages if any.
|
|
|
- *
|
|
|
- * @param userPage A page number.
|
|
|
- * @return A restricted valid page number (example : -2 => 0)
|
|
|
- */
|
|
|
- private int determineValidPageNumberFrom(int userPage) {
|
|
|
- if (userPage <= 0) {
|
|
|
- return 0;
|
|
|
- }
|
|
|
- if (originalUserPages != null) {
|
|
|
- if (userPage >= originalUserPages.length) {
|
|
|
- return originalUserPages.length - 1;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (userPage >= documentPageCount) {
|
|
|
- return documentPageCount - 1;
|
|
|
- }
|
|
|
- }
|
|
|
- return userPage;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Calculate the x/y-offset needed to have the given
|
|
|
- * page centered on the screen. It doesn't take into
|
|
|
- * account the zoom level.
|
|
|
- *
|
|
|
- * @param pageNb The page number.
|
|
|
- * @return The x/y-offset to use to have the pageNb centered.
|
|
|
- */
|
|
|
- private float calculateCenterOffsetForPage(int pageNb) {
|
|
|
- if (swipeVertical) {
|
|
|
- float imageY = -(pageNb * optimalPageHeight + pageNb * spacingPx);
|
|
|
- imageY += getHeight() / 2 - optimalPageHeight / 2;
|
|
|
- return imageY;
|
|
|
- } else {
|
|
|
- float imageX = -(pageNb * optimalPageWidth + pageNb * spacingPx);
|
|
|
- imageX += getWidth() / 2 - optimalPageWidth / 2;
|
|
|
- return imageX;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Calculate the optimal width and height of a page
|
|
|
- * considering the area width and height
|
|
|
- */
|
|
|
- private void calculateOptimalWidthAndHeight() {
|
|
|
- if (state == State.DEFAULT || getWidth() == 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- float maxWidth = getWidth(), maxHeight = getHeight();
|
|
|
- float w = pageWidth, h = pageHeight;
|
|
|
- float ratio = w / h;
|
|
|
- w = maxWidth;
|
|
|
- h = (float) Math.floor(maxWidth / ratio);
|
|
|
- if (h > maxHeight) {
|
|
|
- h = maxHeight;
|
|
|
- w = (float) Math.floor(maxHeight * ratio);
|
|
|
- }
|
|
|
-
|
|
|
- optimalPageWidth = w;
|
|
|
- optimalPageHeight = h;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- public void moveTo(float offsetX, float offsetY) {
|
|
|
- moveTo(offsetX, offsetY, true);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Move to the given X and Y offsets, but check them ahead of time
|
|
|
- * to be sure not to go outside the the big strip.
|
|
|
- *
|
|
|
- * @param offsetX The big strip X offset to use as the left border of the screen.
|
|
|
- * @param offsetY The big strip Y offset to use as the right border of the screen.
|
|
|
- * @param moveHandle whether to move scroll handle or not
|
|
|
- */
|
|
|
- public void moveTo(float offsetX, float offsetY, boolean moveHandle) {
|
|
|
- if (swipeVertical) {
|
|
|
- // Check X offset
|
|
|
- float scaledPageWidth = toCurrentScale(optimalPageWidth);
|
|
|
- if (scaledPageWidth < getWidth()) {
|
|
|
- offsetX = getWidth() / 2 - scaledPageWidth / 2;
|
|
|
- } else {
|
|
|
- if (offsetX > 0) {
|
|
|
- offsetX = 0;
|
|
|
- } else if (offsetX + scaledPageWidth < getWidth()) {
|
|
|
- offsetX = getWidth() - scaledPageWidth;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Check Y offset
|
|
|
- float contentHeight = calculateDocLength();
|
|
|
- if (contentHeight < getHeight()) { // whole document height visible on screen
|
|
|
- offsetY = (getHeight() - contentHeight) / 2;
|
|
|
- } else {
|
|
|
- if (offsetY > 0) { // top visible
|
|
|
- offsetY = 0;
|
|
|
- } else if (offsetY + contentHeight < getHeight()) { // bottom visible
|
|
|
- offsetY = -contentHeight + getHeight();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (offsetY < currentYOffset) {
|
|
|
- scrollDir = ScrollDir.END;
|
|
|
- } else if (offsetY > currentYOffset) {
|
|
|
- scrollDir = ScrollDir.START;
|
|
|
- } else {
|
|
|
- scrollDir = ScrollDir.NONE;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // Check Y offset
|
|
|
- float scaledPageHeight = toCurrentScale(optimalPageHeight);
|
|
|
- if (scaledPageHeight < getHeight()) {
|
|
|
- offsetY = getHeight() / 2 - scaledPageHeight / 2;
|
|
|
- } else {
|
|
|
- if (offsetY > 0) {
|
|
|
- offsetY = 0;
|
|
|
- } else if (offsetY + scaledPageHeight < getHeight()) {
|
|
|
- offsetY = getHeight() - scaledPageHeight;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Check X offset
|
|
|
- float contentWidth = calculateDocLength();
|
|
|
- if (contentWidth < getWidth()) { // whole document width visible on screen
|
|
|
- offsetX = (getWidth() - contentWidth) / 2;
|
|
|
- } else {
|
|
|
- if (offsetX > 0) { // left visible
|
|
|
- offsetX = 0;
|
|
|
- } else if (offsetX + contentWidth < getWidth()) { // right visible
|
|
|
- offsetX = -contentWidth + getWidth();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (offsetX < currentXOffset) {
|
|
|
- scrollDir = ScrollDir.END;
|
|
|
- } else if (offsetX > currentXOffset) {
|
|
|
- scrollDir = ScrollDir.START;
|
|
|
- } else {
|
|
|
- scrollDir = ScrollDir.NONE;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- currentXOffset = offsetX;
|
|
|
- currentYOffset = offsetY;
|
|
|
- float positionOffset = getPositionOffset();
|
|
|
-
|
|
|
- if (moveHandle && scrollHandle != null && !documentFitsView()) {
|
|
|
- scrollHandle.setScroll(positionOffset);
|
|
|
- }
|
|
|
-
|
|
|
- if (onPageScrollListener != null) {
|
|
|
- onPageScrollListener.onPageScrolled(getCurrentPage(), positionOffset);
|
|
|
- }
|
|
|
-
|
|
|
- redraw();
|
|
|
- }
|
|
|
-
|
|
|
- ScrollDir getScrollDir() {
|
|
|
- return scrollDir;
|
|
|
- }
|
|
|
-
|
|
|
- void loadPageByOffset() {
|
|
|
- float offset, optimal, screenCenter;
|
|
|
- float spacingPerPage = spacingPx - (spacingPx / getPageCount());
|
|
|
- if (swipeVertical) {
|
|
|
- offset = currentYOffset;
|
|
|
- optimal = optimalPageHeight + spacingPerPage;
|
|
|
- screenCenter = ((float) getHeight()) / 2;
|
|
|
- } else {
|
|
|
- offset = currentXOffset;
|
|
|
- optimal = optimalPageWidth + spacingPerPage;
|
|
|
- screenCenter = ((float) getWidth()) / 2;
|
|
|
- }
|
|
|
-
|
|
|
- int page = (int) Math.floor((Math.abs(offset) + screenCenter) / toCurrentScale(optimal));
|
|
|
-
|
|
|
- if (page >= 0 && page <= getPageCount() - 1 && page != getCurrentPage()) {
|
|
|
- showPage(page);
|
|
|
- } else {
|
|
|
- loadPages();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- int[] getFilteredUserPages() {
|
|
|
- return filteredUserPages;
|
|
|
- }
|
|
|
-
|
|
|
- int[] getOriginalUserPages() {
|
|
|
- return originalUserPages;
|
|
|
- }
|
|
|
-
|
|
|
- int[] getFilteredUserPageIndexes() {
|
|
|
- return filteredUserPageIndexes;
|
|
|
- }
|
|
|
-
|
|
|
- int getDocumentPageCount() {
|
|
|
- return documentPageCount;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Move relatively to the current position.
|
|
|
- *
|
|
|
- * @param dx The X difference you want to apply.
|
|
|
- * @param dy The Y difference you want to apply.
|
|
|
- * @see #moveTo(float, float)
|
|
|
- */
|
|
|
- public void moveRelativeTo(float dx, float dy) {
|
|
|
- moveTo(currentXOffset + dx, currentYOffset + dy);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Change the zoom level
|
|
|
- */
|
|
|
- public void zoomTo(float zoom) {
|
|
|
- this.zoom = zoom;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Change the zoom level, relatively to a pivot point.
|
|
|
- * It will call moveTo() to make sure the given point stays
|
|
|
- * in the middle of the screen.
|
|
|
- *
|
|
|
- * @param zoom The zoom level.
|
|
|
- * @param pivot The point on the screen that should stays.
|
|
|
- */
|
|
|
- public void zoomCenteredTo(float zoom, PointF pivot) {
|
|
|
- float dzoom = zoom / this.zoom;
|
|
|
- zoomTo(zoom);
|
|
|
- float baseX = currentXOffset * dzoom;
|
|
|
- float baseY = currentYOffset * dzoom;
|
|
|
- baseX += (pivot.x - pivot.x * dzoom);
|
|
|
- baseY += (pivot.y - pivot.y * dzoom);
|
|
|
- moveTo(baseX, baseY);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @see #zoomCenteredTo(float, PointF)
|
|
|
- */
|
|
|
- public void zoomCenteredRelativeTo(float dzoom, PointF pivot) {
|
|
|
- zoomCenteredTo(zoom * dzoom, pivot);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Checks if whole document can be displayed on screen, doesn't include zoom
|
|
|
- *
|
|
|
- * @return true if whole document can displayed at once, false otherwise
|
|
|
- */
|
|
|
- public boolean documentFitsView() {
|
|
|
- int pageCount = getPageCount();
|
|
|
- int spacing = (pageCount - 1) * spacingPx;
|
|
|
- if (swipeVertical) {
|
|
|
- return pageCount * optimalPageHeight + spacing < getHeight();
|
|
|
- } else {
|
|
|
- return pageCount * optimalPageWidth + spacing < getWidth();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void fitToWidth(int page) {
|
|
|
- if (state != State.SHOWN) {
|
|
|
- Log.e(TAG, "Cannot fit, document not rendered yet");
|
|
|
- return;
|
|
|
- }
|
|
|
- fitToWidth();
|
|
|
- jumpTo(page);
|
|
|
- }
|
|
|
-
|
|
|
- public void fitToWidth() {
|
|
|
- if (state != State.SHOWN) {
|
|
|
- Log.e(TAG, "Cannot fit, document not rendered yet");
|
|
|
- return;
|
|
|
- }
|
|
|
- zoomTo(getWidth() / optimalPageWidth);
|
|
|
- setPositionOffset(0);
|
|
|
- }
|
|
|
-
|
|
|
- public int getCurrentPage() {
|
|
|
- return currentPage;
|
|
|
- }
|
|
|
-
|
|
|
- public float getCurrentXOffset() {
|
|
|
- return currentXOffset;
|
|
|
- }
|
|
|
-
|
|
|
- public float getCurrentYOffset() {
|
|
|
- return currentYOffset;
|
|
|
- }
|
|
|
-
|
|
|
- public float toRealScale(float size) {
|
|
|
- return size / zoom;
|
|
|
- }
|
|
|
-
|
|
|
- public float toCurrentScale(float size) {
|
|
|
- return size * zoom;
|
|
|
- }
|
|
|
-
|
|
|
- public float getZoom() {
|
|
|
- return zoom;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isZooming() {
|
|
|
- return zoom != minZoom;
|
|
|
- }
|
|
|
-
|
|
|
- public float getOptimalPageWidth() {
|
|
|
- return optimalPageWidth;
|
|
|
- }
|
|
|
-
|
|
|
- public float getOptimalPageHeight() {
|
|
|
- return optimalPageHeight;
|
|
|
- }
|
|
|
-
|
|
|
- private void setDefaultPage(int defaultPage) {
|
|
|
- this.defaultPage = defaultPage;
|
|
|
- }
|
|
|
-
|
|
|
- public void resetZoom() {
|
|
|
- zoomTo(minZoom);
|
|
|
- }
|
|
|
-
|
|
|
- public void resetZoomWithAnimation() {
|
|
|
- zoomWithAnimation(minZoom);
|
|
|
- }
|
|
|
-
|
|
|
- public void zoomWithAnimation(float centerX, float centerY, float scale) {
|
|
|
- animationManager.startZoomAnimation(centerX, centerY, zoom, scale);
|
|
|
- }
|
|
|
-
|
|
|
- public void zoomWithAnimation(float scale) {
|
|
|
- animationManager.startZoomAnimation(getWidth() / 2, getHeight() / 2, zoom, scale);
|
|
|
- }
|
|
|
-
|
|
|
- private void setScrollHandle(ScrollHandle scrollHandle) {
|
|
|
- this.scrollHandle = scrollHandle;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get page number at given offset
|
|
|
- *
|
|
|
- * @param positionOffset scroll offset between 0 and 1
|
|
|
- * @return page number at given offset, starting from 0
|
|
|
- */
|
|
|
- public int getPageAtPositionOffset(float positionOffset) {
|
|
|
- int page = (int) Math.floor(getPageCount() * positionOffset);
|
|
|
- return page == getPageCount() ? page - 1 : page;
|
|
|
- }
|
|
|
-
|
|
|
- public float getMinZoom() {
|
|
|
- return minZoom;
|
|
|
- }
|
|
|
-
|
|
|
- public void setMinZoom(float minZoom) {
|
|
|
- this.minZoom = minZoom;
|
|
|
- }
|
|
|
-
|
|
|
- public float getMidZoom() {
|
|
|
- return midZoom;
|
|
|
- }
|
|
|
-
|
|
|
- public void setMidZoom(float midZoom) {
|
|
|
- this.midZoom = midZoom;
|
|
|
- }
|
|
|
-
|
|
|
- public float getMaxZoom() {
|
|
|
- return maxZoom;
|
|
|
- }
|
|
|
-
|
|
|
- public void setMaxZoom(float maxZoom) {
|
|
|
- this.maxZoom = maxZoom;
|
|
|
- }
|
|
|
-
|
|
|
- public void useBestQuality(boolean bestQuality) {
|
|
|
- this.bestQuality = bestQuality;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isBestQuality() {
|
|
|
- return bestQuality;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isSwipeVertical() {
|
|
|
- return swipeVertical;
|
|
|
- }
|
|
|
-
|
|
|
- public void setSwipeVertical(boolean swipeVertical) {
|
|
|
- this.swipeVertical = swipeVertical;
|
|
|
- }
|
|
|
-
|
|
|
- public void enableAnnotationRendering(boolean annotationRendering) {
|
|
|
- this.annotationRendering = annotationRendering;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isAnnotationRendering() {
|
|
|
- return annotationRendering;
|
|
|
- }
|
|
|
-
|
|
|
- public void enableRenderDuringScale(boolean renderDuringScale) {
|
|
|
- this.renderDuringScale = renderDuringScale;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean isAntialiasing() {
|
|
|
- return enableAntialiasing;
|
|
|
- }
|
|
|
-
|
|
|
- public void enableAntialiasing(boolean enableAntialiasing) {
|
|
|
- this.enableAntialiasing = enableAntialiasing;
|
|
|
- }
|
|
|
-
|
|
|
- int getSpacingPx() {
|
|
|
- return spacingPx;
|
|
|
- }
|
|
|
-
|
|
|
- private void setSpacing(int spacing) {
|
|
|
- this.spacingPx = Util.getDP(getContext(), spacing);
|
|
|
- }
|
|
|
-
|
|
|
- public boolean doRenderDuringScale() {
|
|
|
- return renderDuringScale;
|
|
|
- }
|
|
|
-
|
|
|
- public PdfDocument.Meta getDocumentMeta() {
|
|
|
- if (pdfDocument == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- return pdfiumCore.getDocumentMeta(pdfDocument);
|
|
|
- }
|
|
|
-
|
|
|
- public List<PdfDocument.Bookmark> getTableOfContents() {
|
|
|
- if (pdfDocument == null) {
|
|
|
- return new ArrayList<>();
|
|
|
- }
|
|
|
- return pdfiumCore.getTableOfContents(pdfDocument);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Use an asset file as the pdf source
|
|
|
- */
|
|
|
- public Configurator fromAsset(String assetName) {
|
|
|
- return new Configurator(new AssetSource(assetName));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Use a file as the pdf source
|
|
|
- */
|
|
|
- public Configurator fromFile(File file) {
|
|
|
- return new Configurator(new FileSource(file));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Use URI as the pdf source, for use with content providers
|
|
|
- */
|
|
|
- public Configurator fromUri(Uri uri) {
|
|
|
- return new Configurator(new UriSource(uri));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Use bytearray as the pdf source, documents is not saved
|
|
|
- *
|
|
|
- * @param bytes
|
|
|
- * @return
|
|
|
- */
|
|
|
- public Configurator fromBytes(byte[] bytes) {
|
|
|
- return new Configurator(new ByteArraySource(bytes));
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator fromStream(InputStream stream) {
|
|
|
- return new Configurator(new InputStreamSource(stream));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Use custom source as pdf source
|
|
|
- */
|
|
|
- public Configurator fromSource(DocumentSource docSource) {
|
|
|
- return new Configurator(docSource);
|
|
|
- }
|
|
|
-
|
|
|
- private enum State {DEFAULT, LOADED, SHOWN, ERROR}
|
|
|
-
|
|
|
- public class Configurator {
|
|
|
-
|
|
|
- private final DocumentSource documentSource;
|
|
|
-
|
|
|
- private int[] pageNumbers = null;
|
|
|
-
|
|
|
- private boolean enableSwipe = true;
|
|
|
-
|
|
|
- private boolean enableDoubletap = true;
|
|
|
-
|
|
|
- private OnDrawListener onDrawListener;
|
|
|
-
|
|
|
- private OnDrawListener onDrawAllListener;
|
|
|
-
|
|
|
- private OnLoadCompleteListener onLoadCompleteListener;
|
|
|
-
|
|
|
- private OnErrorListener onErrorListener;
|
|
|
-
|
|
|
- private OnPageChangeListener onPageChangeListener;
|
|
|
-
|
|
|
- private OnPageScrollListener onPageScrollListener;
|
|
|
-
|
|
|
- private OnRenderListener onRenderListener;
|
|
|
-
|
|
|
- private int defaultPage = 0;
|
|
|
-
|
|
|
- private boolean swipeHorizontal = false;
|
|
|
-
|
|
|
- private boolean annotationRendering = false;
|
|
|
-
|
|
|
- private String password = null;
|
|
|
-
|
|
|
- private ScrollHandle scrollHandle = null;
|
|
|
-
|
|
|
- private boolean antialiasing = true;
|
|
|
-
|
|
|
- private int spacing = 0;
|
|
|
-
|
|
|
- private Configurator(DocumentSource documentSource) {
|
|
|
- this.documentSource = documentSource;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator pages(int... pageNumbers) {
|
|
|
- this.pageNumbers = pageNumbers;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator enableSwipe(boolean enableSwipe) {
|
|
|
- this.enableSwipe = enableSwipe;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator enableDoubletap(boolean enableDoubletap) {
|
|
|
- this.enableDoubletap = enableDoubletap;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator enableAnnotationRendering(boolean annotationRendering) {
|
|
|
- this.annotationRendering = annotationRendering;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator onDraw(OnDrawListener onDrawListener) {
|
|
|
- this.onDrawListener = onDrawListener;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator onDrawAll(OnDrawListener onDrawAllListener) {
|
|
|
- this.onDrawAllListener = onDrawAllListener;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator onLoad(OnLoadCompleteListener onLoadCompleteListener) {
|
|
|
- this.onLoadCompleteListener = onLoadCompleteListener;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator onPageScroll(OnPageScrollListener onPageScrollListener) {
|
|
|
- this.onPageScrollListener = onPageScrollListener;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator onError(OnErrorListener onErrorListener) {
|
|
|
- this.onErrorListener = onErrorListener;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator onPageChange(OnPageChangeListener onPageChangeListener) {
|
|
|
- this.onPageChangeListener = onPageChangeListener;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator onRender(OnRenderListener onRenderListener) {
|
|
|
- this.onRenderListener = onRenderListener;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator defaultPage(int defaultPage) {
|
|
|
- this.defaultPage = defaultPage;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator swipeHorizontal(boolean swipeHorizontal) {
|
|
|
- this.swipeHorizontal = swipeHorizontal;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator password(String password) {
|
|
|
- this.password = password;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator scrollHandle(ScrollHandle scrollHandle) {
|
|
|
- this.scrollHandle = scrollHandle;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator enableAntialiasing(boolean antialiasing) {
|
|
|
- this.antialiasing = antialiasing;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public Configurator spacing(int spacing) {
|
|
|
- this.spacing = spacing;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public void load() {
|
|
|
- PDFView.this.recycle();
|
|
|
- PDFView.this.setOnDrawListener(onDrawListener);
|
|
|
- PDFView.this.setOnDrawAllListener(onDrawAllListener);
|
|
|
- PDFView.this.setOnPageChangeListener(onPageChangeListener);
|
|
|
- PDFView.this.setOnPageScrollListener(onPageScrollListener);
|
|
|
- PDFView.this.setOnRenderListener(onRenderListener);
|
|
|
- PDFView.this.enableSwipe(enableSwipe);
|
|
|
- PDFView.this.enableDoubletap(enableDoubletap);
|
|
|
- PDFView.this.setDefaultPage(defaultPage);
|
|
|
- PDFView.this.setSwipeVertical(!swipeHorizontal);
|
|
|
- PDFView.this.enableAnnotationRendering(annotationRendering);
|
|
|
- PDFView.this.setScrollHandle(scrollHandle);
|
|
|
- PDFView.this.enableAntialiasing(antialiasing);
|
|
|
- PDFView.this.setSpacing(spacing);
|
|
|
- PDFView.this.dragPinchManager.setSwipeVertical(swipeVertical);
|
|
|
- if (pageNumbers != null) {
|
|
|
- PDFView.this.load(documentSource, password, onLoadCompleteListener, onErrorListener, pageNumbers);
|
|
|
- } else {
|
|
|
- PDFView.this.load(documentSource, password, onLoadCompleteListener, onErrorListener);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|