1 package cz.cvut.fel.dce.qrscanner.pdfviewer;
3 import android.content.Context;
4 import android.graphics.Bitmap;
5 import android.graphics.Canvas;
6 import android.graphics.Paint;
7 import android.util.Log;
8 import android.view.View;
12 import cz.cvut.fel.dce.qrscanner.mupdf.MuPDFCore;
15 * View for viewing PDF page.
17 * This view is designed to be used in two ways.
18 * 1) To view the PDF page itself on the device, which is supported by an interface for scrolling, zooming, switching pages and loading pages.
19 * 2) To load the PDF page to a bitmap, which can be accessed in the program by calling getPageBitmap() method.
21 public class PdfPageView extends View {
23 * Value with some allowed minimum and maximum.
26 * @param <T> some comparable numeric data type
28 class IntervalValue<T extends Number & Comparable<? super T>> {
34 * The lowest allowed value. All values stored in the object are
35 * higher or equal this minimum.
39 * The highest allowed value. All values stored in the object are
40 * lower or equal this maximum.
46 * Creates object that represents a value in range <minimum; maximum>.
48 * @param defValue Value to be stored in the object. Must be in the allowed range specified by other two parameters.
49 * @param minimum The lowes allowed value to be stored in the object.
50 * @param maximum The highest allowed value to be stored in the object.
51 * @throws Exception Throwed when minimal, maximal or inserted value were rejected.
53 public IntervalValue(T defValue, T minimum, T maximum) throws Exception {
56 if (!setValue(defValue)) {
57 throw new Exception("Provided value is not in the range.");
62 * Store value in the object. The value must lie between the minimum and maximum
63 * defined for the object.
64 * @param value value to be stored in the object
65 * @return true - vale was stored successfully,<br>false - value could not be inserted because it was out of range.
67 public boolean setValue(T value) {
68 if (value.compareTo(mMinimum) >= 0 && value.compareTo(mMaximum) <= 0) {
78 * Set minimum for values in the object.
79 * Last inserted value is not changed even if it is lower then the new minimum.
80 * @param min new allowed minimum for the values stored to the object.
81 * @throws Exception throwed when new minimum is higher than old maximum.
83 public void setMinimum(T min) throws Exception {
84 if (mMaximum != null && mMinimum != null && mMinimum.compareTo(mMaximum) > 0) {
85 throw new Exception("Minimum is higher then maximum.");
91 * Set maximum for values in the object.
92 * Last inserted value is not changed even if it is higher then the new maximum.
93 * @param max new allowed maximum for the values stored to the object.
94 * @throws Exception throwed when new maximum is lower than old minimum.
96 public void setMaximum(T max) throws Exception {
97 if (mMinimum != null && mMaximum != null && mMaximum.compareTo(mMinimum) < 0) {
98 throw new Exception("Maximum is lower then minimum.");
104 * Get value stored in the object
105 * @return value stored in the object.
107 public T getValue() {
112 * Get the lowest allowed value that can be stored in the object.
113 * @return minimal value
115 public T getMinimum() {
120 * Get the highest allowed value that can be stored in the object.
121 * @return maximal value
123 public T getMaximum() {
129 * Interface for events generated by the view
130 * @author Michal Horn
133 public interface SceneChange {
135 * Invoked every time when the scene should be redrawed.
138 public void onViewChanged(PdfPageView source);
141 * Invoked once the PDF page is loaded and prepared as a bitmap to be shown
144 public void onPageLoaded(PdfPageView source);
147 public static final String TAG = "PdfPageView";
148 protected Paint mPaint;
149 protected IntervalValue<Float> mZoom;
150 protected IntervalValue<Float> mXPosition;
151 protected IntervalValue<Float> mYPosition;
152 protected SceneChange mListener;
153 protected float mSceneHeight;
154 protected float mSceneWidth;
155 protected Bitmap mPdfBitmap;
156 protected File mPdfFile;
157 protected Boolean mPageLoaded;
158 private MuPDFCore mPdfCore;
159 private IntervalValue<Integer> mPage;
160 private MuPDFCore.Cookie mPdfCookie;
162 public PdfPageView(Context context, String filePath) throws Exception {
165 mPdfFile = new File(filePath);
166 if (!mPdfFile.exists()) {
167 throw new PdfViewerException("File " + filePath + " does not exist.");
169 mPaint = new Paint();
170 mPdfCore = new MuPDFCore(context, mPdfFile.getAbsolutePath());
171 mPdfCookie = mPdfCore.new Cookie();
172 if (mPdfCore == null) {
173 throw new PdfViewerException("The PDF file could not be loaded.");
175 mPage = new IntervalValue<>(0, 0, mPdfCore.countPages()-1);
176 Log.i(TAG, "Pages: " + mPage.toString());
180 * Get actual page. The actual page can be selected by setPage() method.
181 * @return actual page.
183 public int getActualPage() {
184 return mPage.getValue();
188 * Get total number of pages in the document
189 * @return total number of pages
191 public int getLastPage() {
192 return mPage.getMaximum();
195 public boolean setPage(int page) {
196 return mPage.setValue(page);
200 * Load PDF and convert selected page to a bitmap.
202 * The page of the PDF document can be selected by setPage() method.
203 * This method can take significant time to finish. Consider calling it in separated thread.
205 public void loadPage() {
206 long startTime = System.currentTimeMillis();
208 Log.i(TAG, "Loading PDF page " + getActualPage());
209 mSceneWidth = mPdfCore.getPageSize(getActualPage()).x;
210 mSceneHeight = mPdfCore.getPageSize(getActualPage()).y;
211 Log.i(TAG, "Page size: " + mSceneWidth + "x" + mSceneHeight);
212 Bitmap.Config mPdfBitmapConf = Bitmap.Config.ARGB_8888;
213 mPdfBitmap = Bitmap.createBitmap((int)mSceneWidth, (int)mSceneHeight, mPdfBitmapConf);
214 mPdfCore.drawPage(mPdfBitmap, 0, (int) mSceneWidth, (int) mSceneHeight, 0, 0, (int) mSceneWidth, (int) mSceneHeight, mPdfCookie);
216 mXPosition = new IntervalValue<>(640.0f/2, -mSceneWidth+640.0f/2, mSceneWidth+640.0f/2);
217 mYPosition = new IntervalValue<>(360.0f/2, -mSceneHeight+360.0f/2, mSceneHeight+360.0f/2);
218 mZoom = new IntervalValue<>(1.0f, 0.2f, 2.0f);
219 } catch (Exception e) {
220 Log.e(TAG, "Error in setting page dimensions: " + e.getMessage());
223 long endTime = System.currentTimeMillis();
225 Log.i(TAG, "PDF page loaded in " + Long.toString(endTime - startTime) + "ms.");
226 if (mListener != null) {
227 mListener.onPageLoaded(this);
232 * Get PDF page bitmap
233 * @return Bitmap of the PDF page or null, if it has not been yet loaded.
235 public Bitmap getPageBitmap() {
240 * Set a listener of the events
241 * @param listener listener of the events
243 public void setListener(SceneChange listener) {
244 mListener = listener;
250 public void clearListener() {
255 * Zoom scene function. The scene can be zoomed in or out only in limits
256 * defined in constructor.
257 * @param deltaRatio when > 0 - zoom in,<br> when < 0 - zoom out
259 public void zoom(float deltaRatio) {
260 if (!mPageLoaded) return;
261 float ratio = mZoom.getValue();
263 if (mZoom.setValue(ratio) && mListener != null) {
264 mListener.onViewChanged(this);
269 * Move scene function. The scene can be moved only in the rectangle specified
270 * by limits defined in constructor.
271 * @param x change of the x coordinate relatively to the actual position
272 * @param y change of the y coordinate relatively to the actual position
274 public void move(float x, float y) {
275 if (!mPageLoaded) return;
276 boolean xWasSet = mXPosition.setValue(mXPosition.getValue() + x/mZoom.getValue()*2.2f);
277 boolean yWasSet = mYPosition.setValue(mYPosition.getValue() + y/mZoom.getValue()*2.2f);
279 if ((xWasSet || yWasSet) && mListener != null) {
280 mListener.onViewChanged(this);
284 * @return actual scene zoom ratio
286 public float getZoomRatio() {
287 return mZoom.getValue();
291 * @return actual scene position on X axis
293 public float getXPosition() {
294 return mXPosition.getValue();
298 * @return actual scene positoin on Y axis
300 public float getYPosition() {
301 return mYPosition.getValue();
305 * @return how tall the scene is
307 public float getSceneHeight() {
312 * @return how wide the scene is
314 public float getSceneWidth() {
319 protected void onDraw(Canvas c) {
320 if (!mPageLoaded) return;
326 public void draw(Canvas c) {
327 if (!mPageLoaded) return;
328 c.translate(c.getWidth() / 2, c.getHeight() / 2);
329 c.scale(mZoom.getValue(), mZoom.getValue());
330 c.translate(-mXPosition.getValue(), -mYPosition.getValue());
331 c.drawBitmap(mPdfBitmap, 0, 0, mPaint);