[ 
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)

Reply via email to