Skip to content

Commit 9b0f525

Browse files
committed
Improve PDF stream pane
* Everything related to the stream pane was moved to a separate package. * Added SYNTAX_STYLE_BINARY handling for the text editor. In this mode it uses the same painter (so that non-ASCII character are displayed in hex), but there is no fancy processing or coloration. Ideally we would have a separate hex editor pane for that... * Temporarily removed our custom menu dialog in the text editor. It was replacing the existing one in RSyntaxTextArea. Instead, we should add additional entries to the existing menu. TBD. * Removed our custom undo manager. RSyntaxManager was using its own, so it caused more issues, than it solved. * Added image stream view back in a basic form. For now, it just shows the image instead of the text editor, but there is no manipulation controls yet. * Additional refactoring.
1 parent f23a4a1 commit 9b0f525

24 files changed

+652
-257
lines changed

src/main/java/com/itextpdf/rups/controller/PdfReaderController.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ This file is part of the iText (R) project.
7272
import com.itextpdf.rups.view.itext.PdfObjectPanel;
7373
import com.itextpdf.rups.view.itext.PdfTree;
7474
import com.itextpdf.rups.view.itext.PlainText;
75+
import com.itextpdf.rups.view.itext.stream.StreamPane;
7576
import com.itextpdf.rups.view.itext.StructureTree;
76-
import com.itextpdf.rups.view.itext.StreamTextEditorPane;
7777
import com.itextpdf.rups.view.itext.XRefTable;
7878
import com.itextpdf.rups.view.itext.treenodes.PdfObjectTreeNode;
7979

@@ -135,7 +135,7 @@ public class PdfReaderController implements IPdfObjectPanelEventListener, IRupsE
135135
/**
136136
* A panel that will show a stream.
137137
*/
138-
protected StreamTextEditorPane streamPane;
138+
protected StreamPane streamPane;
139139

140140
/**
141141
* The factory producing tree nodes.
@@ -205,7 +205,7 @@ public PdfReaderController(TreeSelectionListener treeSelectionListener,
205205

206206
objectPanel = new PdfObjectPanel();
207207
objectPanel.addEventListener(this);
208-
streamPane = new StreamTextEditorPane(this);
208+
streamPane = new StreamPane(this);
209209
JScrollPane debug = new JScrollPane(DebugView.getInstance().getTextArea());
210210
editorTabs = new JTabbedPane();
211211
editorTabs.addTab(Language.STREAM.getString(), null, streamPane, Language.STREAM.getString());
@@ -255,16 +255,6 @@ public JTabbedPane getEditorTabs() {
255255
return editorTabs;
256256
}
257257

258-
/**
259-
* Getter for the object that holds the TextPane
260-
* with the content stream of a PdfStream object.
261-
*
262-
* @return a SyntaxHighlightedStreamPane
263-
*/
264-
public StreamTextEditorPane getStreamPane() {
265-
return streamPane;
266-
}
267-
268258
public PdfSyntaxParser getParser() {
269259
return parser;
270260
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2025 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License version 3
8+
as published by the Free Software Foundation with the addition of the
9+
following permission added to Section 15 as permitted in Section 7(a):
10+
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
11+
APRYSE GROUP. APRYSE GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
12+
OF THIRD PARTY RIGHTS
13+
14+
This program is distributed in the hope that it will be useful, but
15+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
or FITNESS FOR A PARTICULAR PURPOSE.
17+
See the GNU Affero General Public License for more details.
18+
You should have received a copy of the GNU Affero General Public License
19+
along with this program; if not, see http://www.gnu.org/licenses or write to
20+
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21+
Boston, MA, 02110-1301 USA, or download the license from the following URL:
22+
http://itextpdf.com/terms-of-use/
23+
24+
The interactive user interfaces in modified source and object code versions
25+
of this program must display Appropriate Legal Notices, as required under
26+
Section 5 of the GNU Affero General Public License.
27+
28+
In accordance with Section 7(b) of the GNU Affero General Public License,
29+
a covered work must retain the producer line in every PDF that is created
30+
or manipulated using iText.
31+
32+
You can be released from the requirements of the license by purchasing
33+
a commercial license. Buying such a license is mandatory as soon as you
34+
develop commercial activities involving the iText software without
35+
disclosing the source code of your own applications.
36+
These activities include: offering paid services to customers as an ASP,
37+
serving PDFs on the fly in a web application, shipping iText with a closed
38+
source product.
39+
40+
For more information, please contact iText Software Corp. at this
41+
42+
*/
43+
package com.itextpdf.rups.model;
44+
45+
import com.itextpdf.kernel.pdf.PdfName;
46+
import com.itextpdf.kernel.pdf.PdfStream;
47+
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
48+
import com.itextpdf.rups.view.Language;
49+
50+
import java.awt.image.BufferedImage;
51+
import java.io.IOException;
52+
53+
/**
54+
* Static utility class for getting information on PDF streams.
55+
*/
56+
public final class PdfStreamUtil {
57+
private PdfStreamUtil() {
58+
// static class
59+
}
60+
61+
public static BufferedImage getAsImage(PdfStream stream) {
62+
if (!isImage(stream)) {
63+
return null;
64+
}
65+
final PdfImageXObject xObject = new PdfImageXObject(stream);
66+
try {
67+
return xObject.getBufferedImage();
68+
} catch (IOException e) {
69+
LoggerHelper.warn(Language.ERROR_PARSING_IMAGE.getString(), e, PdfStreamUtil.class);
70+
return null;
71+
}
72+
}
73+
74+
public static boolean isImage(PdfStream stream) {
75+
/*
76+
* We will consider stream being an image, if it has /Width and
77+
* /Height number fields present and /Subtype is /Image.
78+
*
79+
* This could skip thumbnail images, as those do not require the
80+
* /Subtype field being there.
81+
*/
82+
return PdfName.Image.equals(stream.getAsName(PdfName.Subtype))
83+
&& (stream.getAsNumber(PdfName.Width) != null)
84+
&& (stream.getAsNumber(PdfName.Height) != null);
85+
}
86+
87+
public static boolean isFont(PdfStream stream) {
88+
/*
89+
* For now just checking, that there is a /Length1 field present. It
90+
* is required for Type 1 and TrueType fonts.
91+
*/
92+
return stream.containsKey(PdfName.Length1);
93+
}
94+
}

src/main/java/com/itextpdf/rups/view/contextmenu/InspectObjectAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ This file is part of the iText (R) project.
4545
import com.itextpdf.rups.view.Language;
4646
import com.itextpdf.rups.view.icons.FrameIconUtil;
4747
import com.itextpdf.rups.view.itext.PdfTree;
48-
import com.itextpdf.rups.view.itext.StreamTextEditorPane;
48+
import com.itextpdf.rups.view.itext.stream.StreamTextEditorPane;
4949
import com.itextpdf.rups.view.itext.treenodes.PdfObjectTreeNode;
5050

5151
import javax.swing.AbstractAction;

src/main/java/com/itextpdf/rups/view/contextmenu/SaveToPdfStreamJTextPaneAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ This file is part of the iText (R) project.
4242
*/
4343
package com.itextpdf.rups.view.contextmenu;
4444

45-
import com.itextpdf.rups.view.itext.StreamTextEditorPane;
45+
import com.itextpdf.rups.view.itext.stream.StreamTextEditorPane;
4646

4747
import java.awt.event.ActionEvent;
4848

src/main/java/com/itextpdf/rups/view/contextmenu/StreamPanelContextMenu.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ This file is part of the iText (R) project.
4343
package com.itextpdf.rups.view.contextmenu;
4444

4545
import com.itextpdf.rups.view.Language;
46-
import com.itextpdf.rups.view.itext.StreamTextEditorPane;
46+
import com.itextpdf.rups.view.itext.stream.StreamTextEditorPane;
4747

4848
import javax.swing.Action;
4949
import javax.swing.JComponent;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2025 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License version 3
8+
as published by the Free Software Foundation with the addition of the
9+
following permission added to Section 15 as permitted in Section 7(a):
10+
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
11+
APRYSE GROUP. APRYSE GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
12+
OF THIRD PARTY RIGHTS
13+
14+
This program is distributed in the hope that it will be useful, but
15+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
or FITNESS FOR A PARTICULAR PURPOSE.
17+
See the GNU Affero General Public License for more details.
18+
You should have received a copy of the GNU Affero General Public License
19+
along with this program; if not, see http://www.gnu.org/licenses or write to
20+
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21+
Boston, MA, 02110-1301 USA, or download the license from the following URL:
22+
http://itextpdf.com/terms-of-use/
23+
24+
The interactive user interfaces in modified source and object code versions
25+
of this program must display Appropriate Legal Notices, as required under
26+
Section 5 of the GNU Affero General Public License.
27+
28+
In accordance with Section 7(b) of the GNU Affero General Public License,
29+
a covered work must retain the producer line in every PDF that is created
30+
or manipulated using iText.
31+
32+
You can be released from the requirements of the license by purchasing
33+
a commercial license. Buying such a license is mandatory as soon as you
34+
develop commercial activities involving the iText software without
35+
disclosing the source code of your own applications.
36+
These activities include: offering paid services to customers as an ASP,
37+
serving PDFs on the fly in a web application, shipping iText with a closed
38+
source product.
39+
40+
For more information, please contact iText Software Corp. at this
41+
42+
*/
43+
package com.itextpdf.rups.view.itext.stream;
44+
45+
import java.awt.Image;
46+
import javax.swing.Icon;
47+
import javax.swing.ImageIcon;
48+
import javax.swing.JLabel;
49+
import javax.swing.JScrollPane;
50+
import javax.swing.SwingConstants;
51+
52+
/**
53+
* Simple pane, which shows an image that can be interacted with via a context
54+
* menu.
55+
*/
56+
public final class StreamImagePane extends JScrollPane {
57+
private final JLabel label;
58+
59+
public StreamImagePane() {
60+
this.label = new JLabel();
61+
this.label.setVerticalAlignment(SwingConstants.TOP);
62+
this.label.setHorizontalAlignment(SwingConstants.LEFT);
63+
setViewportView(this.label);
64+
}
65+
66+
public void setImage(Image image) {
67+
Icon icon = null;
68+
if (image != null) {
69+
icon = new ImageIcon(image);
70+
}
71+
label.setIcon(icon);
72+
}
73+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2025 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License version 3
8+
as published by the Free Software Foundation with the addition of the
9+
following permission added to Section 15 as permitted in Section 7(a):
10+
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
11+
APRYSE GROUP. APRYSE GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
12+
OF THIRD PARTY RIGHTS
13+
14+
This program is distributed in the hope that it will be useful, but
15+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
or FITNESS FOR A PARTICULAR PURPOSE.
17+
See the GNU Affero General Public License for more details.
18+
You should have received a copy of the GNU Affero General Public License
19+
along with this program; if not, see http://www.gnu.org/licenses or write to
20+
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21+
Boston, MA, 02110-1301 USA, or download the license from the following URL:
22+
http://itextpdf.com/terms-of-use/
23+
24+
The interactive user interfaces in modified source and object code versions
25+
of this program must display Appropriate Legal Notices, as required under
26+
Section 5 of the GNU Affero General Public License.
27+
28+
In accordance with Section 7(b) of the GNU Affero General Public License,
29+
a covered work must retain the producer line in every PDF that is created
30+
or manipulated using iText.
31+
32+
You can be released from the requirements of the license by purchasing
33+
a commercial license. Buying such a license is mandatory as soon as you
34+
develop commercial activities involving the iText software without
35+
disclosing the source code of your own applications.
36+
These activities include: offering paid services to customers as an ASP,
37+
serving PDFs on the fly in a web application, shipping iText with a closed
38+
source product.
39+
40+
For more information, please contact iText Software Corp. at this
41+
42+
*/
43+
package com.itextpdf.rups.view.itext.stream;
44+
45+
import com.itextpdf.rups.controller.PdfReaderController;
46+
import com.itextpdf.rups.model.IRupsEventListener;
47+
import com.itextpdf.rups.model.ObjectLoader;
48+
import com.itextpdf.rups.model.PdfStreamUtil;
49+
import com.itextpdf.rups.view.itext.treenodes.PdfObjectTreeNode;
50+
51+
import java.awt.CardLayout;
52+
import java.awt.image.BufferedImage;
53+
import javax.swing.JComponent;
54+
import javax.swing.JPanel;
55+
56+
/**
57+
* Pane for showing PDF stream content.
58+
*
59+
* <p>
60+
* For images the pane shows the image itself, with the relevant image options.
61+
* </p>
62+
*
63+
* <p>
64+
* For everything else (at the moment of writing) a syntax editor is used.
65+
* </p>
66+
*/
67+
public final class StreamPane extends JPanel implements IRupsEventListener {
68+
private final StreamTextEditorPane textEditorPane;
69+
private final StreamImagePane imagePane;
70+
private final JPanel emptyPane;
71+
72+
public StreamPane(PdfReaderController controller) {
73+
this.textEditorPane = new StreamTextEditorPane(controller);
74+
this.textEditorPane.setVisible(false);
75+
this.imagePane = new StreamImagePane();
76+
this.imagePane.setVisible(false);
77+
this.emptyPane = new JPanel();
78+
this.emptyPane.setVisible(true);
79+
80+
setLayout(new CardLayout());
81+
add(this.textEditorPane);
82+
add(this.imagePane);
83+
add(this.emptyPane);
84+
}
85+
86+
public void render(PdfObjectTreeNode target) {
87+
if (target == null || !target.isStream()) {
88+
showPane(emptyPane);
89+
return;
90+
}
91+
final BufferedImage image = PdfStreamUtil.getAsImage(target.getAsStream());
92+
if (image != null) {
93+
imagePane.setImage(image);
94+
showPane(imagePane);
95+
return;
96+
}
97+
textEditorPane.render(target);
98+
showPane(textEditorPane);
99+
}
100+
101+
@Override
102+
public void handleCloseDocument() {
103+
showPane(emptyPane);
104+
textEditorPane.handleCloseDocument();
105+
}
106+
107+
@Override
108+
public void handleOpenDocument(ObjectLoader loader) {
109+
showPane(emptyPane);
110+
textEditorPane.handleOpenDocument(loader);
111+
}
112+
113+
private void showPane(JComponent pane) {
114+
assert pane != null;
115+
116+
showImagePane(imagePane == pane);
117+
showTextEditorPane(textEditorPane == pane);
118+
emptyPane.setVisible(emptyPane == pane);
119+
validate();
120+
}
121+
122+
private void showImagePane(boolean flag) {
123+
imagePane.setVisible(flag);
124+
if (!flag) {
125+
imagePane.setImage(null);
126+
}
127+
}
128+
129+
private void showTextEditorPane(boolean flag) {
130+
textEditorPane.setVisible(flag);
131+
if (!flag) {
132+
textEditorPane.render(null);
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)