参考文章: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/
已经编译好的Poppler库下载地址:https://sourceforge.net/projects/poppler-win32/
我选择下载已经编译好的Poppler库,省事儿。
为了让Poppler能解析中文,还需要编码文件:https://poppler.freedesktop.org/poppler-data-0.4.10.tar.gz
注意:如果不使用编码文件,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/目录下的文件都丢进去。
2、将已编译好的Poppler库中的libpoppler.dll、libpoppler-qt5.dll、libpoppler.dll.a、libpoppler-qt5.dll.a也复制到项目根目录的"poppler"文件夹下。
3、在项目根目录的"poppler"文件夹下新建"share"文件夹,在"share"文件夹下新建"poppler"文件夹。将Poppler编码文件包下的cidToUnicode、cMap、nameToUnicode、unicodeMap四个文件夹拷贝到"项目根目录\poppler\share\poppler\"文件夹下。注意,这一步非常关键!我没有吃饱了撑的在"poppler"下新建个"share"文件夹,又在"share"下新建个"poppler"文件夹。
这样在Qt Creator下执行程序时,程序才会检测到编码文件,才能正确显示中文。
在Linux系统下,很多文章都说过使用"sudo apt-get install poppler-data"命令安装poppler-data即可解决中文乱码问题,就是没有人指导在windows下怎么解决poppler解析中文的问题,这让我思考了很久。看poppler-data的文档,受到启发,猜到编码文件一定是要放在特定的地方才能被程序检测到。试了几次,发现上面的方法。
提前说一下:在正式发布程序时,还要把项目根目录\poppler\文件夹下的share文件夹拷贝到程序的根目录,这样发布的程序才能正确解析中文字符,而且程序所在路径还不能含有中文,否则程序找不到编码文件,一样无法正确解析中文字符。这一问题,可能需要修改并重新编译Poppler库才能解决,还是留给高手们吧,我们能用就行。
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中添加如下内容。因为是在windows下,所以添加win32前缀。
INCLUDEPATH += $$PWD/poppler
win32: LIBS += -L$$PWD/poppler -llibpoppler
win32: LIBS += -L$$PWD/poppler -llibpoppler-qt5
在程序源文件中包含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的大小由 (w
,h
)定义,而与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文档时就不会出现乱码或中文不显示的问题。再次强调,注意程序路径不能含有中文字符!有空格倒没关系。使用人家编译好的东西就是有这样那样的小问题。
列出一些自己摸索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);//甚至可以输出每个文本中的字符和字符的大小、位置
}
}