使用Java将PDF转换为XML

时间:2013-02-12 11:31:09

标签: java xml pdf itext

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

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

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

startElement()类的方法endElement()TransformerHandler已在xml文件中创建了标记。解析器在每个元素的开头调用startElement()方法,在XML文档的每个元素的末尾调用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();
        }
}

1 个答案:

答案 0 :(得分:1)

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

正如@BrunoLowagie指出的那样,你进一步完全忽略了页面内容不仅存在于直接页面内容流中,而且还存在于从那里引用的XObject中,参见ISO 32000-1部分8.10 表单XObjects 。他还指出,内容流中的字符串不必处于阅读顺序中,参见ibidem第9.4节文本对象

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

  

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

第7.7.3.3节 ISO 32000-1 中的页面对象

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

否则请查看iText的...text.pdf.parser包,它已经是解析PDF内容的一个很好的工具。如果您愿意,可以帮助改进它。