dragon66 / icafe

Java library for reading, writing, converting and manipulating images and metadata
Eclipse Public License 1.0
204 stars 58 forks source link

NegativeArraySizeException when try to write XMP #39

Closed sinedsem closed 7 years ago

sinedsem commented 7 years ago

Hello, it's me again. Metadata.insertXMP() produces an exception for certain images.

Problem images: https://github.com/sinedsem/test/blob/master/1.jpg https://github.com/sinedsem/test/blob/master/2.jpg https://github.com/sinedsem/test/blob/master/3.jpg https://github.com/sinedsem/test/blob/master/4.jpg

Code to reproduce issue:

    File source = new File("1.jpg");
    File destination = new File("result.jpg");

    XMPMeta xmpMeta = new XMPMetaImpl();

    ByteArrayOutputStream xmpOut = new ByteArrayOutputStream();
    XMPMetaFactory.serialize(xmpMeta, xmpOut);

    FileInputStream is = new FileInputStream(source);
    FileOutputStream os = new FileOutputStream(destination);

    Metadata.insertXMP(is, os, xmpOut.toString("utf-8"));

    is.close();
    os.close();

StackTrace (Sorry, no line numbers)

java.lang.NegativeArraySizeException: null
    at com.icafe4j.image.jpeg.JPEGTweaker.insertXMP(Unknown Source)
    at com.icafe4j.image.jpeg.JPEGTweaker.insertXMP(Unknown Source)
    at com.icafe4j.image.meta.Metadata.insertXMP(Unknown Source)
dragon66 commented 7 years ago

@sinedsem There is a bug in icafe code when JPEG APP1 field length is less than certain number of bytes. I am going to check in a fix for this. After that, your issue will be fixed.

dragon66 commented 7 years ago

@sinedsem Replace the case APP1 in JPEGTweaker.insertXMP with the following will solve the issue:

case APP1:
    // Read and remove the old XMP data
    length = IOUtils.readUnsignedShortMM(is);
    byte[] temp = new byte[length - 2];
    IOUtils.readFully(is, temp);
    // Remove XMP and ExtendedXMP segments.
    if(temp.length > XMP_EXT_ID.length() && new String(temp, 0, XMP_EXT_ID.length()).equals(XMP_EXT_ID)) {
        ;
    } else if (temp.length > XMP_ID.length() && new String(temp, 0, XMP_ID.length()).equals(XMP_ID)) {
        ;
    } else {                        
        segments.add(new Segment(emarker, length, temp));
        // If it's EXIF, we keep the index
        if(temp.length > EXIF_ID.length() && new String(temp, 0, EXIF_ID.length()).equals(EXIF_ID)) {
            exifIndex = segments.size() - 1;
        }
    }
    marker = IOUtils.readShortMM(is);
    break;
sinedsem commented 7 years ago

@dragon66 will you commit this or you are just telling me what to do?

dragon66 commented 7 years ago

@sinedsem I have checked in a new build.

sinedsem commented 7 years ago

@dragon66, big thank you for the fix! All works! Closing issue.