@capgo/camera-preview 7.15.0 → 7.16.2

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.
@@ -51,9 +51,11 @@ dependencies {
51
51
  implementation 'androidx.exifinterface:exifinterface:1.4.1'
52
52
  implementation 'com.google.android.gms:play-services-location:21.3.0'
53
53
  implementation 'androidx.coordinatorlayout:coordinatorlayout:1.3.0'
54
+ // In-memory EXIF writing (no temp files)
55
+ implementation 'org.apache.commons:commons-imaging:1.0.0-alpha6'
54
56
 
55
57
  // CameraX dependencies
56
- def camerax_version = "1.5.0-rc01"
58
+ def camerax_version = "1.5.0"
57
59
  implementation "androidx.camera:camera-core:${camerax_version}"
58
60
  implementation "androidx.camera:camera-camera2:${camerax_version}"
59
61
  implementation "androidx.camera:camera-lifecycle:${camerax_version}"
@@ -64,4 +66,3 @@ dependencies {
64
66
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
65
67
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
66
68
  }
67
-
@@ -359,47 +359,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
359
359
  fos.flush();
360
360
  fos.close();
361
361
 
362
- // If JPEG and we have source EXIF, copy EXIF tags into the saved file
363
- if ("image/jpeg".equals(mimeType) && sourceExif != null) {
364
- try {
365
- ExifInterface newExif = new ExifInterface(photo.getAbsolutePath());
366
- for (String[] tag : EXIF_TAGS) {
367
- String value = sourceExif.getAttribute(tag[0]);
368
- if (value != null) newExif.setAttribute(tag[0], value);
369
- }
370
- // Normalize orientation for recompressed pixels
371
- newExif.setAttribute(
372
- ExifInterface.TAG_ORIENTATION,
373
- Integer.toString(ExifInterface.ORIENTATION_NORMAL)
374
- );
375
- if (
376
- finalWidth != null &&
377
- finalHeight != null &&
378
- finalWidth > 0 &&
379
- finalHeight > 0
380
- ) {
381
- newExif.setAttribute(
382
- ExifInterface.TAG_PIXEL_X_DIMENSION,
383
- String.valueOf(finalWidth)
384
- );
385
- newExif.setAttribute(
386
- ExifInterface.TAG_PIXEL_Y_DIMENSION,
387
- String.valueOf(finalHeight)
388
- );
389
- newExif.setAttribute(
390
- ExifInterface.TAG_IMAGE_WIDTH,
391
- String.valueOf(finalWidth)
392
- );
393
- newExif.setAttribute(
394
- ExifInterface.TAG_IMAGE_LENGTH,
395
- String.valueOf(finalHeight)
396
- );
397
- }
398
- newExif.saveAttributes();
399
- } catch (Exception ex) {
400
- Log.w(TAG, "Failed to write EXIF to saved gallery image", ex);
401
- }
402
- }
362
+ // No EXIF rewrite here; bytes already contain EXIF when needed
403
363
 
404
364
  // Notify the gallery of the new image
405
365
  MediaScannerConnection.scanFile(
@@ -1269,21 +1229,23 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1269
1229
  @NonNull ImageCapture.OutputFileResults output
1270
1230
  ) {
1271
1231
  try {
1272
- byte[] bytes = imageStream.toByteArray();
1232
+ byte[] originalCaptureBytes = imageStream.toByteArray();
1233
+ byte[] bytes = originalCaptureBytes; // will be replaced if we transform
1273
1234
  int finalWidthOut = -1;
1274
1235
  int finalHeightOut = -1;
1236
+ boolean transformedPixels = false;
1275
1237
 
1276
1238
  ExifInterface exifInterface = new ExifInterface(
1277
- new ByteArrayInputStream(bytes)
1239
+ new ByteArrayInputStream(originalCaptureBytes)
1278
1240
  );
1279
1241
  // Build EXIF JSON from captured bytes (location applied by metadata if provided)
1280
1242
  JSONObject exifData = getExifData(exifInterface);
1281
1243
 
1282
1244
  if (width != null || height != null) {
1283
1245
  Bitmap bitmap = BitmapFactory.decodeByteArray(
1284
- bytes,
1246
+ originalCaptureBytes,
1285
1247
  0,
1286
- bytes.length
1248
+ originalCaptureBytes.length
1287
1249
  );
1288
1250
  bitmap = applyExifOrientation(bitmap, exifInterface);
1289
1251
  Bitmap resizedBitmap = resizeBitmapToMaxDimensions(
@@ -1298,6 +1260,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1298
1260
  stream
1299
1261
  );
1300
1262
  bytes = stream.toByteArray();
1263
+ transformedPixels = true;
1301
1264
 
1302
1265
  // Update EXIF JSON to reflect new dimensions; no in-place EXIF write to bytes
1303
1266
  try {
@@ -1315,9 +1278,9 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1315
1278
  } else {
1316
1279
  // No explicit size/ratio: crop to match current preview content
1317
1280
  Bitmap originalBitmap = BitmapFactory.decodeByteArray(
1318
- bytes,
1281
+ originalCaptureBytes,
1319
1282
  0,
1320
- bytes.length
1283
+ originalCaptureBytes.length
1321
1284
  );
1322
1285
  originalBitmap = applyExifOrientation(
1323
1286
  originalBitmap,
@@ -1331,6 +1294,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1331
1294
  stream
1332
1295
  );
1333
1296
  bytes = stream.toByteArray();
1297
+ transformedPixels = true;
1334
1298
  // Update EXIF JSON to reflect cropped dimensions; no in-place EXIF write to bytes
1335
1299
  try {
1336
1300
  exifData.put("PixelXDimension", previewCropped.getWidth());
@@ -1346,6 +1310,19 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1346
1310
  finalHeightOut = previewCropped.getHeight();
1347
1311
  }
1348
1312
 
1313
+ // After any transform, inject EXIF back into the in-memory JPEG bytes (no temp file)
1314
+ if (transformedPixels) {
1315
+ Integer fW = (finalWidthOut > 0) ? finalWidthOut : null;
1316
+ Integer fH = (finalHeightOut > 0) ? finalHeightOut : null;
1317
+ bytes = injectExifInMemory(
1318
+ bytes,
1319
+ originalCaptureBytes,
1320
+ fW,
1321
+ fH,
1322
+ /*normalizeOrientation*/true
1323
+ );
1324
+ }
1325
+
1349
1326
  // Save to gallery asynchronously if requested, copy EXIF to file
1350
1327
  if (saveToGallery) {
1351
1328
  final byte[] finalBytes = bytes;
@@ -1375,41 +1352,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1375
1352
  outFos.write(bytes);
1376
1353
  outFos.close();
1377
1354
 
1378
- // Write EXIF into the saved file
1379
- try {
1380
- ExifInterface newExif = new ExifInterface(
1381
- outFile.getAbsolutePath()
1382
- );
1383
- for (String[] tag : EXIF_TAGS) {
1384
- String value = exifInterface.getAttribute(tag[0]);
1385
- if (value != null) newExif.setAttribute(tag[0], value);
1386
- }
1387
- newExif.setAttribute(
1388
- ExifInterface.TAG_ORIENTATION,
1389
- Integer.toString(ExifInterface.ORIENTATION_NORMAL)
1390
- );
1391
- if (finalWidthOut > 0 && finalHeightOut > 0) {
1392
- newExif.setAttribute(
1393
- ExifInterface.TAG_PIXEL_X_DIMENSION,
1394
- String.valueOf(finalWidthOut)
1395
- );
1396
- newExif.setAttribute(
1397
- ExifInterface.TAG_PIXEL_Y_DIMENSION,
1398
- String.valueOf(finalHeightOut)
1399
- );
1400
- newExif.setAttribute(
1401
- ExifInterface.TAG_IMAGE_WIDTH,
1402
- String.valueOf(finalWidthOut)
1403
- );
1404
- newExif.setAttribute(
1405
- ExifInterface.TAG_IMAGE_LENGTH,
1406
- String.valueOf(finalHeightOut)
1407
- );
1408
- }
1409
- newExif.saveAttributes();
1410
- } catch (Exception ex) {
1411
- Log.w(TAG, "Failed to embed EXIF into output file", ex);
1412
- }
1355
+ // No EXIF rewrite here; bytes already contain EXIF when needed
1413
1356
 
1414
1357
  // Return a file path; apps can convert via Capacitor.convertFileSrc on JS side
1415
1358
  resultValue = outFile.getAbsolutePath();
@@ -1546,6 +1489,69 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1546
1489
  return exifData;
1547
1490
  }
1548
1491
 
1492
+ // Inject EXIF into a JPEG byte[] fully in-memory using Apache Commons Imaging (no temp files)
1493
+ // Copies EXIF from sourceJpeg (original capture) and updates orientation/dimensions if provided.
1494
+ private byte[] injectExifInMemory(
1495
+ byte[] targetJpeg,
1496
+ byte[] sourceJpegWithExif,
1497
+ Integer finalWidth,
1498
+ Integer finalHeight,
1499
+ boolean normalizeOrientation
1500
+ ) {
1501
+ try {
1502
+ // Quick signature check for JPEG (FF D8 FF)
1503
+ if (
1504
+ targetJpeg == null ||
1505
+ targetJpeg.length < 3 ||
1506
+ (targetJpeg[0] & 0xFF) != 0xFF ||
1507
+ (targetJpeg[1] & 0xFF) != 0xD8 ||
1508
+ (targetJpeg[2] & 0xFF) != 0xFF
1509
+ ) {
1510
+ return targetJpeg; // Not a JPEG; nothing to do
1511
+ }
1512
+
1513
+ // Use Commons Imaging to read EXIF from the original capture bytes
1514
+ org.apache.commons.imaging.formats.jpeg.JpegImageMetadata jpegMetadata =
1515
+ (org.apache.commons.imaging.formats.jpeg.JpegImageMetadata) org.apache.commons.imaging.Imaging.getMetadata(
1516
+ sourceJpegWithExif
1517
+ );
1518
+ org.apache.commons.imaging.formats.tiff.TiffImageMetadata exif =
1519
+ jpegMetadata != null ? jpegMetadata.getExif() : null;
1520
+
1521
+ org.apache.commons.imaging.formats.tiff.write.TiffOutputSet outputSet =
1522
+ exif != null
1523
+ ? exif.getOutputSet()
1524
+ : new org.apache.commons.imaging.formats.tiff.write.TiffOutputSet();
1525
+
1526
+ // Update orientation if requested (normalize to 1)
1527
+ if (normalizeOrientation) {
1528
+ org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory rootDir =
1529
+ outputSet.getOrCreateRootDirectory();
1530
+ rootDir.removeField(
1531
+ org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants.TIFF_TAG_ORIENTATION
1532
+ );
1533
+ rootDir.add(
1534
+ org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants.TIFF_TAG_ORIENTATION,
1535
+ (short) 1
1536
+ );
1537
+ }
1538
+
1539
+ // Optionally update dimensions here. Skipped to maximize compatibility with Commons Imaging 1.0-alpha3.
1540
+
1541
+ java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
1542
+ new org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter()
1543
+ .updateExifMetadataLossless(
1544
+ new java.io.ByteArrayInputStream(targetJpeg),
1545
+ out,
1546
+ outputSet
1547
+ );
1548
+ return out.toByteArray();
1549
+ } catch (Throwable t) {
1550
+ Log.w(TAG, "injectExifInMemory: Failed to write EXIF in memory", t);
1551
+ return targetJpeg; // Fallback: return original bytes
1552
+ }
1553
+ }
1554
+
1549
1555
  private static final String[][] EXIF_TAGS = new String[][] {
1550
1556
  { ExifInterface.TAG_APERTURE_VALUE, "ApertureValue" },
1551
1557
  { ExifInterface.TAG_ARTIST, "Artist" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/camera-preview",
3
- "version": "7.15.0",
3
+ "version": "7.16.2",
4
4
  "description": "Camera preview",
5
5
  "license": "MIT",
6
6
  "repository": {