如何在任何pdf文件中使我的水印文本不可选?

 七城赣州击剑_966 发布于 2023-02-13 11:09

我使用itextpdf在pdf文件中完成了水印文本,但是当我复制pdf文件的实际文本时,它也允许我们复制水印文本.反正我们可以将水印文本限制为不可选择吗?

Image watermark_image = Image.getInstance(imageFile.getAbsolutePath());

while (i < num_of_pages) {
    i++;
    //To pass our watermark over text
    add_waterMark = pdfStamper.getOverContent(i);

    //To pass our watermark under text
    //add_waterMark = pdfStamper.getUnderContent(i);

    // watermark_image.
    watermark_image.setAbsolutePosition(0, 0);

    add_waterMark.beginText();
    //add_waterMark.setTextRenderingMode(number_of_pages);

    //watermark_image is png file
    add_waterMark.addImage(watermark_image);

    add_waterMark.endText();
}

我使用PdfContentByte编写代码,它是空心水平的,但我可以在这里复制水印文本:(我想用PdfPatternPainter替换我的代码,如果可能的话,因为PdfPatternPainter继承了PdfContentByte的所有字段.

这是使用PdfContentByte的代码:

int n = reader.getNumberOfPages();

        PdfContentByte under;
        PdfGState gstate = new PdfGState();
        gstate.setFillOpacity(0.35f);
        gstate.setStrokeOpacity(0.35f);
        BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD,
                BaseFont.WINANSI, BaseFont.EMBEDDED);

        Rectangle size = reader.getPageSizeWithRotation(1);
//          float angle = (float) ((180 * (Math.asin(size.getHeight()
//                  / Math.sqrt(size.getWidth() * size.getWidth()
//                          + size.getHeight() *       size.getHeight())))) / Math.PI);
        int i = 1;

        while (i < n + 1) {

            under = stamper.getOverContent(i);
            under.setColorStroke(new BaseColor(192, 192, 192));
            i++;
            under.beginText();
                under.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
            under.setLineWidth(0.85f);
            under.setLineDash(0.4f, 0.4f, 0.2f);

            under.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                    "user name must required", 250, 780,
                    MYConstants.WATERMARK_PAGE_ANGLE);
            under.setFontAndSize(font, 42);
            under.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                    "user Company name required", 200, 730,
                    MyConstants.WATERMARK_PAGE_ANGLE);
            under.setFontAndSize(font, 42);
            under.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                    "Plesae enter your email id", 150, 680,
                    MyConstants.WATERMARK_PAGE_ANGLE);


            under.endText();
        }

     stamper.close();
} 

mkl.. 8

在对原始问题的评论中,OP澄清了

这里唯一需要的是,每当您尝试选择pdf的整个文本时,都不应选择水印文本.我只是想知道有没有其他方法,然后使用png图像文件.

这听起来像使用带有文本内容的模式可能适合您.这里是使用iText的概念验证:

void addTextPatternToOverContent(File source, File target) throws IOException, DocumentException
{
    PdfReader reader = new PdfReader(source.getPath());
    OutputStream os = new FileOutputStream(target);
    PdfStamper stamper = new PdfStamper(reader, os);

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(200, 150);
    painter.setColorFill(BaseColor.BLACK);
    painter.beginText();
    painter.setTextMatrix(AffineTransform.getTranslateInstance(0, 50));
    painter.setFontAndSize(BaseFont.createFont(), 100);
    painter.showText("Test");
    painter.endText();

    for (int i = reader.getNumberOfPages(); i > 0; i--)
    {
        PdfContentByte overContent = stamper.getOverContent(i);
        overContent.setColorFill(new PatternColor(painter));
        overContent.rectangle(200, 300, 200, 150);
        overContent.fill();
    }

    stamper.close();
    os.close();
    reader.close();
}

在Adobe Reader中无法选择模式中的文本.而@gwillie,这里不需要繁重的工作,这不是安全功能,一旦你知道它放在哪里就很容易找到.

请注意,模式可能变幻无常.对于文本的自由定位,您可能希望在固定形式的xobject中填充具有该模式的矩形(因此与图案切片一起使用)并将该xobject放在任何您想要的位置.

很可能你会想要应用透明度,或者不填充,但只是用这个图案画家对字母进行细细描边:

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(200, 150);
    painter.setColorStroke(BaseColor.BLACK);
    painter.beginText();
    painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
    painter.setTextMatrix(AffineTransform.getTranslateInstance(0, 50));
    painter.setFontAndSize(BaseFont.createFont(), 100);
    painter.showText("Test");
    painter.endText();

PS:将OP的新详细代码考虑在内,添加一个方法,因为非复制和粘贴功能可能如下所示:

void addUserPatternToOverContent(File source, File target) throws IOException, DocumentException
{
    PdfReader reader = new PdfReader(source.getPath());
    OutputStream os = new FileOutputStream(target);
    PdfStamper stamper = new PdfStamper(reader, os);

    Rectangle pageSize = reader.getPageSize(1);
    final float WATERMARK_PAGE_ANGLE = -72;

    BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(pageSize.getWidth(), pageSize.getHeight());
    painter.setColorStroke(new BaseColor(192, 192, 192));
    painter.setLineWidth(0.85f);
    painter.setLineDash(0.4f, 0.4f, 0.2f);

    painter.beginText();
    painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
    painter.setFontAndSize(font, 42);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
            "user name must required", 250, 780,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
            "user Company name required", 200, 730,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
            "Plesae enter your email id", 150, 680,
            WATERMARK_PAGE_ANGLE);
    painter.endText();

    for (int i = reader.getNumberOfPages(); i > 0; i--)
    {
        Rectangle thisPageSize = reader.getPageSize(i);
        PdfContentByte overContent = stamper.getOverContent(i);
        overContent.setColorFill(new PatternColor(painter));
        overContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(), thisPageSize.getHeight());
        overContent.fill();
    }

    stamper.close();
    os.close();
    reader.close();
}

这显示例如

水标志应用于样本页面

顺便说一下,我将Helvetica Bold更改为未嵌入,因为它是标准的14种字体之一.

PPS:在评论中,OP还在进一步思考

是否有任何方式我们的水印文本去实际文本的背景和在图像的情况下它前进.我尝试过getUnderContent(),但是我们的Pdf图像隐藏了这个文本.

如果没有对页面内容进行排序(第一个图像,然后是水印,那么文本 - 通常不是一般的),可以尝试用图像收集所有区域(使用iText解析器包类),然后将水印放到不足之处,以及过度的内容也只是在你找到图像的组合区域中放置了水印.更容易实现,但有点脏.例如

void addUserPatternOverAndUnder(File source, File target) throws IOException, DocumentException
{
    PdfReader reader = new PdfReader(source.getPath());
    OutputStream os = new FileOutputStream(target);
    PdfStamper stamper = new PdfStamper(reader, os);

    Rectangle pageSize = reader.getPageSize(1);
    final float WATERMARK_PAGE_ANGLE = -60;

    BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

    PdfPatternPainter painter = stamper.getOverContent(1).createPattern(pageSize.getWidth(),
            pageSize.getHeight());
    painter.setColorStroke(new BaseColor(0, 192, 192));
    painter.setLineWidth(0.85f);
    painter.setLineDash(0.4f, 0.4f, 0.2f);

    painter.beginText();
    painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
    painter.setFontAndSize(font, 42);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "user name must required", 150, 780,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "user Company name required", 100, 730,
            WATERMARK_PAGE_ANGLE);
    painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "Plesae enter your email id", 050, 680,
            WATERMARK_PAGE_ANGLE);
    painter.endText();

    for (int i = reader.getNumberOfPages(); i > 0; i--)
    {
        Rectangle thisPageSize = reader.getPageSize(i);

        PdfContentByte underContent = stamper.getUnderContent(i);
        underContent.setColorFill(new PatternColor(painter));
        underContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(),
                thisPageSize.getHeight());
        underContent.fill();

        List path = getImageBordersPathPoints(reader, i);
        if (path != null && !path.isEmpty())
        {
            PdfContentByte overContent = stamper.getOverContent(i);
            overContent.setColorFill(new PatternColor(painter));

            for (int index = 0; index < path.size(); index++)
            {
                Vector corner = path.get(index);
                if (index % 4 == 0)
                {
                    overContent.moveTo(corner.get(Vector.I1), corner.get(Vector.I2));
                }
                else
                {
                    overContent.lineTo(corner.get(Vector.I1), corner.get(Vector.I2));
                    if (index % 4 == 3)
                    {
                        overContent.closePath();
                    }
                }
            }
            overContent.fill();
        }
    }

    stamper.close();
    os.close();
    reader.close();
}

static Vector A = new Vector(0, 0, 1);
static Vector B = new Vector(1, 0, 1);
static Vector C = new Vector(1, 1, 1);
static Vector D = new Vector(0, 1, 1);
static List positive = Arrays.asList(A, B, C, D);
static List negative = Arrays.asList(A, D, C, B);

List getImageBordersPathPoints(PdfReader reader, int page) throws IOException
{
    final List result = new ArrayList();
    RenderListener listener = new RenderListener()
    {
        public void renderText(TextRenderInfo renderInfo)
        {
        }

        public void endTextBlock()
        {
        }

        public void beginTextBlock()
        {
        }

        public void renderImage(ImageRenderInfo renderInfo)
        {
            Matrix ctm = renderInfo.getImageCTM();
            List unitCorners = ctm.getDeterminant() > 0 ? positive : negative;

            for (Vector corner : unitCorners)
            {
                result.add(corner.cross(ctm));
            }
        }
    };

    PdfReaderContentParser parser = new PdfReaderContentParser(reader);
    parser.processContent(page, listener);
    return result;
}

(Vectorcom.itextpdf.text.pdf.parser.Vector)

结果(Dexx徽标是图像,地址是文本):

水标志以新的方式应用于样本页面

不幸的是,解析API还没有发出矢量图形信号.因此,矢量图形(例如,基本上是使用矢量图形操作绘制的填充矩形的彩色背景)覆盖水印.

1 个回答
  • 在对原始问题的评论中,OP澄清了

    这里唯一需要的是,每当您尝试选择pdf的整个文本时,都不应选择水印文本.我只是想知道有没有其他方法,然后使用png图像文件.

    这听起来像使用带有文本内容的模式可能适合您.这里是使用iText的概念验证:

    void addTextPatternToOverContent(File source, File target) throws IOException, DocumentException
    {
        PdfReader reader = new PdfReader(source.getPath());
        OutputStream os = new FileOutputStream(target);
        PdfStamper stamper = new PdfStamper(reader, os);
    
        PdfPatternPainter painter = stamper.getOverContent(1).createPattern(200, 150);
        painter.setColorFill(BaseColor.BLACK);
        painter.beginText();
        painter.setTextMatrix(AffineTransform.getTranslateInstance(0, 50));
        painter.setFontAndSize(BaseFont.createFont(), 100);
        painter.showText("Test");
        painter.endText();
    
        for (int i = reader.getNumberOfPages(); i > 0; i--)
        {
            PdfContentByte overContent = stamper.getOverContent(i);
            overContent.setColorFill(new PatternColor(painter));
            overContent.rectangle(200, 300, 200, 150);
            overContent.fill();
        }
    
        stamper.close();
        os.close();
        reader.close();
    }
    

    在Adobe Reader中无法选择模式中的文本.而@gwillie,这里不需要繁重的工作,这不是安全功能,一旦你知道它放在哪里就很容易找到.

    请注意,模式可能变幻无常.对于文本的自由定位,您可能希望在固定形式的xobject中填充具有该模式的矩形(因此与图案切片一起使用)并将该xobject放在任何您想要的位置.

    很可能你会想要应用透明度,或者不填充,但只是用这个图案画家对字母进行细细描边:

        PdfPatternPainter painter = stamper.getOverContent(1).createPattern(200, 150);
        painter.setColorStroke(BaseColor.BLACK);
        painter.beginText();
        painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
        painter.setTextMatrix(AffineTransform.getTranslateInstance(0, 50));
        painter.setFontAndSize(BaseFont.createFont(), 100);
        painter.showText("Test");
        painter.endText();
    

    PS:将OP的新详细代码考虑在内,添加一个方法,因为非复制和粘贴功能可能如下所示:

    void addUserPatternToOverContent(File source, File target) throws IOException, DocumentException
    {
        PdfReader reader = new PdfReader(source.getPath());
        OutputStream os = new FileOutputStream(target);
        PdfStamper stamper = new PdfStamper(reader, os);
    
        Rectangle pageSize = reader.getPageSize(1);
        final float WATERMARK_PAGE_ANGLE = -72;
    
        BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
    
        PdfPatternPainter painter = stamper.getOverContent(1).createPattern(pageSize.getWidth(), pageSize.getHeight());
        painter.setColorStroke(new BaseColor(192, 192, 192));
        painter.setLineWidth(0.85f);
        painter.setLineDash(0.4f, 0.4f, 0.2f);
    
        painter.beginText();
        painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
        painter.setFontAndSize(font, 42);
        painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                "user name must required", 250, 780,
                WATERMARK_PAGE_ANGLE);
        painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                "user Company name required", 200, 730,
                WATERMARK_PAGE_ANGLE);
        painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
                "Plesae enter your email id", 150, 680,
                WATERMARK_PAGE_ANGLE);
        painter.endText();
    
        for (int i = reader.getNumberOfPages(); i > 0; i--)
        {
            Rectangle thisPageSize = reader.getPageSize(i);
            PdfContentByte overContent = stamper.getOverContent(i);
            overContent.setColorFill(new PatternColor(painter));
            overContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(), thisPageSize.getHeight());
            overContent.fill();
        }
    
        stamper.close();
        os.close();
        reader.close();
    }
    

    这显示例如

    水标志应用于样本页面

    顺便说一下,我将Helvetica Bold更改为未嵌入,因为它是标准的14种字体之一.

    PPS:在评论中,OP还在进一步思考

    是否有任何方式我们的水印文本去实际文本的背景和在图像的情况下它前进.我尝试过getUnderContent(),但是我们的Pdf图像隐藏了这个文本.

    如果没有对页面内容进行排序(第一个图像,然后是水印,那么文本 - 通常不是一般的),可以尝试用图像收集所有区域(使用iText解析器包类),然后将水印放到不足之处,以及过度的内容也只是在你找到图像的组合区域中放置了水印.更容易实现,但有点脏.例如

    void addUserPatternOverAndUnder(File source, File target) throws IOException, DocumentException
    {
        PdfReader reader = new PdfReader(source.getPath());
        OutputStream os = new FileOutputStream(target);
        PdfStamper stamper = new PdfStamper(reader, os);
    
        Rectangle pageSize = reader.getPageSize(1);
        final float WATERMARK_PAGE_ANGLE = -60;
    
        BaseFont font = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
    
        PdfPatternPainter painter = stamper.getOverContent(1).createPattern(pageSize.getWidth(),
                pageSize.getHeight());
        painter.setColorStroke(new BaseColor(0, 192, 192));
        painter.setLineWidth(0.85f);
        painter.setLineDash(0.4f, 0.4f, 0.2f);
    
        painter.beginText();
        painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_STROKE);
        painter.setFontAndSize(font, 42);
        painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "user name must required", 150, 780,
                WATERMARK_PAGE_ANGLE);
        painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "user Company name required", 100, 730,
                WATERMARK_PAGE_ANGLE);
        painter.showTextAlignedKerned(Element.ALIGN_MIDDLE, "Plesae enter your email id", 050, 680,
                WATERMARK_PAGE_ANGLE);
        painter.endText();
    
        for (int i = reader.getNumberOfPages(); i > 0; i--)
        {
            Rectangle thisPageSize = reader.getPageSize(i);
    
            PdfContentByte underContent = stamper.getUnderContent(i);
            underContent.setColorFill(new PatternColor(painter));
            underContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(),
                    thisPageSize.getHeight());
            underContent.fill();
    
            List<Vector> path = getImageBordersPathPoints(reader, i);
            if (path != null && !path.isEmpty())
            {
                PdfContentByte overContent = stamper.getOverContent(i);
                overContent.setColorFill(new PatternColor(painter));
    
                for (int index = 0; index < path.size(); index++)
                {
                    Vector corner = path.get(index);
                    if (index % 4 == 0)
                    {
                        overContent.moveTo(corner.get(Vector.I1), corner.get(Vector.I2));
                    }
                    else
                    {
                        overContent.lineTo(corner.get(Vector.I1), corner.get(Vector.I2));
                        if (index % 4 == 3)
                        {
                            overContent.closePath();
                        }
                    }
                }
                overContent.fill();
            }
        }
    
        stamper.close();
        os.close();
        reader.close();
    }
    
    static Vector A = new Vector(0, 0, 1);
    static Vector B = new Vector(1, 0, 1);
    static Vector C = new Vector(1, 1, 1);
    static Vector D = new Vector(0, 1, 1);
    static List<Vector> positive = Arrays.asList(A, B, C, D);
    static List<Vector> negative = Arrays.asList(A, D, C, B);
    
    List<Vector> getImageBordersPathPoints(PdfReader reader, int page) throws IOException
    {
        final List<Vector> result = new ArrayList<Vector>();
        RenderListener listener = new RenderListener()
        {
            public void renderText(TextRenderInfo renderInfo)
            {
            }
    
            public void endTextBlock()
            {
            }
    
            public void beginTextBlock()
            {
            }
    
            public void renderImage(ImageRenderInfo renderInfo)
            {
                Matrix ctm = renderInfo.getImageCTM();
                List<Vector> unitCorners = ctm.getDeterminant() > 0 ? positive : negative;
    
                for (Vector corner : unitCorners)
                {
                    result.add(corner.cross(ctm));
                }
            }
        };
    
        PdfReaderContentParser parser = new PdfReaderContentParser(reader);
        parser.processContent(page, listener);
        return result;
    }
    

    (Vectorcom.itextpdf.text.pdf.parser.Vector)

    结果(Dexx徽标是图像,地址是文本):

    水标志以新的方式应用于样本页面

    不幸的是,解析API还没有发出矢量图形信号.因此,矢量图形(例如,基本上是使用矢量图形操作绘制的填充矩形的彩色背景)覆盖水印.

    2023-02-13 11:12 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有