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;
13 import cz.cvut.fel.dce.qrscanner.mupdf.MuPDFCore;
16 * Created by Michal Horn on 16.2.15.
18 public class PdfPageView extends View {
20 * Value with some allowed minimum and maximum.
23 * @param <T> some comparable numeric data type
25 class IntervalValue<T extends Number & Comparable<? super T>> {
31 * The lowest allowed value. All values stored in the object are
32 * higher or equal this minimum.
36 * The highest allowed value. All values stored in the object are
37 * lower or equal this maximum.
43 * Creates object that represents a value in range <minimum; maximum>.
45 * @param defValue Value to be stored in the object. Must be in the allowed range specified by other two parameters.
46 * @param minimum The lowes allowed value to be stored in the object.
47 * @param maximum The highest allowed value to be stored in the object.
48 * @throws Exception Throwed when minimal, maximal or inserted value were rejected.
50 public IntervalValue(T defValue, T minimum, T maximum) throws Exception {
53 if (!setValue(defValue)) {
54 throw new Exception("Provided value is not in the range.");
59 * Store value in the object. The value must lie between the minimum and maximum
60 * defined for the object.
61 * @param value value to be stored in the object
62 * @return true - vale was stored successfully,<br>false - value could not be inserted because it was out of range.
64 public boolean setValue(T value) {
65 if (value.compareTo(mMinimum) >= 0 && value.compareTo(mMaximum) <= 0) {
75 * Set minimum for values in the object.
76 * Last inserted value is not changed even if it is lower then the new minimum.
77 * @param min new allowed minimum for the values stored to the object.
78 * @throws Exception throwed when new minimum is higher than old maximum.
80 public void setMinimum(T min) throws Exception {
81 if (mMaximum != null && mMinimum != null && mMinimum.compareTo(mMaximum) > 0) {
82 throw new Exception("Minimum is higher then maximum.");
88 * Set maximum for values in the object.
89 * Last inserted value is not changed even if it is higher then the new maximum.
90 * @param max new allowed maximum for the values stored to the object.
91 * @throws Exception throwed when new maximum is lower than old minimum.
93 public void setMaximum(T max) throws Exception {
94 if (mMinimum != null && mMaximum != null && mMaximum.compareTo(mMinimum) < 0) {
95 throw new Exception("Maximum is lower then minimum.");
101 * Get value stored in the object
102 * @return value stored in the object.
104 public T getValue() {
109 * Get the lowest allowed value that can be stored in the object.
110 * @return minimal value
112 public T getMinimum() {
117 * Get the highest allowed value that can be stored in the object.
118 * @return maximal value
120 public T getMaximum() {
126 * Interface for events generated by the view
127 * @author Michal Horn
130 public interface SceneChange {
132 * Invoked every time when the scene should be redrawed.
135 public void onViewChanged(PdfPageView source);
138 * Invoked once the PDF page is loaded and prepared as a bitmap to be shown
141 public void onPageLoaded(PdfPageView source);
144 public static final String TAG = "PdfPageView";
145 protected Paint mPaint;
146 protected IntervalValue<Float> mZoom;
147 protected IntervalValue<Float> mXPosition;
148 protected IntervalValue<Float> mYPosition;
149 protected SceneChange mListener;
150 protected float mSceneHeight;
151 protected float mSceneWidth;
152 protected Bitmap mPdfBitmap;
153 protected File mPdfFile;
154 protected Boolean mPageLoaded;
155 private MuPDFCore mPdfCore;
156 private IntervalValue<Integer> mPage;
157 private MuPDFCore.Cookie mPdfCookie;
159 public PdfPageView(Context context, String filePath) throws Exception {
162 mPdfFile = new File(filePath);
163 if (!mPdfFile.exists()) {
164 throw new PdfViewerException("File " + filePath + " does not exist.");
166 mPaint = new Paint();
167 mPdfCore = new MuPDFCore(context, mPdfFile.getAbsolutePath());
168 mPdfCookie = mPdfCore.new Cookie();
169 if (mPdfCore == null) {
170 throw new PdfViewerException("The PDF file could not be loaded.");
172 mPage = new IntervalValue<>(0, 0, mPdfCore.countPages()-1);
173 Log.i(TAG, "Pages: " + mPage.toString());
176 public int getActualPage() {
177 return mPage.getValue();
180 public int getLastPage() {
181 return mPage.getMaximum();
184 public boolean setPage(int page) {
185 return mPage.setValue(page);
188 public void loadPage() {
189 long startTime = System.currentTimeMillis();
191 Log.i(TAG, "Loading PDF page " + getActualPage());
192 mSceneWidth = mPdfCore.getPageSize(getActualPage()).x;
193 mSceneHeight = mPdfCore.getPageSize(getActualPage()).y;
194 Log.i(TAG, "Page size: " + mSceneWidth + "x" + mSceneHeight);
195 Bitmap.Config mPdfBitmapConf = Bitmap.Config.ARGB_8888;
196 mPdfBitmap = Bitmap.createBitmap((int)mSceneWidth, (int)mSceneHeight, mPdfBitmapConf);
197 mPdfCore.drawPage(mPdfBitmap, 0, (int) mSceneWidth, (int) mSceneHeight, 0, 0, (int) mSceneWidth, (int) mSceneHeight, mPdfCookie);
199 mXPosition = new IntervalValue<>(0.0f, -mSceneWidth, mSceneWidth);
200 mYPosition = new IntervalValue<>(0.0f, -mSceneHeight, mSceneHeight);
201 mZoom = new IntervalValue<>(0.2f, 0.2f, 10.0f);
202 } catch (Exception e) {
203 Log.e(TAG, "Error in setting page dimensions.");
206 long endTime = System.currentTimeMillis();
208 Log.i(TAG, "PDF page loaded in " + Long.toString(endTime - startTime) + "ms.");
209 if (mListener != null) {
210 mListener.onPageLoaded(this);
214 public Bitmap getPageBitmap() {
221 * Set a listener of the events
222 * @param listener listener of the events
224 public void setListener(SceneChange listener) {
225 mListener = listener;
228 public void clearListener() {
233 * Zoom scene function. The scene can be zoomed in or out only in limits
234 * defined in constructor.
235 * @param deltaRatio when > 0 - zoom in,<br> when < 0 - zoom out
237 public void zoom(float deltaRatio) {
238 if (!mPageLoaded) return;
239 float ratio = mZoom.getValue();
241 if (mZoom.setValue(ratio) == true && mListener != null) {
242 mListener.onViewChanged(this);
247 * Move scene function. The scene can be moved only in the rectangle specified
248 * by limits defined in constructor.
249 * @param x change of the x coordinate relatively to the actual position
250 * @param y change of the y coordinate relatively to the actual position
252 public void move(float x, float y) {
253 if (!mPageLoaded) return;
254 boolean xWasSet = mXPosition.setValue(mXPosition.getValue() + x/mZoom.getValue()*2.2f);
255 boolean yWasSet = mYPosition.setValue(mYPosition.getValue() + y/mZoom.getValue()*2.2f);
257 if ((xWasSet || yWasSet) && mListener != null) {
258 mListener.onViewChanged(this);
262 * @return actual scene zoom ratio
264 public float getZoomRatio() {
265 return mZoom.getValue();
269 * @return actual scene position on X axis
271 public float getXPosition() {
272 return mXPosition.getValue();
276 * @return actual scene positoin on Y axis
278 public float getYPosition() {
279 return mYPosition.getValue();
283 * @return how tall the scene is
285 public float getSceneHeight() {
290 * @return how wide the scene is
292 public float getSceneWidth() {
297 protected void onDraw(Canvas c) {
298 if (!mPageLoaded) return;
304 public void draw(Canvas c) {
305 if (!mPageLoaded) return;
306 c.translate(c.getWidth()/2, c.getHeight()/2);
307 c.scale(mZoom.getValue(), mZoom.getValue());
308 c.translate(-mXPosition.getValue(), -mYPosition.getValue());
309 c.drawBitmap(mPdfBitmap, 0, 0, mPaint);