Qt5使用Poppler库解析中文PDF文件

参考文章:https://blog.csdn.net/chy555chy/article/details/51613545

开发环境:Windows 10、Qt 5.9.1、Qt Creator 4.3.1、MinGW 5.3.0

Poppler开源库,下载地址:https://poppler.freedesktop.org/

Qt5使用Poppler库解析中文PDF文件_第1张图片

已经编译好的Poppler库下载地址:https://sourceforge.net/projects/poppler-win32/

Qt5使用Poppler库解析中文PDF文件_第2张图片

Qt5使用Poppler库解析中文PDF文件_第3张图片

我选择下载已经编译好的Poppler库,省事儿。

为了让Poppler能解析中文,还需要编码文件:https://poppler.freedesktop.org/poppler-data-0.4.10.tar.gz

Qt5使用Poppler库解析中文PDF文件_第4张图片

注意:如果不使用编码文件,Poppler解析出来的PDF文件可能无法显示中文,在Qt Creator中调试时会报如下内容的错误:

"Error: Missing language pack for 'Adobe-GB1' mapping"
"Error: Unknown font tag 'F1'"
"Error (286): No font in show"

后文将讲解如何使用编码文件。

万事俱备,开始!

一、将需要的文件拷贝到项目下

1、新建项目,在项目的根目录新建一个“poppler”文件夹,将Poppler开源库中poppler-20.12.1/poppler-20.12.1/qt5/src/目录下的文件都丢进去。

Qt5使用Poppler库解析中文PDF文件_第5张图片

2、将已编译好的Poppler库中的libpoppler.dll、libpoppler-qt5.dll、libpoppler.dll.a、libpoppler-qt5.dll.a也复制到项目根目录的"poppler"文件夹下。

Qt5使用Poppler库解析中文PDF文件_第6张图片

Qt5使用Poppler库解析中文PDF文件_第7张图片

3、在项目根目录的"poppler"文件夹下新建"share"文件夹,在"share"文件夹下新建"poppler"文件夹。将Poppler编码文件包下的cidToUnicode、cMap、nameToUnicode、unicodeMap四个文件夹拷贝到"项目根目录\poppler\share\poppler\"文件夹下。注意,这一步非常关键!我没有吃饱了撑的在"poppler"下新建个"share"文件夹,又在"share"下新建个"poppler"文件夹。

Qt5使用Poppler库解析中文PDF文件_第8张图片

Qt5使用Poppler库解析中文PDF文件_第9张图片

这样在Qt Creator下执行程序时,程序才会检测到编码文件,才能正确显示中文。

在Linux系统下,很多文章都说过使用"sudo apt-get install poppler-data"命令安装poppler-data即可解决中文乱码问题,就是没有人指导在windows下怎么解决poppler解析中文的问题,这让我思考了很久。看poppler-data的文档,受到启发,猜到编码文件一定是要放在特定的地方才能被程序检测到。试了几次,发现上面的方法。

提前说一下:在正式发布程序时,还要把项目根目录\poppler\文件夹下的share文件夹拷贝到程序的根目录,这样发布的程序才能正确解析中文字符,而且程序所在路径还不能含有中文,否则程序找不到编码文件,一样无法正确解析中文字符。这一问题,可能需要修改并重新编译Poppler库才能解决,还是留给高手们吧,我们能用就行。

Qt5使用Poppler库解析中文PDF文件_第10张图片

4、最终,项目根目录下,"poppler"文件夹的文件结构如下:

项目根目录\poppler\
│  CMakeLists.txt
│  Doxyfile
│  libpoppler-qt5.dll
│  libpoppler-qt5.dll.a
│  libpoppler.dll
│  libpoppler.dll.a
│  Mainpage.dox
│  poppler-annotation-helper.h
│  ......
│  
└─share
    └─poppler
        ├─cidToUnicode
        │      Adobe-CNS1
        │      ......
        │      
        ├─cMap
        │  ├─Adobe-CNS1
        │  │      Adobe-CNS1-0
        │  │      ......
        │  │      
        │  ├─Adobe-GB1
        │  │      Adobe-GB1-0
        │  │      ......
        │  │      
        │  ├─Adobe-Japan1
        │  │      78-EUC-H
        │  │      ......
        │  │      
        │  ├─Adobe-Japan2
        │  │      Adobe-Japan2-0
        │  │      
        │  ├─Adobe-Korea1
        │  │      Adobe-Korea1-0
        │  │      ......
        │  │      
        │  └─Adobe-KR
        │          Adobe-KR-0
        │          ......
        │          
        ├─nameToUnicode
        │      Bulgarian
        │      Greek
        │      Thai
        │      
        └─unicodeMap
                Big5
                ......
                

二、配置pro文件,引用头文件和库文件

在项目的工程文件*.pro中添加如下内容。因为是在windows下,所以添加win32前缀。

INCLUDEPATH += $$PWD/poppler
win32: LIBS += -L$$PWD/poppler -llibpoppler
win32: LIBS += -L$$PWD/poppler -llibpoppler-qt5

三、使用Poppler类

在程序源文件中包含poppler-qt5.h头文件

#include "poppler-qt5.h"

打开并加载一个PDF文档

QString filename=QFileDialog::getOpenFileName(this,QString::fromUtf8("打开"),"","pdf file (*.pdf)");//获取pdf文件路径
if(filename.isEmpty()){
    return;//如果文件路径为空则返回
}
Poppler::Document* document = Poppler::Document::load(filename);//加载pdf文件
if (!document || document->isLocked()) {

  ui->label_message->setText(QString::fromUtf8("无法打开文件:%1").arg(filename));//如果加载不成功,输出错误信息,并返回

  delete document;
  return;
}

将PDF文档的每一页输出为QImage图像

//再次检查document是否可用                                                     
if (document == 0) {                                                           
  ui->label_message->setText(QString::fromUtf8("文件内无内容:%1").arg(filename));//不可用则输出错误信息,并返回    
  return;                                                                      
} 

//将PDF文档的每一页输出为QImage
for(int pageNumber=0;pageNumbernumPages();pageNumber++){

  //获取指定页码的页面,页码从0开始
  Poppler::Page* pdfPage = document->page(pageNumber);
  if (pdfPage == 0) {                                                               
    qDebug() << QString::fromUtf8("获取页面失败:%1").arg(filename);//如果获取页面失败,输出错误信息并返回
    return;                                                                         
  }

  //将页面内容转换为QImage
  double xres=72.0, yres=72.0; int x=-1, y=-1, width=-1, height=-1;                                 
  QImage image = pdfPage->renderToImage(xres, yres, x, y, width, height);           
  if (image.isNull()) {                                                                            
    qDebug() << QString::fromUtf8("pdf转图片失败:%1").arg(filename);//如果输出为QImage失败,输出错误信息并返回 
    return;                                                                                        
  }  
  
  //使用QImage,可以让QImage显示在QLabel中,或者进一步处理,作其他用途……

  //使用完后记得释放掉Poppler::Page* pdfPage
  delete pdfPage; 
}

最后,别忘了释放掉document

delete document;

在将PDF页面输出为QImage的过程中,有个关键的函数:QImage Poppler::Page::renderToImage ( double xres = 72.0, double yres = 72.0, int x = -1, int y = -1, int w = -1, int h = -1, Rotation rotate = Rotate0 )。在应用过程中,我换了几个参数,输出效果都不尽人意。最终还是使用了默认的参数,输出效果最好。这几个参数还有待日后慢慢理解吧,这里先将官方文档翻译如下,便于自己日后回忆。

QImage Poppler::Page::renderToImage(double xres = 72.0,double yres = 72.0,int x = -1,int y = -1,int w = -1,int h = -1,Rotation rotate = Rotate0 )const

该函数使用 Document renderer 将PDF页面转换为QImage并输出。

如果 x = y = w = h = -1, 该函数将会根据参数xres和yres定义的水平和垂直分辨率自动计算待生成图像的大小。否则,该函数仅转换由参数(x、y、w、h)定义的页面的一部分。转换后的QImage的大小由 (wh)定义,而与PDF页面的大小无关。(我理解x,y,w,h四个参数所定义的矩形框是一个“选取框”,如果该矩形框在PDF页面内,那么就将PDF页面在矩形框内的部分输出为QImage图像。如果x = y = w = h = -1,则将这个PDF页面输出为QImage图像。)

参数

x 定义矩形框左上角的X坐标(以像素为单位)
y 定义矩形框左上角的y坐标(以像素为单位)
w 定义矩形框的宽度(以像素为单位)
h 定义矩形框的高度(以像素为单位)
xres 定义绘图设备的水平分辨率,单位为“点/英寸”。(比如xres=72代表水平方向每英寸有72个点)
yres 定义绘图设备的垂直分辨率,单位为“点/英寸”。
rotate

定义如何旋转页面。这是一个Poppler::Page::Rotation枚举类型。

Rotate0代表不旋转。

Rotate90代表顺时针旋转90度。

Rotate180代表旋转180度。

Rotate270代表顺时针旋转270度(即逆时针旋转90度)

注意

参数(x,y,w,h)没有进行严格的测试。异常或无意义的参数可能会得到意想不到的输出结果。

返回值

返回一幅QImage图像。如果转换失败,返回一个空QImage

四、发布程序

发布程序时记得将项目根目录\poppler\下的libpoppler.dll、libpoppler-qt5.dll和share文件夹全部复制到程序根目录。有share文件夹和share文件夹内的编码文件,读取中文PDF文档时就不会出现乱码或中文不显示的问题。再次强调,注意程序路径不能含有中文字符!有空格倒没关系。使用人家编译好的东西就是有这样那样的小问题。

Qt5使用Poppler库解析中文PDF文件_第11张图片

五、其他用法

列出一些自己摸索Poppler Qt5的过程中使用过的程序片段

统计PDF文档中嵌入文件的数量

const QList< Poppler::EmbeddedFile * > embedded = document->embeddedFiles ();
qDebug() << "EmbeddedFile count:" << embedded.count();

遍历PDF文档中所使用的所有字体

const QList fonts = document->fonts();
Q_FOREACH (const Poppler::FontInfo &font, fonts) {         
        if (font.name().isNull()) {                        
            qDebug() << "font name:" << "[none]";          
        } else {                                           
            qDebug() << "font name:" << font.name();       
        }                                                  
        qDebug() << "type name:" << font.typeName() <<     
        "is embedded:" << font.isEmbedded() <<             
        "subset:" << font.isSubset() <<                    
        "file:" << font.file();                            
 }                                                     

获取PDF文档中某一页的所有文本

QListtexts = pdfPage->textList();//pdfPage是一个Poppler::Page的指针
for(int i=0;itext() << "\nBoundingBox:" << texts.at(i)->boundingBox();//输出文本内容和文本框的大小、位置
    for(int j=0;jtext().count();j++){                                              
        qDebug() << "Char[" << j << "]:" << texts.at(i)->charBoundingBox(j);//甚至可以输出每个文本中的字符和字符的大小、位置
    }                                                                           
}

 

你可能感兴趣的:(Qt,Qt5,Poppler,中文,pdf)