package com.xindao.ocr.swingui.swing.jpanel; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; public class PdfPreviewPanel extends JPanel { private PDDocument document; private int pageNumber; private BufferedImage pageImage; private Rectangle2D selection = null; private Rectangle2D lastSelection = null; // 上次选择的区域 private Point startDrag = null; private float scale = 1.0f; private Point translation = new Point(0, 0); // 平移量 private Point lastDragPoint = null; private float pdfWidth; private float pdfHeight; private boolean isDraggingPage = false; // 是否正在拖拽页面 private static final Color SELECTION_COLOR = new Color(255, 193, 7, 100); // 半透明黄色 private static final Color LAST_SELECTION_COLOR = new Color(76, 175, 80, 100); // 半透明绿色,用于标识上次选择的区域 public PdfPreviewPanel(PDDocument doc, int pageNum, Rectangle2D lastArea) throws IOException { this.document = doc; this.pageNumber = pageNum; this.lastSelection = lastArea; // 渲染PDF页面为图片 PDFRenderer renderer = new PDFRenderer(doc); pageImage = renderer.renderImage(pageNumber); // 获取PDF页面尺寸 pdfWidth = pageImage.getWidth(); pdfHeight = pageImage.getHeight(); // 如果有上次选择的区域,默认选中 if (lastSelection != null) { this.selection = lastSelection; } // 添加鼠标监听器处理选择和拖拽 addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { // 检查点击位置是否在PDF页面上 Rectangle2D pageBounds = getPageBounds(); if (pageBounds.contains(e.getPoint())) { // 检查是否点击在选择区域内 if (selection != null && selection.contains(e.getPoint())) { startDrag = e.getPoint(); isDraggingPage = false; } else { // 开始新的选择 startDrag = e.getPoint(); selection = null; isDraggingPage = false; } } else { // 点击在页面外,准备拖拽整个页面 lastDragPoint = e.getPoint(); isDraggingPage = true; } repaint(); } @Override public void mouseReleased(MouseEvent e) { startDrag = null; lastDragPoint = null; isDraggingPage = false; repaint(); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { if (isDraggingPage && lastDragPoint != null) { // 拖拽整个页面 int dx = e.getX() - lastDragPoint.x; int dy = e.getY() - lastDragPoint.y; translation.x += dx; translation.y += dy; lastDragPoint = e.getPoint(); repaint(); } else if (startDrag != null) { // 拖拽选择区域 Point endDrag = e.getPoint(); selection = createRectangle(startDrag, endDrag); repaint(); } } }); // 设置面板首选大小 setPreferredSize(new Dimension(pageImage.getWidth(), pageImage.getHeight())); } // 获取PDF页面在面板中的边界 private Rectangle2D getPageBounds() { return new Rectangle2D.Double( translation.getX(), translation.getY(), pageImage.getWidth() * scale, pageImage.getHeight() * scale ); } private Rectangle2D createRectangle(Point p1, Point p2) { int x = Math.min(p1.x, p2.x); int y = Math.min(p1.y, p2.y); int width = Math.abs(p1.x - p2.x); int height = Math.abs(p1.y - p2.y); return new Rectangle2D.Double(x, y, width, height); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); // 绘制背景 g2.setColor(new Color(240, 240, 240)); g2.fillRect(0, 0, getWidth(), getHeight()); // 绘制PDF页面 if (pageImage != null) { // 保存当前变换 AffineTransform originalTransform = g2.getTransform(); // 应用平移和缩放 g2.translate(translation.x, translation.y); g2.scale(scale, scale); // 绘制PDF图像 g2.drawImage(pageImage, 0, 0, null); // 绘制上次选择的区域(如果有) if (lastSelection != null && (selection == null || !selection.equals(lastSelection))) { g2.setColor(LAST_SELECTION_COLOR); g2.fill(lastSelection); g2.setColor(new Color(76, 175, 80)); g2.setStroke(new BasicStroke(1)); g2.draw(lastSelection); } // 绘制当前选择区域 if (selection != null) { // 转换选择区域坐标到PDF坐标系统 Rectangle2D pdfSelection = new Rectangle2D.Double( selection.getX() - translation.x, selection.getY() - translation.y, selection.getWidth(), selection.getHeight() ); pdfSelection = new Rectangle2D.Double( pdfSelection.getX() / scale, pdfSelection.getY() / scale, pdfSelection.getWidth() / scale, pdfSelection.getHeight() / scale ); g2.setColor(SELECTION_COLOR); g2.fill(pdfSelection); g2.setColor(Color.ORANGE); g2.setStroke(new BasicStroke(2)); g2.draw(pdfSelection); } // 恢复变换 g2.setTransform(originalTransform); } } public Rectangle2D getSelection() { return selection; } public float getScale() { return scale; } public Point getTranslation() { return translation; } public float getPdfHeight() { return pdfHeight; } }