import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFPage;
import jsyntaxpane.DefaultSyntaxKit;
import org.xhtmlrenderer.pdf.ITextRenderer;
import org.xhtmlrenderer.simple.FSScrollPane;
import org.xhtmlrenderer.simple.XHTMLPanel;
import org.xhtmlrenderer.simple.extend.XhtmlNamespaceHandler;
import org.xhtmlrenderer.util.XRLog;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.ImageObserver;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.math.BigInteger;
public class Review {
private String lastRenderedHex;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Review();
}
});
}
public Review() {
JFrame f = new JFrame(Review.class.getName());
final Container c = f.getContentPane();
c.setLayout(new BorderLayout());
JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
split.setOneTouchExpandable(true);
split.setDividerLocation(300);
c.add(split, BorderLayout.CENTER);
DefaultSyntaxKit.initKit();
final JEditorPane codeEditor = new JEditorPane();
JScrollPane scrPane = new JScrollPane(codeEditor);
split.setBottomComponent(scrPane);
JTabbedPane tabs = new JTabbedPane();
final XHTMLPanel xhtmlPanel = new XHTMLPanel();
tabs.addTab("HTML", new FSScrollPane(xhtmlPanel));
final FSPagePanel pdfPanel = new FSPagePanel();
tabs.addTab("PDF", new JScrollPane(pdfPanel));
JPanel top = new JPanel(new BorderLayout());
JPanel controls = new JPanel();
JButton render = new JButton(new AbstractAction("Render") {
public void actionPerformed(ActionEvent e) {
reRender(codeEditor, xhtmlPanel, pdfPanel);
}
});
controls.add(render);
top.add(controls, BorderLayout.NORTH);
top.add(tabs, BorderLayout.CENTER);
split.setTopComponent(top);
c.doLayout();
setContent(codeEditor);
f.setSize(800, 600);
f.setVisible(true);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
/*EventQueue.invokeLater(new Runnable() {
public void run() {
reRender(codeEditor, xhtmlPanel, pdfPanel);
}
});*/
}
private void reRender(JEditorPane codeEditor, XHTMLPanel xhtmlPanel, FSPagePanel pdfPanel) {
String content = codeEditor.getText();
String currentHex = getDigestHex(content.getBytes());
if (currentHex.equals(lastRenderedHex)) {
XRLog.general("Already rendered, skipping");
return;
}
try {
xhtmlPanel.setDocumentFromString(content, null, new XhtmlNamespaceHandler());
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(content);
renderer.layout();
renderer.createPDF(os);
os.flush();
ByteBuffer buf = ByteBuffer.wrap(os.toByteArray());
PDFFile pdffile = new PDFFile(buf);
// show the first page
XRLog.general("Page count: " + pdffile.getNumPages());
PDFPage page = pdffile.getPage(0);
pdfPanel.setZoom(-3.0);
pdfPanel.showPage(page);
lastRenderedHex = currentHex;
XRLog.general("MD5 via BI: " + currentHex);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getDigestHex(byte[] buffer) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(buffer);
return new BigInteger(1, md5.digest()).toString(16).toUpperCase();
} catch (NoSuchAlgorithmException e) {
}
return null;
}
private void setContent(JEditorPane codeEditor) {
codeEditor.setContentType("text/xml");
StringBuffer sb = new StringBuffer(256);
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n");
sb.append("<head>\n");
sb.append("<style type=\"text/css\">\n");
sb.append("</style>\n");
sb.append("</head>\n\n");
sb.append("<body>\n");
sb.append(" <p>Hello World</p>\n");
sb.append("</body>\n");
sb.append("</html>\n");
codeEditor.setText(sb.toString());
}
public class FSPagePanel extends JPanel implements ImageObserver, MouseListener, MouseMotionListener {
Image currentImage;
PDFPage currentPage;
Dimension prevSize;
Flag flag = new Flag();
AffineTransform af;
double zoomFactor = -1.0;
double scale = 1.0;
public final static double MAX_SCALE = 5;
public final static double MIN_SCALE = 0.2;
JViewport jvp = null;
public final int SCROLLBAR_BORDER = 2;
public final int SCROLLBAR_WIDTH = SCROLLBAR_BORDER + (Integer) UIManager.get("ScrollBar.width");
public FSPagePanel() {
super();
setFocusable(true);
addMouseListener(this);
addMouseMotionListener(this);
setBackground(Color.darkGray);
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
/**
* Stop the generation of any previous page, and draw the new one.
*
* @param page the PDFPage to draw.
*/
public synchronized void showPage(PDFPage page) {
// stop drawing the previous page
if (currentPage != null && prevSize != null) {
currentPage.stop(prevSize.width, prevSize.height, null);
}
// set up the new page
currentPage = page;
if (page == null) {
// no page
currentImage = null;
af = null;
repaint();
} else {
// start drawing -- clear the flag to indicate we're in progress.
flag.clear();
Dimension sz = getSize();
if (this.getParent().getParent() instanceof JScrollPane) {
sz = ((JScrollPane) this.getParent().getParent()).getSize();
sz = new Dimension(sz.width - SCROLLBAR_BORDER, sz.height - SCROLLBAR_BORDER);
}
if (sz.width + sz.height <= 0) {
// no image to draw.
return;
}
Dimension pageSize = page.getUnstretchedSize(sz.width, sz.height, null);
// get the new image
int imw = pageSize.width;
int imh = pageSize.height;
int scrollbarWidth = 0;
if (zoomFactor == -1.0) {//fit page
scale = Math.min(sz.getWidth() / imw, sz.getHeight() / imh);
imw *= scale;
imh *= scale;
setSize(sz.width, sz.height);
setPreferredSize(getSize());
} else if (zoomFactor == -2.0) {//fit h
if ((sz.height * page.getAspectRatio()) > sz.width) {
scrollbarWidth = SCROLLBAR_WIDTH;
} else {
scrollbarWidth = 0;
}
scale = (sz.getHeight() - scrollbarWidth) / imh;
imh = sz.height - scrollbarWidth;
imw = (int) (imw * scale);
setSize(Math.max(imw, sz.width), imh);
setPreferredSize(getSize());
} else if (zoomFactor == -3.0) {//fit w
if (sz.width > (sz.height * page.getAspectRatio())) {
scrollbarWidth = SCROLLBAR_WIDTH;
} else {
scrollbarWidth = 0;
}
scale = (sz.getWidth() - scrollbarWidth) / imw;
imw = sz.width - scrollbarWidth;
imh = (int) (imh * scale);
setSize(imw, Math.max(imh, sz.height));
setPreferredSize(getSize());
} else if (zoomFactor == -4.0) {
imw = (int) page.getWidth();
imh = (int) page.getHeight();
scale = Math.min(sz.getWidth() / imw, sz.getHeight() / imh);
setSize(Math.max(imw, imh > sz.height ? sz.width - SCROLLBAR_WIDTH : sz.width), Math.max(imh, imw > sz.width ? sz.height - SCROLLBAR_WIDTH : sz.height));
setPreferredSize(getSize());
} else if (zoomFactor > 0) {
scale = zoomFactor;
imw *= zoomFactor;
imh *= zoomFactor;
setSize(Math.max(imw, imh > sz.height ? sz.width - SCROLLBAR_WIDTH : sz.width), Math.max(imh, imw > sz.width ? sz.height - SCROLLBAR_WIDTH : sz.height));
setPreferredSize(getSize());
}
currentImage = page.getImage(imw, imh, null, this, true, true);
prevSize = pageSize;
repaint();
if (this.getParent() instanceof JViewport) {
((JViewport) this.getParent()).setViewPosition(new Point(0, 0));
}
}
}
public void paint(Graphics g1) {
Graphics2D g2d = (Graphics2D) g1;
try {
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
if (currentImage == null) {
g2d.setColor(Color.black);
} else {
// draw the image
int imw = currentImage.getWidth(null);
int imh = currentImage.getHeight(null);
// draw it centered within the panel
int offx = (getWidth() - imw) / 2;
int offy = (getHeight() - imh) / 2;
if (offx < 0) {
offx = 0;
}
if (offy < 0) {
offy = 0;
}
g2d.drawImage(currentImage, offx, offy, this);
af = null;
}
} finally {
g2d.dispose();
}
requestFocus();
}
public void setZoom(double zf) {
if (zf > 1) {
this.zoomFactor = Math.min(zf, MAX_SCALE);
} else if (zf < 1 && zf > 0) {
this.zoomFactor = Math.max(zf, MIN_SCALE);
} else if (zf == -4.0 || zf == -1.0 || zf == -2.0 || zf == -3.0) {
this.zoomFactor = zf;
}
showPage(currentPage);
}
public double getScale() {
return this.scale;
}
public PDFPage getPage() {
return currentPage;
}
public Dimension getCurSize() {
return prevSize;
}
public void waitForCurrentPage() {
flag.waitForFlag();
}
/**
* x location of the mouse-down event
*/
int downx;
/**
* y location of the mouse-down event
*/
int downy;
/**
* Handles a mousePressed event
*/
public void mousePressed(MouseEvent evt) {
downx = evt.getX();
downy = evt.getY();
}
public void mouseReleased(MouseEvent evt) {
}
public void mouseClicked(MouseEvent evt) {
}
public void mouseEntered(MouseEvent evt) {
}
public void mouseExited(MouseEvent evt) {
}
public void mouseMoved(MouseEvent evt) {
}
public void mouseDragged(MouseEvent evt) {
if (!(this.getParent() instanceof JViewport)) {
return;
}
int x = evt.getX();
int y = evt.getY();
int dx = x - downx;
int dy = y - downy;
if (jvp == null) {
jvp = (JViewport) this.getParent();
}
x = Math.max(jvp.getViewPosition().x - dx, 0);
y = Math.max(jvp.getViewPosition().y - dy, 0);
x = Math.min(x, getWidth() - jvp.getViewRect().width);
y = Math.min(y, getHeight() - jvp.getViewRect().height);
jvp.setViewPosition(new Point(x, y));
}
}
public class Flag {
private boolean isSet;
/**
* Sets the flag. Any pending waitForFlag calls will now return.
*/
public synchronized void set() {
isSet = true;
notifyAll();
}
/**
* Clears the flag. Do this before calling waitForFlag.
*/
public synchronized void clear() {
isSet = false;
}
/**
* Waits for the flag to be set, if it is not set already.
* This method catches InterruptedExceptions, so if you want
* notification of interruptions, use interruptibleWaitForFlag
* instead.
*/
public synchronized void waitForFlag() {
if (!isSet) {
try {
wait();
} catch (InterruptedException ie) {
}
}
}
/**
* Waits for the flag to be set, if it is not set already.
*/
public synchronized void interruptibleWaitForFlag()
throws InterruptedException {
if (!isSet) {
wait();
}
}
}
}