mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-27 00:00:02 -04:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c61e47b3e7 | |||
| 1d47d2ef90 | |||
| 94554b0660 | |||
| a39bca4d2f | |||
| fdbbcc54a8 | |||
| 00cd9471dd | |||
| eb6a3bde39 | |||
| d3dd4c29d5 | |||
| 3355acfa03 | |||
| b70e52561a | |||
| 49ddbfa85e | |||
| 215db21569 | |||
| 0ac8011053 | |||
| abbbca9be3 | |||
| bc5473aec6 | |||
| aca8b1256a | |||
| 18c1edf0e5 | |||
| 6a01466ebb | |||
| b19df1640b | |||
| e8e3e0f1d6 | |||
| c249a21c8c | |||
| 182b2fdfa9 | |||
| df068e350d | |||
| b55c623b87 | |||
| 09573b52ac | |||
| ff7edbd223 | |||
| d8f0cd97a2 | |||
| f6971a9bce | |||
| 821965df0d | |||
| ed46305d31 | |||
| ca3adb7c45 | |||
| 59f76209bc | |||
| 2076235313 | |||
| 88bebf31f8 | |||
| 37beb21c29 | |||
| 88c0d27516 | |||
| dcd4ffccf4 | |||
| f35f5c6b24 | |||
| 9dcf53d985 | |||
| 44a2066b79 | |||
| 382c51db7c | |||
| 660f3f7e86 | |||
| b4c30872c4 | |||
| 402d4d466d | |||
| 7860bf7e17 | |||
| d52522fb80 | |||
| 8a8c6b1931 | |||
| 2930708a54 | |||
| 5661e7459c | |||
| 4d45ea4966 | |||
| 34852f7be5 | |||
| abc929a531 | |||
| 247a09ca61 | |||
| c2880fe793 | |||
| c9e522475b | |||
| 1da16e4d7a |
@@ -1,53 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: Reported bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Version information**
|
||||
1. The version of the TwelveMonkeys ImageIO library in use.
|
||||
For example: 4.0.0
|
||||
|
||||
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
|
||||
For example:
|
||||
|
||||
java version "1.8.0_271"
|
||||
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
|
||||
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
|
||||
|
||||
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Compile the below sample code
|
||||
2. Download the sample image file
|
||||
3. Run the code with the sample file
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Example code**
|
||||
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
|
||||
|
||||
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
|
||||
|
||||
**Sample file(s)**
|
||||
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
|
||||
|
||||
**Stak trace**
|
||||
Always include the stack trace you experience.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
Do not add screenshots of code or stack traces. 😀
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: New feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem or use case is.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: Trouble shooting and programming help
|
||||
about: "General programming issues will reach a wider audience at StackOverflow. Tag
|
||||
questions with javax-imageio and/or twelvemonkeys \U0001F600 "
|
||||
title: ''
|
||||
labels: Trouble-shooting
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
General programming issues and problems will reach a much wider audience at StackOverflow, we suggest you ask them there. This will offload our work with maintaining the library, and make sure you get better help sooner.
|
||||
|
||||
Tag the question with `javax-imageio` and/or `twelvemonkeys` and we'll find them there.
|
||||
@@ -1,11 +0,0 @@
|
||||
**What is fixed** Add link to the issue this PR fixes.
|
||||
|
||||
Example: Fixes #42.
|
||||
|
||||
**Why is this change proposed** If this change does *not* fix an open issue, briefly describe the rationale for this PR.
|
||||
|
||||
**What is changed** Briefly describe the changes proposed in this pull request:
|
||||
|
||||
* Fixed rare exception happening in `x >= 42` case
|
||||
* Small optimization of `decompress()` method
|
||||
* Corrected API doc for `compress()` method to reflect current implementation
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
|
||||
package com.twelvemonkeys.xml;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import org.w3c.dom.DOMConfiguration;
|
||||
import org.w3c.dom.DOMImplementationList;
|
||||
import org.w3c.dom.Document;
|
||||
@@ -38,9 +41,6 @@ import org.w3c.dom.ls.DOMImplementationLS;
|
||||
import org.w3c.dom.ls.LSOutput;
|
||||
import org.w3c.dom.ls.LSSerializer;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* {@code DOMImplementationLS} backed implementation.
|
||||
*
|
||||
@@ -88,17 +88,6 @@ public final class DOMSerializer {
|
||||
output.setCharacterStream(pStream);
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: Is it useful?
|
||||
public void setNewLine(final String pNewLine) {
|
||||
serializer.setNewLine(pNewLine);
|
||||
}
|
||||
|
||||
public String getNewLine() {
|
||||
return serializer.getNewLine();
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Specifies wether the serializer should use indentation and optimize for
|
||||
* readability.
|
||||
@@ -169,13 +158,7 @@ public final class DOMSerializer {
|
||||
try {
|
||||
return DOMImplementationRegistry.newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,16 +30,23 @@
|
||||
|
||||
package com.twelvemonkeys.xml;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
/**
|
||||
* XMLSerializer
|
||||
@@ -290,7 +297,7 @@ public class XMLSerializer {
|
||||
}
|
||||
|
||||
private static int appendAndEscape(final String pString, int pStart, final int pEnd, final StringBuilder pBuilder, final String pEntity) {
|
||||
pBuilder.append(pString.substring(pStart, pEnd));
|
||||
pBuilder.append(pString, pStart, pEnd);
|
||||
pBuilder.append(pEntity);
|
||||
return pEnd + 1;
|
||||
}
|
||||
@@ -527,8 +534,7 @@ public class XMLSerializer {
|
||||
builder = factory.newDocumentBuilder();
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
//noinspection ThrowableInstanceNeverThrown BOGUS
|
||||
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
DOMImplementation dom = builder.getDOMImplementation();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
+158
-20
@@ -39,6 +39,7 @@ import com.twelvemonkeys.util.LRUHashMap;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -96,6 +97,8 @@ public final class ColorSpaces {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static final int CS_GENERIC_CMYK = 5001;
|
||||
|
||||
static final int ICC_PROFILE_HEADER_SIZE = 128;
|
||||
|
||||
// Weak references to hold the color spaces while cached
|
||||
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<>(null);
|
||||
private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<>(null);
|
||||
@@ -104,16 +107,8 @@ public final class ColorSpaces {
|
||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<>(10);
|
||||
|
||||
static {
|
||||
try {
|
||||
// Force invocation of ProfileDeferralMgr.activateProfiles() to avoid JDK-6986863
|
||||
ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData();
|
||||
}
|
||||
catch (Throwable disasters) {
|
||||
System.err.println("ICC Color Profile not properly activated due to the exception below.");
|
||||
System.err.println("Expect to see JDK-6986863 in action, and consider filing a bug report to your JRE provider.");
|
||||
|
||||
disasters.printStackTrace();
|
||||
}
|
||||
// In case we didn't activate through SPI already
|
||||
ProfileDeferralActivator.activateProfiles();
|
||||
}
|
||||
|
||||
private ColorSpaces() {}
|
||||
@@ -148,9 +143,11 @@ public final class ColorSpaces {
|
||||
|
||||
private static byte[] getProfileHeaderWithProfileId(final ICC_Profile profile) {
|
||||
// Get *entire profile data*... :-/
|
||||
byte[] data = profile.getData();
|
||||
return getProfileHeaderWithProfileId(profile.getData());
|
||||
}
|
||||
|
||||
// Clear out preferred CMM, platform & creator, as these does not affect the profile in any way
|
||||
private static byte[] getProfileHeaderWithProfileId(byte[] data) {
|
||||
// Clear out preferred CMM, platform & creator, as these don't affect the profile in any way
|
||||
// - LCMS updates CMM + creator to "lcms" and platform to current platform
|
||||
// - KCMS keeps the values in the file...
|
||||
Arrays.fill(data, ICC_Profile.icHdrCmmId, ICC_Profile.icHdrCmmId + 4, (byte) 0);
|
||||
@@ -167,7 +164,7 @@ public final class ColorSpaces {
|
||||
System.arraycopy(md5, 0, data, ICC_Profile.icHdrProfileID, md5.length);
|
||||
|
||||
// ICC profile header is the first 128 bytes
|
||||
return Arrays.copyOf(data, 128);
|
||||
return Arrays.copyOf(data, ICC_PROFILE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
private static byte[] computeMD5(byte[] data) {
|
||||
@@ -203,25 +200,35 @@ public final class ColorSpaces {
|
||||
Key key = new Key(profileHeader);
|
||||
|
||||
synchronized (cache) {
|
||||
ICC_ColorSpace cs = cache.get(key);
|
||||
ICC_ColorSpace cs = getCachedCS(key);
|
||||
|
||||
if (cs == null) {
|
||||
cs = new ICC_ColorSpace(profile);
|
||||
|
||||
validateColorSpace(cs);
|
||||
|
||||
// On LCMS, validation *alters* the profile header, need to re-generate key
|
||||
key = profileCleaner.validationAltersProfileHeader()
|
||||
? new Key(getProfileHeaderWithProfileId(cs.getProfile()))
|
||||
: key;
|
||||
|
||||
cache.put(key, cs);
|
||||
|
||||
// On LCMS, validation *alters* the profile header, need to re-generate key
|
||||
if (profileCleaner.validationAltersProfileHeader()) {
|
||||
cache.put(new Key(getProfileHeaderWithProfileId(cs.getProfile())), cs);
|
||||
}
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
}
|
||||
|
||||
private static ICC_ColorSpace getCachedCS(Key profileKey) {
|
||||
synchronized (cache) {
|
||||
return cache.get(profileKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static ICC_ColorSpace getCachedCS(final byte[] profileHeader) {
|
||||
return getCachedCS(new Key(profileHeader));
|
||||
}
|
||||
|
||||
private static void validateColorSpace(final ICC_ColorSpace cs) {
|
||||
// Validate the color space, to avoid caching bad profiles/color spaces
|
||||
// Will throw IllegalArgumentException or CMMException if the profile is bad
|
||||
@@ -318,6 +325,137 @@ public final class ColorSpaces {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public static ICC_Profile readProfileRaw(final InputStream input) throws IOException {
|
||||
return ICC_Profile.getInstance(input);
|
||||
}
|
||||
|
||||
public static ICC_Profile readProfile(final InputStream input) throws IOException {
|
||||
// TODO: Implement this smarter?
|
||||
// Could read the header 128 bytes, get size + magic, then read read rest into array and feed the byte[] method...
|
||||
ICC_Profile profile = ICC_Profile.getInstance(input);
|
||||
|
||||
if (profile == null) {
|
||||
throw new IllegalArgumentException("Invalid ICC Profile Data");
|
||||
}
|
||||
|
||||
return createProfile(profile.getData());
|
||||
}
|
||||
|
||||
public static ICC_Profile createProfileRaw(final byte[] input) {
|
||||
try {
|
||||
return readProfileRaw(new ByteArrayInputStream(input));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalArgumentException("Invalid ICC Profile Data", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ICC_Profile createProfile(final byte[] input) {
|
||||
Validate.notNull(input, "input");
|
||||
|
||||
if (input.length < ICC_PROFILE_HEADER_SIZE) { // Can't be less than size of ICC header
|
||||
throw new IllegalArgumentException("Truncated ICC profile, length < 128: " + input.length);
|
||||
}
|
||||
int size = intBigEndian(input, 0);
|
||||
if (size < 0 || size > input.length) {
|
||||
throw new IllegalArgumentException("Truncated ICC profile, length < " + size + ": " + input.length);
|
||||
}
|
||||
|
||||
if (input[36] != 'a' || input[37] != 'c' || input[38] != 's' || input[39] != 'p') {
|
||||
throw new IllegalArgumentException("Not an ICC profile, missing file signature");
|
||||
}
|
||||
|
||||
// Look up in cache before returning, these are already validated
|
||||
byte[] profileHeader = getProfileHeaderWithProfileId(input);
|
||||
int csType = getCsType(profileHeader);
|
||||
|
||||
ICC_ColorSpace internal = getInternalCS(csType, profileHeader);
|
||||
if (internal != null) {
|
||||
return internal.getProfile();
|
||||
}
|
||||
|
||||
ICC_ColorSpace cached = getCachedCS(profileHeader);
|
||||
if (cached != null) {
|
||||
return cached.getProfile();
|
||||
}
|
||||
|
||||
// WEIRDNESS: Unlike the InputStream version, the byte version
|
||||
// of ICC_Profile.getInstance() does not discard extra bytes at the end.
|
||||
// We'll chop them off here for convenience
|
||||
byte[] profileBytes = input.length == size ? input : Arrays.copyOf(input, size);
|
||||
ICC_Profile profile = ICC_Profile.getInstance(profileBytes);
|
||||
|
||||
// We'll validate & cache by creating a color space and returning its profile...
|
||||
// TODO: Rewrite with separate cache for profiles...
|
||||
return createColorSpace(profile).getProfile();
|
||||
}
|
||||
|
||||
private static int intBigEndian(byte[] data, int index) {
|
||||
return (data[index] & 0xff) << 24 | (data[index + 1] & 0xff) << 16 | (data[index + 2] & 0xff) << 8 | (data[index + 3] & 0xff);
|
||||
}
|
||||
|
||||
private static int getCsType(byte[] profileHeader) {
|
||||
int csSig = intBigEndian(profileHeader, ICC_Profile.icHdrColorSpace);
|
||||
|
||||
// TODO: Wonder why they didn't just use the sig as type, when there is obviously a 1:1 mapping...
|
||||
|
||||
switch (csSig) {
|
||||
case ICC_Profile.icSigXYZData:
|
||||
return ColorSpace.TYPE_XYZ;
|
||||
case ICC_Profile.icSigLabData:
|
||||
return ColorSpace.TYPE_Lab;
|
||||
case ICC_Profile.icSigLuvData:
|
||||
return ColorSpace.TYPE_Luv;
|
||||
case ICC_Profile.icSigYCbCrData:
|
||||
return ColorSpace.TYPE_YCbCr;
|
||||
case ICC_Profile.icSigYxyData:
|
||||
return ColorSpace.TYPE_Yxy;
|
||||
case ICC_Profile.icSigRgbData:
|
||||
return ColorSpace.TYPE_RGB;
|
||||
case ICC_Profile.icSigGrayData:
|
||||
return ColorSpace.TYPE_GRAY;
|
||||
case ICC_Profile.icSigHsvData:
|
||||
return ColorSpace.TYPE_HSV;
|
||||
case ICC_Profile.icSigHlsData:
|
||||
return ColorSpace.TYPE_HLS;
|
||||
case ICC_Profile.icSigCmykData:
|
||||
return ColorSpace.TYPE_CMYK;
|
||||
// Note: There is no TYPE_* 10...
|
||||
case ICC_Profile.icSigCmyData:
|
||||
return ColorSpace.TYPE_CMY;
|
||||
case ICC_Profile.icSigSpace2CLR:
|
||||
return ColorSpace.TYPE_2CLR;
|
||||
case ICC_Profile.icSigSpace3CLR:
|
||||
return ColorSpace.TYPE_3CLR;
|
||||
case ICC_Profile.icSigSpace4CLR:
|
||||
return ColorSpace.TYPE_4CLR;
|
||||
case ICC_Profile.icSigSpace5CLR:
|
||||
return ColorSpace.TYPE_5CLR;
|
||||
case ICC_Profile.icSigSpace6CLR:
|
||||
return ColorSpace.TYPE_6CLR;
|
||||
case ICC_Profile.icSigSpace7CLR:
|
||||
return ColorSpace.TYPE_7CLR;
|
||||
case ICC_Profile.icSigSpace8CLR:
|
||||
return ColorSpace.TYPE_8CLR;
|
||||
case ICC_Profile.icSigSpace9CLR:
|
||||
return ColorSpace.TYPE_9CLR;
|
||||
case ICC_Profile.icSigSpaceACLR:
|
||||
return ColorSpace.TYPE_ACLR;
|
||||
case ICC_Profile.icSigSpaceBCLR:
|
||||
return ColorSpace.TYPE_BCLR;
|
||||
case ICC_Profile.icSigSpaceCCLR:
|
||||
return ColorSpace.TYPE_CCLR;
|
||||
case ICC_Profile.icSigSpaceDCLR:
|
||||
return ColorSpace.TYPE_DCLR;
|
||||
case ICC_Profile.icSigSpaceECLR:
|
||||
return ColorSpace.TYPE_ECLR;
|
||||
case ICC_Profile.icSigSpaceFCLR:
|
||||
return ColorSpace.TYPE_FCLR;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid ICC color space signature: " + csSig); // TODO: fourCC?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color space specified by the given color space constant.
|
||||
* <p>
|
||||
@@ -465,7 +603,7 @@ public final class ColorSpaces {
|
||||
}
|
||||
}
|
||||
|
||||
// Cache header profile data to avoid excessive array creation/copying in static inner class for on-demand lazy init
|
||||
// Cache header profile data to avoid excessive array creation/copying. Use static inner class for on-demand lazy init
|
||||
private static class sRGB {
|
||||
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_sRGB));
|
||||
}
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider;
|
||||
|
||||
/**
|
||||
* This class exists to force early invocation of {@code ProfileDeferralMgr.activateProfiles()},
|
||||
* in an attempt to avoid JDK-6986863 and related bugs in Java < 17.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-6986863">JDK-6986863</a>
|
||||
*/
|
||||
final class ProfileDeferralActivator {
|
||||
|
||||
static {
|
||||
activateProfilesInternal();
|
||||
}
|
||||
|
||||
private static void activateProfilesInternal() {
|
||||
try {
|
||||
// Force invocation of ProfileDeferralMgr.activateProfiles() to avoid JDK-6986863 and friends.
|
||||
// Relies on static initializer in ColorConvertOp to actually invoke ProfileDeferralMgr.activateProfiles()
|
||||
Class.forName("java.awt.image.ColorConvertOp");
|
||||
}
|
||||
catch (Throwable disasters) {
|
||||
System.err.println("ProfileDeferralMgr.activateProfiles() failed. ICC Color Profiles may not work properly, see stack trace below.");
|
||||
System.err.println("For more information, see https://bugs.openjdk.java.net/browse/JDK-6986863");
|
||||
System.err.println("Please upgrade to Java 17 or later where this bug is fixed, or ask your JRE provider to backport the fix.");
|
||||
System.err.println();
|
||||
System.err.println("If you can't update to Java 17, a possible workaround is to add");
|
||||
System.err.println("\tClass.forName(\"java.awt.image.ColorConvertOp\");");
|
||||
System.err.println("*early* in your application startup code, to force profile activation before profiles are accessed.");
|
||||
System.err.println();
|
||||
|
||||
disasters.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static void activateProfiles() {
|
||||
// This method exists for other classes in the package to
|
||||
// ensure this class' static initializer is run.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not a service provider, but exploits the SPI mechanism as a hook to force early profile activation.
|
||||
*/
|
||||
public static final class Spi extends ImageInputStreamSpi {
|
||||
@Override public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
activateProfiles();
|
||||
|
||||
deregisterProvider(registry, this, category);
|
||||
}
|
||||
|
||||
@Override public String getDescription(Locale locale) {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@Override public ImageInputStream createInputStreamInstance(Object input, boolean useCache, File cacheDir) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,19 +158,18 @@ public final class IIOUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL BE MOVED/RENAMED, DO NOT USE.
|
||||
*
|
||||
* @param registry the registry to unregister from.
|
||||
* @param provider the provider to unregister.
|
||||
* @param category the category to unregister from.
|
||||
*/
|
||||
public static <T> void deregisterProvider(final ServiceRegistry registry, final IIOServiceProvider provider, final Class<T> category) {
|
||||
// http://www.ibm.com/developerworks/java/library/j-jtp04298.html
|
||||
registry.deregisterServiceProvider(category.cast(provider), category);
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL BE MOVED/RENAMED, DO NOT USE.
|
||||
*
|
||||
* @param registry the registry to lookup from.
|
||||
* @param providerClassName name of the provider class.
|
||||
|
||||
+67
-1
@@ -35,6 +35,7 @@ import org.junit.Test;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -204,7 +205,7 @@ public class ColorSpacesTest {
|
||||
|
||||
@Test
|
||||
public void testEqualHeadersDifferentProfile() throws IOException {
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header...
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header (but are different)...
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/color_match_rgb.icc"));
|
||||
|
||||
@@ -215,4 +216,69 @@ public class ColorSpacesTest {
|
||||
|
||||
assertNotSame(cs1, cs2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileBytesSame() throws IOException {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile1 = ColorSpaces.createProfile(profile.getData());
|
||||
ICC_Profile profile2 = ColorSpaces.createProfile(profile.getData());
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSame() throws IOException {
|
||||
ICC_Profile profile1 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileDifferent() throws IOException {
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header (but are different profiles)...
|
||||
ICC_Profile profile1 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/color_match_rgb.icc"));
|
||||
|
||||
assertNotSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileBytesSameAsCached() throws IOException {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_ColorSpace cs1 = ColorSpaces.createColorSpace(profile);
|
||||
ICC_Profile profile2 = ColorSpaces.createProfile(profile.getData());
|
||||
|
||||
assertEquals(cs1.getProfile(), profile2);
|
||||
assertSame(cs1.getProfile(), profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSameAsCached() throws IOException {
|
||||
ICC_ColorSpace cs1 = ColorSpaces.createColorSpace(ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")));
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
|
||||
assertEquals(cs1.getProfile(), profile2);
|
||||
assertSame(cs1.getProfile(), profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileBytesSameAsInternal() {
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile2 = ColorSpaces.createProfile(ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData());
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSameAsInternal() throws IOException {
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(new ByteArrayInputStream(ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData()));
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
protected abstract ImageWriterSpi createProvider();
|
||||
|
||||
protected final T createWriter() throws IOException {
|
||||
return writerClass.cast(provider.createWriterInstance());
|
||||
return writerClass.cast(provider.createWriterInstance(null));
|
||||
}
|
||||
|
||||
protected abstract List<? extends RenderedImage> getTestData();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jai-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jep262-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||
|
||||
+7
-12
@@ -614,7 +614,7 @@ public final class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private ICC_Profile ensureDisplayProfile(final ICC_Profile profile) {
|
||||
private ICC_Profile ensureDisplayProfile(final ICC_Profile profile) throws IOException {
|
||||
// NOTE: This is probably not the right way to do it... :-P
|
||||
// TODO: Consider moving method to ColorSpaces class or new class in imageio.color package
|
||||
|
||||
@@ -630,7 +630,7 @@ public final class JPEGImageReader extends ImageReaderBase {
|
||||
|
||||
intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first
|
||||
|
||||
return ICC_Profile.getInstance(profileData);
|
||||
return ColorSpaces.createProfile(profileData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -954,7 +954,7 @@ public final class JPEGImageReader extends ImageReaderBase {
|
||||
int iccChunkDataSize = segment.data.length - segmentDataStart;
|
||||
int iccSize = segment.data.length < segmentDataStart + 4 ? 0 : intFromBigEndian(segment.data, segmentDataStart);
|
||||
|
||||
return readICCProfileSafe(stream, allowBadIndexes, iccSize, iccChunkDataSize);
|
||||
return readICCProfileSafe(stream, allowBadIndexes);
|
||||
}
|
||||
else if (!segments.isEmpty()) {
|
||||
// NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order...
|
||||
@@ -1012,25 +1012,20 @@ public final class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes, iccSize, iccChunkDataSize);
|
||||
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile, final int iccSize, final int iccChunkDataSize) throws IOException {
|
||||
if (iccSize < 0 || iccSize > iccChunkDataSize) {
|
||||
processWarningOccurred(String.format("Truncated 'ICC_PROFILE' chunk(s), size: %d. Ignoring ICC profile.", iccSize));
|
||||
return null;
|
||||
}
|
||||
|
||||
private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile) {
|
||||
try {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(stream);
|
||||
ICC_Profile profile = ColorSpaces.readProfileRaw(stream);
|
||||
|
||||
// NOTE: Need to ensure we have a display profile *before* validating, for the caching to work
|
||||
return allowBadProfile ? profile : ColorSpaces.validateProfile(ensureDisplayProfile(profile));
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
catch (IOException | RuntimeException e) {
|
||||
// NOTE: Throws either IllegalArgumentException or CMMException, depending on platform.
|
||||
// Usual reason: Broken tools store truncated ICC profiles in a single ICC_PROFILE chunk...
|
||||
processWarningOccurred(String.format("Bad 'ICC_PROFILE' chunk(s): %s. Ignoring ICC profile.", e.getMessage()));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
|
||||
+3
-3
@@ -30,6 +30,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.metadata.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.psd.PSD;
|
||||
@@ -42,7 +43,6 @@ import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
@@ -353,8 +353,8 @@ public final class JPEGSegmentUtil {
|
||||
Directory psd = new PSDReader().read(stream);
|
||||
Entry iccEntry = psd.getEntryById(PSD.RES_ICC_PROFILE);
|
||||
if (iccEntry != null) {
|
||||
ICC_ColorSpace colorSpace = new ICC_ColorSpace(ICC_Profile.getInstance((byte[]) iccEntry.getValue()));
|
||||
System.err.println("colorSpace: " + colorSpace);
|
||||
ICC_Profile profile = ColorSpaces.createProfileRaw((byte[]) iccEntry.getValue());
|
||||
System.err.println("ICC Profile: " + profile);
|
||||
}
|
||||
System.err.println("PSD: " + psd);
|
||||
System.err.println(TIFFReader.HexDump.dump(segment.data));
|
||||
|
||||
+42
-127
@@ -30,22 +30,27 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.metadata.tiff;
|
||||
|
||||
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getType;
|
||||
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.MetadataWriter;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getType;
|
||||
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength;
|
||||
|
||||
/**
|
||||
* TIFFWriter
|
||||
*
|
||||
@@ -57,24 +62,7 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
|
||||
private static final int WORD_LENGTH = 2;
|
||||
private static final int LONGWORD_LENGTH = 4;
|
||||
|
||||
// TODO: We probably want to gloss over client code writing IFDs in BigTIFF (or vice versa) somehow... Silently convert IFD -> IFD8
|
||||
private final boolean longOffsets;
|
||||
private final int offsetSize;
|
||||
private final long entryLength;
|
||||
private final int directoryCountLength;
|
||||
|
||||
public TIFFWriter() {
|
||||
this(LONGWORD_LENGTH);
|
||||
}
|
||||
|
||||
public TIFFWriter(int offsetSize) {
|
||||
this.offsetSize = Validate.isTrue(offsetSize == 4 || offsetSize == 8, offsetSize, "offsetSize must be 4 for TIFF or 8 for BigTIFF");
|
||||
|
||||
longOffsets = offsetSize == 8;
|
||||
directoryCountLength = longOffsets ? 8 : WORD_LENGTH;
|
||||
entryLength = 2 * WORD_LENGTH + 2 * offsetSize;
|
||||
}
|
||||
private static final long ENTRY_LENGTH = 12;
|
||||
|
||||
public boolean write(final Collection<? extends Entry> entries, final ImageOutputStream stream) throws IOException {
|
||||
return write(new IFD(entries), stream);
|
||||
@@ -103,7 +91,7 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
}
|
||||
|
||||
// Offset to next IFD (EOF)
|
||||
writeOffset(stream, 0);
|
||||
stream.writeInt(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -112,12 +100,7 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
// Header
|
||||
ByteOrder byteOrder = stream.getByteOrder();
|
||||
stream.writeShort(byteOrder == ByteOrder.BIG_ENDIAN ? TIFF.BYTE_ORDER_MARK_BIG_ENDIAN : TIFF.BYTE_ORDER_MARK_LITTLE_ENDIAN);
|
||||
stream.writeShort(longOffsets ? TIFF.BIGTIFF_MAGIC : TIFF.TIFF_MAGIC);
|
||||
|
||||
if (longOffsets) {
|
||||
stream.writeShort(offsetSize); // Always 8 in this case
|
||||
stream.writeShort(0);
|
||||
}
|
||||
stream.writeShort(42);
|
||||
}
|
||||
|
||||
public long writeIFD(final Collection<Entry> entries, final ImageOutputStream stream) throws IOException {
|
||||
@@ -139,42 +122,37 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
long dataSize = computeDataSize(ordered);
|
||||
|
||||
// Offset to this IFD
|
||||
final long ifdOffset = stream.getStreamPosition() + dataSize + offsetSize;
|
||||
final long ifdOffset = stream.getStreamPosition() + dataSize + LONGWORD_LENGTH;
|
||||
|
||||
if (!isSubIFD) {
|
||||
writeOffset(stream, ifdOffset);
|
||||
dataOffset += offsetSize;
|
||||
stream.writeInt(assertIntegerOffset(ifdOffset));
|
||||
dataOffset += LONGWORD_LENGTH;
|
||||
|
||||
// Seek to offset
|
||||
stream.seek(ifdOffset);
|
||||
}
|
||||
else {
|
||||
dataOffset += directoryCountLength + ordered.size() * entryLength;
|
||||
dataOffset += WORD_LENGTH + ordered.size() * ENTRY_LENGTH;
|
||||
}
|
||||
|
||||
// Write directory
|
||||
writeDirectoryCount(stream, ordered.size());
|
||||
stream.writeShort(ordered.size());
|
||||
|
||||
for (Entry entry : ordered) {
|
||||
// Write tag id, type & value count
|
||||
// Write tag id
|
||||
stream.writeShort((Integer) entry.getIdentifier());
|
||||
// Write tag type
|
||||
stream.writeShort(getType(entry));
|
||||
writeValueCount(stream, getCount(entry));
|
||||
// Write value count
|
||||
stream.writeInt(getCount(entry));
|
||||
|
||||
// Write value
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Directory) {
|
||||
if (value instanceof CompoundDirectory) {
|
||||
// Can't have both nested and linked IFDs
|
||||
throw new AssertionError("SubIFD cannot contain linked IFDs");
|
||||
}
|
||||
|
||||
// We can't write offset here, we need to write value, as both LONG/IFD and LONG8/IFD8 is allowed
|
||||
// TODO: Or possibly gloss over, by always writing IFD8 for BigTIFF?
|
||||
long streamPosition = stream.getStreamPosition() + offsetSize;
|
||||
writeValueInline(dataOffset, getType(entry), stream);
|
||||
if (entry.getValue() instanceof Directory) {
|
||||
// TODO: This could possibly be a compound directory, in which case the count should be > 1
|
||||
stream.writeInt(assertIntegerOffset(dataOffset));
|
||||
long streamPosition = stream.getStreamPosition();
|
||||
stream.seek(dataOffset);
|
||||
Directory subIFD = (Directory) value;
|
||||
Directory subIFD = (Directory) entry.getValue();
|
||||
writeIFD(subIFD, stream, true);
|
||||
dataOffset += computeDataSize(subIFD);
|
||||
stream.seek(streamPosition);
|
||||
@@ -187,26 +165,8 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
return ifdOffset;
|
||||
}
|
||||
|
||||
private void writeDirectoryCount(ImageOutputStream stream, int count) throws IOException {
|
||||
if (longOffsets) {
|
||||
stream.writeLong(count);
|
||||
}
|
||||
else {
|
||||
stream.writeShort(count);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeValueCount(ImageOutputStream stream, int count) throws IOException {
|
||||
if (longOffsets) {
|
||||
stream.writeLong(count);
|
||||
}
|
||||
else {
|
||||
stream.writeInt(count);
|
||||
}
|
||||
}
|
||||
|
||||
public long computeIFDSize(final Collection<? extends Entry> directory) {
|
||||
return directoryCountLength + computeDataSize(new IFD(directory)) + directory.size() * entryLength;
|
||||
public long computeIFDSize(final Collection<Entry> directory) {
|
||||
return WORD_LENGTH + computeDataSize(new IFD(directory)) + directory.size() * ENTRY_LENGTH;
|
||||
}
|
||||
|
||||
private long computeDataSize(final Directory directory) {
|
||||
@@ -219,13 +179,13 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
throw new IllegalArgumentException(String.format("Unknown size for entry %s", entry));
|
||||
}
|
||||
|
||||
if (length > offsetSize) {
|
||||
if (length > LONGWORD_LENGTH) {
|
||||
dataSize += length;
|
||||
}
|
||||
|
||||
if (entry.getValue() instanceof Directory) {
|
||||
Directory subIFD = (Directory) entry.getValue();
|
||||
long subIFDSize = directoryCountLength + computeDataSize(subIFD) + subIFD.size() * entryLength;
|
||||
long subIFDSize = WORD_LENGTH + subIFD.size() * ENTRY_LENGTH + computeDataSize(subIFD);
|
||||
dataSize += subIFDSize;
|
||||
}
|
||||
}
|
||||
@@ -273,11 +233,11 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
short type = getType(entry);
|
||||
long valueLength = getValueLength(type, getCount(entry));
|
||||
|
||||
if (valueLength <= offsetSize) {
|
||||
if (valueLength <= LONGWORD_LENGTH) {
|
||||
writeValueInline(entry.getValue(), type, stream);
|
||||
|
||||
// Pad
|
||||
for (long i = valueLength; i < offsetSize; i++) {
|
||||
for (long i = valueLength; i < LONGWORD_LENGTH; i++) {
|
||||
stream.write(0);
|
||||
}
|
||||
|
||||
@@ -388,28 +348,12 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
doubles = (double[]) value;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported type for TIFF DOUBLE: " + value.getClass());
|
||||
throw new IllegalArgumentException("Unsupported type for TIFF FLOAT: " + value.getClass());
|
||||
}
|
||||
|
||||
stream.writeDoubles(doubles, 0, doubles.length);
|
||||
|
||||
break;
|
||||
case TIFF.TYPE_LONG8:
|
||||
case TIFF.TYPE_SLONG8:
|
||||
if (longOffsets) {
|
||||
long[] longs;
|
||||
|
||||
if (value instanceof long[]) {
|
||||
longs = (long[]) value;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported type for TIFF LONG8: " + value.getClass());
|
||||
}
|
||||
|
||||
stream.writeLongs(longs, 0, longs.length);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported TIFF type: " + type);
|
||||
@@ -433,7 +377,6 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
break;
|
||||
case TIFF.TYPE_LONG:
|
||||
case TIFF.TYPE_SLONG:
|
||||
case TIFF.TYPE_IFD:
|
||||
stream.writeInt(((Number) value).intValue());
|
||||
break;
|
||||
case TIFF.TYPE_RATIONAL:
|
||||
@@ -448,13 +391,6 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
case TIFF.TYPE_DOUBLE:
|
||||
stream.writeDouble(((Number) value).doubleValue());
|
||||
break;
|
||||
case TIFF.TYPE_LONG8:
|
||||
case TIFF.TYPE_SLONG8:
|
||||
case TIFF.TYPE_IFD8:
|
||||
if (longOffsets) {
|
||||
stream.writeLong(((Number) value).longValue());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported TIFF type: " + type);
|
||||
@@ -463,39 +399,18 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
}
|
||||
|
||||
private void writeValueAt(final long dataOffset, final Object value, final short type, final ImageOutputStream stream) throws IOException {
|
||||
writeOffset(stream, dataOffset);
|
||||
stream.writeInt(assertIntegerOffset(dataOffset));
|
||||
long position = stream.getStreamPosition();
|
||||
stream.seek(dataOffset);
|
||||
writeValueInline(value, type, stream);
|
||||
stream.seek(position);
|
||||
}
|
||||
|
||||
public void writeOffset(final ImageOutputStream output, long offset) throws IOException {
|
||||
if (longOffsets) {
|
||||
output.writeLong(assertLongOffset(offset));
|
||||
}
|
||||
else {
|
||||
output.writeInt(assertIntegerOffset(offset)); // Treated as unsigned
|
||||
}
|
||||
}
|
||||
|
||||
public int offsetSize() {
|
||||
return offsetSize;
|
||||
}
|
||||
|
||||
private int assertIntegerOffset(final long offset) throws IIOException {
|
||||
if (offset < 0 || offset > Integer.MAX_VALUE - (long) Integer.MIN_VALUE) {
|
||||
private int assertIntegerOffset(long offset) throws IIOException {
|
||||
if (offset > Integer.MAX_VALUE - (long) Integer.MIN_VALUE) {
|
||||
throw new IIOException("Integer overflow for TIFF stream");
|
||||
}
|
||||
|
||||
return (int) offset;
|
||||
}
|
||||
|
||||
private long assertLongOffset(final long offset) throws IIOException {
|
||||
if (offset < 0) {
|
||||
throw new IIOException("Long overflow for BigTIFF stream");
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
+54
-27
@@ -30,11 +30,21 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.metadata.xmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.MetadataReader;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -43,13 +53,11 @@ import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.MetadataReader;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
/**
|
||||
* XMPReader
|
||||
@@ -67,10 +75,9 @@ public final class XMPReader extends MetadataReader {
|
||||
public Directory read(final ImageInputStream input) throws IOException {
|
||||
Validate.notNull(input, "input");
|
||||
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory factory = createDocumentBuilderFactory();
|
||||
|
||||
// TODO: Consider parsing using SAX?
|
||||
// TODO: Determine encoding and parse using a Reader...
|
||||
// TODO: Refactor scanner to return inputstream?
|
||||
@@ -79,9 +86,6 @@ public final class XMPReader extends MetadataReader {
|
||||
builder.setErrorHandler(new DefaultHandler());
|
||||
Document document = builder.parse(new InputSource(IIOUtil.createStreamAdapter(input)));
|
||||
|
||||
// XMLSerializer serializer = new XMLSerializer(System.err, System.getProperty("file.encoding"));
|
||||
// serializer.serialize(document);
|
||||
|
||||
String toolkit = getToolkit(document);
|
||||
Node rdfRoot = document.getElementsByTagNameNS(XMP.NS_RDF, "RDF").item(0);
|
||||
NodeList descriptions = document.getElementsByTagNameNS(XMP.NS_RDF, "Description");
|
||||
@@ -92,10 +96,33 @@ public final class XMPReader extends MetadataReader {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException(e); // TODO: Or IOException?
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
|
||||
// Security: Disable XInclude & expanding entity references ("bombs"), not needed for XMP
|
||||
factory.setXIncludeAware(false);
|
||||
factory.setExpandEntityReferences(false);
|
||||
|
||||
// Security: Enable "secure processing", to prevent DoS attacks
|
||||
factory.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
|
||||
// Security: Remove possibility to access external DTDs or Schema, not needed for XMP
|
||||
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
|
||||
|
||||
// Security: Disable loading of external DTD and entities, not needed for XMP
|
||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
private String getToolkit(Document document) {
|
||||
NodeList xmpmeta = document.getElementsByTagNameNS(XMP.NS_X, "xmpmeta");
|
||||
|
||||
@@ -109,7 +136,7 @@ public final class XMPReader extends MetadataReader {
|
||||
}
|
||||
|
||||
private XMPDirectory parseDirectories(final Node pParentNode, NodeList pNodes, String toolkit) {
|
||||
Map<String, List<Entry>> subdirs = new LinkedHashMap<String, List<Entry>>();
|
||||
Map<String, List<Entry>> subdirs = new LinkedHashMap<>();
|
||||
|
||||
for (Node desc : asIterable(pNodes)) {
|
||||
if (desc.getParentNode() != pParentNode) {
|
||||
@@ -127,7 +154,7 @@ public final class XMPReader extends MetadataReader {
|
||||
// Lookup
|
||||
List<Entry> dir = subdirs.get(node.getNamespaceURI());
|
||||
if (dir == null) {
|
||||
dir = new ArrayList<Entry>();
|
||||
dir = new ArrayList<>();
|
||||
subdirs.put(node.getNamespaceURI(), dir);
|
||||
}
|
||||
|
||||
@@ -139,7 +166,7 @@ public final class XMPReader extends MetadataReader {
|
||||
else {
|
||||
// TODO: This method contains loads of duplication an should be cleaned up...
|
||||
// Support attribute short-hand syntax
|
||||
Map<String, List<Entry>> subsubdirs = new LinkedHashMap<String, List<Entry>>();
|
||||
Map<String, List<Entry>> subsubdirs = new LinkedHashMap<>();
|
||||
|
||||
parseAttributesForKnownElements(subsubdirs, node);
|
||||
|
||||
@@ -161,7 +188,7 @@ public final class XMPReader extends MetadataReader {
|
||||
}
|
||||
}
|
||||
|
||||
List<Directory> entries = new ArrayList<Directory>(subdirs.size());
|
||||
List<Directory> entries = new ArrayList<>(subdirs.size());
|
||||
|
||||
// TODO: Should we still allow asking for a subdirectory by item id?
|
||||
for (Map.Entry<String, List<Entry>> entry : subdirs.entrySet()) {
|
||||
@@ -179,7 +206,7 @@ public final class XMPReader extends MetadataReader {
|
||||
|
||||
private RDFDescription parseAsResource(Node node) {
|
||||
// See: http://www.w3.org/TR/REC-rdf-syntax/#section-Syntax-parsetype-resource
|
||||
List<Entry> entries = new ArrayList<Entry>();
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
|
||||
for (Node child : asIterable(node.getChildNodes())) {
|
||||
if (child.getNodeType() != Node.ELEMENT_NODE) {
|
||||
@@ -204,7 +231,7 @@ public final class XMPReader extends MetadataReader {
|
||||
List<Entry> dir = subdirs.get(attr.getNamespaceURI());
|
||||
|
||||
if (dir == null) {
|
||||
dir = new ArrayList<Entry>();
|
||||
dir = new ArrayList<>();
|
||||
subdirs.put(attr.getNamespaceURI(), dir);
|
||||
}
|
||||
|
||||
@@ -216,7 +243,7 @@ public final class XMPReader extends MetadataReader {
|
||||
for (Node child : asIterable(node.getChildNodes())) {
|
||||
if (XMP.NS_RDF.equals(child.getNamespaceURI()) && "Alt".equals(child.getLocalName())) {
|
||||
// Support for <rdf:Alt><rdf:li> -> return a Map<String, Object> keyed on xml:lang
|
||||
Map<String, Object> alternatives = new LinkedHashMap<String, Object>();
|
||||
Map<String, Object> alternatives = new LinkedHashMap<>();
|
||||
for (Node alternative : asIterable(child.getChildNodes())) {
|
||||
if (XMP.NS_RDF.equals(alternative.getNamespaceURI()) && "li".equals(alternative.getLocalName())) {
|
||||
NamedNodeMap attributes = alternative.getAttributes();
|
||||
@@ -230,7 +257,7 @@ public final class XMPReader extends MetadataReader {
|
||||
else if (XMP.NS_RDF.equals(child.getNamespaceURI()) && ("Seq".equals(child.getLocalName()) || "Bag".equals(child.getLocalName()))) {
|
||||
// Support for <rdf:Seq><rdf:li> -> return array
|
||||
// Support for <rdf:Bag><rdf:li> -> return array/unordered collection (how can a serialized collection not have order?)
|
||||
List<Object> seq = new ArrayList<Object>();
|
||||
List<Object> seq = new ArrayList<>();
|
||||
|
||||
for (Node sequence : asIterable(child.getChildNodes())) {
|
||||
if (XMP.NS_RDF.equals(sequence.getNamespaceURI()) && "li".equals(sequence.getLocalName())) {
|
||||
|
||||
-345
@@ -1,345 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.metadata.tiff;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.*;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.imageio.stream.ImageOutputStreamImpl;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* TIFFWriterTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: TIFFWriterTest.java,v 1.0 18.07.13 09:53 haraldk Exp$
|
||||
*/
|
||||
public class BigTIFFWriterTest extends MetadataWriterAbstractTest {
|
||||
|
||||
@Override
|
||||
protected InputStream getData() throws IOException {
|
||||
// TODO: Replace with BigTIFF resource
|
||||
return getResource("/exif/exif-jpeg-segment.bin").openStream();
|
||||
}
|
||||
|
||||
protected TIFFReader createReader() {
|
||||
return new TIFFReader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TIFFWriter createWriter() {
|
||||
return new TIFFWriter(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReadSimple() throws IOException {
|
||||
ArrayList<Entry> entries = new ArrayList<>();
|
||||
entries.add(new TIFFEntry(TIFF.TAG_ORIENTATION, TIFF.TYPE_SHORT, 1));
|
||||
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_SHORT, 1600));
|
||||
entries.add(new AbstractEntry(TIFF.TAG_IMAGE_HEIGHT, 900) {});
|
||||
entries.add(new TIFFEntry(TIFF.TAG_ARTIST, TIFF.TYPE_ASCII, "Harald K."));
|
||||
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
|
||||
Directory directory = new AbstractDirectory(entries) {};
|
||||
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
|
||||
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
|
||||
createWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
|
||||
assertEquals(164, data.length);
|
||||
assertEquals('M', data[0]);
|
||||
assertEquals('M', data[1]);
|
||||
assertEquals(0, data[2]);
|
||||
assertEquals(43, data[3]);
|
||||
|
||||
Directory read = createReader().read(new ByteArrayImageInputStream(data));
|
||||
|
||||
assertNotNull(read);
|
||||
assertEquals(5, read.size());
|
||||
|
||||
// TODO: Assert that the tags are written in ascending order (don't test the read directory, but the file structure)!
|
||||
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_SOFTWARE));
|
||||
assertEquals("TwelveMonkeys ImageIO", read.getEntryById(TIFF.TAG_SOFTWARE).getValue());
|
||||
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_WIDTH));
|
||||
assertEquals(1600, read.getEntryById(TIFF.TAG_IMAGE_WIDTH).getValue());
|
||||
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_HEIGHT));
|
||||
assertEquals(900, read.getEntryById(TIFF.TAG_IMAGE_HEIGHT).getValue());
|
||||
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_ORIENTATION));
|
||||
assertEquals(1, read.getEntryById(TIFF.TAG_ORIENTATION).getValue());
|
||||
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_ARTIST));
|
||||
assertEquals("Harald K.", read.getEntryById(TIFF.TAG_ARTIST).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteMotorola() throws IOException {
|
||||
ArrayList<Entry> entries = new ArrayList<>();
|
||||
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
|
||||
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_LONG, Integer.MAX_VALUE));
|
||||
Directory directory = new AbstractDirectory(entries) {};
|
||||
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
|
||||
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
|
||||
|
||||
imageStream.setByteOrder(ByteOrder.BIG_ENDIAN); // BE = Motorola
|
||||
|
||||
createWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
|
||||
assertEquals(94, data.length);
|
||||
assertEquals('M', data[0]);
|
||||
assertEquals('M', data[1]);
|
||||
assertEquals(0, data[2]);
|
||||
assertEquals(43, data[3]);
|
||||
|
||||
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(data));
|
||||
|
||||
assertNotNull(read);
|
||||
assertEquals(2, read.size());
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_SOFTWARE));
|
||||
assertEquals("TwelveMonkeys ImageIO", read.getEntryById(TIFF.TAG_SOFTWARE).getValue());
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_WIDTH));
|
||||
assertEquals((long) Integer.MAX_VALUE, read.getEntryById(TIFF.TAG_IMAGE_WIDTH).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteIntel() throws IOException {
|
||||
ArrayList<Entry> entries = new ArrayList<>();
|
||||
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
|
||||
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_LONG, Integer.MAX_VALUE));
|
||||
Directory directory = new AbstractDirectory(entries) {};
|
||||
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
|
||||
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
|
||||
|
||||
imageStream.setByteOrder(ByteOrder.LITTLE_ENDIAN); // LE = Intel
|
||||
|
||||
createWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
|
||||
assertEquals(94, data.length);
|
||||
assertEquals('I', data[0]);
|
||||
assertEquals('I', data[1]);
|
||||
assertEquals(43, data[2]);
|
||||
assertEquals(0, data[3]);
|
||||
|
||||
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(data));
|
||||
|
||||
assertNotNull(read);
|
||||
assertEquals(2, read.size());
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_SOFTWARE));
|
||||
assertEquals("TwelveMonkeys ImageIO", read.getEntryById(TIFF.TAG_SOFTWARE).getValue());
|
||||
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_WIDTH));
|
||||
assertEquals((long) Integer.MAX_VALUE, read.getEntryById(TIFF.TAG_IMAGE_WIDTH).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestingIFD8Long8() throws IOException {
|
||||
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
|
||||
|
||||
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(artist)));
|
||||
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubSubIFD)));
|
||||
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubIFD)));
|
||||
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(subSubIFD)));
|
||||
|
||||
Directory directory = new IFD(Collections.<Entry>singletonList(subIFD));
|
||||
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
|
||||
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
|
||||
|
||||
createWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
|
||||
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(output.toByteArray()));
|
||||
|
||||
assertNotNull(read);
|
||||
assertEquals(1, read.size());
|
||||
assertEquals(subIFD, read.getEntryById(TIFF.TAG_SUB_IFD)); // Recursively tests content!
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestingIFDLong() throws IOException {
|
||||
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
|
||||
|
||||
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(artist)));
|
||||
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubSubIFD)));
|
||||
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubIFD)));
|
||||
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(subSubIFD)));
|
||||
|
||||
Directory directory = new IFD(Collections.<Entry>singletonList(subIFD));
|
||||
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
|
||||
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
|
||||
|
||||
createWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
|
||||
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(output.toByteArray()));
|
||||
|
||||
assertNotNull(read);
|
||||
assertEquals(1, read.size());
|
||||
assertEquals(subIFD, read.getEntryById(TIFF.TAG_SUB_IFD)); // Recursively tests content!
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWriteRead() throws IOException {
|
||||
Directory original = createReader().read(getDataAsIIS());
|
||||
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(256);
|
||||
ImageOutputStream imageOutput = ImageIO.createImageOutputStream(output);
|
||||
|
||||
try {
|
||||
createWriter().write(original, imageOutput);
|
||||
}
|
||||
finally {
|
||||
imageOutput.close();
|
||||
}
|
||||
|
||||
Directory read = createReader().read(new ByteArrayImageInputStream(output.toByteArray()));
|
||||
|
||||
assertEquals(original, read);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeIFDSize() throws IOException {
|
||||
ArrayList<Entry> entries = new ArrayList<>();
|
||||
entries.add(new TIFFEntry(TIFF.TAG_ORIENTATION, TIFF.TYPE_SHORT, 1));
|
||||
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_SHORT, 1600));
|
||||
entries.add(new AbstractEntry(TIFF.TAG_IMAGE_HEIGHT, 900) {});
|
||||
entries.add(new TIFFEntry(TIFF.TAG_ARTIST, TIFF.TYPE_ASCII, "Harald K."));
|
||||
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
|
||||
|
||||
TIFFWriter writer = createWriter();
|
||||
|
||||
ImageOutputStream stream = new NullImageOutputStream();
|
||||
writer.writeIFD(entries, stream);
|
||||
|
||||
assertEquals(140, writer.computeIFDSize(entries));
|
||||
assertEquals(148, stream.getStreamPosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeIFDSizeNestedIFD8Long8() throws IOException {
|
||||
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
|
||||
|
||||
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(artist)));
|
||||
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubSubIFD)));
|
||||
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubIFD)));
|
||||
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(subSubIFD)));
|
||||
|
||||
List<Entry> entries = Collections.<Entry>singletonList(subIFD);
|
||||
|
||||
TIFFWriter writer = createWriter();
|
||||
|
||||
ImageOutputStream stream = new NullImageOutputStream();
|
||||
writer.writeIFD(entries, stream);
|
||||
|
||||
assertEquals(162, writer.computeIFDSize(entries));
|
||||
assertEquals(170, stream.getStreamPosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeIFDSizeNestedIFDLong() throws IOException {
|
||||
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
|
||||
|
||||
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(artist)));
|
||||
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubSubIFD)));
|
||||
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubIFD)));
|
||||
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(subSubIFD)));
|
||||
|
||||
List<Entry> entries = Collections.<Entry>singletonList(subIFD);
|
||||
|
||||
TIFFWriter writer = createWriter();
|
||||
|
||||
ImageOutputStream stream = new NullImageOutputStream();
|
||||
writer.writeIFD(entries, stream);
|
||||
|
||||
assertEquals(162, writer.computeIFDSize(entries)); // 162 = 5 * (8 + 20) + 22
|
||||
assertEquals(170, stream.getStreamPosition()); // 170 = 8 + 5 * (8 + 20) + 22
|
||||
}
|
||||
|
||||
private static class NullImageOutputStream extends ImageOutputStreamImpl {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
streamPos++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
streamPos += len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
throw new UnsupportedOperationException("Method read not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
throw new UnsupportedOperationException("Method read not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-11
@@ -33,7 +33,6 @@ package com.twelvemonkeys.imageio.metadata.tiff;
|
||||
import com.twelvemonkeys.imageio.metadata.*;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -85,7 +84,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
|
||||
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
|
||||
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
|
||||
createWriter().write(directory, imageStream);
|
||||
new TIFFWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
@@ -133,7 +132,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
|
||||
|
||||
imageStream.setByteOrder(ByteOrder.BIG_ENDIAN); // BE = Motorola
|
||||
|
||||
createWriter().write(directory, imageStream);
|
||||
new TIFFWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
@@ -168,7 +167,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
|
||||
|
||||
imageStream.setByteOrder(ByteOrder.LITTLE_ENDIAN); // LE = Intel
|
||||
|
||||
createWriter().write(directory, imageStream);
|
||||
new TIFFWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
@@ -205,7 +204,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
|
||||
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
|
||||
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
|
||||
|
||||
createWriter().write(directory, imageStream);
|
||||
new TIFFWriter().write(directory, imageStream);
|
||||
imageStream.flush();
|
||||
|
||||
assertEquals(output.size(), imageStream.getStreamPosition());
|
||||
@@ -248,10 +247,9 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
|
||||
TIFFWriter writer = createWriter();
|
||||
|
||||
ImageOutputStream stream = new NullImageOutputStream();
|
||||
writer.writeIFD(entries, stream);
|
||||
writer.write(new IFD(entries), stream);
|
||||
|
||||
assertEquals(94, writer.computeIFDSize(entries));
|
||||
assertEquals(98, stream.getStreamPosition());
|
||||
assertEquals(stream.getStreamPosition(), writer.computeIFDSize(entries) + 12);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -268,10 +266,9 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
|
||||
TIFFWriter writer = createWriter();
|
||||
|
||||
ImageOutputStream stream = new NullImageOutputStream();
|
||||
writer.writeIFD(entries, stream);
|
||||
writer.write(new IFD(entries), stream);
|
||||
|
||||
assertEquals(92, writer.computeIFDSize(entries)); // 92 = 5 * (2 + 12) + 22
|
||||
assertEquals(96, stream.getStreamPosition()); // 96 = 4 + 5 * (2 + 12) + 22
|
||||
assertEquals(stream.getStreamPosition(), writer.computeIFDSize(entries) + 12);
|
||||
}
|
||||
|
||||
private static class NullImageOutputStream extends ImageOutputStreamImpl {
|
||||
|
||||
+83
-12
@@ -30,26 +30,32 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.metadata.xmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest;
|
||||
|
||||
/**
|
||||
* XMPReaderTest
|
||||
@@ -483,4 +489,69 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
|
||||
assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/PixelYDimension"), hasValue("550"));
|
||||
assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/NativeDigest"), hasValue("36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;A7F21D25E2C562F152B2C4ECC9E534DA"));
|
||||
}
|
||||
|
||||
@Test(timeout = 1500L)
|
||||
public void testNoExternalRequest() throws Exception {
|
||||
// TODO: Use dynamic port?
|
||||
try (HTTPServer server = new HTTPServer(7777)) {
|
||||
try {
|
||||
createReader().read(getResourceAsIIS("/xmp/xmp-jpeg-xxe.xml"));
|
||||
} catch (IOException ioe) {
|
||||
if (ioe.getMessage().contains("501")) {
|
||||
throw new AssertionError("Reading should not cause external requests", ioe);
|
||||
}
|
||||
|
||||
// Any other exception is a bug (but might happen if the parser does not support certain features)
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class HTTPServer implements AutoCloseable {
|
||||
private final ServerSocket server;
|
||||
private final Thread thread;
|
||||
|
||||
HTTPServer(int port) throws IOException {
|
||||
server = new ServerSocket(port, 1);
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override public void run() {
|
||||
serve();
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private void serve() {
|
||||
try {
|
||||
Socket client = server.accept();
|
||||
|
||||
// Get the input stream, don't care about the request
|
||||
try (InputStream inputStream = client.getInputStream()) {
|
||||
while (inputStream.available() > 0) {
|
||||
if (inputStream.read() == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Answer with 501, this will cause the client to throw IOException
|
||||
try (OutputStream outputStream = client.getOutputStream()) {
|
||||
outputStream.write("HTTP/1.0 501 Not Implemented\r\n\r\n".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (server.isClosed() && e instanceof SocketException) {
|
||||
// Socket closed due to server close, all good
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void close() throws Exception {
|
||||
server.close();
|
||||
thread.join(); // It's advised against throwing InterruptedException here, but this is not production code...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?><!DOCTYPE root [<!ENTITY % ext SYSTEM 'http://localhost:7777/xxx'> %ext;]>
|
||||
<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 10.16'>
|
||||
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:xmpMM='http://ns.adobe.com/xap/1.0/mm/'>
|
||||
<xmpMM:InstanceID>xmp.iid:7EDC21BF-371B-4189-90AF-C83A54A6A190</xmpMM:InstanceID>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<?xpacket end='w'?>
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pdf</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pict</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pnm</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-psd</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
@@ -55,7 +56,7 @@ final class ICCProfile extends PSDImageResource {
|
||||
@Override
|
||||
protected void readData(final ImageInputStream pInput) throws IOException {
|
||||
try (InputStream stream = IIOUtil.createStreamAdapter(pInput, size)) {
|
||||
profile = ICC_Profile.getInstance(stream);
|
||||
profile = ColorSpaces.readProfile(stream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* PSDImageWriteParam
|
||||
*/
|
||||
public final class PSDImageWriteParam extends ImageWriteParam {
|
||||
|
||||
PSDImageWriteParam() {
|
||||
this(Locale.getDefault());
|
||||
}
|
||||
|
||||
PSDImageWriteParam(final Locale locale) {
|
||||
super(locale);
|
||||
|
||||
compressionTypes = new String[] {
|
||||
"None",
|
||||
"PackBits",
|
||||
// Two ZIP compression types are defined in spec, never seen in the wild...
|
||||
// "ZIP",
|
||||
// "ZIP+Predictor",
|
||||
};
|
||||
compressionType = compressionTypes[1];
|
||||
canWriteCompressed = true;
|
||||
}
|
||||
|
||||
static int getCompressionType(final ImageWriteParam param) {
|
||||
if (param == null || param.getCompressionMode() != MODE_EXPLICIT || param.getCompressionType() == null || param.getCompressionType().equals("None")) {
|
||||
return PSD.COMPRESSION_NONE;
|
||||
}
|
||||
else if (param.getCompressionType().equals("PackBits")) {
|
||||
return PSD.COMPRESSION_RLE;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Unsupported compression type: %s", param.getCompressionType()));
|
||||
}
|
||||
}
|
||||
-400
@@ -1,400 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.RasterUtils;
|
||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Minimal ImageWriter for Adobe Photoshop Document (PSD) format.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDImageWriter.java,v 1.0 Apr 29, 2008 4:45:52 PM haraldk Exp$
|
||||
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/">Adobe Photoshop File Formats Specification<a>
|
||||
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">Adobe Photoshop File Format Summary<a>
|
||||
*/
|
||||
public final class PSDImageWriter extends ImageWriterBase {
|
||||
|
||||
PSDImageWriter(ImageWriterSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageWriteParam getDefaultWriteParam() {
|
||||
return new PSDImageWriteParam(getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(IIOMetadata streamMetadata, IIOImage iioImage, ImageWriteParam param) throws IOException {
|
||||
assertOutput();
|
||||
imageOutput.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
RenderedImage image = iioImage.getRenderedImage();
|
||||
SampleModel sampleModel = image.getSampleModel();
|
||||
|
||||
int colorComponents = image.getColorModel().getColorSpace().getNumComponents();
|
||||
int channels = sampleModel.getNumBands();
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
int bits = getBitsPerSample(sampleModel);
|
||||
int mode = getColorMode(image.getColorModel());
|
||||
// TODO: Allow stream metadata or param to force PSD/PSB (version 1/2)?
|
||||
boolean largeFormat = width > PSDHeader.PSD_MAX_SIZE || height > PSDHeader.PSD_MAX_SIZE;
|
||||
|
||||
new PSDHeader(channels, width, height, bits, mode, largeFormat).write(imageOutput);
|
||||
writeColorModeData(image, mode);
|
||||
writeImageResources(image, mode);
|
||||
|
||||
// Length of the layer and mask information section. (**PSB** length is 8 bytes.)
|
||||
// TODO: Write an empty dummy layer here, if there's alpha? See below... Or see if Photoshop handles alpha if no layers at all...
|
||||
if (largeFormat) {
|
||||
imageOutput.writeLong(0);
|
||||
}
|
||||
else {
|
||||
imageOutput.writeInt(0);
|
||||
}
|
||||
|
||||
processImageStarted(0);
|
||||
|
||||
// Image Data Section (composite layer only).
|
||||
// The last section of a Photoshop file contains the image pixel data.
|
||||
// Image data is stored in planar order: first all the red data, then all the green data, etc.
|
||||
// Each plane is stored in scan-line order, with no pad bytes,
|
||||
final int compression = PSDImageWriteParam.getCompressionType(param);
|
||||
imageOutput.writeShort(compression);
|
||||
|
||||
long byteCountPos = imageOutput.getStreamPosition();
|
||||
// PSB (large format) byte counts are actually 32 bit offsets, not 16 bit as described in spec
|
||||
int[] byteCounts = new int[compression == PSD.COMPRESSION_RLE ? height * channels : 0];
|
||||
imageOutput.skipBytes(byteCounts.length * (largeFormat ? 4 : 2));
|
||||
|
||||
// TODO: Loop over tiles?
|
||||
Raster tile = sampleModel.getTransferType() == DataBuffer.TYPE_INT && sampleModel instanceof SinglePixelPackedSampleModel
|
||||
? RasterUtils.asByteRaster(image.getTile(0, 0))
|
||||
: image.getTile(0, 0);
|
||||
|
||||
for (int channel = 0; channel < channels; channel++) {
|
||||
// TODO: Alpha issues:
|
||||
// 1. Alpha channel is written (but not read, because there are no layers, and alpha is considered present only if layer count is negative)
|
||||
// - Can we write a small hidden layer, just to have -1 layers?
|
||||
// 2. Alpha needs to be premultiplied against white background (to avoid inverse halo)
|
||||
Raster channelRaster = tile.createChild(0, 0, width, height, 0, 0, new int[] {channel});
|
||||
|
||||
switch (bits) {
|
||||
case 1:
|
||||
// TODO: Figure out why we can't write multi-pixel packed 1 bit samples as bytes...
|
||||
case 8:
|
||||
write8BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
|
||||
break;
|
||||
case 16:
|
||||
write16BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
|
||||
break;
|
||||
case 32:
|
||||
write32BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(); // Should be guarded against already
|
||||
}
|
||||
|
||||
processImageProgress(channel * 100f / channels);
|
||||
}
|
||||
|
||||
updateByteCounts(byteCountPos, byteCounts, largeFormat);
|
||||
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
private void updateByteCounts(long byteCountPos, int[] byteCounts, boolean largeFormat) throws IOException {
|
||||
if (byteCounts.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update byte counts for RLE
|
||||
long pos = imageOutput.getStreamPosition();
|
||||
|
||||
imageOutput.seek(byteCountPos);
|
||||
if (largeFormat) {
|
||||
imageOutput.writeInts(byteCounts, 0, byteCounts.length);
|
||||
}
|
||||
else {
|
||||
for (int byteCount : byteCounts) {
|
||||
imageOutput.writeShort(byteCount);
|
||||
}
|
||||
}
|
||||
|
||||
imageOutput.seek(pos);
|
||||
}
|
||||
|
||||
private void writeColorModeData(RenderedImage image, int mode) throws IOException {
|
||||
if (mode == PSD.COLOR_MODE_INDEXED) {
|
||||
IndexColorModel icm = (IndexColorModel) image.getColorModel();
|
||||
|
||||
// Indexed color images: length is 768; color data contains the color table for the image, in non-interleaved order.
|
||||
imageOutput.writeInt(768);
|
||||
byte[] colors = new byte[256];
|
||||
|
||||
icm.getReds(colors);
|
||||
imageOutput.write(colors);
|
||||
icm.getGreens(colors);
|
||||
imageOutput.write(colors);
|
||||
icm.getBlues(colors);
|
||||
imageOutput.write(colors);
|
||||
}
|
||||
else {
|
||||
imageOutput.writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeImageResources(RenderedImage image, int mode) throws IOException {
|
||||
// Length of image resource section. The length may be zero
|
||||
imageOutput.writeInt(0);
|
||||
long startImageResources = imageOutput.getStreamPosition();
|
||||
|
||||
// Write ICC color profile if not "native" sRGB or gray (or bitmap/indexed)
|
||||
if (mode != PSD.COLOR_MODE_BITMAP && mode != PSD.COLOR_MODE_INDEXED) {
|
||||
ColorSpace colorSpace = image.getColorModel().getColorSpace();
|
||||
if (!colorSpace.isCS_sRGB() && colorSpace instanceof ICC_ColorSpace) {
|
||||
ICC_Profile profile = ((ICC_ColorSpace) colorSpace).getProfile();
|
||||
ICCProfile.writeData(imageOutput, profile);
|
||||
}
|
||||
}
|
||||
|
||||
// Write creator software (Exif)
|
||||
Entry software = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO PSD writer " + originatingProvider.getVersion());
|
||||
PSDEXIF1Data.writeData(imageOutput, Collections.singleton(software));
|
||||
|
||||
long endImageResources = imageOutput.getStreamPosition();
|
||||
|
||||
// Update image resources length
|
||||
imageOutput.seek(startImageResources - 4);
|
||||
imageOutput.writeInt((int) (endImageResources - startImageResources));
|
||||
imageOutput.seek(endImageResources);
|
||||
}
|
||||
|
||||
private void write8BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
byte[] rowBytes = null;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
rowBytes = (byte[]) raster.getDataElements(0, y, width, 1, rowBytes);
|
||||
|
||||
// Photoshop likes to store CMYK values inverted (but not the alpha value)
|
||||
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
|
||||
for (int i = 0; i < rowBytes.length; i++) {
|
||||
rowBytes[i] = (byte) (0xff - rowBytes[i] & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
if (compression == PSD.COMPRESSION_NONE) {
|
||||
imageOutput.write(rowBytes);
|
||||
}
|
||||
else if (compression == PSD.COMPRESSION_RLE) {
|
||||
long startPos = imageOutput.getStreamPosition();
|
||||
|
||||
// The RLE compressed data follows, with each scan line compressed separately
|
||||
try (OutputStream stream = new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder())) {
|
||||
stream.write(rowBytes);
|
||||
}
|
||||
|
||||
long endPos = imageOutput.getStreamPosition();
|
||||
byteCounts[y + channel * height] = (int) (endPos - startPos);
|
||||
}
|
||||
else {
|
||||
throw new IIOException("PSD with ZIP compression not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void write16BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
short[] row = null;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
row = (short[]) raster.getDataElements(0, y, width, 1, row);
|
||||
|
||||
// Photoshop likes to store CMYK values inverted (but not the alpha value)
|
||||
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
|
||||
for (int i = 0; i < row.length; i++) {
|
||||
row[i] = (short) (0xffff - row[i] & 0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
if (compression == PSD.COMPRESSION_NONE) {
|
||||
imageOutput.writeShorts(row, 0, row.length);
|
||||
}
|
||||
else if (compression == PSD.COMPRESSION_RLE) {
|
||||
long startPos = imageOutput.getStreamPosition();
|
||||
|
||||
// The RLE compressed data follows, with each scan line compressed separately
|
||||
try (DataOutputStream stream = new DataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder()))) {
|
||||
for (short sample : row) {
|
||||
stream.writeShort(sample);
|
||||
}
|
||||
}
|
||||
|
||||
long endPos = imageOutput.getStreamPosition();
|
||||
byteCounts[y + channel * height] = (int) (endPos - startPos);
|
||||
}
|
||||
else {
|
||||
throw new IIOException("PSD with ZIP compression not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void write32BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
int[] row = null;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
row = (int[]) raster.getDataElements(0, y, width, 1, row);
|
||||
|
||||
// Photoshop likes to store CMYK values inverted (but not the alpha value)
|
||||
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
|
||||
for (int i = 0; i < row.length; i++) {
|
||||
row[i] = 0xffffffff - row[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (compression == PSD.COMPRESSION_NONE) {
|
||||
imageOutput.writeInts(row, 0, row.length);
|
||||
}
|
||||
else if (compression == PSD.COMPRESSION_RLE) {
|
||||
long startPos = imageOutput.getStreamPosition();
|
||||
|
||||
// The RLE compressed data follows, with each scan line compressed separately
|
||||
try (DataOutputStream stream = new DataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder()))) {
|
||||
for (int sample : row) {
|
||||
stream.writeInt(sample);
|
||||
}
|
||||
}
|
||||
|
||||
long endPos = imageOutput.getStreamPosition();
|
||||
byteCounts[y + channel * height] = (int) (endPos - startPos);
|
||||
}
|
||||
else {
|
||||
throw new IIOException("PSD with ZIP compression not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int getColorMode(ColorModel colorModel) {
|
||||
if (colorModel instanceof IndexColorModel) {
|
||||
if (colorModel.getPixelSize() == 1) {
|
||||
return PSD.COLOR_MODE_BITMAP;
|
||||
}
|
||||
else {
|
||||
return PSD.COLOR_MODE_INDEXED;
|
||||
}
|
||||
}
|
||||
|
||||
int csType = colorModel.getColorSpace().getType();
|
||||
switch (csType) {
|
||||
case ColorSpace.TYPE_GRAY:
|
||||
if (colorModel.getPixelSize() == 1) {
|
||||
return PSD.COLOR_MODE_BITMAP;
|
||||
}
|
||||
else {
|
||||
return PSD.COLOR_MODE_GRAYSCALE;
|
||||
}
|
||||
case ColorSpace.TYPE_RGB:
|
||||
return PSD.COLOR_MODE_RGB;
|
||||
case ColorSpace.TYPE_CMYK:
|
||||
return PSD.COLOR_MODE_CMYK;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported color space type for PSD: " + csType);
|
||||
}
|
||||
}
|
||||
|
||||
static int getBitsPerSample(SampleModel sampleModel) {
|
||||
int bits = sampleModel.getSampleSize(0);
|
||||
|
||||
for (int i = 1; i < sampleModel.getNumBands(); i++) {
|
||||
if (bits != sampleModel.getSampleSize(i)) {
|
||||
throw new IllegalArgumentException("All samples must be of equal size for PSD: " + bits);
|
||||
}
|
||||
}
|
||||
|
||||
switch (bits) {
|
||||
case 1:
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
return (short) bits;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported sample size for PSD (expected 1, 8, 16 or 32): " + bits);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
BufferedImage image = ImageIO.read(new File(args[0]));
|
||||
ImageIO.write(image, "PSD", new File("test.psd"));
|
||||
}
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriter;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter.getBitsPerSample;
|
||||
import static com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter.getColorMode;
|
||||
|
||||
/**
|
||||
* PSDImageWriterSpi
|
||||
*/
|
||||
public final class PSDImageWriterSpi extends ImageWriterSpiBase {
|
||||
|
||||
public PSDImageWriterSpi() {
|
||||
super(new PSDProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEncodeImage(ImageTypeSpecifier type) {
|
||||
// PSD supports:
|
||||
// - 1, 8, 16 or 32 bit/sample
|
||||
// - Number of samples <= 56
|
||||
// - RGB, CMYK, Gray, Indexed color
|
||||
try {
|
||||
getBitsPerSample(type.getSampleModel());
|
||||
getColorMode(type.getColorModel());
|
||||
}
|
||||
catch (IllegalArgumentException ignore) {
|
||||
// We can't write this type
|
||||
return false;
|
||||
}
|
||||
|
||||
return type.getNumBands() <= 56; // Can't be negative
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageWriter createWriterInstance(Object extension) {
|
||||
return new PSDImageWriter(this);
|
||||
}
|
||||
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Adobe Photoshop Document (PSD) image writer";
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -54,8 +54,8 @@ final class PSDProviderInfo extends ReaderWriterProviderInfo {
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.psd.PSDImageReader",
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi"},
|
||||
"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter",
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
|
||||
null,
|
||||
null,
|
||||
false, null, null, null, null,
|
||||
true, PSDMetadata.NATIVE_METADATA_FORMAT_NAME, PSDMetadata.NATIVE_METADATA_FORMAT_CLASS_NAME, null, null
|
||||
);
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* PSDImageWriterTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PSDImageWriterTest.java,v 1.0 05/05/2021 haraldk Exp$
|
||||
*/
|
||||
public class PSDImageWriterTest extends ImageWriterAbstractTest<PSDImageWriter> {
|
||||
@Override
|
||||
protected ImageWriterSpi createProvider() {
|
||||
return new PSDImageWriterSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends RenderedImage> getTestData() {
|
||||
return Arrays.asList(
|
||||
new BufferedImage(300, 200, BufferedImage.TYPE_INT_RGB),
|
||||
new BufferedImage(301, 199, BufferedImage.TYPE_INT_ARGB),
|
||||
new BufferedImage(299, 201, BufferedImage.TYPE_3BYTE_BGR),
|
||||
new BufferedImage(160, 90, BufferedImage.TYPE_4BYTE_ABGR),
|
||||
new BufferedImage(90, 160, BufferedImage.TYPE_BYTE_GRAY),
|
||||
new BufferedImage(30, 20, BufferedImage.TYPE_USHORT_GRAY),
|
||||
new BufferedImage(30, 20, BufferedImage.TYPE_BYTE_BINARY),
|
||||
new BufferedImage(30, 20, BufferedImage.TYPE_BYTE_INDEXED)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-reference</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-sgi</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tga</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-thumbsdb</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff-jai-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JAI Metadata Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff-jdk-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
||||
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BigTIFFImageWriterSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BigTIFFImageWriterSpi.java,v 1.0 18.09.13 12:46 haraldk Exp$
|
||||
*/
|
||||
public final class BigTIFFImageWriterSpi extends ImageWriterSpiBase {
|
||||
// TODO: Implement canEncodeImage better
|
||||
|
||||
public BigTIFFImageWriterSpi() {
|
||||
super(new BigTIFFProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||
// TODO: Test bit depths compatibility
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TIFFImageWriter createWriterInstance(final Object extension) {
|
||||
return new TIFFImageWriter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(final Locale locale) {
|
||||
return "BigTIFF image writer";
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -50,8 +50,8 @@ final class BigTIFFProviderInfo extends ReaderWriterProviderInfo {
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader",
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.tiff.BigTIFFImageReaderSpi"},
|
||||
"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter",
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.tiff.BigTIFFImageWriterSpi"},
|
||||
null,
|
||||
null,
|
||||
false, TIFFStreamMetadata.SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadataFormat", null, null,
|
||||
true, TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat", null, null
|
||||
);
|
||||
|
||||
+6
-2
@@ -30,14 +30,14 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
/**
|
||||
* CCITT Modified Huffman RLE, Group 3 (T4) and Group 4 (T6) fax compression.
|
||||
*
|
||||
@@ -198,6 +198,10 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
|
||||
try {
|
||||
decodeRow();
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
// Mask the AIOOBE as an IOException
|
||||
throw new IOException("Malformed CCITT stream", e);
|
||||
}
|
||||
catch (EOFException e) {
|
||||
// TODO: Rewrite to avoid throw/catch for normal flow...
|
||||
if (decodedLength != 0) {
|
||||
|
||||
+30
-52
@@ -30,45 +30,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tiff;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.createStreamAdapter;
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.CMMException;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.color.CIELabColorConverter;
|
||||
import com.twelvemonkeys.imageio.color.CIELabColorConverter.Illuminant;
|
||||
@@ -99,6 +60,35 @@ import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.CMMException;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.createStreamAdapter;
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* ImageReader implementation for Aldus/Adobe Tagged Image File Format (TIFF).
|
||||
* <p>
|
||||
@@ -2507,20 +2497,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
||||
Entry entry = currentIFD.getEntryById(TIFF.TAG_ICC_PROFILE);
|
||||
|
||||
if (entry != null) {
|
||||
byte[] value = (byte[]) entry.getValue();
|
||||
|
||||
// Validate ICC profile size vs actual value size
|
||||
int size = (value[0] & 0xff) << 24 | (value[1] & 0xff) << 16 | (value[2] & 0xff) << 8 | (value[3] & 0xff);
|
||||
if (size < 0 || size > value.length) {
|
||||
processWarningOccurred("Ignoring truncated ICC profile: Bad ICC profile size (" + size + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// WEIRDNESS: Reading profile from InputStream is somehow more compatible
|
||||
// than reading from byte array (chops off extra bytes + validates profile).
|
||||
ICC_Profile profile = ICC_Profile.getInstance(new ByteArrayInputStream(value));
|
||||
return ColorSpaces.validateProfile(profile);
|
||||
return ColorSpaces.createProfile((byte[]) entry.getValue());
|
||||
}
|
||||
catch (CMMException | IllegalArgumentException e) {
|
||||
processWarningOccurred("Ignoring broken/incompatible ICC profile: " + e.getMessage());
|
||||
|
||||
+20
-16
@@ -204,26 +204,26 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
long streamPosition = imageOutput.getStreamPosition();
|
||||
|
||||
long ifdSize = tiffWriter.computeIFDSize(entries.values());
|
||||
long stripOffset = streamPosition + tiffWriter.offsetSize() + ifdSize + tiffWriter.offsetSize();
|
||||
long stripOffset = streamPosition + 4 + ifdSize + 4;
|
||||
long stripByteCount = ((long) renderedImage.getWidth() * renderedImage.getHeight() * pixelSize + 7L) / 8L;
|
||||
|
||||
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, TIFF.TYPE_LONG, stripOffset));
|
||||
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, TIFF.TYPE_LONG, stripByteCount));
|
||||
|
||||
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes care of ordering tags
|
||||
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags
|
||||
nextIFDPointerOffset = imageOutput.getStreamPosition();
|
||||
|
||||
// If we have a previous IFD, update pointer
|
||||
if (streamPosition > lastIFDPointerOffset) {
|
||||
imageOutput.seek(lastIFDPointerOffset);
|
||||
tiffWriter.writeOffset(imageOutput, ifdPointer);
|
||||
imageOutput.writeInt((int) ifdPointer);
|
||||
imageOutput.seek(nextIFDPointerOffset);
|
||||
}
|
||||
|
||||
tiffWriter.writeOffset(imageOutput, 0); // Update next IFD pointer later
|
||||
imageOutput.writeInt(0); // Update next IFD pointer later
|
||||
}
|
||||
else {
|
||||
tiffWriter.writeOffset(imageOutput, 0); // Update current IFD pointer later
|
||||
imageOutput.writeInt(0); // Update current IFD pointer later
|
||||
}
|
||||
|
||||
long stripOffset = imageOutput.getStreamPosition();
|
||||
@@ -262,7 +262,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, TIFF.TYPE_LONG, stripOffset));
|
||||
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, TIFF.TYPE_LONG, stripByteCount));
|
||||
|
||||
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes care of ordering tags
|
||||
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes case of ordering tags
|
||||
|
||||
nextIFDPointerOffset = imageOutput.getStreamPosition();
|
||||
|
||||
@@ -270,10 +270,10 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
// However, need to update here, because to the writeIFD method writes the pointer, but at the incorrect offset
|
||||
// TODO: Refactor writeIFD to take an offset
|
||||
imageOutput.seek(lastIFDPointerOffset);
|
||||
tiffWriter.writeOffset(imageOutput, ifdPointer);
|
||||
imageOutput.writeInt((int) ifdPointer);
|
||||
imageOutput.seek(nextIFDPointerOffset);
|
||||
|
||||
tiffWriter.writeOffset(imageOutput, 0); // Next IFD pointer updated later
|
||||
imageOutput.writeInt(0); // Next IFD pointer updated later
|
||||
}
|
||||
|
||||
return nextIFDPointerOffset;
|
||||
@@ -959,15 +959,11 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
configureStreamByteOrder(streamMetadata, imageOutput);
|
||||
|
||||
writingSequence = true;
|
||||
sequenceTIFFWriter = new TIFFWriter(isBigTIFF() ? 8 : 4);
|
||||
sequenceTIFFWriter = new TIFFWriter();
|
||||
sequenceTIFFWriter.writeTIFFHeader(imageOutput);
|
||||
sequenceLastIFDPos = imageOutput.getStreamPosition();
|
||||
}
|
||||
|
||||
private boolean isBigTIFF() throws IOException {
|
||||
return "bigtiff".equalsIgnoreCase(getFormatName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToSequence(final IIOImage image, final ImageWriteParam param) throws IOException {
|
||||
if (!writingSequence) {
|
||||
@@ -1023,7 +1019,8 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
|
||||
BufferedImage original;
|
||||
// BufferedImage original = ImageIO.read(file);
|
||||
try (ImageInputStream inputStream = ImageIO.createImageInputStream(file)) {
|
||||
ImageInputStream inputStream = ImageIO.createImageInputStream(file);
|
||||
try {
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
|
||||
|
||||
if (!readers.hasNext()) {
|
||||
@@ -1053,6 +1050,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
|
||||
original = reader.read(0, param);
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
System.err.println("original: " + original);
|
||||
|
||||
@@ -1088,11 +1088,12 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
// output.deleteOnExit();
|
||||
|
||||
System.err.println("output: " + output);
|
||||
TIFFImageWriter writer = new TIFFImageWriter(new TIFFImageWriterSpi());
|
||||
TIFFImageWriter writer = new TIFFImageWriter(null);
|
||||
// ImageWriter writer = ImageIO.getImageWritersByFormatName("PNG").next();
|
||||
// ImageWriter writer = ImageIO.getImageWritersByFormatName("BMP").next();
|
||||
ImageOutputStream stream = ImageIO.createImageOutputStream(output);
|
||||
|
||||
try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
|
||||
try {
|
||||
writer.setOutput(stream);
|
||||
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
@@ -1110,6 +1111,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
||||
writer.write(null, new IIOImage(image, null, null), param);
|
||||
System.err.println("Write time: " + (System.currentTimeMillis() - start) + " ms");
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
System.err.println("output.length: " + output.length());
|
||||
|
||||
|
||||
-1
@@ -1,2 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi
|
||||
com.twelvemonkeys.imageio.plugins.tiff.BigTIFFImageWriterSpi
|
||||
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.tiff;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.ImageReaderAbstractTest.assertRGBEquals;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
/**
|
||||
* BigTIFFImageWriterTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BigTIFFImageWriterTest.java,v 1.0 19.09.13 13:22 haraldk Exp$
|
||||
*/
|
||||
public class BigTIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter> {
|
||||
@Override
|
||||
protected ImageWriterSpi createProvider() {
|
||||
return new BigTIFFImageWriterSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends RenderedImage> getTestData() {
|
||||
return Arrays.asList(
|
||||
new BufferedImage(300, 200, BufferedImage.TYPE_INT_RGB),
|
||||
new BufferedImage(301, 199, BufferedImage.TYPE_INT_ARGB),
|
||||
new BufferedImage(299, 201, BufferedImage.TYPE_3BYTE_BGR),
|
||||
new BufferedImage(160, 90, BufferedImage.TYPE_4BYTE_ABGR),
|
||||
new BufferedImage(90, 160, BufferedImage.TYPE_BYTE_GRAY),
|
||||
new BufferedImage(30, 20, BufferedImage.TYPE_USHORT_GRAY)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundrtip() throws IOException {
|
||||
TIFFImageWriter writer = createWriter();
|
||||
ImageReader reader = ImageIO.getImageReader(writer);
|
||||
|
||||
try (ImageInputStream input = ImageIO.createImageInputStream(getClassLoaderResource("/bigtiff/BigTIFF.tif"))) {
|
||||
reader.setInput(input);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
ByteArrayOutputStream temp = new ByteArrayOutputStream();
|
||||
try (ImageOutputStream output = ImageIO.createImageOutputStream(temp)) {
|
||||
writer.setOutput(output);
|
||||
writer.write(image);
|
||||
}
|
||||
finally {
|
||||
writer.dispose();
|
||||
}
|
||||
|
||||
// Validate we actually write BigTIFF
|
||||
byte[] data = temp.toByteArray();
|
||||
assertArrayEquals(new byte[] { 'M', 'M', 0, TIFF.BIGTIFF_MAGIC}, Arrays.copyOf(data, 4));
|
||||
|
||||
// Read image back and see that it is the same
|
||||
try (ImageInputStream stream = new ByteArrayImageInputStream(data)) {
|
||||
reader.setInput(stream);
|
||||
BufferedImage after = reader.read(0);
|
||||
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
assertRGBEquals("Pixel values differ: ", image.getRGB(x, y), after.getRGB(x, y), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -253,7 +253,7 @@ public class CCITTFaxDecoderStreamTest {
|
||||
@Test
|
||||
public void testDecodeMissingRows() throws IOException {
|
||||
// See https://github.com/haraldk/TwelveMonkeys/pull/225 and https://github.com/haraldk/TwelveMonkeys/issues/232
|
||||
InputStream inputStream = getClass().getResourceAsStream("/tiff/ccitt_tolessrows.tif");
|
||||
InputStream inputStream = getResourceAsStream("/tiff/ccitt_tolessrows.tif");
|
||||
|
||||
// Skip until StripOffsets: 8
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -299,7 +299,7 @@ public class CCITTFaxDecoderStreamTest {
|
||||
public void testMoreChangesThanColumnsFile() throws IOException {
|
||||
// See https://github.com/haraldk/TwelveMonkeys/issues/328
|
||||
// 26 changes on 24 columns: H0w1b, H1w1b, ..., H1w0b
|
||||
InputStream stream = getClass().getResourceAsStream("/tiff/ccitt-too-many-changes.tif");
|
||||
InputStream stream = getResourceAsStream("/tiff/ccitt-too-many-changes.tif");
|
||||
|
||||
// Skip bytes before StripOffsets: 86
|
||||
for (int i = 0; i < 86; i++) {
|
||||
@@ -336,7 +336,7 @@ public class CCITTFaxDecoderStreamTest {
|
||||
|
||||
@Test
|
||||
public void testG3AOE() throws IOException {
|
||||
InputStream inputStream = getClass().getResourceAsStream("/tiff/ccitt/g3aoe.tif");
|
||||
InputStream inputStream = getResourceAsStream("/tiff/ccitt/g3aoe.tif");
|
||||
|
||||
// Skip until StripOffsets: 8
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -353,4 +353,17 @@ public class CCITTFaxDecoderStreamTest {
|
||||
byte[] bytes = new byte[216 * 1168]; // 1728 x 1168 pixel, 1 bpp => 216 bytes * 1168
|
||||
new DataInputStream(stream).readFully(bytes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
@Test(expected = IOException.class)
|
||||
public void testAIOBEInCorruptStreamShouldThrowIOException() throws IOException {
|
||||
// From #645
|
||||
try (InputStream ccittFaxDecoderStream = new CCITTFaxDecoderStream(getResourceAsStream("/ccitt/645.ccitt"), 7, 4, 0, false)) {
|
||||
while(ccittFaxDecoderStream.read() != -1); // Just read until the end
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getResourceAsStream(String name) {
|
||||
return getClass().getResourceAsStream(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
��δΨ]L–Γ Hΰ„t\eΡΔGΘDn0�£ά―θ�‘σ™#£��βθΎGFtGGΔmΡ|�‘Ρβρ6�ήGD|Η
|
||||
N΄T�JΝΕΡ„#π�1"λ������������Μ’²θ�Ί�H!eΒ"Ψ4Α":DqC”;#Άρ΄B"&$Κ�@�|6)Θ&V{ ΄�Aq‡H·!;�ς‡P‚Ϊ&8„‰c‚Π"θ$&΄GD|Ό�EZ(�#TΕ^|[�`†96�σPPα ‚¤Δ–1²?SHp‚dq�‹,!Ζ!„“#°A$ ��
|
||||
@‚Η;r±�AΔDDπΑBvR
|
||||
΅Α$C ‚G‚� @… �BVw "c¤%<e~Θθ(s�1h�£ΐ¤#/ΠQ.b0Δ$
|
||||
ΒD�8@…""‘C�t0„Y|Έt£�Κph$�‰Η;�pAF
|
||||
ώ":�8‚Π�ΐν�΅�vΔ�ε8bΈ��±$9Η<φβΎ"
‰qR�ΘΰΉ<΅Θ8βqΒ�ΠA28Λ ¬¨EΠEAP!a5²9�Dq—FdG9NS�1HJ›¤&�β1†nM(DDB*�HωΡ�Ε��H!)α>Pι>%�3κ�ηΓ‘,Γ�Ο
|
||||
Λ”X+—"‡ ":ΔYΈ¤‘!Ι�β’�rpcƒ.lL8"8.G0Ζ�H ‚K�‰C°ΌDp@„γβ'���
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-webp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: WebP plugin</name>
|
||||
|
||||
+1
-1
@@ -287,7 +287,7 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
long chunkStart = imageInput.getStreamPosition();
|
||||
|
||||
if (nextChunk == WebP.CHUNK_ICCP) {
|
||||
iccProfile = ICC_Profile.getInstance(IIOUtil.createStreamAdapter(imageInput, chunkLength));
|
||||
iccProfile = ColorSpaces.readProfile(IIOUtil.createStreamAdapter(imageInput, chunkLength));
|
||||
}
|
||||
else {
|
||||
processWarningOccurred(String.format("Expected 'ICCP' chunk, '%s' chunk encountered", fourCC(nextChunk)));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-xwd</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: XWD plugin</name>
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Twelvemonkeys</name>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<connection>scm:git:https://github.com/haraldk/TwelveMonkeys</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/haraldk/TwelveMonkeys</developerConnection>
|
||||
<url>https://github.com/haraldk/TwelveMonkeys</url>
|
||||
<tag>HEAD</tag>
|
||||
<tag>twelvemonkeys-3.7.0</tag>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-image</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -60,7 +60,7 @@
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.3.3</version>
|
||||
<version>1.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user