【问题标题】:How to add overlay text with link annotations to existing pdf?如何将带有链接注释的覆盖文本添加到现有的pdf?
【发布时间】:2015-02-18 09:04:31
【问题描述】:

我想在我的叠加文本中添加一个链接。我读过使用Anchor 仅适用于从头开始制作的文档,但不适用于现有的pdf。我的代码正在为每个页面添加覆盖文本。我的目标是使该文本的一部分可点击。我不知道如何制作作为短语一部分的链接注释。

这是我的代码:

            int n = reader.getNumberOfPages();
            // step 4: we add content
            PdfImportedPage page;
            PdfCopy.PageStamp stamp;
            for (int j = 0; j < n; )
            {
                ++j;
                page = writer.getImportedPage(reader, j);
                if (i == 1) {
                    stamp = writer.createPageStamp(page);
                    Rectangle mediabox = reader.getPageSize(j);
                    Rectangle crop = new Rectangle(mediabox);
                    writer.setCropBoxSize(crop);
                    // add overlay text
                    Paragraph p = new Paragraph();
                    p.setAlignment(Element.ALIGN_CENTER);
                    FONT_URL_OVERLAY.setColor(0, 191, 255);
                    // get current user
                    EPerson loggedin = context.getCurrentUser();
                    String eperson = null;
                    if (loggedin != null)
                    {
                        eperson = loggedin.getFullName();
                    }
                    else eperson = "Anonymous";
                    Phrase downloaded = new Phrase();
                    Chunk site = new Chunk("My Website",FONT_URL_OVERLAY);
                    site.setAction(new PdfAction("http://www.mywebsite.com"));
                    downloaded.add(new Chunk("Downloaded by [" + eperson + "] from ", FONT_OVERLAY));
                    downloaded.add(site);
                    downloaded.add(new Chunk(" on ", FONT_OVERLAY));
                    downloaded.add(new Chunk(new SimpleDateFormat("MMMM d, yyyy").format(new Date()), FONT_OVERLAY));
                    downloaded.add(new Chunk(" at ", FONT_OVERLAY));
                    downloaded.add(new Chunk(new SimpleDateFormat("h:mm a z").format(new Date()), FONT_OVERLAY));
                    p.add(downloaded);
                    ColumnText.showTextAligned(stamp.getOverContent(), Element.ALIGN_CENTER, p,
                            crop.getLeft(10), crop.getHeight() / 2 + crop.getBottom(), 90);
                    stamp.alterContents();
                }
                writer.addPage(page);
            }

所以我的叠加层看起来像这样:

由 [Anonymous] 于 2015 年 2 月 17 日凌晨 1:20 CST 从我的网站下载

如何将我的网站转换为链接注释?在 SO 中搜索,我找到了这个post,但我不知道如何将添加链接注释应用到我的覆盖文本的一部分。

提前致谢。

编辑:如何将带有链接注释的旋转覆盖文本添加到现有 pdf?
感谢Bruno Lowagie 竭尽全力回答我的问题。虽然我最初询问如何将覆盖文本中的链接注释添加到现有的 pdf,但他还在他的答案的 cmets 部分解决了我的问题,即如果覆盖文本被旋转,则正确设置坐标。

【问题讨论】:

    标签: java pdf itext


    【解决方案1】:

    您正在使用ColumnText.showAligned(),这足以添加一行没有任何特殊功能的文本,但是如果您希望锚点起作用,则需要以不同的方式使用ColumnText

    这在AddLinkAnnotation2 示例中显示:

    public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
        PdfContentByte canvas = stamper.getOverContent(1);
        Font bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
        Chunk chunk = new Chunk("The Best iText Questions on StackOverflow", bold);
        chunk.setAnchor("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
        Phrase p = new Phrase("Download ");
        p.add(chunk);
        p.add(" and discover more than 200 questions and answers.");
        ColumnText ct = new ColumnText(canvas);
        ct.setSimpleColumn(36, 700, 559, 750);
        ct.addText(p);
        ct.go();
        stamper.close();
        reader.close();
    }
    

    在这种情况下,我们为ColumnText 对象定义一个矩形,我们将Phrase 添加到列中,然后我们go()

    如果您检查结果,link_annotation2.pdf,您会注意到您可以单击粗体字。

    ColumnText.showTextAligned() 没有计划支持此功能。这是一种方便的方法,可以用作上面显示的少数几行的快捷方式,但有一些已知的限制:不换行,忽略交互性,...

    更新 1:在评论部分,您提出了一个关于旋转内容和链接的附加问题。

    旋转内容并不困难。有不止一种方法可以做到这一点。旋转链接并非易事,因为链接是一种注释,而注释不是内容的一部分。

    我们先来看看AddLinkAnnotation3

    public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
        AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 6);
        stamper.getWriter().setPageEvent(new AddAnnotation(stamper, transform));
        PdfContentByte canvas = stamper.getOverContent(1);
        Font bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
        Chunk chunk = new Chunk("The Best iText Questions on StackOverflow", bold);
        chunk.setGenericTag("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
        Phrase p = new Phrase("Download ");
        p.add(chunk);
        p.add(" and discover more than 200 questions and answers.");
        canvas.saveState();
        canvas.concatCTM(transform);
        ColumnText ct = new ColumnText(canvas);
        ct.setSimpleColumn(300, 0, 800, 400);
        ct.addText(p);
        ct.go();
        canvas.restoreState();
        stamper.close();
        reader.close();
    }
    

    在本例中,我们定义了 30 度的变换 (Math.PI / 6):

    AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 6);
    

    我们在渲染列时使用这种转换:

    canvas.saveState();
    canvas.concatCTM(transform);
    // render column
    canvas.restoreState();
    

    这会旋转内容,但我们还没有添加任何注释。相反,我们定义了一个页面事件:

    stamper.getWriter().setPageEvent(new AddAnnotation(stamper, transform));
    

    我们引入了一个通用标签:

    chunk.setGenericTag("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
    

    为了添加注解,我们在页面事件实现中使用了一些魔法:

    public class AddAnnotation extends PdfPageEventHelper {
        protected PdfStamper stamper;
        protected AffineTransform transform;
    
        public AddAnnotation(PdfStamper stamper, AffineTransform transform) {
            this.stamper = stamper;
            this.transform = transform;
        }
    
        @Override
        public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) {
            float[] pts = {rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop()};
            transform.transform(pts, 0, pts, 0, 2);
            float[] dstPts = {pts[0], pts[1], pts[2], pts[3]};
            rect = new Rectangle(dstPts[0], dstPts[1], dstPts[2], dstPts[3]);
            PdfAnnotation annot = PdfAnnotation.createLink(writer, rect, PdfAnnotation.HIGHLIGHT_INVERT, new PdfAction(text));
            stamper.addAnnotation(annot, 1);
        }
    }
    

    我们创建了一个注解,但在我们这样做之前,我们对矩形进行了转换。这可以确保文本与需要可点击的文本相匹配,但是......这可能不是您所期望的:

    您可能希望旋转矩形,这是可能的,但更多的是数学运算。例如:您可以创建一个更适合的多边形:ITextShape Clickable Polygon or path

    幸运的是,您不需要 30 度角,您希望将文本旋转 90 度角。在这种情况下,您就没有上面屏幕截图中显示的奇怪效果了。

    看看AddLinkAnnotation4

    public class AddAnnotation extends PdfPageEventHelper {
        protected PdfStamper stamper;
        protected AffineTransform transform;
    
        public AddAnnotation(PdfStamper stamper, AffineTransform transform) {
            this.stamper = stamper;
            this.transform = transform;
        }
    
        @Override
        public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) {
            float[] pts = {rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop()};
            transform.transform(pts, 0, pts, 0, 2);
            float[] dstPts = {pts[0], pts[1], pts[2], pts[3]};
            rect = new Rectangle(dstPts[0], dstPts[1], dstPts[2], dstPts[3]);
            PdfAnnotation annot = PdfAnnotation.createLink(writer, rect, PdfAnnotation.HIGHLIGHT_INVERT, new PdfAction(text));
            annot.setBorder(new PdfBorderArray(0, 0, 0));
            stamper.addAnnotation(annot, 1);
        }
    
    }
    

    如您所见,我添加了一行来移除边框(除非您重新定义 PdfBorderArray,否则边框默认存在)。

    其余代码也几乎相同。我们现在定义Math.PI / 2(90 度)的角度。

    public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
        AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 2);
        stamper.getWriter().setPageEvent(new AddAnnotation(stamper, transform));
        PdfContentByte canvas = stamper.getOverContent(1);
        Font bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
        Chunk chunk = new Chunk("The Best iText Questions on StackOverflow", bold);
        chunk.setGenericTag("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
        Phrase p = new Phrase("Download ");
        p.add(chunk);
        p.add(" and discover more than 200 questions and answers.");
        canvas.saveState();
        canvas.concatCTM(transform);
        ColumnText ct = new ColumnText(canvas);
        ct.setSimpleColumn(36, -559, 806, -36);
        ct.addText(p);
        ct.go();
        canvas.restoreState();
        stamper.close();
        reader.close();
    }
    

    注意页面的左下角是轴心点,因此我们需要调整我们添加列的坐标,否则你会旋转页面可见区域之外的所有内容。

    更新 2:

    在另一条评论中,您询问的是在旋转坐标系中添加文本时需要使用的坐标。

    我画了这张图:

    在顶部,您在页面中间添加了 MIDDLE 一词,但这不是它出现的位置:您将所有内容旋转 90 度,因此 MIDDLE 一词将在您的页面之外旋转 em>(进入阴影区域)。该词将在 PDF 中,但您永远不会看到它。

    如果您查看我的代码,您会发现我使用了这些坐标:

    ct.setSimpleColumn(36, -559, 806, -36);
    

    这是可见区域之外(它低于实际页面尺寸),但是当我将所有内容旋转 90 度时,它会旋转进入可见区域。 p>

    如果你看我的图,你可以看到坐标为 (0, 0), (0, -595), (842, -598) 和 (842, 0) 的页面旋转了 90 度,因此得到与坐标为 (0, 0)、(595, 0)、(595, 842) 和 (0, 842) 的页面重合。这就是我们在高中时都学过的数学类型;-)

    您在crop.getLeft(10), crop.getHeight() / 2 + crop.getBottom() 位置添加文本。如果你知道文本会旋转 90 度,你应该使用crop.getHeight() / 2 + crop.getBottom(), -crop.getLeft()

    了解原因的最好方法是画一幅画。

    【讨论】:

    • 有没有办法对 setSimpleColumn 应用旋转?我的覆盖文本应该垂直居中并在页面的最左侧旋转 90 度。谢谢。
    • 这不是微不足道的,因为注释不是内容的一部分,因此如果您旋转内容,它们会被旋转。但这并不意味着这是不可能的。如果您是 iText 客户,请在问题跟踪器中为客户创建工单,您的请求将得到优先处理。如果您不是客户,则必须等到我有时间举例说明。
    • 感谢布鲁诺更新您的回答。我很快就会接受这个答案。不过,我还有最后一个问题,关于 setSimpleColumn 中的坐标设置。我在我的问题中说过,我的覆盖文本应该垂直居中,这是因为我事先不知道源 pdf 的实际页面大小。为什么我不能在 setSimpleColumn 中应用 crop.getLeft(10), crop.getHeight() / 2 + crop.getBottom() 作为我的参数?我似乎无法将文本垂直居中。再次感谢。
    • 我在link_annotation3.pdflink_annotation4.pdf 中发现了一些东西。尽管文本 StackOverflow 上的最佳 iText 问题 是可点击的,但它不会转到 pages.itextpdf.com/ebook-stackoverflow-questions.html。使用 acrobat 检查后发现链接属性中的操作是“转到此文档中的页面”而不是“打开 Web 链接”。
    • 我是一个非常忙碌的人。我在两次会议之间回答了你的问题。这就是我犯了一个小错误的原因:我使用了text 而不是new PdfAction(text)。我已经在回答中更正了这一点,现在我将更改网站上的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多