From 87f50e4a8737c8a80abdf85f41404ba4161b4534 Mon Sep 17 00:00:00 2001 From: Laszlo Balazs-Csiki Date: Mon, 15 Jan 2024 22:48:27 +0100 Subject: [PATCH] metadata refactoring --- pom.xml | 6 - .../pixelitor/menus/file/MetaDataPanel.java | 175 +++++++----------- .../menus/file/MetaDataTreeTableModel.java | 85 +++------ 3 files changed, 85 insertions(+), 181 deletions(-) diff --git a/pom.xml b/pom.xml index 705298052..b859c67e8 100644 --- a/pom.xml +++ b/pom.xml @@ -123,12 +123,6 @@ - - com.drewnoakes - metadata-extractor - 2.19.0 - - org.swinglabs.swingx swingx-all diff --git a/src/main/java/pixelitor/menus/file/MetaDataPanel.java b/src/main/java/pixelitor/menus/file/MetaDataPanel.java index edec0db14..f343d6852 100644 --- a/src/main/java/pixelitor/menus/file/MetaDataPanel.java +++ b/src/main/java/pixelitor/menus/file/MetaDataPanel.java @@ -17,38 +17,38 @@ package pixelitor.menus.file; -import com.drew.imaging.ImageMetadataReader; -import com.drew.imaging.ImageProcessingException; -import com.drew.metadata.Metadata; import org.jdesktop.swingx.JXTreeTable; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import pixelitor.Composition; -import pixelitor.gui.GUIText; import pixelitor.gui.utils.DialogBuilder; import pixelitor.gui.utils.Dialogs; import pixelitor.gui.utils.PAction; -import pixelitor.io.FileUtils; +import pixelitor.menus.file.MetaDataTreeTableModel.NameValue; import pixelitor.utils.Messages; +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.stream.ImageInputStream; import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; import java.awt.BorderLayout; import java.awt.FlowLayout; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.dnd.*; import java.io.File; import java.io.IOException; -import java.util.List; +import java.io.UncheckedIOException; +import java.util.Iterator; import static java.awt.BorderLayout.CENTER; -import static java.awt.BorderLayout.EAST; import static java.awt.BorderLayout.NORTH; -import static java.awt.BorderLayout.WEST; import static java.awt.FlowLayout.LEFT; import static java.lang.String.format; import static pixelitor.gui.GUIText.CLOSE_DIALOG; -public class MetaDataPanel extends JPanel implements DropTargetListener { +public class MetaDataPanel extends JPanel { private final JXTreeTable treeTable; private MetaDataPanel(MetaDataTreeTableModel model) { @@ -62,8 +62,7 @@ private MetaDataPanel(MetaDataTreeTableModel model) { JScrollPane sp = new JScrollPane(treeTable); add(sp, CENTER); - JPanel northPanel = new JPanel(new BorderLayout()); - JPanel northLeftPanel = new JPanel(new FlowLayout(LEFT)); + JPanel northPanel = new JPanel(new FlowLayout(LEFT)); JButton expandButton = new JButton(new PAction( "Expand All", treeTable::expandAll)); @@ -73,33 +72,12 @@ private MetaDataPanel(MetaDataTreeTableModel model) { "Collapse All", treeTable::collapseAll)); collapseButton.setName("collapseButton"); - northLeftPanel.add(expandButton); - northLeftPanel.add(collapseButton); - northPanel.add(northLeftPanel, WEST); + northPanel.add(expandButton); + northPanel.add(collapseButton); - JButton helpButton = new JButton(new PAction( - GUIText.HELP, this::showHelp)); - - JPanel northRightPanel = new JPanel(); - northRightPanel.add(helpButton); - northPanel.add(northRightPanel, EAST); add(northPanel, NORTH); setupColumnsWidths(); - - new DropTarget(this, this); - } - - private void showHelp() { - String txt = "You can drag external multimedia files on the Metadata window " + - "to see their Exif, IPTC, etc. information." + - "
It can read a different set of files than the rest of Pixelitor." + - "


Supported file types: JPEG, TIFF, WebP, PNG, BMP, GIF, HEIC, PSD, " + - "ICO, PCX, MP3, WAV, QuickTime, MP4, AVI." + - "
Supported Camera Raw file types: NEF (Nikon), CR2 (Canon), " + - "ORF (Olympus), ARW (Sony),
RW2 (Panasonic), " + - "RWL (Leica), SRW (Samsung)."; - Dialogs.showInfoDialog(this, "Show Metadata Help", txt); } private void setupColumnsWidths() { @@ -107,81 +85,56 @@ private void setupColumnsWidths() { treeTable.getColumnModel().getColumn(1).setMinWidth(200); } - @Override - public void dragEnter(DropTargetDragEvent dtde) { - handleOngoingDrag(dtde); - } - - @Override - public void dragOver(DropTargetDragEvent dtde) { - handleOngoingDrag(dtde); - } - - @Override - public void dropActionChanged(DropTargetDragEvent dtde) { - - } - - @Override - public void dragExit(DropTargetEvent dte) { - - } - - @Override - public void drop(DropTargetDropEvent e) { - Transferable transferable = e.getTransferable(); - DataFlavor[] flavors = transferable.getTransferDataFlavors(); - for (DataFlavor flavor : flavors) { - if (flavor.isFlavorJavaFileListType()) { - // this is where we get after dropping a file or directory - e.acceptDrop(DnDConstants.ACTION_COPY); - - try { - @SuppressWarnings("unchecked") - List list = (List) transferable.getTransferData(flavor); - File file = list.getFirst(); - if (file.isFile()) { - changeFile(file); - } - } catch (UnsupportedFlavorException | IOException ex) { - e.rejectDrop(); - } - e.dropComplete(true); - return; + private static TreeNode extractMetadata(File file) throws IOException { + IIOMetadata metadata = null; + try (ImageInputStream iis = ImageIO.createImageInputStream(file)) { + Iterator readers = ImageIO.getImageReaders(iis); + if (readers.hasNext()) { + // pick the first available ImageReader + ImageReader reader = readers.next(); + // attach source to the reader + reader.setInput(iis, true); + // read metadata of first image + metadata = reader.getImageMetadata(0); } } - - // DataFlavor not recognized - e.rejectDrop(); - } - - private static void handleOngoingDrag(DropTargetDragEvent dtde) { - if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { - dtde.acceptDrag(DnDConstants.ACTION_COPY); - } else { - dtde.rejectDrag(); + String[] formatNames = metadata.getMetadataFormatNames(); + DefaultMutableTreeNode root = new DefaultMutableTreeNode("root"); + for (String formatName : formatNames) { + Node node = metadata.getAsTree(formatName); + root.add(toSwingNode(node)); } + return root; } - private void changeFile(File file) { - Metadata metadata = extractMetadata(file); - if (metadata != null) { - treeTable.setTreeTableModel(new MetaDataTreeTableModel(metadata)); - setupColumnsWidths(); - JDialog d = (JDialog) SwingUtilities.getWindowAncestor(this); - d.setTitle("Metadata for " + file.getName()); + private static DefaultMutableTreeNode toSwingNode(Node node) { + DefaultMutableTreeNode swingNode; + + swingNode = new DefaultMutableTreeNode(node.getNodeName()); + + NamedNodeMap map = node.getAttributes(); + if (map != null) { + int length = map.getLength(); + if (length == 1) { + NameValue nameValue = new NameValue(node.getNodeName(), map.item(0).getNodeValue()); + return new DefaultMutableTreeNode(nameValue); + } else { + for (int i = 0; i < length; i++) { + Node attr = map.item(i); + String nodeName = attr.getNodeName(); + String nodeValue = attr.getNodeValue(); + NameValue nameValue = new NameValue(nodeName, nodeValue); + swingNode.add(new DefaultMutableTreeNode(nameValue)); + } + } } - } - private static Metadata extractMetadata(File file) { - Metadata metadata; - try { - metadata = ImageMetadataReader.readMetadata(file); - } catch (ImageProcessingException | IOException e) { - Messages.showException(e); - return null; + NodeList nodeList = node.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node childNode = nodeList.item(i); + swingNode.add(toSwingNode(childNode)); } - return metadata; + return swingNode; } public static void showInDialog(Composition comp) { @@ -200,13 +153,13 @@ public static void showInDialog(Composition comp) { Messages.showError("File not found", msg, comp.getDialogParent()); return; } - if (FileUtils.hasTGAExtension(file.getName())) { - String msg = "Metadata for TGA files isn't supported yet."; - Messages.showError("TGA File", msg, comp.getDialogParent()); - return; + TreeNode metadataRoot; + try { + metadataRoot = extractMetadata(file); + } catch (IOException e) { + throw new UncheckedIOException(e); } - Metadata metadata = extractMetadata(file); - MetaDataPanel panel = new MetaDataPanel(new MetaDataTreeTableModel(metadata)); + MetaDataPanel panel = new MetaDataPanel(new MetaDataTreeTableModel(metadataRoot)); new DialogBuilder() .title("Metadata for " + file.getName()) .content(panel) diff --git a/src/main/java/pixelitor/menus/file/MetaDataTreeTableModel.java b/src/main/java/pixelitor/menus/file/MetaDataTreeTableModel.java index bd67bce93..8af906270 100644 --- a/src/main/java/pixelitor/menus/file/MetaDataTreeTableModel.java +++ b/src/main/java/pixelitor/menus/file/MetaDataTreeTableModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -17,66 +17,55 @@ package pixelitor.menus.file; -import com.drew.metadata.Directory; -import com.drew.metadata.Metadata; -import com.drew.metadata.Tag; import org.jdesktop.swingx.treetable.AbstractTreeTableModel; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; /** * The model for the metadata tree-table */ public class MetaDataTreeTableModel extends AbstractTreeTableModel { private static final String[] COLUMN_NAMES = {"Name", "Value"}; - private final List dirNodes = new ArrayList<>(); - public MetaDataTreeTableModel(Metadata metadata) { - super(new Object()); - for (Directory directory : metadata.getDirectories()) { - dirNodes.add(new DirNode(directory)); - } + public MetaDataTreeTableModel(TreeNode root) { + super(root); } @Override public Object getValueAt(Object node, int column) { - if (node instanceof DirNode dir) { - return switch (column) { - case 0 -> dir.name(); + DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node; + Object userObject = treeNode.getUserObject(); + return switch (userObject) { + case String s -> switch (column) { + // a String means a "directory node" with no value + case 0 -> s; case 1 -> null; default -> throw new IllegalStateException("Unexpected column: " + column); }; - } else if (node instanceof TagNode tag) { - return switch (column) { - case 0 -> tag.name(); - case 1 -> tag.value(); + case NameValue nameValue -> switch (column) { + // a leaf node was found + case 0 -> nameValue.name(); + case 1 -> nameValue.value(); default -> throw new IllegalStateException("Unexpected column: " + column); }; - } - return null; + default -> throw new IllegalStateException("Unexpected type: " + userObject.getClass().getName()); + }; } @Override public Object getChild(Object parent, int index) { - if (parent instanceof DirNode dir) { - return dir.nodes.get(index); - } - return dirNodes.get(index); + return ((TreeNode) parent).getChildAt(index); } @Override public int getChildCount(Object parent) { - if (parent instanceof DirNode dir) { - return dir.nodes.size(); - } - return dirNodes.size(); + return ((TreeNode) parent).getChildCount(); } @Override public int getIndexOfChild(Object parent, Object child) { - return ((TagNode) child).index(); + return ((TreeNode) parent).getIndex((TreeNode) child); } @Override @@ -89,38 +78,6 @@ public String getColumnName(int column) { return COLUMN_NAMES[column]; } - @Override - public boolean isLeaf(Object node) { - return node instanceof TagNode; - } - - static class DirNode { - private final Directory dir; - private final List nodes = new ArrayList<>(); - - DirNode(Directory dir) { - this.dir = dir; - Collection tags = dir.getTags(); - int tagIndex = 0; - for (Tag tag : tags) { - String tagName = tag.getTagName(); - if (tagName.startsWith("Unknown")) { - continue; - } - String description = tag.getDescription(); - if (description != null && description.startsWith("Unknown")) { - continue; - } - nodes.add(new TagNode(tagName, description, tagIndex)); - tagIndex++; - } - } - - public String name() { - return dir.getName(); - } - } - - private record TagNode(String name, String value, int index) { + public record NameValue(String name, String value) { } }