Comments (17)
Hi Steinar,
Short answer: JPEGImage10Metadata
is (currently) read-only, see the isReadOnly
method. Invoking mergeTree
/setFromTree
on an instance, will fail.
Long answer: You should still be able to achieve what you want, see #668. π
Especially this comment where I try to outline a workaround. I think it worked for jAlbum.
Try that first, but if you can't make it work, let me know, and we'll see if I can help.
from twelvemonkeys.
I did this instead, and then I got further:
try(var imageOutputStream = new MemoryCacheImageOutputStream(bytes)) {
new TIFFWriter().write(entries, imageOutputStream);
}
Now the EXIF metadata parses without throwing an EOFException.
Current problem: my replacement of the JFIF comment didn't survive the addition of modifying EXIF comments: https://gist.github.com/steinarb/da81e5d71a6d9e44058ef02598972f3a#file-oldalbumserviceprovider-java-L30
from twelvemonkeys.
from twelvemonkeys.
Sounds good!
I'll close this issue as resolved, unless you have more questions about JPEG metadata? If so just reopen, or create a new, more specific issue.
from twelvemonkeys.
Thanks, Harald! The workaround of using the default metadata of the writer and replacing the content with the modified metadata tree of the writer worked. Working code example below.
Next up for me is to find the exif segment of the metadata, if present, or create one of there isn't one, and then insert 3 values:
- last modified date
- the image description
- the user comment
Can I do this in a simple way from the existing metadata object (where I can see the exif data in the debugger)?
Or do I have to read the exif segment from the input stream?
And when/if I have the exif segent can I easily add it to the default metadata object?
I assume I will have to insert it into the tree?
File downloadImageUrlToTempFile(AlbumEntry albumEntry, Path tempDir) {
var imageUrl = albumEntry.getImageUrl();
if (imageUrl == null || imageUrl.isEmpty()) {
throw new OldAlbumException(String.format("Unable to download album entry matching id=%d, imageUrl is missing", albumEntry.getId()));
}
var fileName = findFileNamePartOfUrl(imageUrl);
var tempfile = tempDir.resolve(fileName).toFile();
IIOImage image = null;
ImageWriter writer = null;
try {
HttpURLConnection connection = getConnectionFactory().connect(imageUrl);
connection.setRequestMethod("GET");
try(var inputStream = ImageIO.createImageInputStream(connection.getInputStream())) {
var readers = ImageIO.getImageReaders(inputStream);
if (readers.hasNext()) {
var reader = readers.next();
writer = ImageIO.getImageWriter(reader);
reader.setInput(inputStream);
image = reader.readAll(0, null);
}
}
} catch (IOException e) {
throw new OldAlbumException(String.format("Unable to download album entry matching id=%d from url=\"%s\"", albumEntry.getId(), albumEntry.getImageUrl()), e);
}
var metadata = image.getMetadata();
var metadataAsTree = metadata.getAsTree("javax_imageio_1.0");
findJfifCommentNode(metadataAsTree)
.ifPresent(node -> node.setAttribute("value", albumEntry.getDescription()));
try (var outputStream = ImageIO.createImageOutputStream(new FileOutputStream(tempfile))){
writer.setOutput(outputStream);
var param = writer.getDefaultWriteParam();
var modifiedMetadata = writer.getDefaultImageMetadata(ImageTypeSpecifiers.createFromRenderedImage(image.getRenderedImage()), param);
modifiedMetadata.setFromTree("javax_imageio_1.0", metadataAsTree);
image.setMetadata(modifiedMetadata);
writer.write(image);
Files.setLastModifiedTime(tempfile.toPath(), FileTime.from(albumEntry.getLastModified().toInstant()));
return tempfile;
} catch (IOException e) {
throw new OldAlbumException(String.format("Unable to save local copy of album entry matching id=%d from url=\"%s\"", albumEntry.getId(), albumEntry.getImageUrl()), e);
}
}
Optional<IIOMetadataNode> findJfifCommentNode(Node metadataAsTree) {
return StreamSupport.stream(iterable(metadataAsTree.getChildNodes()).spliterator(), false)
.filter(n -> "Text".equals(n.getNodeName()))
.findFirst()
.flatMap(n -> StreamSupport.stream(iterable(n.getChildNodes()).spliterator(), false).findFirst());
}
public static Iterable<IIOMetadataNode> iterable(final NodeList nodeList) {
return () -> new Iterator<IIOMetadataNode>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < nodeList.getLength();
}
@Override
public IIOMetadataNode next() {
if (!hasNext())
throw new NoSuchElementException();
return (IIOMetadataNode) nodeList.item(index++);
}
};
}
from twelvemonkeys.
Adding exif to the tree is possibly covered here? #586 (comment)
(Seems a little cumbersome...?) Is it easier if the exif segment already exists I wonder?
(Edit: yes it easier if the exif segment exists, it says so in the text of the comment above the code example which (I think describes) how to create an exif segment from scratch)
from twelvemonkeys.
Tried the approach outlined in #586 (comment) and https://stackoverflow.com/q/36029295 but I got the following error message when setting the metadata:
Caused by: javax.imageio.metadata.IIOInvalidTreeException: Invalid node: markerSequence
Here is what I've tried to do: https://gist.github.com/steinarb/0ab59ad7c17b7ac15b757dd67368dad9
Here is the complete stack trace: https://gist.github.com/steinarb/a2ae1019aa90a4ade2ca49387f6135b9
I'm sure there is a simple error copying from the examples, but I'm unable to sport the error.
from twelvemonkeys.
Sorry, no time right now, but: javax_imageio_1.0
is the format neutral metadata format. You need the βJPEG nativeβ format to access/write Exif, ie javax_imageio_jpeg_image_1.0
.
from twelvemonkeys.
Did changing to the javax_imageio_jpeg_image_1.0
metadata format help? π
Your code does a lot, so it would probably help to organize things a little different to make things easier to understand... But I'm fairly sure this was the reason for the exception (as there is no markerSequece
in the "plug-in neutral" format).
The "plug-in neutral" format is good for getting a few essential values in a standard way, but it's not so good if you need to mess with format specific things like Exif in JPEG. I believe the JPEG native format always has a markerSequence
node, but I guess it doesn't hurt to be on the safe side...
javax_imageio_1.0 DTD
javax_imageio_jpeg_image_1.0 DTD
from twelvemonkeys.
from twelvemonkeys.
Ok, now I'm back to getting java.io.EOFException.
The exception comes when I'm trying to read the metadata of the transformed image in the test and it comes when trying to read a stream containing just "Exif" followed by to 0-byte values.
I.e. it looks like the line doesn't do anything:
new TIFFWriter().write(entries, new MemoryCacheImageOutputStream(bytes));
The size of the bytes ByteArrayOutputStream doesn't change in the above line.
This is the current version of the complete code: https://gist.github.com/steinarb/251e02cda3de13c9cc2845a2c9020f4a#file-oldalbumserviceprovider-java-L35
from twelvemonkeys.
The latest version (with try-with-resources) closes (and implicitly flushes) the stream, to commit from the memory cache to the underlying ByteArrayOuputStream
. The previous code would probably also have worked if you explicitly invoked flush()
on the MemoryCacheImageOutputStream
.
Not sure about why the comment didn't survive. As mentioned, there's too much "going on" for me to fully understand what's happening. Try writing this as a test case, and I'll be able to help you better. π
Are you sure the findJfifCommentNode
method works as intended? Are you sure there was an existing comment in the file to replace in the first place? Are you sure the ifPresent
block is even executed?
PS: This code won't work as intended:
var markerSequence = (IIOMetadataNode) metadataAsTree.getElementsByTagName("markerSequence").item(0);
if (markerSequence == null) {
markerSequence = new IIOMetadataNode("markerSequence");
metadataAsTree.appendChild(markerSequence);
}
If there is no "markerSequence"
node, the node list will be empty, and there won't be an item 0... You'll either get a node or an exception, markerSequence
will never be null
.
from twelvemonkeys.
from twelvemonkeys.
I'm trying to find the right node in the new tree. As said I can't see
the tree nodes well in the debugger and the debugger goes a bit crazy
when I try to navigate them.
One thing this odd XML-ish node structure is good for, is that it's.. well.. XML. π
You can dump the whole tree like this:
new XMLSerializer(System.out, "UTF-8").serialize(tree, false);
(using com.twelvemonkeys.xml. XMLSerializer
, other serializers will probably do fine too..)
The only thing you won't see there it the userObject
s, because the javax.imageio
API invented userObject
instead of reusing userData
, not really sure why...
I found that the comment was put into a node named "com" by stepping
through the build of the native tree in the debugger.But I found a node named "com" directly under markerSequence and tried
replacing it, but that probably wasn't the right one.
According to the DTD I referenced above, the com
(JFIF comment) marker is a child of markerSequence
. It can't appear anywhere else.
from twelvemonkeys.
Here's the tidied up code: https://gist.github.com/steinarb/ec1c22513a64870a880f21e134ea33fb
Now it will reside in a git branch waiting for the OSGi-fied version of Twelvemonkey to be released. π
Note that I'm currently just storing the description in the JFIF comment (i.e. the way I did things back in 1996), instead of in the EXIF usercomment.
It's trivial to store it there as well. I take suggestions as what to do (store it in the exif user comment? continue to use the JFIF comment? Use both?).
Do other programs use any of these fields?
I know google photo uses the lastmodifieddate EXIF field.
Does any applications out there use the EXIF ImageDescription?
Does any applications out there use either the JFIF comment (it has been around for a while...)?
Does any applications out there use the EXIF UserComment?
from twelvemonkeys.
Now it will reside in a git branch waiting for the OSGi-fied version of Twelvemonkey to be released. π
I thought I mentioned it, but 3.10.0 with your OSGi support was released last week.
I take suggestions as what to do (store it in the exif user comment? continue to use the JFIF comment? Use both?).
Do other programs use any of these fields?
Wikipedia lists a few programs, but that's just a small subset. I don't have a list of applications, but mostly use only Preview on MacOS, which displays a lot of TIFF and Exif/GPS metadata. You should probably just test with the software you use, or the software you want to support.
For Exif vs JFIF comment, it's a bit of apples and oranges discussion. The COM marker is just a generic "comment", while the TIFF/Exif tags have a specific semantic meaning, allowing for much richer metadata. I don't see a problem using both. There's also IPTC metadata and XMP metadata worth considering, if you want to jump into the meta data rabbit hole... π
from twelvemonkeys.
from twelvemonkeys.
Related Issues (20)
- Support for DDS (Microsoft DirectDraw Surface) HOT 14
- ImageUtil.createRotated() unexpectedly adds an alpha channel HOT 2
- TIFF: Support SAMPLEFORMAT_COMPLEXINT (5) HOT 3
- WebP plugin stops working when other plugins are enabled too HOT 2
- PSD: Get layer kind (type) to metadata HOT 1
- Twelvemonkeys Servlet should support Jakarta HOT 6
- JPEGImageReader read invalid image without throwing exception HOT 4
- PSD: When make PNG from PSD using TwelveMonkeys, it's different from Photoshop. HOT 4
- Change Maven GPG plugin to use Bouncy Castle signer
- Unsupported BitsPerSample for SampleFormat 3/Floating Point (expected 16/32): 64 HOT 4
- PSD: BufferOverflowException if encoder adds garbage to the end of rows
- Dynamic images in WebP format, and even ordinary GIF images, cannot read delays after using this plugin. HOT 2
- Warning system custom messages HOT 3
- Unable to read dynamic webp using javax.imageio.ImageIO#read(java.io.InputStream) on aws lambda HOT 1
- TIFF: ArrayIndexOutOfBoundsException in HorizontalDeDifferencingFloatingPointStream.decodeDeltaBytes HOT 3
- JPEG 2000 format support HOT 1
- TIFF: Decoding chunky gray + extra samples is broken HOT 1
- heic-image HOT 1
- WebP: "Coordinate out of bounds!" exception thrown when reading certain files HOT 2
- Please make BufferedChannelImageInputStream public HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from twelvemonkeys.