@scr2em/capacitor-scanner 6.0.8 → 6.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,10 @@ import android.content.pm.PackageManager;
6
6
  import android.graphics.Bitmap;
7
7
  import android.graphics.BitmapFactory;
8
8
  import android.graphics.Color;
9
+ import android.graphics.Matrix;
10
+ import androidx.exifinterface.media.ExifInterface;
9
11
  import android.net.Uri;
12
+ import android.os.Build;
10
13
  import android.provider.Settings;
11
14
  import android.util.Base64;
12
15
  import android.util.DisplayMetrics;
@@ -51,8 +54,10 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions;
51
54
  import com.google.mlkit.vision.barcode.BarcodeScanning;
52
55
  import com.google.mlkit.vision.common.InputImage;
53
56
 
57
+ import java.io.ByteArrayInputStream;
54
58
  import java.io.ByteArrayOutputStream;
55
59
  import java.io.IOException;
60
+ import java.io.InputStream;
56
61
  import java.util.concurrent.ExecutionException;
57
62
  import java.util.concurrent.Executor;
58
63
  import java.util.concurrent.Executors;
@@ -392,11 +397,9 @@ public class CapacitorScannerPlugin extends Plugin {
392
397
  return;
393
398
  }
394
399
 
395
- // Ratio must be between 0 and 1, where 0 or 1 means no reduction.
396
400
  Double qualityRatioObj = call.getDouble("qualityRatio", 1.0);
397
401
  double qualityRatio = qualityRatioObj != null ? qualityRatioObj : 1.0;
398
402
 
399
-
400
403
  // Temporary stream to receive the captured image data.
401
404
  ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
402
405
 
@@ -412,32 +415,94 @@ public class CapacitorScannerPlugin extends Plugin {
412
415
  byte[] originalBytes = outputStream.toByteArray();
413
416
  int originalSize = originalBytes.length; // in bytes
414
417
 
418
+ BitmapFactory.Options options = new BitmapFactory.Options();
419
+ options.inJustDecodeBounds = true;
420
+ BitmapFactory.decodeByteArray(originalBytes, 0, originalSize, options);
421
+
415
422
  // If qualityRatio is between 0 and 1 exclusively, perform quality reduction.
416
423
  if (qualityRatio > 0 && qualityRatio < 1) {
417
424
  // Calculate the target size in bytes.
418
425
  int targetSize = (int) (originalSize * qualityRatio);
419
426
 
427
+ // Read EXIF orientation data from original image
428
+ int orientation = ExifInterface.ORIENTATION_UNDEFINED;
429
+
430
+ try {
431
+ InputStream inputStream = new ByteArrayInputStream(originalBytes);
432
+ ExifInterface exif = null;
433
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
434
+ exif = new ExifInterface(inputStream);
435
+ orientation = exif.getAttributeInt(
436
+ ExifInterface.TAG_ORIENTATION,
437
+ ExifInterface.ORIENTATION_UNDEFINED);
438
+ }
439
+ inputStream.close();
440
+ } catch (IOException e) {
441
+ echo("Warning: Could not read EXIF orientation: " + e.getMessage());
442
+ }
443
+
444
+
420
445
  // Decode the captured image into a Bitmap.
421
- Bitmap originalBitmap = BitmapFactory.decodeByteArray(originalBytes, 0, originalSize);
446
+ options.inJustDecodeBounds = false;
447
+ options.inSampleSize = calculateInSampleSize(options, 1024, 1024); // Max size of 1024x1024
448
+ Bitmap originalBitmap = BitmapFactory.decodeByteArray(originalBytes, 0, originalSize, options);
422
449
  if (originalBitmap == null) {
423
450
  call.reject("Failed to decode captured image.");
424
451
  return;
425
452
  }
426
453
 
454
+ // Apply rotation based on EXIF orientation
455
+ Matrix matrix = new Matrix();
456
+ switch (orientation) {
457
+ case ExifInterface.ORIENTATION_ROTATE_90:
458
+ matrix.postRotate(90);
459
+ break;
460
+ case ExifInterface.ORIENTATION_ROTATE_180:
461
+ matrix.postRotate(180);
462
+ break;
463
+ case ExifInterface.ORIENTATION_ROTATE_270:
464
+ matrix.postRotate(270);
465
+ break;
466
+ case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
467
+ matrix.postScale(-1, 1);
468
+ break;
469
+ case ExifInterface.ORIENTATION_FLIP_VERTICAL:
470
+ matrix.postScale(1, -1);
471
+ break;
472
+ }
473
+
474
+ // Apply the rotation if needed
475
+ if (!matrix.isIdentity()) {
476
+ Bitmap rotatedBitmap = Bitmap.createBitmap(
477
+ originalBitmap, 0, 0,
478
+ originalBitmap.getWidth(), originalBitmap.getHeight(),
479
+ matrix, true);
480
+ originalBitmap.recycle(); // Recycle the old bitmap to free memory.
481
+ originalBitmap = rotatedBitmap;
482
+ }
483
+
427
484
  // Prepare a stream to hold the compressed bitmap.
428
485
  ByteArrayOutputStream compressedStream = new ByteArrayOutputStream();
429
- int quality = 100; // start with maximum quality (100)
486
+ int quality = 100; // Start with maximum quality (100)
430
487
 
431
- // Iteratively compress the bitmap until it reaches the target file size.
488
+ // Iteratively compress the bitmap until it reaches the target file size or quality drops to 10%.
489
+ int maxIterations = 10; // Limit iterations to prevent excessive looping.
490
+ int iterations = 0;
432
491
  do {
433
492
  compressedStream.reset();
434
493
  originalBitmap.compress(Bitmap.CompressFormat.JPEG, quality, compressedStream);
435
- quality -= 5; // Lower the quality step by step.
436
- } while (compressedStream.toByteArray().length > targetSize && quality > 10);
494
+ quality -= 5; // Reduce quality step by step.
495
+ iterations++;
496
+ } while (compressedStream.toByteArray().length > targetSize && quality > 10 && iterations < maxIterations);
437
497
 
438
498
  // Use the compressed image bytes.
439
499
  originalBytes = compressedStream.toByteArray();
440
500
 
501
+ Bitmap finalBitmap = BitmapFactory.decodeByteArray(originalBytes, 0, originalBytes.length);
502
+ if (finalBitmap != null) {
503
+ finalBitmap.recycle();
504
+ }
505
+
441
506
  // Cleanup: recycle the bitmap and close the compressed stream.
442
507
  originalBitmap.recycle();
443
508
  compressedStream.close();
@@ -451,6 +516,7 @@ public class CapacitorScannerPlugin extends Plugin {
451
516
  } catch (Exception e) {
452
517
  call.reject("Failed to process image: " + e.getMessage());
453
518
  } finally {
519
+ // Ensure resources are cleaned up.
454
520
  try {
455
521
  outputStream.close();
456
522
  } catch (IOException e) {
@@ -472,7 +538,23 @@ public class CapacitorScannerPlugin extends Plugin {
472
538
  });
473
539
  }
474
540
 
541
+ private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
542
+ // Calculate the largest inSampleSize that is a power of 2 and keeps the image smaller than the requested width/height.
543
+ final int height = options.outHeight;
544
+ final int width = options.outWidth;
545
+ int inSampleSize = 1;
546
+
547
+ if (height > reqHeight || width > reqWidth) {
548
+ final int halfHeight = height / 2;
549
+ final int halfWidth = width / 2;
475
550
 
551
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps the image smaller than the requested width/height.
552
+ while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
553
+ inSampleSize *= 2;
554
+ }
555
+ }
556
+ return inSampleSize;
557
+ }
476
558
 
477
559
 
478
560
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scr2em/capacitor-scanner",
3
- "version": "6.0.8",
3
+ "version": "6.0.9",
4
4
  "description": "scan codes",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",