新闻资讯 | 控件产品 | 技术文章 | 购物车 | 合作伙伴 | 问题解答 | 电子简报 | PDF论坛 | 资源下载
您现在的位置:技术文章>PDF开发篇 首页|PDF应用篇|PDF开发篇|解决方案

从pdf文档中提取出文本

发布日期:2007-8-27 作者:NeWi 出处:译自 www.codeproject.com

简介
PDF文档的应用十分普遍,通常它的内容是压缩的。本文提供了一段可以用来从PDF文件中提取文本的简单的C代码

为什么要编写这段代码?
Adobe允许你提交PDF文件,提取成文本或HTML后再通过邮件发送给你。但是假如你需要自己提取文本或在程序中加入这个功能的话,需要花费很多时间。也许你还需要对文本应用某些特殊格式(如,添加tab分隔符)以便它们能够导入到Execl中(比如,你需要将PDF文档中包含的表格数据导入到Excel中,这就是编写这段代码的目的)。

在Code Project上有几个创建PDF文档的项目,但是没有不使用商业PDF库提取文本的免费代码。这段代码可以满足有这方面需要的用户的需求。

有一些读取或创建PDF的开发包,但是你用于商业用途的话必须注册或签署各种协议。这里提供的代码非常简单和基础,但是它是完全免费的。它只用到了免费的ZLIB库。

基础
你可以从adobe网站下载类似DFReference15_v5.pdf的文档,里面解释了PDF文档的内部结构。简单地说,每个PDF文件包含一系列对象。每个对象可能需要一个或多个filter来解压也可能包含流数据。文本流通常使用FlateDecode filter压缩,可以使用ZLib(http://www.zlib.org)的代码来解压。
每个对象的数据位于"stream"和"endstream"标识之间。解压后,需要对数据进行处理以便提取出文本。数据中通常包含一个或多个带有格式指令的text对象(以BT开始,已ET结束),你可以通过一步步的了解这个程序加深对PDF文件结构的理解。

关于代码
这段程序包含了非常简单的基本的C代码。它先将整个PDF文件读到缓冲区中,然后重复搜索"stream" 及 "endstream" 段。它没有检查应该使用那种算法,而是假定使用的FlateDecode(如果PDF中使用的不是FlateDecode的话,通常)。数据流解压后,需要对它进行处理。在处理过程中,程序搜索代表text对象的 BT 和 ET标识,分别进行处理,提取出文本信息,假定是否需要tab分隔符或换行符。

这段代码还很不完善,但是它确实演示了如何从pdf文档中提取出文本。
这段代码是可运行的,所以当用它处理一个PDF文档时,它可以很好的提取出文本。

使用代码
下载中包含一个C文件,使用它的话,创建一个Win32控制台程序,把这个C文件加入到项目中。你也许需要下载免费的"zlib compiled DLL"压缩包。把zdll.lib解压到你的项目目录中并把链接到项目中,您需要把zlib1.dll复制到项目目录下,还要把zconf.h和zlib.h复制到项目目录并添加到项目中。

现在可以看看这个程序了,注意输入输出文件的名称是在main方法中指定的。

改进
如果感兴趣的话,可以编写一个图形界面。这段代码对于从表格中提取数据然后导入到excel中非常有用,由于加入了tab分隔符,可以保持原有的分栏。

部分代码

首先定位Stream段

size_t streamstart = FindStringInBuffer (buffer, "stream", filelen);
size_t streamend = FindStringInBuffer (buffer, "endstream", filelen);

找到数据段后,解压缩

z_stream zstrm; ZeroMemory(&zstrm, sizeof(zstrm));
zstrm.avail_in = streamend - streamstart + 1;
zstrm.avail_out = outsize;
zstrm.next_in = (Bytef*)(buffer + streamstart);
zstrm.next_out = (Bytef*)output;
int rsti = inflateInit(&zstrm);
if (rsti == Z_OK)
{
  int rst2 = inflate (&zstrm, Z_FINISH);
  if (rst2 >= 0)
  {
    //Ok, got something, extract the text:
    size_t totout = zstrm.total_out;
    ProcessOutput(fileo, output, totout);
  }
}

主要的工作在ProcessOutput 方法中完成,它处理解压后的流从中提取出text对象。

void ProcessOutput(FILE* file, char* output, size_t len)
{
  //Are we currently inside a text object?
  bool intextobject = false;
  //Is the next character literal 
  //(e.g. \\ to get a \ character or \( to get ( ):
  bool nextliteral = false;

  //() Bracket nesting level. Text appears inside ()
  int rbdepth = 0;

  //Keep previous chars to extract numbers etc.:
  char oc[oldchar];
  int j=0;
  for (j=0; j<oldchar; j++) oc[j]=' ';

  for (size_t i=0; i<len; i++)
  {
    char c = output[i];
    if (intextobject)
    {
      if (rbdepth==0 && seen2("TD", oc))
      {
        //Positioning.
        //See if a new line has to start or just a tab:
        float num = ExtractNumber(oc,oldchar-5);
        if (num>1.0)
        {
          fputc(0x0d, file);
          fputc(0x0a, file);
        }
        if (num<1.0)
        {
          fputc('\t', file);
        }
      }
      if (rbdepth==0 && seen2("ET", oc))
      {
        //End of a text object, also go to a new line.
        intextobject = false;
        fputc(0x0d, file);
        fputc(0x0a, file);
      }
      else if (c=='(' && rbdepthSPAN class=cs-literal>0 && !nextliteral) 
      {
        //Start outputting text!
        rbdepth=1;
        //See if a space or tab (>1000) is called for by looking
        //at the number in front of (
        int num = ExtractNumber(oc,oldchar-1);
        if (num>0)
        {
          if (num>1000.0)
          {
            fputc('\t', file);
          }
          else if (num>100.0)
          {
            fputc(' ', file);
          }
        }
      }
      else if (c==')' && rbdepth==1 && !nextliteral) 
      {
        //Stop outputting text
        rbdepth=0;
      }
      else if (rbdepth==1) 
      {
        //Just a normal text character:
        if (c=='\\' && !nextliteral)
        {
          //Only print out next character 
          //no matter what. Do not interpret.
          nextliteral = true;
        }
        else
        {
          nextliteral = false;
          if ( ((c>=' ') && (c<='~')) || ((c>=128) && (c<255)) )
          {
            fputc(c, file);
          }
        }
      }
    }
    //Store the recent characters for 
    //when we have to go back for a number:
    for (j=0; j<oldchar-1; j++) oc[j]=oc[j+1];
      oc[oldchar-1]=c;
    if (!intextobject)
    {
      if (seen2("BT", oc))
      {
        //Start of a text object:
        intextobject = true;
      }
    }
  }
}
源代码下载
发布人:管理员 【报告错误】·【推荐好友】·【打印文章】
相关评论 本文现有 8 条评论 评分人数: 3 平均分: 5
cjcse   2009-1-16 16:50:42
好像只是对pdf中的stream做了解压,然后通过BT和ET提取其中的文本,但是没有考虑编码问题,一般中文文档的话基本上出来都是乱码,如果文档是采用utf16或者gb的话没有问题,如果是其他编码就麻烦了,比如Entity-H之类的,望提供解决方案。
asa   2008-7-22 11:15:46
真不错
小呆猪   2012-1-13 15:06:02
我下载了代码,和网上的zlib连接有错误,求版主提供相关的zlib万分感谢
shepi   2009-5-6 11:01:23
不错
风雨任我行   2008-10-17 9:06:15
支持
小呆猪   2012-1-13 10:43:25
很好
kevin   2008-9-26 19:48:40
hao
kevin   2008-8-11 17:29:52
不错
相关评论
用户名: 密  码:
说明:输入正确的用户名和密码才能参与评论。如果您不是本站会员,你可以注册为本站会员。
注意:文章中的链接、内容等需要修改的错误,请用报告错误,以利文档及时修改。
注意:请不要在评论中含与内容无关的广告链接,违者封ID
文章搜索
推荐文章
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
循序渐进学习iTextShar
热门点击
一个简单的PDF文件结构的分析(22570)
PHP的FDF文档支持(16209)
用C#制作PDF文件全攻略(13653)
Word转换PDF格式的C#或(12165)
pdfMaker文章:PDF加(11658)
ITextRenderer学习(11369)
PDF文件加密仿真(11075)
怎样用iReport制作Web(10436)
iText PDF概述(1)(9957)
循序渐进学习iTextShar(9894)
热门评论
从pdf文档中提取出文本(8)
PDFlib开发:创建超文本元(2)
客户端自动打印PDF(Prin(2)
浅谈PDFlib中文输出(一)(2)
xml的转换之pdf 2 ((1)
xml的转换之pdf 1 (x(1)
PDF知识讲座(1)(1)
PDFBox使用简介(1)
使用正则表达式计算PDF文档的(1)
word转pdf(1)