在 iText 中操作路径、颜色等

2024-01-10

我需要分析PDF文件的路径数据并使用iText 7操作内容。操作包括删除/替换和着色。

我可以使用如下代码来分析图形:

public class ContentParsing {
    public static void main(String[] args) throws IOException {
        new ContentParsing().inspectPdf("testdata/test.pdf");
    }

    public void inspectPdf(String path) throws IOException {
        File file = new File(path);
        PdfDocument pdf = new PdfDocument(new PdfReader(file.getAbsolutePath()));
        PdfDocumentContentParser parser = new PdfDocumentContentParser(pdf);
        for (int i=1; i<=pdf.getNumberOfPages(); i++) {
            parser.processContent(i, new PathEventListener());
        }
        pdf.close();
    }
}


public class PathEventListener implements IEventListener {
    public void eventOccurred(IEventData eventData, EventType eventType) {
        PathRenderInfo pathRenderInfo = (PathRenderInfo) eventData;
        for ( Subpath subpath : pathRenderInfo.getPath().getSubpaths() ) {
            for ( IShape segment : subpath.getSegments() ) {
                // Here goes some path analysis code
                System.out.println(segment.getBasePoints());
            }
        }
    }

    public Set<EventType> getSupportedEvents() {
        Set<EventType> supportedEvents = new HashSet<EventType>();
        supportedEvents.add(EventType.RENDER_PATH);
        return supportedEvents;
    }
}

现在,如何处理内容并将其写回到 PDF 中?我是否必须构建一个全新的 PDF 文档并复制所有内容(以经过操作的形式),或者我可以以某种方式直接操作读取的 PDF 数据吗?


现在,如何处理内容并将其写回到 PDF 中?我是否必须构建一个全新的 PDF 文档并复制所有内容(以经过操作的形式),或者我可以以某种方式直接操作读取的 PDF 数据吗?

本质上,您正在寻找一个类,它不仅仅是解析 PDF 内容流并发出其中的指令信号,就像PdfCanvasProcessor (the PdfDocumentContentParser你使用的只是一个非常薄的包装纸PdfCanvasProcessor),但它也会根据您转发回的指令重新创建内容流。

通用内容流编辑器类

对于 iText 5.5.x,可以在以下位置找到此类内容流编辑器类的概念验证:这个答案 https://stackoverflow.com/a/35915789/1729265(Java 版本位于答案文本的下方)。

这是 iText 7 概念验证的移植:

public class PdfCanvasEditor extends PdfCanvasProcessor
{
    /**
     * This method edits the immediate contents of a page, i.e. its content stream.
     * It explicitly does not descent into form xobjects, patterns, or annotations.
     */
    public void editPage(PdfDocument pdfDocument, int pageNumber) throws IOException
    {
        if ((pdfDocument.getReader() == null) || (pdfDocument.getWriter() == null))
        {
            throw new PdfException("PdfDocument must be opened in stamping mode.");
        }

        PdfPage page = pdfDocument.getPage(pageNumber);
        PdfResources pdfResources = page.getResources();
        PdfCanvas pdfCanvas = new PdfCanvas(new PdfStream(), pdfResources, pdfDocument);
        editContent(page.getContentBytes(), pdfResources, pdfCanvas);
        page.put(PdfName.Contents, pdfCanvas.getContentStream());
    }

    /**
     * This method processes the content bytes and outputs to the given canvas.
     * It explicitly does not descent into form xobjects, patterns, or annotations.
     */
    public void editContent(byte[] contentBytes, PdfResources resources, PdfCanvas canvas)
    {
        this.canvas = canvas;
        processContent(contentBytes, resources);
        this.canvas = null;
    }

    /**
     * <p>
     * This method writes content stream operations to the target canvas. The default
     * implementation writes them as they come, so it essentially generates identical
     * copies of the original instructions the {@link ContentOperatorWrapper} instances
     * forward to it.
     * </p>
     * <p>
     * Override this method to achieve some fancy editing effect.
     * </p> 
     */
    protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
    {
        PdfOutputStream pdfOutputStream = canvas.getContentStream().getOutputStream();
        int index = 0;

        for (PdfObject object : operands)
        {
            pdfOutputStream.write(object);
            if (operands.size() > ++index)
                pdfOutputStream.writeSpace();
            else
                pdfOutputStream.writeNewLine();
        }
    }

    //
    // constructor giving the parent a dummy listener to talk to 
    //
    public PdfCanvasEditor()
    {
        super(new DummyEventListener());
    }

    //
    // Overrides of PdfContentStreamProcessor methods
    //
    @Override
    public IContentOperator registerContentOperator(String operatorString, IContentOperator operator)
    {
        ContentOperatorWrapper wrapper = new ContentOperatorWrapper();
        wrapper.setOriginalOperator(operator);
        IContentOperator formerOperator = super.registerContentOperator(operatorString, wrapper);
        return formerOperator instanceof ContentOperatorWrapper ? ((ContentOperatorWrapper)formerOperator).getOriginalOperator() : formerOperator;
    }

    //
    // members holding the output canvas and the resources
    //
    protected PdfCanvas canvas = null;

    //
    // A content operator class to wrap all content operators to forward the invocation to the editor
    //
    class ContentOperatorWrapper implements IContentOperator
    {
        public IContentOperator getOriginalOperator()
        {
            return originalOperator;
        }

        public void setOriginalOperator(IContentOperator originalOperator)
        {
            this.originalOperator = originalOperator;
        }

        @Override
        public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
        {
            if (originalOperator != null && !"Do".equals(operator.toString()))
            {
                originalOperator.invoke(processor, operator, operands);
            }
            write(processor, operator, operands);
        }

        private IContentOperator originalOperator = null;
    }

    //
    // A dummy event listener to give to the underlying canvas processor to feed events to
    //
    static class DummyEventListener implements IEventListener
    {
        @Override
        public void eventOccurred(IEventData data, EventType type)
        { }

        @Override
        public Set<EventType> getSupportedEvents()
        {
            return null;
        }
    }
}

(PdfCanvasEditor.java https://github.com/mkl-public/testarea-itext7/blob/master/src/main/java/mkl/testarea/itext7/content/PdfCanvasEditor.java#L39)

的解释来自iText 5 的答案 https://stackoverflow.com/a/35915789/1729265仍然适用,从 iText 5.5.x 到 iText 7.0.x,解析框架没有太大变化。

使用示例

不幸的是,您以非常模糊的措辞描述了您想要如何更改内容。因此,我只是移植了一些使用原始 iText 5 内容流编辑器类的 iText 5 示例:

水印去除

这些是用例的端口这个答案 https://stackoverflow.com/a/38572474/1729265.

testRemoveBoldMTTextDocument

此示例删除以名称以“BoldMT”结尾的字体编写的所有文本:

try (   InputStream resource = getClass().getResourceAsStream("document.pdf");
        PdfReader pdfReader = new PdfReader(resource);
        OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "document-noBoldMTText.pdf"));
        PdfWriter pdfWriter = new PdfWriter(result);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
    PdfCanvasEditor editor = new PdfCanvasEditor()
    {

        @Override
        protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
        {
            String operatorString = operator.toString();

            if (TEXT_SHOWING_OPERATORS.contains(operatorString))
            {
                if (getGraphicsState().getFont().getFontProgram().getFontNames().getFontName().endsWith("BoldMT"))
                    return;
            }
            
            super.write(processor, operator, operands);
        }

        final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
    };
    for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
    {
        editor.editPage(pdfDocument, i);
    }
}

(编辑页面内容.java https://github.com/mkl-public/testarea-itext7/blob/master/src/test/java/mkl/testarea/itext7/content/EditPageContent.java#L61测试方法testRemoveBoldMTTextDocument)

testRemoveBigTextDocument

此示例删除所有使用大字体书写的文本:

try (   InputStream resource = getClass().getResourceAsStream("document.pdf");
        PdfReader pdfReader = new PdfReader(resource);
        OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "document-noBigText.pdf"));
        PdfWriter pdfWriter = new PdfWriter(result);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
    PdfCanvasEditor editor = new PdfCanvasEditor()
    {

        @Override
        protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
        {
            String operatorString = operator.toString();

            if (TEXT_SHOWING_OPERATORS.contains(operatorString))
            {
                if (getGraphicsState().getFontSize() > 100)
                    return;
            }
            
            super.write(processor, operator, operands);
        }

        final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
    };
    for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
    {
        editor.editPage(pdfDocument, i);
    }
}

(编辑页面内容.java https://github.com/mkl-public/testarea-itext7/blob/master/src/test/java/mkl/testarea/itext7/content/EditPageContent.java#L114测试方法testRemoveBigTextDocument)

文字颜色改变

这是用例的一个端口这个答案 https://stackoverflow.com/a/40709845/1729265.

testChangeBlackTextToGreenDocument

此示例将黑色文本的颜色更改为绿色。

try (   InputStream resource = getClass().getResourceAsStream("document.pdf");
        PdfReader pdfReader = new PdfReader(resource);
        OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "document-blackTextToGreen.pdf"));
        PdfWriter pdfWriter = new PdfWriter(result);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
    PdfCanvasEditor editor = new PdfCanvasEditor()
    {

        @Override
        protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
        {
            String operatorString = operator.toString();

            if (TEXT_SHOWING_OPERATORS.contains(operatorString))
            {
                if (currentlyReplacedBlack == null)
                {
                    Color currentFillColor = getGraphicsState().getFillColor();
                    if (Color.BLACK.equals(currentFillColor))
                    {
                        currentlyReplacedBlack = currentFillColor;
                        super.write(processor, new PdfLiteral("rg"), Arrays.asList(new PdfNumber(0), new PdfNumber(1), new PdfNumber(0), new PdfLiteral("rg")));
                    }
                }
            }
            else if (currentlyReplacedBlack != null)
            {
                if (currentlyReplacedBlack instanceof DeviceCmyk)
                {
                    super.write(processor, new PdfLiteral("k"), Arrays.asList(new PdfNumber(0), new PdfNumber(0), new PdfNumber(0), new PdfNumber(1), new PdfLiteral("k")));
                }
                else if (currentlyReplacedBlack instanceof DeviceGray)
                {
                    super.write(processor, new PdfLiteral("g"), Arrays.asList(new PdfNumber(0), new PdfLiteral("g")));
                }
                else
                {
                    super.write(processor, new PdfLiteral("rg"), Arrays.asList(new PdfNumber(0), new PdfNumber(0), new PdfNumber(0), new PdfLiteral("rg")));
                }
                currentlyReplacedBlack = null;
            }

            super.write(processor, operator, operands);
        }

        Color currentlyReplacedBlack = null;

        final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
    };
    for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
    {
        editor.editPage(pdfDocument, i);
    }
}

(编辑页面内容.java https://github.com/mkl-public/testarea-itext7/blob/master/src/test/java/mkl/testarea/itext7/content/EditPageContent.java#L179测试方法testChangeBlackTextToGreenDocument)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 iText 中操作路径、颜色等 的相关文章

随机推荐

  • 在 Windows 上托管闪亮的应用程序

    首先我的问题与 如何在 Windows 服务器上托管 Shiny 应用程序 https stackoverflow com questions 46848995 how to host shiny apps on windows serve
  • nslog时间戳

    我想记录设备运动时间戳属性 设备运动位于 CMMotionManager devicemotion timestamp 类中 有任何想法吗 这是我提出的解决方案 因为日期是根据 Apple 文档确定的 时间戳是自电话启动以来的时间量 以秒为
  • 禁用文本选择 UITextView

    我想禁用 UITextView 上的文本选择 到现在为止我所拥有的 已经完成的是 BOOL canPerformAction SEL action withSender id sender UIMenuController sharedMe
  • 错误:boost/scoped_ptr.hpp:使用 libkml 时没有此类文件或目录

    我正在开发一个使用谷歌地图数据库的软件 我搜索了 google 发现一个 C 库可以做到这一点 libkml 我已经下载了一些示例代码website http code google com p libkml source browse t
  • Git Bash shell 无法创建符号链接

    当我尝试从以下位置创建符号链接时Git Bash https superuser com questions 1053633 what is git bash for windows anywayshell 每次都失败 ln s c Use
  • iOS – UILocalNotification 对同一个通知触发了两次

    如果我安排两个UILocalNotifications 并将它们设置为在完全相同的 fireDate 处触发 然后在设备上 这不是模拟器的bug 上的fireDateapplication didReceiveLocalNotificati
  • 无法更新 git 存储库:“sign_and_send_pubkey:不支持相互签名”

    我正在使用 git 存储库 并使用 ssh 密钥与其连接 直到今天都运行良好 我正在执行拉取操作 并收到以下 ssh 错误 sign and send pubkey no mutual signature supported 然后它会要求输
  • 为什么在 C++ 函数中可以返回对象引用?

    这是网站上的示例 http www cplusplus com doc tutorial classes2 http www cplusplus com doc tutorial classes2 我知道这是一个有效的例子 但是 我不明白为
  • 对于无法到达的目的地,ICMP 回显请求/回复的正确流程是什么?

    Goal 我需要能够 ping 网络交换机以确定它是否可用 这是为了告诉用户网络电缆已拔出 网络交换机不可用或网络通信路径中存在其他问题 我意识到这不是一个全面的诊断工具 但有总比没有好 Design 我计划使用带有原始套接字的 ICMP
  • 使用 HttpWebRequest 传输大文件时出现内存不足异常

    使用大文件的 Http Put 时出现内存不足异常 我正在使用异步模型 如代码所示 我正在尝试将 8K 数据块发送到 Windows 2008 R2 服务器 当我尝试写入超过 536 868 864 字节的数据块时 始终会发生异常 异常发生
  • 神经网络:输入层由神经元组成吗?

    我目前正在研究神经网络理论 我发现到处都写到它由以下几层组成 输入层 隐藏层 输出层 我看到一些图形描述将输入层显示为网络中的真实节点 而其他图形描述则将该层显示为值向量 x1 x2 xn 什么是正确的结构 输入层 是真正的神经元层吗 或者
  • 如何在 VS Code 终端中突出显示要复制和粘贴的文本?

    如何创建突出显示终端文本的快捷方式 Ctrl Shift 右 左 Ctrl Shift End Ctrl Shift Home 正如在 PowerShell ISE 中实现的那样 如何创建按单词移动光标的快捷方式 Ctrl 右 左 类似于
  • 单选按钮样式

    我想用纯 CSS 设计单选按钮的样式 没有类或 ID 只需输入 type radio 我想对未选择和选择使用背景图像 然而 vendor appearance none 不适用于 Trident 或 Gecko 只是Webkit 在这些浏览
  • 语法错误:“继续”在循环中不正确

    我已经为这个错误苦苦挣扎了一段时间 对于口译员为什么抱怨 继续 似乎有不同的看法 所以我想提供下面的错误代码 import tweepy import time def writeHandlesToFile file open dataFi
  • PostgreSQL 中布尔数据类型输出“yes/no”而不是“t/f”

    如何进行会返回的查询yes no代替t f 真假 目前的解决方案是 SELECT credit card holders token IS NOT NULL AS premium 我找到了一个 Ruby 解决方案 Rails 或 Ruby
  • 将外部音频文件提取到 Web 音频 API 缓冲区 - cors 错误?

    我正在做一个项目 我希望从 xeno canto 档案中随机选择来创建一个 虚拟黎明合唱团 我正在使用 webAudio API 在 javascript 中执行此操作 我有一个示例列表及其网址 例如 xeno canto org soun
  • Node.js 堆栈错误超过 10 行?

    有没有办法让 Node js 堆栈错误超过 10 行 function a dieInHell function b a function c b function d c function e d function f e functio
  • 将代表向量添加到总行数不均匀的数据框中

    我正在尝试找到一种自动处理大型数据集以添加两个因素的方法 但数据可能包含不均匀的行 我尝试使用 rep 函数来执行此操作 但这仅在数据帧具有偶数时才有效 x lt c 1 3 5 7 9 y lt c 2 4 6 8 10 df lt da
  • 如何使用 cURL 执行 PUT 请求?

    如何使用 cURL 测试 RESTful PUT 或 DELETE 方法 使用巨大的 https en wikipedia org wiki Letter case Majuscule X使用您想要的任何 HTTP 动词进行标记 curl
  • 在 iText 中操作路径、颜色等

    我需要分析PDF文件的路径数据并使用iText 7操作内容 操作包括删除 替换和着色 我可以使用如下代码来分析图形 public class ContentParsing public static void main String arg