【问题标题】:Using Java to convert PDF to XML使用 Java 将 PDF 转换为 XML
【发布时间】:2013-02-12 11:31:08
【问题描述】:

我正在尝试创建一个原型来将 PDF 文件转换为 XML 文件。结果有点奇怪,所有的字符都变成了符号。我认为这个错误是StringBuffer 从字节数组中获取数据的地方。有Java知识的人可以帮忙吗?

此原型软件使用iText API。为了阅读 PDF 文件,我们使用了PDFReader 类。数据首先转换为字节数组,然后使用Stringbuffer,再次转换为字符串。然后我们使用了StreamResult,它作为 XML 转换结果的持有者。

之后,Transformer 类处理来自各种来源的 XML,并将转换输出写入各种接收器。然后TransformerHandler 监听 SAX ContentHandler,解析事件并将其转换为结果。

TransformerHandler 类的startElement()endElement() 方法在xml 文件中创建了标签。解析器在 XML 文档中每个元素的开头调用startElement() 方法,在每个元素的结尾调用endElement()

import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
import java.io.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

public class Cp2x {

        static StreamResult streamResult;
        static TransformerHandler handler;
        static AttributesImpl atts;

        public static void main(String[] args) throws IOException {

                try {
                        Document document = new Document();
                        document.open();
                        PdfReader reader = new PdfReader("C:\\helloworld.pdf");
                        PdfDictionary page = reader.getPageN(1);
                        PRIndirectReference objectReference = (PRIndirectReference) page
                                        .get(PdfName.CONTENTS);
                        PRStream stream = (PRStream) PdfReader
                                        .getPdfObject(objectReference);
                        byte[] streamBytes = PdfReader.getStreamBytes(stream);
                        PRTokeniser tokeniser = new PRTokeniser(streamBytes);

                        StringBuffer string_buffer = new StringBuffer();
                        while (tokeniser.nextToken()) {
                                if (tokeniser.getTokenType() == PRTokeniser.TK_STRING) {
                                        string_buffer.append(tokeniser.getStringValue());
                                }
                        }
                        String test = string_buffer.toString();
                        streamResult = new StreamResult("test.xml");
                        initXML();
                        process(test);
                        closeXML();
                        document.add(new Paragraph(".."));
                        document.close();
                } catch (Exception e) {
                }
        }

        public static void initXML() throws ParserConfigurationException,
                        TransformerConfigurationException, SAXException {
                SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory
                                .newInstance();

                handler = tf.newTransformerHandler();
                Transformer serializer = handler.getTransformer();
                serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
                serializer.setOutputProperty(
                                "{http://xml.apache.org/xslt}indent-amount", "4");
                serializer.setOutputProperty(OutputKeys.INDENT, "yes");
                handler.setResult(streamResult);
                handler.startDocument();
                atts = new AttributesImpl();
                handler.startElement("", "", "Document", atts);
        }

        public static void process(String s) throws SAXException {
                String[] elements = s.split("\\|");
                atts.clear();
                handler.startElement("", "", "Note", atts);
                handler.characters(elements[0].toCharArray(), 0, elements[0].length());
                handler.endElement("", "", "Note");
        }

        public static void closeXML() throws SAXException {
                handler.endElement("", "", "Document");
                handler.endDocument();
        }
}

【问题讨论】:

  • “结果有点奇怪,所有的字符都变成了符号”似乎是编码问题。在将字节数组转换为字符串时传递编码。根据用例,可以使用 StringBuilder 而不是 StingBuffer。
  • 您也忽略了 XObjects 中存在的所有内容;你将如何在 XML 中捕获这些?另外:您只是在阅读 String 对象,并且没有考虑到文本的实际顺序。为什么要使用过时的 iText 版本?当前版本的 iText 具有更好的解析器。它甚至可以使用 TaggedPdfReaderTool 将标记的 PDF 转换为 XML 文件:itextpdf.com/examples/iia.php?id=281(显然只有在您的 PDF 被标记时才会生成 XML)。

标签: java xml pdf itext


【解决方案1】:

正如@sudmong 所说,存在编码问题:PRTokeniser不应用于从 inside 页面内容流中读取字符串,它只能在它们之外正常工作,因为它假定一个特殊字符编码,而页面内容流中字符串的编码完全取决于内容描述该步骤中当前字体的编码。参照。 ISO 32000-1 第 7.3.4.2 节 Literal Strings 用于内容流外的字符串,9.6.6 字符编码 用于内容流内的字符串。

正如@BrunoLowagie 指出的那样,您还完全忽略了页面内容不仅在直接页面内容流中,而且在从那里引用的 XObjects 中,参见。 ISO 32000-1 第 8.10 节 Form XObjects。他还指出,内容流中的字符串不必按阅读顺序排列,参见。 ibidem 第 9.4 节 文本对象

您也忽略了页面字典的 Contents 条目的值可以是流或流数组:

该值应为单个流或流数组。如果该值是一个数组,则效果应该就像数组中的所有流按顺序连接起来形成一个流一样。符合标准的编写者可以在图像对象和其他资源出现时创建它们,即使它们会中断内容流。流之间的划分可能只发生在词汇标记之间的边界(参见 7.2,“词汇约定”),但与页面的逻辑内容或组织无关。使用或生成 PDF 文件的应用程序不需要保留 Contents 数组的现有结构。符合标准的作者不得创建不包含任何元素的 Contents 数组。

第 7.7.3.3 节页面对象ISO 32000-1

如果你真的想自己编写一个解析器,你最好先学习ISO 32000-1

否则请查看 iText 的...text.pdf.parserpackage,它已经是解析 PDF 内容的好工具。如果你喜欢它,你可以帮助改进它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-07
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-29
    相关资源
    最近更新 更多