_pages = null;
62 | // async part
63 | private Handler _handler = new Handler();
64 | // background thread
65 | private Thread _thread = null;
66 | // exception if happened
67 | private Exception _error = null;
68 |
69 | /**
70 | * the rendered dimensions in {@code Pixels} of the {@link AbstractViewRenderer}
71 | */
72 | private int _renderWidth = 0;
73 | private int _renderHeight = 0;
74 |
75 | public PdfDocument(Context ctx) {
76 | setContext(ctx);
77 |
78 | _pages = new ArrayList<>();
79 | _pages_rendered = new ArrayList<>();
80 | }
81 |
82 | /**
83 | * the document orientation, contains aspect ratio info
84 | */
85 | public enum A4_MODE {
86 | PORTRAIT(0.707f, A4.PORTRAIT), LANDSCAPE(1.41f, A4.LANDSCAPE);
87 |
88 | private float _ar;
89 | private float[] _a4 = null;
90 |
91 | A4_MODE(float ar, float[] mode) {
92 | _ar = ar;
93 | _a4 = mode;
94 | }
95 |
96 | /**
97 | *
98 | * @return the aspect ratio of the mode {@code ar = width/height}
99 | */
100 | public float aspectRatio() { return _ar; }
101 |
102 | /**
103 | *
104 | * @return the corresponding mode in pdfjet lib
105 | */
106 | public float[] A4() {
107 | return _a4;
108 | }
109 | }
110 |
111 | /**
112 | * add a page with a custom class view renderer. please note that the bitmap of the view will be recycled.
113 | *
114 | * @param page a view renderer instance
115 | *
116 | * @see com.hendrix.pdfmyxml.viewRenderer.AbstractViewRenderer
117 | */
118 | public void addPage(AbstractViewRenderer page) {
119 | if(_inflateOnMainThread)
120 | renderView(page);
121 | else
122 | _pages.add(page);
123 | }
124 |
125 | /**
126 | * add a page with a rendered bitmap. the bitmap will not be recycled, it's up to
127 | * the user to recycle.
128 | *
129 | * @param page a bitmap
130 | *
131 | */
132 | public void addPage(Bitmap page) {
133 | ByteArrayInputStream stream = BitmapUtils.bitmapToPngInputStream(page);
134 | _pages_rendered.add(stream);
135 | }
136 |
137 | /**
138 | * clear all of the pages and rendered pages
139 | */
140 | public void clearPages()
141 | {
142 | _pages.clear();
143 | _pages_rendered.clear();
144 | }
145 |
146 | /**
147 | * set the rendered width in {@code Pixels} of the {@link AbstractViewRenderer}
148 | *
149 | * @param value width in {@code Pixels}
150 | */
151 | public void setRenderWidth(int value)
152 | {
153 | _renderWidth = value;
154 | }
155 |
156 | /**
157 | * set the rendered height in {@code Pixels} of the {@link AbstractViewRenderer}
158 | *
159 | * @param value height in {@code Pixels}
160 | */
161 | public void setRenderHeight(int value)
162 | {
163 | _renderHeight = value;
164 | }
165 |
166 | /**
167 | * set the context
168 | *
169 | * @param ctx the context
170 | */
171 | public void setContext(Context ctx) {
172 | _ctx = ctx;
173 | }
174 |
175 | /**
176 | *
177 | * @return get the orientation
178 | *
179 | * @see PdfDocument.A4_MODE
180 | */
181 | public A4_MODE getOrientation() {
182 | return _orientation;
183 | }
184 |
185 | /**
186 | * set the orientation
187 | *
188 | * @param orientation {@code {PORTRAIT, LANDSCAPE}}
189 | *
190 | * @see PdfDocument.A4_MODE
191 | */
192 | public void setOrientation(A4_MODE orientation) {
193 | _orientation = orientation;
194 | }
195 |
196 | /**
197 | * set the text message for the progress dialog
198 | *
199 | * @param resId a string resource identifier
200 | */
201 | public void setProgressMessage(int resId) {
202 | _txtProgressMessage = _ctx.getString(resId);
203 | }
204 |
205 | /**
206 | * set the text title for the progress dialog
207 | *
208 | * @param resId a string resource identifier
209 | */
210 | public void setProgressTitle(int resId) {
211 | _txtProgressTitle = _ctx.getString(resId);
212 | }
213 |
214 | /**
215 | *
216 | * @return the pdf file name
217 | */
218 | public String getFileName() {
219 | return file_name;
220 | }
221 |
222 | /**
223 | * set the file name
224 | *
225 | * @param fileName the pdf file name
226 | */
227 | public void setFileName(String fileName) {
228 | file_name = fileName;
229 | }
230 |
231 | /**
232 | * set the file save directory
233 | *
234 | * @param directory the pdf file save directory
235 | */
236 | public void setSaveDirectory(File directory) {
237 | save_directory = directory;
238 | }
239 |
240 | /**
241 | *
242 | * @return the pdf {@link java.io.File} if available
243 | */
244 | public File getFile() {
245 | return file;
246 | }
247 |
248 | /**
249 | * does the a PDF is generating now?
250 | *
251 | * @return {@code true/false}
252 | */
253 | public boolean isWorking() {
254 | return _isWorking;
255 | }
256 |
257 | /**
258 | * set the inflation of views on the Main thread.
259 | * use it, in case you are having inflation errors.
260 | * by default, and even though not recommended by Google, the
261 | * inflation and rendering to bitmaps happens on the background thread.
262 | *
263 | * @param enabled {@code true/false}
264 | */
265 | public void setInflateOnMainThread(boolean enabled) {
266 | _inflateOnMainThread = enabled;
267 | }
268 |
269 | /**
270 | * create the pdf and render according to report types and a time frame
271 | *
272 | * @param window an {@link android.app.Activity} in which to display a progress dialog (Optional)
273 | */
274 | public void createPdf(Context window) {
275 | if (isWorking())
276 | return;
277 |
278 | Resources res = _ctx.getResources();
279 |
280 | if (window != null) {
281 | if(_ringProgressDialog !=null) {
282 | _ringProgressDialog.dismiss();
283 | }
284 |
285 | _ringProgressDialog = ProgressDialog.show(window, _txtProgressTitle, _txtProgressMessage, true);
286 |
287 | if(!_ringProgressDialog.isShowing())
288 | _ringProgressDialog.show();
289 | }
290 |
291 | _thread = new Thread(new Runnable() {
292 | @Override
293 | public void run() {
294 | _isWorking = true;
295 |
296 | //_pages_rendered.clear();
297 |
298 | if(!_inflateOnMainThread) {
299 | for (AbstractViewRenderer view : _pages) {
300 | Log.i(TAG_PDF_MY_XML, "render page");
301 | renderView(view);
302 | }
303 | }
304 |
305 | internal_generatePdf();
306 |
307 | Log.i(TAG_PDF_MY_XML, "pdf 1");
308 |
309 | clearPages();
310 |
311 | Log.i(TAG_PDF_MY_XML, "pdf 2");
312 |
313 | // go back to the main thread
314 | _handler.post(new Runnable() {
315 | @Override
316 | public void run() {
317 | if(_ringProgressDialog !=null)
318 | _ringProgressDialog.dismiss();
319 |
320 | if(_listener != null) {
321 | if(_error != null)
322 | _listener.onError(_error);
323 | else
324 | _listener.onComplete(file);
325 | }
326 |
327 | Log.i(TAG_PDF_MY_XML, "pdf 3");
328 |
329 | release();
330 | }
331 | });
332 |
333 | }
334 |
335 | });
336 |
337 | _thread.setPriority(Thread.MAX_PRIORITY);
338 | _thread.start();
339 | }
340 |
341 | private Callback _listener = null;
342 |
343 | /**
344 | * set a listener for the PDF generation events
345 | *
346 | * @param listener a {@link com.hendrix.pdfmyxml.PdfDocument.Callback}
347 | */
348 | public void setListener(Callback listener) {
349 | _listener = listener;
350 | }
351 |
352 | private void internal_generatePdf()
353 | {
354 | String name = (file_name == null) ? sDefault_Filename_prefix + System.currentTimeMillis() : file_name;
355 |
356 | file_name = name + ".pdf";
357 | //File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "pdf");
358 | File dir = save_directory == null ? _ctx.getExternalFilesDir(null) : save_directory;
359 |
360 | file = new File(dir, file_name);
361 | _error = null;
362 |
363 | try {
364 | FileOutputStream fos = new FileOutputStream(file);//"Example_01.pdf");
365 | PDF pdf = new com.pdfjet.PDF(fos);
366 |
367 | Page page;
368 | Image image;
369 | float ar;
370 |
371 | for (InputStream inputStream : _pages_rendered) {
372 | page = new Page(pdf, _orientation.A4());
373 | image = new Image(pdf, inputStream, ImageType.PNG);
374 |
375 | Log.i(TAG_PDF_MY_XML, "add page");
376 |
377 | inputStream.close(); //doesn't do anything in byte array
378 |
379 | ar = page.getWidth() / image.getWidth();
380 |
381 | image.scaleBy(ar);
382 |
383 | image.drawOn(page);
384 | }
385 |
386 | pdf.flush();
387 | fos.close();
388 | }
389 | catch (Exception exc) {
390 | _error = exc;
391 | }
392 |
393 | }
394 |
395 | /**
396 | * render the view
397 | *
398 | * @param page {@link com.hendrix.pdfmyxml.viewRenderer.AbstractViewRenderer} instance
399 | */
400 | private void renderView(AbstractViewRenderer page) {
401 | page.attachContext(_ctx);
402 |
403 | if(_renderWidth==0 || _renderHeight==0)
404 | if(Build.VERSION.SDK_INT <= 17)
405 | Log.e(TAG_PDF_MY_XML, "_renderWidth,_renderHeight==0 on API <= 17 can lead to bad behaviour with RelativeLayout and may crash, please use explicit values!!!");
406 |
407 | Bitmap bmp = page.render(_renderWidth, _renderHeight);
408 | ByteArrayInputStream stream = BitmapUtils.bitmapToPngInputStream(bmp);
409 |
410 | page.disposeBitmap();
411 |
412 | _pages_rendered.add(stream);
413 | }
414 |
415 | /**
416 | * release this class for future usage
417 | */
418 | private void release() {
419 | _pages.clear();
420 | _pages_rendered.clear();
421 |
422 | _error = null;
423 | _isWorking = false;
424 |
425 | file_name = null;
426 | file = null;
427 |
428 | if(_ringProgressDialog != null) {
429 | _ringProgressDialog.dismiss();
430 | _ringProgressDialog = null;
431 | }
432 | }
433 |
434 | /**
435 | * dispose the item
436 | */
437 | @Override
438 | public void dispose() {
439 | release();
440 |
441 | _listener = null;
442 | _handler = null;
443 | _thread = null;
444 | _ringProgressDialog = null;
445 | }
446 |
447 | /**
448 | * callback interface for PDF creation
449 | */
450 | public interface Callback {
451 |
452 | /**
453 | * successful completion of pdf
454 | *
455 | * @param file the file
456 | */
457 | void onComplete(File file);
458 |
459 | /**
460 | * error creating the PDF
461 | */
462 | void onError(Exception e);
463 | }
464 |
465 | /**
466 | * a mutable builder for document
467 | */
468 | public static class Builder {
469 | private PdfDocument _doc = null;
470 |
471 | public Builder(Context ctx) {
472 | _doc = new PdfDocument(ctx);
473 | }
474 |
475 | /**
476 | * create the pdf document instance. afterwards, use {@link com.hendrix.pdfmyxml.PdfDocument#createPdf(Context)}
477 | *
478 | * @return a {@link com.hendrix.pdfmyxml.PdfDocument}
479 | */
480 | public PdfDocument create() {
481 | return _doc;
482 | }
483 |
484 | /**
485 | * add a page with a custom class view renderer. please note that the bitmap of the view will be recycled.
486 | *
487 | * @param page a view renderer instance
488 | *
489 | * @see com.hendrix.pdfmyxml.viewRenderer.AbstractViewRenderer
490 | */
491 | public Builder addPage(AbstractViewRenderer page) {
492 | _doc.addPage(page);
493 |
494 | return this;
495 | }
496 |
497 | /**
498 | * set the rendered width in {@code Pixels} of the {@link AbstractViewRenderer}
499 | *
500 | * @param value width in {@code Pixels}
501 | */
502 | public Builder renderWidth(int value)
503 | {
504 | _doc.setRenderWidth(value);
505 |
506 | return this;
507 | }
508 |
509 | /**
510 | * set the rendered height in {@code Pixels} of the {@link AbstractViewRenderer}
511 | *
512 | * @param value height in {@code Pixels}
513 | */
514 | public Builder renderHeight(int value)
515 | {
516 | _doc.setRenderHeight(value);
517 |
518 | return this;
519 | }
520 |
521 | /**
522 | * add a page with a rendered bitmap. the bitmap will not be recycled, it's up to
523 | * the user to recycle.
524 | *
525 | * @param page a bitmap
526 | *
527 | */
528 | public Builder addPage(Bitmap page) {
529 | _doc.addPage(page);
530 |
531 | return this;
532 | }
533 |
534 | /**
535 | * set the file name
536 | *
537 | * @param name the pdf file name
538 | */
539 | public Builder filename(String name) {
540 | _doc.setFileName(name);
541 |
542 | return this;
543 | }
544 |
545 | /**
546 | * set the file save directory
547 | *
548 | * @param directory the pdf file save directory
549 | */
550 | public Builder saveDirectory(File directory) {
551 | _doc.setSaveDirectory(directory);
552 |
553 | return this;
554 | }
555 |
556 | /**
557 | * set the inflation of views on the Main thread.
558 | * use it, in case you are having inflation errors.
559 | * by default, and even though not recommended by Google, the
560 | * inflation and rendering to bitmaps happens on the background thread.
561 | *
562 | * @param enabled {@code true/false}
563 | */
564 | public Builder inflateOnMainThread(boolean enabled) {
565 | _doc.setInflateOnMainThread(enabled);
566 |
567 | return this;
568 | }
569 |
570 | /**
571 | * set a listener for the PDF generation events
572 | *
573 | * @param listener a {@link com.hendrix.pdfmyxml.PdfDocument.Callback}
574 | */
575 | public Builder listener(Callback listener) {
576 | _doc.setListener(listener);
577 |
578 | return this;
579 | }
580 |
581 | /**
582 | * set the orientation
583 | *
584 | * @param mode {@code {PORTRAIT, LANDSCAPE}}
585 | *
586 | * @see PdfDocument.A4_MODE
587 | */
588 | public Builder orientation(A4_MODE mode) {
589 | _doc.setOrientation(mode);
590 |
591 | return this;
592 | }
593 |
594 | /**
595 | * set the text message for the progress dialog
596 | *
597 | * @param resId a string resource identifier
598 | */
599 | public Builder progressMessage(int resId) {
600 | _doc.setProgressMessage(resId);
601 |
602 | return this;
603 | }
604 |
605 | /**
606 | * set the text title for the progress dialog
607 | *
608 | * @param resId a string resource identifier
609 | */
610 | public Builder progressTitle(int resId) {
611 | _doc.setProgressTitle(resId);
612 |
613 | return this;
614 | }
615 |
616 | }
617 |
618 | }
619 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/interfaces/IData.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.interfaces;
2 |
3 | public interface IData {
4 |
5 | void setData(Object data);
6 | Object getData();
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/interfaces/IDisposable.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.interfaces;
2 |
3 | public interface IDisposable {
4 | /**
5 | * dispose the item
6 | */
7 | void dispose();
8 | }
9 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/interfaces/IId.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.interfaces;
2 |
3 | public interface IId {
4 |
5 | void setId(String id);
6 | String getId();
7 | }
8 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/utils/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.utils;
2 |
3 | import android.content.res.Resources;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Bitmap.CompressFormat;
6 | import android.graphics.Bitmap.Config;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.BitmapShader;
9 | import android.graphics.Canvas;
10 | import android.graphics.Color;
11 | import android.graphics.Matrix;
12 | import android.graphics.Paint;
13 | import android.graphics.PorterDuff.Mode;
14 | import android.graphics.PorterDuffXfermode;
15 | import android.graphics.Rect;
16 | import android.graphics.Shader.TileMode;
17 | import android.util.DisplayMetrics;
18 | import android.util.TypedValue;
19 |
20 | import java.io.ByteArrayInputStream;
21 | import java.io.ByteArrayOutputStream;
22 |
23 | /**
24 | * @author Tomer Shalev
25 | */
26 | @SuppressWarnings("UnusedDeclaration")
27 | public final class BitmapUtils
28 | {
29 | private static DisplayMetrics _dm;
30 | private static Resources _resources;
31 |
32 | private BitmapUtils() {
33 | }
34 |
35 | public static void init(Resources resources)
36 | {
37 | _resources = resources;
38 | _dm = _resources.getDisplayMetrics();
39 | }
40 |
41 | public enum ScaleMode {
42 | STRETCH, LETTERBOX, ZOOM, NONE
43 | }
44 |
45 | /**
46 | * not the regular bitmap resizer. much smarter with resizing and cropping options and resizing with proportions.
47 | * @param bmSrc the source bitmap
48 | * @param rectDest rectangle, you need to pass at least width or height or both. For example, specify only width to scale, and height will be calc dynamically
49 | * @param scaleMode - one of the enums: ScaleMode {STRETCH,LETTERBOX,ZOOM,NONE}
50 | * @param fitBitmapResult effective cropping of dead pixels, applied only when ScaleMode.LETTERBOX
is used
51 | * @param scaleConditionally scale only if rectDest < bmSrc.rect
, i.e rectDest
is contained inside bmSrc.rect
52 | * @return resulting bitmap
53 | *
54 | * TODO:
55 | * - add translation for zoom, center images
56 | * - requires QA (i didnt have time)
57 | *
58 | */
59 | static public Bitmap resizeBitmap(Bitmap bmSrc, Rect rectDest, ScaleMode scaleMode, boolean fitBitmapResult,
60 | boolean scaleConditionally, boolean recycleBmpSource)
61 | {
62 | Matrix mat = new Matrix();
63 | Bitmap bmResult;
64 |
65 | int wOrig = bmSrc.getWidth();
66 | int hOrig = bmSrc.getHeight();
67 |
68 | int wScaleTo = rectDest.width();
69 | int hScaleTo = rectDest.height();
70 |
71 | if(wScaleTo==0 && hScaleTo==0)
72 | throw new Error("resizeBitmap: need at least $scaleTo.width or $scaleTo.height");
73 |
74 | float arW = (wScaleTo != 0) ? (float)wScaleTo / (float)wOrig : Float.NaN;
75 | float arH = (hScaleTo != 0) ? (float)hScaleTo / (float)hOrig : Float.NaN;
76 |
77 | arW = Float.isNaN(arW) ? arH: arW;
78 | arH = Float.isNaN(arH) ? arW: arH;
79 |
80 | float arMin = Math.min(arW, arH);
81 | float arMax = Math.max(arW, arH);
82 |
83 | wScaleTo = (wScaleTo != 0) ? wScaleTo : (int)(((float)wOrig)*arW);
84 | hScaleTo = (hScaleTo != 0) ? hScaleTo : (int)(((float)hOrig)*arH);
85 |
86 | if(scaleConditionally) {
87 | boolean isScaleConditionally = (wScaleTo >= bmSrc.getWidth()) && (hScaleTo >= bmSrc.getHeight());
88 | if(isScaleConditionally)
89 | return Bitmap.createBitmap(bmSrc);
90 | }
91 |
92 | bmResult = Bitmap.createBitmap(wScaleTo, hScaleTo, Config.ARGB_8888);
93 |
94 | Canvas canvas = new Canvas(bmResult);
95 |
96 | switch(scaleMode)
97 | {
98 | case STRETCH:
99 | {
100 | mat.reset();
101 | mat.postScale(arW, arH);
102 |
103 | canvas.drawBitmap(bmSrc, mat, null);
104 |
105 | break;
106 | }
107 | case LETTERBOX:
108 | {
109 | if(fitBitmapResult) {
110 | if(bmResult != null)
111 | bmResult.recycle();
112 |
113 | wScaleTo = (int)((float)wOrig * arMin);
114 | hScaleTo = (int)((float)hOrig * arMin);
115 | bmResult = Bitmap.createBitmap(wScaleTo, hScaleTo, Config.ARGB_8888);
116 |
117 | canvas.setBitmap(bmResult);
118 | }
119 |
120 | mat.reset();
121 | mat.postScale(arMin, arMin);
122 |
123 | canvas.drawBitmap(bmSrc, mat, null);
124 |
125 | break;
126 | }
127 | case ZOOM:
128 | {
129 | mat.reset();
130 | mat.postScale(arMax, arMax);
131 |
132 | canvas.drawBitmap(bmSrc, mat, null);
133 |
134 | break;
135 | }
136 | case NONE:
137 | {
138 | mat.reset();
139 |
140 | canvas.drawBitmap(bmSrc, mat, null);
141 |
142 | break;
143 | }
144 |
145 | }
146 |
147 | if(recycleBmpSource)
148 | bmSrc.recycle();
149 |
150 | return bmResult;
151 | }
152 |
153 | /**
154 | *
155 | * @param picturePath complete path name for the file to be decoded.
156 | * @return Bitmap instance
157 | */
158 | public static Bitmap pathToBitmap(String picturePath)
159 | {
160 | BitmapFactory.Options opts = new BitmapFactory.Options();
161 |
162 | opts.inDither = false; //Disable Dithering mode
163 | opts.inPurgeable = true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
164 | opts.inInputShareable = true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
165 | opts.inTempStorage = new byte[32 * 1024];
166 |
167 | return BitmapFactory.decodeFile(picturePath, opts);
168 | }
169 |
170 | /**
171 | * cut a circle from a bitmap
172 | *
173 | * @param picturePath complete path name for the file.
174 | * @param destCube the cube dimension of dest bitmap
175 | * @return the bitmap
176 | */
177 | public static Bitmap cutCircleFromBitmap(String picturePath, int destCube)
178 | {
179 | BitmapFactory.Options opts = new BitmapFactory.Options();
180 |
181 | opts.inDither = false; //Disable Dithering mode
182 | opts.inPurgeable = true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
183 | opts.inInputShareable = true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
184 | opts.inTempStorage = new byte[32 * 1024];
185 |
186 | Bitmap bitmapImg = BitmapFactory.decodeFile(picturePath, opts);
187 |
188 | int cube = destCube;
189 |
190 | if(bitmapImg == null)
191 | return null;
192 |
193 | int smallest = Math.min(bitmapImg.getWidth(), bitmapImg.getHeight());
194 |
195 | Bitmap output = Bitmap.createBitmap(cube, cube, Config.ARGB_8888);
196 | Canvas canvas = new Canvas(output);
197 |
198 | final int color = 0xff424242;
199 | final Paint paint = new Paint();
200 |
201 | int left = (int) ((bitmapImg.getWidth() - smallest) * 0.5);
202 | int top = (int) ((bitmapImg.getHeight() - smallest) * 0.5);
203 |
204 | final Rect rectSrc = new Rect(left, top, left + smallest, top + smallest);
205 | final Rect rectDest = new Rect(0, 0, cube, cube);
206 |
207 | paint.setAntiAlias(true);
208 |
209 | canvas.drawARGB(0, 0, 0, 0);
210 |
211 | paint.setColor(color);
212 |
213 | canvas.drawCircle(cube / 2, cube / 2, cube / 2, paint);
214 |
215 | paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
216 |
217 | canvas.drawBitmap(bitmapImg, rectSrc, rectDest, paint);
218 |
219 | bitmapImg.recycle();
220 |
221 | return output;
222 | }
223 |
224 | /**
225 | * Bitmap into compressed PNG
226 | *
227 | * @param image the Bitmap
228 | * @return byte array of PNG
229 | */
230 | public static byte[] bitmapToPng(final Bitmap image)
231 | {
232 | if (image == null)
233 | return null;
234 |
235 | ByteArrayOutputStream ba = new ByteArrayOutputStream();
236 |
237 | if (image.compress(CompressFormat.PNG, 100, ba))
238 | return ba.toByteArray();
239 | else
240 | return null;
241 | }
242 |
243 | /**
244 | * Bitmap into compressed PNG as InputStream object
245 | *
246 | * @param image the Bitmap
247 | * @return compressed PNG as InputStream object
248 | */
249 | public static ByteArrayInputStream bitmapToPngInputStream(final Bitmap image)
250 | {
251 | return new ByteArrayInputStream(bitmapToPng(image));
252 | }
253 |
254 | /**
255 | * Bitmap into compressed jpeg
256 | *
257 | * @param image the Bitmap
258 | * @param quality quality of compression
259 | * @return byte array of jpeg
260 | */
261 | public static byte[] bitmapToJpg(final Bitmap image, final int quality)
262 | {
263 | if (image == null)
264 | return null;
265 |
266 | ByteArrayOutputStream ba = new ByteArrayOutputStream();
267 |
268 | if (image.compress(CompressFormat.JPEG, quality, ba))
269 | return ba.toByteArray();
270 | else
271 | return null;
272 | }
273 |
274 | /**
275 | * raw byteArray into Bitmap
276 | *
277 | * @param image the byteArray
278 | * @return Bitmap
279 | */
280 | public static Bitmap imgToBitmap(final byte[] image)
281 | {
282 | if (image == null)
283 | return null;
284 |
285 | return BitmapFactory.decodeByteArray(image, 0, image.length);
286 | }
287 |
288 | /**
289 | * dp to pixels
290 | * @param val dp
291 | * @return pixels
292 | */
293 | public static int dpToPx(float val)
294 | {
295 | return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, val, _dm);
296 | }
297 |
298 | /**
299 | * mask bitmap
300 | * @param src todo
301 | * @param mask todo
302 | * @param dst todo
303 | * @param dstCanvas todo
304 | * @param paint todo
305 | * @param paintMode todo
306 | * @return todo
307 | */
308 | public static Bitmap maskBitmap(Bitmap src, Bitmap mask, Bitmap dst, Canvas dstCanvas, Paint paint, PorterDuffXfermode paintMode)
309 | {
310 | if (dst == null)
311 | dst = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
312 |
313 | if (dstCanvas == null)
314 | dstCanvas = new Canvas(dst);
315 |
316 | if (paintMode == null)
317 | paintMode = new PorterDuffXfermode(Mode.DST_IN);
318 |
319 | if (paint == null) {
320 | paint = new Paint(Paint.ANTI_ALIAS_FLAG);
321 | paint.setXfermode(paintMode);
322 | }
323 |
324 | dstCanvas.drawBitmap(src, 0, 0, null);
325 | dstCanvas.drawBitmap(mask, 0, 0, paint);
326 |
327 | paint.setXfermode(null);
328 |
329 | return dst;
330 | }
331 |
332 | /**
333 | * mask a bitmap
334 | *
335 | * @param src the bitmap to mask
336 | * @param drawableResId a resource id of the mask
337 | * @return a bitmap
338 | */
339 | public static Bitmap maskBitmap(Bitmap src, int drawableResId)
340 | {
341 | Bitmap mask = BitmapFactory.decodeResource(_resources, drawableResId);
342 |
343 | return maskBitmap(src, mask, null, null, null, null);
344 | }
345 |
346 | /**
347 | * create a circle from cutout from a bitmap.
348 | * does not alter sizes.
349 | *
350 | * @param bitmap the bitmap
351 | * @see #cutCircleFromBitmap(String, int)
352 | * @return a bitmap circle cutout
353 | */
354 | public static Bitmap roundBitMap(Bitmap bitmap)
355 | {
356 | Bitmap circleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
357 |
358 | BitmapShader shader = new BitmapShader (bitmap, TileMode.CLAMP, TileMode.CLAMP);
359 | Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
360 |
361 | paint.setShader(shader);
362 |
363 | Canvas c = new Canvas(circleBitmap);
364 |
365 | c.drawCircle(bitmap.getWidth()/2, bitmap.getHeight()/2, bitmap.getWidth()/2, paint);
366 |
367 | return circleBitmap;
368 | }
369 |
370 | public static Bitmap getCroppedBitmap(Bitmap bmp, int radius)
371 | {
372 | Bitmap sbmp;
373 |
374 | if(bmp.getWidth() != radius || bmp.getHeight() != radius)
375 | sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false);
376 | else
377 | sbmp = bmp;
378 |
379 | Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(), Config.ARGB_8888);
380 | Canvas canvas = new Canvas(output);
381 |
382 | //final int color = 0xffa19774;
383 | final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
384 | final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight());
385 |
386 | paint.setAntiAlias(true);
387 | paint.setFilterBitmap(true);
388 | paint.setDither(true);
389 |
390 | canvas.drawARGB(0, 0, 0, 0);
391 |
392 | paint.setColor(Color.parseColor("#BAB399"));
393 |
394 | canvas.drawCircle(sbmp.getWidth() / 2+0.7f, sbmp.getHeight() / 2+0.7f, sbmp.getWidth() / 2+0.1f, paint);
395 |
396 | paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
397 |
398 | canvas.drawBitmap(sbmp, rect, rect, paint);
399 |
400 | return output;
401 | }
402 |
403 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/utils/MeasureUtils.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.utils;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * @author Tomer Shalev
7 | */
8 | @SuppressWarnings("unused")
9 | public class MeasureUtils {
10 |
11 | private MeasureUtils() {
12 | }
13 |
14 | static public int DIPToPixels(Context context, float dip)
15 | {
16 | final float scale = context.getResources().getDisplayMetrics().density;
17 |
18 | // also can use
19 | //TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 65, context.getResources().getDisplayMetrics());
20 |
21 | return (int) (dip * scale + 0.5f);
22 | }
23 |
24 | static public int pixelsToDIP(Context context, int pixels)
25 | {
26 | final float scale = context.getResources().getDisplayMetrics().density;
27 |
28 | return (int) ((pixels - 0.5f) / scale);
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/viewRenderer/AbstractViewRenderer.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.viewRenderer;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | import com.hendrix.pdfmyxml.utils.MeasureUtils;
12 |
13 | /**
14 | * @author Tomer Shalev
15 | */
16 | abstract public class AbstractViewRenderer implements IViewRenderer {
17 | protected View _view = null;
18 | protected Context _ctx = null;
19 | private Bitmap _bmp = null;
20 | private boolean _flagReuseBitmap = false;
21 | private Object _data = null;
22 |
23 | /**
24 | *
25 | * @param ctx a context
26 | * @param layoutResId a layout resource id
27 | */
28 | public AbstractViewRenderer(Context ctx, int layoutResId) {
29 | this(ctx, LayoutInflater.from(ctx).inflate(layoutResId, null));
30 | }
31 |
32 | /**
33 | *
34 | * @param ctx a context
35 | * @param view an inflated view
36 | */
37 | public AbstractViewRenderer(Context ctx, View view) {
38 | attachContext(ctx);
39 |
40 | _view = view;
41 | }
42 |
43 | protected AbstractViewRenderer() {
44 | throw new UnsupportedOperationException();
45 | }
46 |
47 | /**
48 | * attach a context to the renderer
49 | *
50 | * @param ctx the context
51 | */
52 | @Override
53 | public void attachContext(Context ctx) {
54 | _ctx = ctx;
55 | }
56 |
57 | /**
58 | * render the view with a reused {@link Bitmap}
59 | *
60 | * @param bitmap a bitmap to render into (reused)
61 | * @param width the width
62 | * @param height the height
63 | *
64 | *
Note:
65 | * on API <= 17, you must give explicit {@code width} and {@code height} because of a bug in {@link android.widget.RelativeLayout}
66 | */
67 | @Override
68 | public final Bitmap render(Bitmap bitmap, int width, int height) {
69 | validate();
70 |
71 | initView(getView());
72 |
73 | View view = getView();
74 |
75 | int specWidth = View.MeasureSpec.makeMeasureSpec(width, width==0 ? View.MeasureSpec.UNSPECIFIED : View.MeasureSpec.EXACTLY);
76 | int specHeight = View.MeasureSpec.makeMeasureSpec(height, height==0 ? View.MeasureSpec.UNSPECIFIED : View.MeasureSpec.EXACTLY);
77 |
78 | view.measure(specWidth, specHeight);
79 |
80 | view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
81 |
82 | // recycle bitmap
83 | Bitmap b = bitmap;//Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
84 |
85 | Canvas c = new Canvas(b);
86 | c.translate(-view.getScrollX(), -view.getScrollY());
87 |
88 | view.draw(c);
89 |
90 | return _bmp=b;
91 | }
92 |
93 | /**
94 | * render the bitmap
95 | *
96 | * @param width the wanted width for rendering, in Pixels. if 0, then the view will measure itself as big as it needs to be(only on API > 17).
97 | * @param height the wanted height for rendering, in Pixels. if 0, then the view will measure itself as big as it needs to be(only on API > 17).
98 | *
99 | * Note:
100 | * on API <= 17, you must give explicit {@code width} and {@code height} because of a bug in {@link android.widget.RelativeLayout}
101 | */
102 | @Override final public Bitmap render(int width, int height) {
103 | validate();
104 |
105 | initView(getView());
106 |
107 | View view = getView();
108 |
109 | int specWidth = View.MeasureSpec.makeMeasureSpec(width == 0 ? ViewGroup.LayoutParams.WRAP_CONTENT : width, width == 0 ? View.MeasureSpec.UNSPECIFIED : View.MeasureSpec.EXACTLY);
110 | int specHeight = View.MeasureSpec.makeMeasureSpec(height == 0 ? ViewGroup.LayoutParams.WRAP_CONTENT : height, height==0 ? View.MeasureSpec.UNSPECIFIED : View.MeasureSpec.EXACTLY);
111 | int a = ViewGroup.LayoutParams.MATCH_PARENT;
112 | System.out.println("a");
113 |
114 | try {
115 | view.measure(specWidth, specHeight);
116 | }
117 | catch (NullPointerException exc) {
118 | exc.printStackTrace();
119 | }
120 |
121 | view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
122 |
123 | Bitmap b;
124 |
125 | // recycle bitmap
126 | if(!_flagReuseBitmap) {
127 | disposeBitmap();
128 | b = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
129 | }
130 | else {
131 | // reuse bitmap
132 | b = (_bmp==null || _bmp.isRecycled()) ? Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888) : _bmp;
133 | b.eraseColor(Color.TRANSPARENT);
134 | }
135 |
136 | Canvas c = new Canvas(b);
137 | c.translate(-view.getScrollX(), -view.getScrollY());
138 |
139 | view.draw(c);
140 |
141 | return _bmp=b;
142 | }
143 |
144 | final public Bitmap render2(int width, int height) {
145 | validate();
146 |
147 | initView(getView());
148 |
149 | View view = getView();
150 |
151 | int specWidth = View.MeasureSpec.makeMeasureSpec(MeasureUtils.DIPToPixels(_ctx, width), width==0 ? View.MeasureSpec.UNSPECIFIED : View.MeasureSpec.EXACTLY);
152 | int specHeight = View.MeasureSpec.makeMeasureSpec(MeasureUtils.DIPToPixels(_ctx, height), height==0 ? View.MeasureSpec.UNSPECIFIED : View.MeasureSpec.EXACTLY);
153 |
154 | try {
155 | view.measure(specWidth, specHeight);
156 | }
157 | catch (NullPointerException exc) {
158 | exc.printStackTrace();
159 | }
160 |
161 | view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
162 |
163 | Bitmap b;
164 |
165 | // recycle bitmap
166 | if(!_flagReuseBitmap) {
167 | disposeBitmap();
168 | b = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
169 | }
170 | else {
171 | // reuse bitmap
172 | b = (_bmp==null || _bmp.isRecycled()) ? Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888) : _bmp;
173 | b.eraseColor(Color.TRANSPARENT);
174 | }
175 |
176 | Canvas c = new Canvas(b);
177 | c.translate(-view.getScrollX(), -view.getScrollY());
178 |
179 | view.draw(c);
180 |
181 | return _bmp=b;
182 | }
183 |
184 | /**
185 | * return the rendered bitmap
186 | *
187 | * @return the rendered bitmap
188 | */
189 | @Override
190 | public Bitmap getBitmap() {
191 | return _bmp;
192 | }
193 |
194 | /**
195 | * get the view to render
196 | *
197 | * @return the view
198 | */
199 | @Override
200 | public View getView() {
201 | return _view;
202 | }
203 |
204 | /**
205 | * width of the canvas
206 | *
207 | * @return width
208 | */
209 | @Override
210 | public int getWidth() {
211 | return getView().getWidth();
212 | }
213 |
214 | /**
215 | * height of the canvas
216 | *
217 | * @return height
218 | */
219 | @Override
220 | public int getHeight() {
221 | return getView().getHeight();
222 | }
223 |
224 | /**
225 | * init the view. this is where you will do setup of
226 | * the view's subviews, assign text etc...
227 | */
228 | abstract protected void initView(View view);
229 |
230 | /**
231 | * validate the input
232 | *
233 | * @throws IllegalArgumentException if context or view is null
234 | */
235 | private void validate() {
236 | if(_ctx==null)
237 | throw new IllegalArgumentException("ViewRenderer:: context was not set!!");
238 | if(_view==null)
239 | throw new IllegalArgumentException("ViewRenderer:: view or layout resource was not set!!");
240 | }
241 |
242 | /**
243 | *
244 | * @return flag reuse bitmap
245 | */
246 | public boolean isReuseBitmap() {
247 | return _flagReuseBitmap;
248 | }
249 |
250 | /**
251 | * flag for indicating bitmap re usage if possible for multiple.
252 | *
253 | * @param flagReuseBitmap {@code true/false}
254 | */
255 | public void setReuseBitmap(boolean flagReuseBitmap) {
256 | _flagReuseBitmap = flagReuseBitmap;
257 | }
258 |
259 | /**
260 | * dispose the item
261 | */
262 | @Override
263 | public void dispose() {
264 | _bmp.recycle();
265 | _view = null;
266 | _ctx = null;
267 | }
268 |
269 | /**
270 | * dispose the bitmap
271 | */
272 | public void disposeBitmap() {
273 | if(_bmp != null)
274 | _bmp.recycle();
275 | _bmp = null;
276 | }
277 |
278 | @Override
279 | public void setData(Object data) {
280 | _data = data;
281 | }
282 |
283 | @Override
284 | public Object getData() {
285 | return _data;
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/viewRenderer/IViewRenderer.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.viewRenderer;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.view.View;
6 |
7 | import com.hendrix.pdfmyxml.interfaces.IData;
8 | import com.hendrix.pdfmyxml.interfaces.IDisposable;
9 |
10 | /**
11 | * @author Tomer Shalev
12 | */
13 | public interface IViewRenderer extends IDisposable, IData {
14 |
15 | /**
16 | * attach a context to the renderer
17 | *
18 | * @param ctx the context
19 | */
20 | void attachContext(Context ctx);
21 |
22 | /**
23 | * render the view
24 | *
25 | * @param width the width
26 | * @param height the height
27 | */
28 | Bitmap render(int width, int height);
29 |
30 | /**
31 | * render the view
32 | *
33 | * @param bitmap a bitmap to render into
34 | * @param width the width
35 | * @param height the height
36 | */
37 | Bitmap render(Bitmap bitmap, int width, int height);
38 |
39 | /**
40 | * return the rendered bitmap
41 | *
42 | * @return the rendered bitmap
43 | */
44 | Bitmap getBitmap();
45 |
46 | /**
47 | * get the view to render
48 | *
49 | * @return the view
50 | */
51 | View getView();
52 |
53 | /**
54 | * width of the canvas
55 | *
56 | * @return width
57 | */
58 | int getWidth();
59 |
60 | /**
61 | * height of the canvas
62 | *
63 | * @return height
64 | */
65 | int getHeight();
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/viewRenderer/RecycledViewRenderer.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.viewRenderer;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 |
7 | /**
8 | * a View renderer with recycling capabilities.
9 | *
10 | * @author Tomer Shalev
11 | */
12 | public class RecycledViewRenderer extends SimpleViewRenderer {
13 |
14 | public RecycledViewRenderer() {
15 | }
16 |
17 | /**
18 | * recycle the view renderer with a new view
19 | *
20 | * @param ctx the context
21 | * @param view a new view
22 | */
23 | public RecycledViewRenderer recycleWith(Context ctx, View view) {
24 | attachContext(ctx);
25 |
26 | _view = view;
27 |
28 | return this;
29 | }
30 |
31 | /**
32 | * recycle the view renderer with a new view
33 | *
34 | * @param ctx the context
35 | * @param layoutResId a resource layout id
36 | */
37 | public RecycledViewRenderer recycleWith(Context ctx, int layoutResId) {
38 | return recycleWith(ctx, LayoutInflater.from(ctx).inflate(layoutResId, null));
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/viewRenderer/SimpleViewRenderer.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.viewRenderer;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | /**
7 | * a simple view renderer implementation of abstract view renderer with empty init.
8 | * i.e - use it to render a view that was already measured or a layout resource
9 | * that will get as big as it needs to
10 | * @author Tomer Shalev
11 | */
12 | public class SimpleViewRenderer extends AbstractViewRenderer {
13 | /**
14 | * @param ctx a context
15 | * @param layoutResId a layout resource id
16 | */
17 | public SimpleViewRenderer(Context ctx, int layoutResId) {
18 | super(ctx, layoutResId);
19 | }
20 |
21 | /**
22 | * @param ctx a context
23 | * @param view a view
24 | */
25 | public SimpleViewRenderer(Context ctx,View view) {
26 | super(ctx, view);
27 | }
28 |
29 | public SimpleViewRenderer() {}
30 |
31 | /**
32 | * empty view init
33 | *
34 | * @param view the view
35 | */
36 | @Override
37 | protected void initView(View view) {
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/lib/src/main/java/com/hendrix/pdfmyxml/viewRenderer/ViewRendererFactory.java:
--------------------------------------------------------------------------------
1 | package com.hendrix.pdfmyxml.viewRenderer;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.view.View;
6 |
7 | /**
8 | * a recycled view renderer factory.
9 | *
10 | * @author Tomer Shalev
11 | */
12 | public class ViewRendererFactory {
13 | volatile static private RecycledViewRenderer _rvr = new RecycledViewRenderer();
14 |
15 | /**
16 | * recycle a view renderer with the following
17 | *
18 | * @param ctx the context
19 | * @param view the view to renderer
20 | * @param width the width
21 | * @param height the height
22 | *
23 | * @return a bitmap of the view
24 | */
25 | static public Bitmap getBitmapOfView(Context ctx, View view, int width, int height) {
26 | return _rvr.recycleWith(ctx, view).render(width, height);
27 | }
28 |
29 | /**
30 | * recycle a view renderer with the following
31 | *
32 | * @param ctx the context
33 | * @param layoutResId the layout resource identifier
34 | * @param width the width
35 | * @param height the height
36 | *
37 | * @return a bitmap of the view
38 | */
39 | static public Bitmap getBitmapOfView(Context ctx, int layoutResId, int width, int height) {
40 | return _rvr.recycleWith(ctx, layoutResId).render(width, height);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PdfDroid
3 |
4 |
--------------------------------------------------------------------------------
/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:/src_mreshet/android/AndroidStudioProjects/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':lib'
2 |
--------------------------------------------------------------------------------