基于Swing实现的PDFViewer

发布时间 2023-09-04 11:05:59作者: End_Unripe

最近因项目需求,需要使用Swing实现PDFViewer,并且需要鼠标拖动,放大缩小等操作,一开始在网上也找到了PDF-Renderer,但是一看原理,不也就是将PDF文件转化为image而已,目前解决掉了拖动以及放大缩小的BUG问题。
如下使用apache-pdfbox转换的PDF,当然也可以替换为iText或者别的依赖
代码如下:

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;

/**
 * PDF查看器
 * @author Liukaixin
 */
public class PDFViewer extends JPanel {

private static final long serialVersionUID = 1L;
	
	private String imagePath;

	// 鼠标按下时的坐标 以及 更新后的坐标
    private int startX, startY;

    // 当前的位置偏移
    private int offsetX = 0, offsetY = 0;

    // 缩放比例,默认为 1.0
    private double scale = 1.0;
    
    // 缩放基数
    private double scaleRadix = 1.02;
    
    // 拖动边距
    private int margin = 100;
    
    private Image image;

    public PDFViewer(String imagePath) {
    	this.imagePath = imagePath;

        // 添加鼠标滚轮监听器
        addMouseWheelListener(new MouseWheelListener() {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
            	int notches = e.getWheelRotation();
            	
            	// 获取画布宽高
                int canvansWidth = getWidth();
                int canvansHeight = getHeight();
                
                // 获取缩放后的图片大小
                int imageWidth = 0;
                int imageHeight = 0;
                
                if(notches < 0) {
                	imageWidth = (int) (image.getWidth(null) * scale * scaleRadix);
                	imageHeight = (int) (image.getHeight(null) * scale * scaleRadix);
                }else {
                	imageWidth = (int) (image.getWidth(null) * scale / scaleRadix);
                	imageHeight = (int) (image.getHeight(null) * scale / scaleRadix);
                }
            	
            	// 计算当前image位置偏移后的位置
                int leftTopX = (offsetX + (canvansWidth - imageWidth) / 2);
                int leftTopY = (offsetY + (canvansHeight - imageHeight) / 2);
                
                int leftDownX = leftTopX;
                int leftDownY = leftTopY + imageHeight;
                
                int rightTopX = leftTopX + imageWidth;
                int rightTopY = leftTopY;
                
                int rightDownX = rightTopX;
                int rightDownY = leftDownY;
                
                if(leftTopX <= canvansWidth - margin && leftDownX <= canvansWidth - margin && rightTopX >= margin && rightDownX >= margin && leftTopY < canvansHeight - margin && leftDownY >= margin && rightTopY <= canvansHeight - margin && rightDownY >= margin) {
                	 if (notches < 0) {
                		 // 滚轮向上,放大画布
                		 if(scale <= 2) {
                			 scale *= scaleRadix;
                		 }
                     } else {
                         // 滚轮向下,缩小画布
                    	 if(scale >= 0.2) {
                    		 scale /= scaleRadix;
                    	 }
                     }
                }
                // 重新绘图
                repaint();
            }
        });

        // 为组件设置鼠标监听事件
        addMouseListener(new MouseAdapter() {
        	// 鼠标按下
        	@Override
            public void mousePressed(MouseEvent e) {
            	setCursor(new Cursor(Cursor.MOVE_CURSOR));
                // 记录鼠标按下时的坐标
                startX = e.getX();
                startY = e.getY();
            }
            
            // 鼠标释放
            @Override
            public void mouseReleased(MouseEvent e) {
            	setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        });

        // 添加鼠标动作监听
        addMouseMotionListener(new MouseAdapter() {
            // 鼠标拖动
        	@Override
            public void mouseDragged(MouseEvent e) {
                // 统计本次鼠标移动的相对值
                int dx = e.getX() - startX;
                int dy = e.getY() - startY;
                
                // 获取画布宽高
                int canvansWidth = getWidth();
                int canvansHeight = getHeight();
                
                // 获取图片大小
                int imageWidth = (int) (image.getWidth(null) * scale);
                int imageHeight = (int) (image.getHeight(null) * scale);
                
                // 计算当前image位置偏移后的位置
                int leftTopX = (offsetX + (canvansWidth - imageWidth) / 2) + dx;
                int leftTopY = (offsetY + (canvansHeight - imageHeight) / 2) + dy;
                
                int leftDownX = leftTopX;
                int leftDownY = leftTopY + imageHeight;
                
                int rightTopX = leftTopX + imageWidth;
                int rightTopY = leftTopY;
                
                int rightDownX = rightTopX;
                int rightDownY = leftDownY;
                
                if(leftTopX <= canvansWidth - margin && leftDownX <= canvansWidth - margin && rightTopX >= margin && rightDownX >= margin) {
                	// 偏移量累加 x
                	offsetX += dx;
                	// 记录当前拖动后的位置 x
                	startX += dx;
                }
                
                if(leftTopY < canvansHeight - margin && leftDownY >= margin && rightTopY <= canvansHeight - margin && rightDownY >= margin) {
            		// 偏移量累加 y
            		offsetY += dy;
            		// 记录当前拖动后的位置 y
                	startY += dy;
                }
            	// 重新绘图
                repaint();
            }
        });
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        setBackground(new Color(26, 57, 79));

        // 画布进行整体偏移
        Graphics2D g2 = (Graphics2D)g;

        // 缩放画布
        //g2.scale(scale, scale);

        // 拖动画布
        g2.translate(offsetX, offsetY);

        // 获取图片
        image = Toolkit.getDefaultToolkit().getImage(imagePath);
        if(null != image) {
        	// 绘制图像
            int imageWidth = (int) (image.getWidth(null) * scale);
            int imageHeight = (int) (image.getHeight(null) * scale);
            
            // 获取画布宽高
            int canvansWidth = getWidth();
            int canvansHeight = getHeight();
            
            int x = (canvansWidth - imageWidth) / 2;
            int y = (canvansHeight - imageHeight) / 2;
            
            g2.drawImage(image, x, y, imageWidth, imageHeight, null);
        }
    }

    public static void main(String[] args) throws IOException {
        // 创建 JFrame 窗口
        JFrame frame = new JFrame("Large Canvas");

        // 设置窗口关闭行为 点击右上角关闭按钮 关闭窗口并退出应用
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 创建画布
        PDFViewer canvas = new PDFViewer(pdfToImage());

        // 将画布放入滚动布局
        JScrollPane scrollPane = new JScrollPane(canvas);

        // 将滚动布局放入窗口
        frame.getContentPane().add(scrollPane);

        // 窗口自适应
        frame.pack();

        // 窗口设置可见
        frame.setVisible(true);
    }
    
    public static String pdfToImage() throws IOException{
    	String imagePth = "";
		String sourceDir = "C:\\Users\\Administrator\\Desktop\\test.pdf";
        String destinationDir = "C:\\Users\\Administrator\\Desktop\\";

        File sourceFile = new File(sourceDir);
        File destinationFile = new File(destinationDir);
        if (!destinationFile.exists()) {
            destinationFile.mkdir();
            System.out.println("Folder Created -> "+ destinationFile.getAbsolutePath());
        }
        if (sourceFile.exists()) {
            System.out.println("Images copied to Folder: "+ destinationFile.getName());             
            PDDocument document = PDDocument.load(sourceDir);
            List<PDPage> list = document.getDocumentCatalog().getAllPages();
            System.out.println("Total files to be converted -> "+ list.size());

            String fileName = sourceFile.getName().replace(".pdf", "");             
            int pageNumber = 1;
            BufferedImage image = list.get(0).convertToImage();
            imagePth = destinationDir + fileName +"_"+ pageNumber +".png";
            File outputfile = new File(imagePth);
            System.out.println("Image Created -> "+ outputfile.getName());
            ImageIO.write(image, "png", outputfile);
            
            document.close();
            System.out.println("Converted Images are saved at -> "+ imagePth);
        } else {
            System.err.println(sourceFile.getName() +" File not exists");
        }
        return imagePth;
	}

}