[ https://issues.apache.org/jira/browse/IMAGING-319?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17491005#comment-17491005 ]
Gary Lucas commented on IMAGING-319: ------------------------------------ Okay, found it. In the code below, the method looped through all the available free elements and found one it calls "bestFit". It is going to store the new data into the available space. But TIFF files have a rule that the offsets have to be an even multiple of 2. So there's a check to see if the offset is odd and, if it is, the code advances the offset forward one. The problem is that it doesn't recognize that by advancing the offset, it's reduced the amount of available space (bestFit.length). So, the "excessLength" computation below will be incorrect. If some subsequent element is an exact match for the incorrect excessLength value, it will overwrite the unused space and clobber whatever follows. In this case, the thing that got clobbered was the first byte of EXIF tag 0x9010. The probability of this happening is small, but non zero. It is just luck that Sicheng Yang's data sample triggered the issue. {quote} long offset = bestFit.offset; if ((offset & 1L) != 0) { offset += 1; } outputItem.setOffset(offset); unusedElements.remove(bestFit); if (bestFit.length > outputItemLength) { // not a perfect fit. final long excessOffset = bestFit.offset + outputItemLength; final int excessLength = bestFit.length - outputItemLength; unusedElements.add(new TiffElement.Stub(excessOffset, excessLength)); // make sure the new element is in the correct order. unusedElements.sort(ELEMENT_SIZE_COMPARATOR); Collections.reverse(unusedElements); } } {quote} I re-wrote the code as follows. It works. Writing a JUnit test for this is going to be extremely difficult. {quote} unusedElements.remove(bestFit); long offset = bestFit.offset; int length = bestFit.length; if ((offset & 1L) != 0) { // offsets have to be at a multiple of 2 offset += 1; length -=1; } outputItem.setOffset(offset); if (length > outputItemLength) { // not a perfect fit. final long excessOffset = offset + outputItemLength; final int excessLength = length - outputItemLength; unusedElements.add(new TiffElement.Stub(excessOffset, excessLength)); // make sure the new element is in the correct order. unusedElements.sort(ELEMENT_SIZE_COMPARATOR); Collections.reverse(unusedElements); } {quote} > updateExifMetadataLossless lost the first character of a String > --------------------------------------------------------------- > > Key: IMAGING-319 > URL: https://issues.apache.org/jira/browse/IMAGING-319 > Project: Commons Imaging > Issue Type: Bug > Components: Format: JPEG > Affects Versions: 1.0-alpha2 > Reporter: Sicheng Yang > Priority: Major > Attachments: Screen Shot 2021-11-26 at 4.01.06 PM-1.png, Screen Shot > 2021-11-26 at 4.01.21 PM-1.png, iPhone12-geotag.JPG > > > I try to use TiffOutputSet to generate a new image. However, if a tag that > contains String, the program may miss the first character of the String. > > import java.io.*; > import org.apache.commons.imaging.ImageReadException; > import org.apache.commons.imaging.ImageWriteException; > import org.apache.commons.imaging.Imaging; > import org.apache.commons.imaging.common.ImageMetadata; > import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; > import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter; > import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; > import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet; > public class LibraryTest { > public static void main(String[] args) throws ImageReadException, > IOException, ImageWriteException { > File source = new File("./assets/iPhone12-geotag.JPG"); > File result = new > File("./assets/results/editted-iPhone12-geotag.JPG"); > final ImageMetadata metadata = Imaging.getMetadata(source); > final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; > final TiffImageMetadata exif = jpegMetadata.getExif(); > TiffOutputSet outputSet = exif.getOutputSet(); > BufferedOutputStream bufferedOutputStream = new > BufferedOutputStream(new FileOutputStream(result)); > new ExifRewriter().updateExifMetadataLossless(source, > bufferedOutputStream, outputSet); > } > } > > This is the sample code. > Tag value in original image > !image-2021-11-26-16-01-58-645.png! > Tag value in output image > !image-2021-11-26-16-04-12-185.png! -- This message was sent by Atlassian Jira (v8.20.1#820001)