diff --git a/pom.xml b/pom.xml
index eab2bbbb..4c8a0cbc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -309,18 +309,6 @@
sh.tak.appbundler
appbundle-maven-plugin
1.2.0
-
-
- org.codehaus.plexus
- plexus-archiver
- 3.6.0
-
-
- org.apache.velocity
- velocity-tools
- 2.0
-
-
package
@@ -329,6 +317,18 @@
+
+
+ org.apache.velocity
+ velocity-tools
+ 2.0
+
+
+ org.codehaus.plexus
+ plexus-archiver
+ 3.6.0
+
+
com.itextpdf.rups.RupsLauncher
Info.plist
@@ -340,4 +340,4 @@
-
\ No newline at end of file
+
diff --git a/src/main/java/com/itextpdf/rups/shims/RupsPdfString.java b/src/main/java/com/itextpdf/rups/shims/RupsPdfString.java
new file mode 100644
index 00000000..270a7579
--- /dev/null
+++ b/src/main/java/com/itextpdf/rups/shims/RupsPdfString.java
@@ -0,0 +1,39 @@
+package com.itextpdf.rups.shims;
+
+import com.itextpdf.kernel.pdf.PdfString;
+
+public class RupsPdfString extends PdfString {
+ public RupsPdfString(String value, String encoding) {
+ super(value, encoding);
+ }
+
+ public RupsPdfString(String value) {
+ super(value);
+ }
+
+ public RupsPdfString(byte[] content) {
+ super(content);
+ }
+
+ public RupsPdfString(PdfString unpatchedPdfString){
+ this(unpatchedPdfString.getValueBytes());
+ this.setHexWriting(unpatchedPdfString.isHexWriting());
+ this.encoding = unpatchedPdfString.getEncoding();
+ this.directOnly = !unpatchedPdfString.isIndirect();
+ this.indirectReference = unpatchedPdfString.getIndirectReference();
+ }
+
+ protected RupsPdfString(byte[] content, boolean hexWriting) {
+ super(content, hexWriting);
+ }
+
+ @Override
+ public String toString() {
+ String wrapper;
+ if(isHexWriting())
+ wrapper = "<%s>";
+ else
+ wrapper = "(%s)";
+ return String.format(wrapper, new String(encodeBytes(getValueBytes())));
+ }
+}
diff --git a/src/main/java/com/itextpdf/rups/view/contextmenu/CopyToClipboardAction.java b/src/main/java/com/itextpdf/rups/view/contextmenu/CopyToClipboardAction.java
index 8080b892..8c195f59 100644
--- a/src/main/java/com/itextpdf/rups/view/contextmenu/CopyToClipboardAction.java
+++ b/src/main/java/com/itextpdf/rups/view/contextmenu/CopyToClipboardAction.java
@@ -42,8 +42,14 @@ This file is part of the iText (R) project.
*/
package com.itextpdf.rups.view.contextmenu;
+import com.itextpdf.rups.view.itext.PdfTree;
+import com.itextpdf.rups.view.itext.treenodes.PdfObjectTreeNode;
+
import javax.swing.JTextPane;
import java.awt.Component;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
/**
@@ -54,23 +60,32 @@ This file is part of the iText (R) project.
public class CopyToClipboardAction extends AbstractRupsAction {
+ Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+
public CopyToClipboardAction(String name, Component invoker) {
super(name, invoker);
}
public void actionPerformed(ActionEvent e) {
boolean nothingSelected = false;
- JTextPane textPane = (JTextPane) invoker;
+
+ if (invoker instanceof JTextPane) {
+ JTextPane textPane = (JTextPane) invoker;
- if (textPane.getSelectedText() == null || textPane.getSelectedText().trim().length() == 0) {
- nothingSelected = true;
- textPane.selectAll();
- }
+ if (textPane.getSelectedText() == null || textPane.getSelectedText().trim().length() == 0) {
+ nothingSelected = true;
+ textPane.selectAll();
+ }
- textPane.copy();
+ textPane.copy();
- if (nothingSelected) {
- textPane.select(0, 0);
+ if (nothingSelected) {
+ textPane.select(0, 0);
+ }
+ } else if (invoker instanceof PdfTree) {
+ PdfTree tree = (PdfTree) invoker;
+ PdfObjectTreeNode selectionNode = (PdfObjectTreeNode) tree.getSelectionPath().getLastPathComponent();
+ clipboard.setContents(new StringSelection(selectionNode.getPdfObject().toString()), null);
}
}
}
diff --git a/src/main/java/com/itextpdf/rups/view/contextmenu/PdfTreeContextMenu.java b/src/main/java/com/itextpdf/rups/view/contextmenu/PdfTreeContextMenu.java
index 1ee055af..c8943c64 100644
--- a/src/main/java/com/itextpdf/rups/view/contextmenu/PdfTreeContextMenu.java
+++ b/src/main/java/com/itextpdf/rups/view/contextmenu/PdfTreeContextMenu.java
@@ -67,6 +67,10 @@ public static JPopupMenu getPopupMenu(Component component) {
new SaveToFilePdfTreeAction(Language.SAVE_RAW_BYTES_TO_FILE.getString(), component, true)
));
+ popup.add(getJMenuItem(
+ new CopyToClipboardAction(Language.COPY_TO_CLIPBOARD.getString(), component)
+ ));
+
popup.add(getJMenuItem(
new SaveToFilePdfTreeAction(Language.SAVE_TO_FILE.getString(), component, false)
));
diff --git a/src/main/java/com/itextpdf/rups/view/itext/treenodes/PdfObjectTreeNode.java b/src/main/java/com/itextpdf/rups/view/itext/treenodes/PdfObjectTreeNode.java
index fad819b1..f785565e 100644
--- a/src/main/java/com/itextpdf/rups/view/itext/treenodes/PdfObjectTreeNode.java
+++ b/src/main/java/com/itextpdf/rups/view/itext/treenodes/PdfObjectTreeNode.java
@@ -48,6 +48,7 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.pdf.PdfObject;
import com.itextpdf.kernel.pdf.PdfString;
import com.itextpdf.rups.model.LoggerHelper;
+import com.itextpdf.rups.shims.RupsPdfString;
import com.itextpdf.rups.view.Language;
import com.itextpdf.rups.view.icons.IconFetcher;
import com.itextpdf.rups.view.icons.IconTreeNode;
@@ -132,6 +133,8 @@ protected PdfObjectTreeNode(PdfObject object) {
break;
case PdfObject.STRING:
icon = IconFetcher.getIcon(STRING_ICON);
+ // Patch to normalise PdfString's toString() behaviour with the remainder of iText's serialization.
+ this.object = new RupsPdfString((PdfString) object);
break;
}
}
@@ -398,4 +401,12 @@ public PdfName getPdfDictionaryType() {
}
return null;
}
+
+ /**
+ * Returns the Key that forms the node's final path component.
+ * @return a {@link PdfName} object or {@code null}
+ */
+ public PdfName getKey() {
+ return key;
+ }
}
diff --git a/src/test/java/com/itextpdf/rups/shims/RupsPdfStringTest.java b/src/test/java/com/itextpdf/rups/shims/RupsPdfStringTest.java
new file mode 100644
index 00000000..71c6190c
--- /dev/null
+++ b/src/test/java/com/itextpdf/rups/shims/RupsPdfStringTest.java
@@ -0,0 +1,63 @@
+package com.itextpdf.rups.shims;
+
+import com.itextpdf.test.annotations.type.UnitTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(UnitTest.class)
+public class RupsPdfStringTest {
+
+ @Test
+ public void toStringBasicTest() {
+ RupsPdfString pdfString = new RupsPdfString("Test");
+
+ Assert.assertEquals("String is () Delimited.", "(Test)", pdfString.toString());
+ }
+
+ @Test
+ public void toStringHexTest() {
+ byte[] hexArray = "Test".getBytes();
+ StringBuilder hexString = new StringBuilder("<");
+ for (byte b : hexArray) {
+ hexString.append(Integer.toHexString(b));
+ }
+ hexString.append(">");
+
+ RupsPdfString pdfString = new RupsPdfString(hexArray);
+ pdfString.setHexWriting(true);
+
+ Assert.assertEquals("String is <> Delimited.", hexString.toString(), pdfString.toString());
+ }
+
+ @Test
+ public void toStringBalancedTest(){
+ String balanced = "Test (of paretheses)";
+ RupsPdfString pdfString = new RupsPdfString(balanced);
+ Assert.assertEquals("Balanced parens are escaped:", "(Test \\(of paretheses\\))", pdfString.toString());
+ // Note: This is optional, but performed this way in iText to avoid too much overhead evaluating the balance of symbols.
+ }
+
+ @Test
+ public void toStringUnbalancedTest() {
+ String unbalanced = "Test :-)";
+ RupsPdfString pdfString = new RupsPdfString(unbalanced);
+ Assert.assertEquals("Unbalanced parens are escaped:", "(Test :-\\))", pdfString.toString());
+ }
+
+ @Test
+ public void toStringUnbalancedTest_Two() {
+ String unbalanced_two = ")Test :-(";
+ RupsPdfString pdfString_two = new RupsPdfString(unbalanced_two);
+ Assert.assertEquals("Unbalanced parens are escaped:", "(\\)Test :-\\()", pdfString_two.toString());
+ }
+
+ @Test
+ public void toStringUnbalancedTest_Three(){
+ String unbalanced_three = "@<----(( Robin Hood Test";
+ RupsPdfString pdfString_three = new RupsPdfString(unbalanced_three);
+ Assert.assertEquals("Unbalanced parens are escaped:", "(@<----\\(\\( Robin Hood Test)", pdfString_three.toString());
+ }
+
+}
+
diff --git a/src/test/java/com/itextpdf/rups/view/contextmenu/PdfTreeContextMenuTest.java b/src/test/java/com/itextpdf/rups/view/contextmenu/PdfTreeContextMenuTest.java
new file mode 100644
index 00000000..49b61ec1
--- /dev/null
+++ b/src/test/java/com/itextpdf/rups/view/contextmenu/PdfTreeContextMenuTest.java
@@ -0,0 +1,47 @@
+package com.itextpdf.rups.view.contextmenu;
+
+import com.itextpdf.test.annotations.type.UnitTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.MenuElement;
+import java.util.Arrays;
+
+@Category(UnitTest.class)
+public class PdfTreeContextMenuTest {
+
+ @Test
+ public void jMenuLengthTest() {
+ JPopupMenu popupMenu = PdfTreeContextMenu.getPopupMenu(null);
+
+ MenuElement[] subElements = popupMenu.getSubElements();
+ Assert.assertEquals(4, subElements.length);
+ }
+
+ @Test
+ public void jMenuSubItemTypeTest() {
+ JPopupMenu popupMenu = PdfTreeContextMenu.getPopupMenu(null);
+
+ MenuElement[] subElements = popupMenu.getSubElements();
+
+ Assert.assertTrue(Arrays.stream(subElements).allMatch(menuElement -> menuElement instanceof JMenuItem));
+ }
+
+ @Test
+ public void assignedActionsTest() {
+ JPopupMenu popupMenu = PdfTreeContextMenu.getPopupMenu(null);
+
+ MenuElement[] subElements = popupMenu.getSubElements();
+
+ Assert.assertTrue(Arrays.stream(subElements).anyMatch(element -> ((JMenuItem) element).getAction() instanceof InspectObjectAction));
+
+ Assert.assertTrue(Arrays.stream(subElements).anyMatch(element -> ((JMenuItem) element).getAction() instanceof SaveToFilePdfTreeAction));
+
+ Assert.assertTrue(Arrays.stream(subElements).anyMatch(element -> ((JMenuItem) element).getAction() instanceof CopyToClipboardAction));
+
+ Assert.assertTrue(Arrays.stream(subElements).anyMatch(element -> ((JMenuItem) element).getAction() instanceof SaveToFilePdfTreeAction));
+ }
+}