首页 > 解决方案 > 将图像上绘制的形状写入 PDF 文件时会反转

问题描述

我有一个转换为图像的 PDF 文件。能够在图像上书写,但是当我尝试将形状/线条保存到 pdf 中时,点不是它们的位置并且形状是倒置的。

这是我画的。

在此处输入图像描述

这是保存的 PDF 文件。 在此处输入图像描述

我真的很抱歉,但这是我为了复制问题而可以制作的最短代码。

我知道这个问题不好,但我希望有人能提供帮助。

感谢蒂尔曼·豪舍尔的耐心等待。

   package pdfwriter;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;

public class Example {

    private static class MyImagePanel extends JPanel {

        final BufferedImage image;
        final float scale = .38f;
        AffineTransform atg;
        Point start = new Point();
        Point end = new Point();
        boolean isNewLine = true;
        static ArrayList<Line2D> lines = new ArrayList<>();
        static PDDocument document;

        public MyImagePanel() throws IOException {

            document = PDDocument.load(new File("path"));
            PDFRenderer renderer = new PDFRenderer(document);
            image = renderer.renderImageWithDPI(0, 200, ImageType.RGB);

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseReleased(MouseEvent e) {
                    if (end != start) {
                        Line2D line = new Line2D.Float(start.x, start.y, end.x, end.y);
                        lines.add(line);
                        Point2D p = calcCoordinates(e);
                        start = new Point();
                        start.x = (int) p.getX();
                        start.y = (int) p.getY();
                        repaint();
                    }

                }
            });
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    Point2D p = calcCoordinates(e);
                    end = new Point();

                    end.x = (int) p.getX();
                    end.y = (int) p.getY();
                    repaint();
                }
            });
        }

        private Point2D calcCoordinates(MouseEvent e) {
            Point p = new Point(e.getX(), e.getY());
            try {
                return atg.inverseTransform(p, null);
            } catch (NoninvertibleTransformException ex) {
                return p;
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2D = (Graphics2D) g.create();

            double affineX = (getWidth() - scale * image.getWidth()) / 2;
            double affineY = (getHeight() - scale * image.getHeight()) / 2;

            AffineTransform at = new AffineTransform();
            at.translate(affineX, affineY);
            at.scale(scale, scale);

            AffineTransform atf = g2D.getTransform();
            atf.concatenate(at);

            atg = (AffineTransform) atf.clone();

            g2D.setTransform(atf);

            g2D.drawImage(image, 0, 0, this);
            try {
                g2D.drawLine(start.x, start.y, end.x, end.y);
            } catch (NullPointerException e) {

            }

            for (Line2D l : lines) {
                g2D.draw(l);
            }

            g2D.dispose();
        }

        public static void save() {

            try {
                PDPage page = document.getPage(0);

                PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true, true);

                for (Line2D l : lines) {
                    contentStream.moveTo((float) l.getX1(), (float) l.getY1());
                    contentStream.lineTo((float) l.getX2(), (float) l.getY2());
                    contentStream.stroke();
                }

                contentStream.close();

                document.save(
                        new File("path"));
                document.close();
            } catch (IOException ex) {
                Logger.getLogger(Example.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

        @Override
        public Dimension getPreferredSize() {
            int width = (int) (scale * image.getWidth());
            int height = (int) (scale * image.getHeight());
            return new Dimension(width, height);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("PDF");
        frame.setSize(1500, 1200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        MyImagePanel imagePanel = null;
        try {
            imagePanel = new MyImagePanel();
        } catch (IOException ex) {
            Logger.getLogger(Example.class
                    .getName()).log(Level.SEVERE, null, ex);
        }

        JButton btn = new JButton("Save");

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MyImagePanel.save();
            }
        });

        btn.setBounds(10, 0, 70, 30);

        frame.add(btn);
        JPanel pnl = new JPanel();
        pnl.add(imagePanel);
        pnl.setBounds(0, 100, 1500, 1200);
        frame.add(pnl);
        frame.revalidate();
        frame.repaint();
        frame.setVisible(true);
    }

}

标签: javaswingpdfpdfbox

解决方案


PDF 坐标从底部开始。所以你必须从页面高度中扣除它。(如果有裁剪框,​​那么它会更复杂一些,您必须将原点添加到计算的坐标中,因为裁剪框就是您所看到的)。你需要从calcCoordinates=> 变换反转逆变换。

所以代码应该是这样的:

for (Line2D l : lines)
{
    Point2D p1 = new Point2D.Double(l.getX1(), l.getY1());
    Point2D p2 = new Point2D.Double(l.getX2(), l.getY2());
    p1 = atg.transform(p1, null);
    p2 = atg.transform(p2, null);
    contentStream.moveTo((float) p1.getX(), page.getMediaBox().getHeight() - (float) p1.getY());
    contentStream.lineTo((float) p2.getX(), page.getMediaBox().getHeight() - (float) p2.getY());
    contentStream.stroke();
}

为了使它工作,atg必须是可访问的,因为我从“保存”中删除了“静态”,并在异常处理程序中添加了一个“返回”,并更改MyImagePanel.save()imagePanel.save();.

但是等等,还有更多……不知何故,这些线条不完全适合。略有不同...原因:比例不是 0.38,大约是 0.36(72 / 200),200 是您的 dpi。您的错误是有两个相互依赖的固定值,但这种依赖不在代码中。你的代码应该是这样的:

final int DPI = 200;
final float scale = 72f / DPI;

完整代码:

public class SO57387803DrawShapesInPDF
{

    private static class MyImagePanel extends JPanel
    {

        final BufferedImage image;
        //final float scale = .38f;
        final int DPI = 200;
        final float scale = 72f / DPI;

        AffineTransform atg;
        Point start = new Point();
        Point end = new Point();
        boolean isNewLine = true;
        static ArrayList<Line2D> lines = new ArrayList<>();
        static PDDocument document;

        public MyImagePanel() throws IOException
        {
            document = PDDocument.load(new File("XXXX.pdf"));
            PDFRenderer renderer = new PDFRenderer(document);
            image = renderer.renderImageWithDPI(0, DPI, ImageType.RGB);

            addMouseListener(new MouseAdapter()
            {
                @Override
                public void mouseReleased(MouseEvent e)
                {
                    if (end != start)
                    {
                        Line2D line = new Line2D.Float(start.x, start.y, end.x, end.y);
                        lines.add(line);
                        Point2D p = calcCoordinates(e);
                        start = new Point();
                        start.x = (int) p.getX();
                        start.y = (int) p.getY();

                        repaint();
                    }

                }
            });
            addMouseMotionListener(new MouseMotionAdapter()
            {
                @Override
                public void mouseMoved(MouseEvent e)
                {
                    Point2D p = calcCoordinates(e);
                    end = new Point();

                    end.x = (int) p.getX();
                    end.y = (int) p.getY();
                    repaint();
                }
            });
        }

        private Point2D calcCoordinates(MouseEvent e)
        {
            Point p = new Point(e.getX(), e.getY());
            try
            {
                return atg.inverseTransform(p, null);
            }
            catch (NoninvertibleTransformException ex)
            {
                return p;
            }
        }

        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g);

            Graphics2D g2D = (Graphics2D) g.create();

            double affineX = (getWidth() - scale * image.getWidth()) / 2;
            double affineY = (getHeight() - scale * image.getHeight()) / 2;

            AffineTransform at = new AffineTransform();
            at.translate(affineX, affineY);
            at.scale(scale, scale);

            AffineTransform atf = g2D.getTransform();
            atf.concatenate(at);
            atg = (AffineTransform) at.clone();

            g2D.setTransform(atf);

            g2D.drawImage(image, 0, 0, this);
            try
            {
                g2D.drawLine(start.x, start.y, end.x, end.y);
            }
            catch (NullPointerException e)
            {

            }

            for (Line2D l : lines)
            {
                g2D.draw(l);
            }

            g2D.dispose();
        }

        public void save()
        {

            try
            {
                PDPage page = document.getPage(0);

                PDPageContentStream contentStream = new PDPageContentStream(document, page, AppendMode.APPEND, true, true);

                for (Line2D l : lines)
                {
                    Point2D p1 = new Point2D.Double(l.getX1(), l.getY1());
                    Point2D p2 = new Point2D.Double(l.getX2(), l.getY2());
                    p1 = atg.transform(p1, null);
                    p2 = atg.transform(p2, null);
                    //contentStream.moveTo((float) l.getX1(), (float) l.getY1());
                    //contentStream.lineTo((float) l.getX2(), (float) l.getY2());
                    contentStream.moveTo((float) p1.getX(), page.getMediaBox().getHeight() - (float) p1.getY());
                    contentStream.lineTo((float) p2.getX(), page.getMediaBox().getHeight() - (float) p2.getY());
                    contentStream.stroke();
                }

                contentStream.close();

                document.save(new File("saved.pdf"));
                document.close();
            }
            catch (IOException ex)
            {
                Logger.getLogger(SO57387803DrawShapesInPDF.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

        @Override
        public Dimension getPreferredSize()
        {
            int width = (int) (scale * image.getWidth());
            int height = (int) (scale * image.getHeight());
            return new Dimension(width, height);
        }
    }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("PDF");
        frame.setSize(1500, 1200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        // MyImagePanel imagePanel = null;
        MyImagePanel imagePanel;
        try
        {
            imagePanel = new MyImagePanel();
        }
        catch (IOException ex)
        {
            Logger.getLogger(SO57387803DrawShapesInPDF.class
                    .getName()).log(Level.SEVERE, null, ex);
            return; // or there would be an uninitialized variable
        }

        JButton btn = new JButton("Save");

        btn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                imagePanel.save();
                //MyImagePanel.save();
            }
        });

        btn.setBounds(10, 0, 70, 30);

        frame.add(btn);
        JPanel pnl = new JPanel();
        pnl.add(imagePanel);
        pnl.setBounds(0, 100, 1500, 1200);
        frame.add(pnl);
        frame.revalidate();
        frame.repaint();
        frame.setVisible(true);
    }

}

推荐阅读