20231006_222307
一、官网
官网有大致的介绍,以及最新版本的xpdf源码的下载链接。但是没有具体讲怎么移植xpdf。
二、windows下的xpdf的编译
2.1、准备windows下的编译环境
我的编译环境
win10操作系统
vs2015社区版
qt版本:qt5.9.5
在官网下载工具链。如下图红色框中所示,点击后即可下载。这个压缩包是linux下的格式,解压需要费点功夫。
下载并解压后放在xpdf源码目录下,如下图所示。
2.2、编译freetype
在如下图的路径,双击使用vs打开freetype.sln(我的vs是vs2015社区版),选择release和win32后,点击运行进行编译,编程出dll文件。(由于dll不能直接运行,因此会报如下的错误,忽略即可)。
编译完成在 如下的路径下即可看到dll和lib文件。
工程路径:\freetype-2.12.0\builds\windows\vc2010
编译结构路径:\freetype-2.12.0\objs
在xpdf源码目录新建文件夹freetype,将刚才生成的lib和dll文件拷贝到该文件夹中,就完成了freetype的编译。
2.3、编译lcms
在如下图路径打开lcms的工程,右键点击lcms2_dll将其设为启动项目,选择release和win32后,点击运行进行编译,编程出dll文件。
工程路径:\lcms2-2.12\projects\vc2015
编译结构路径:\lcms2-2.12\bin
在xpdf源码目录新建文件夹lcms,将刚才生成的lib和dll文件拷贝到该文件夹中,就完成了lcms的编译。
2.4、编译zlib
zlib-1.2.12版本有点bug,需要先处理一下。
1、拷贝zlib-1.2.11(去网上下载一个)中的masmx76到contrib目录下;
2、修改代码
(1)修改函数ulong zexport crc32_combine(crc1, crc2, len2)
z_off_t len2改为z_off64_t len2;
(2)修改函数:ulong zexport crc32_combine_gen(len2)
z_off_t len2改为z_off64_t len2
(3)修改函数:ulong crc32_combine_op(crc1, crc2, op)
改为ulong zexport crc32_combine_op(crc1, crc2, op)
3、右键zlibvc,设置 safeseh 映像是不安全的异常处理程序关闭
完成 后开始正式的编译。
在如下图路径打开zlib的工程,选择release和win32后,点击运行进行编译,编程出dll文件。
工程路径:\zlib-1.2.12\contrib\vstudio\vc14
编译结构路径:\zlib-1.2.12\contrib\vstudio\vc14\x86\zlibdllrelease
在xpdf源码目录新建文件夹zlib,将刚才生成的lib和dll文件拷贝到该文件夹中,就完成了zlib的编译。
如果出现异常情况:
问题1.没有bld_ml32.bat
编译器报错详情:
问题解决:查看contrib目录下,确实没有masmx86,拷贝zlib-1.2.11中的masmx86和masmx64到contrib目录下,问题解决。
问题2.实参的字节长度不同于以前的调用或引用
编译器报错详情如下:
问题解决:在crc32.c中修改如下内容
1.修改函数ulong zexport crc32_combine(crc1, crc2, len2)
ulong crc1;
ulong crc2;
z_off_t len2;为z_off64_t len2;
2.修改函数:1.ulong zexport crc32_combine_gen(len2)
z_off_t len2;为z_off64_t len2
3.修改:
ulong crc32_combine_op(crc1, crc2, op)为
ulong zexport crc32_combine_op(crc1, crc2, op)
问题3:模块对于 safeseh 映像是不安全的。
使用release编译,忽略其他编译警告,编译通过。
2.5、编译libpng
libpng的编译以来zlib,因此需要将zlib的源码放在libpng源码的同级目录(前面已经放过了)
打开zlib.props文件,修改zlib的路径和版本(放在同级目录就不需要修改路径,修改版本就行)
在如下图路径打开libpng的工程,右键点击libpng将其设为启动项目,选择release和win32后,点击运行进行编译,编程出dll文件。
工程路径:\libpng-1.6.35\projects\vstudio
编译结构路径:\libpng-1.6.35\projects\vstudio\release
在xpdf源码目录新建文件夹libpng,将刚才生成的lib和dll文件拷贝到该文件夹中,就完成了libpng的编译。
2.6、编译xpdf
2.6.1 设置编译参数
(1) 在xpdf源码目录下新建bulid文件夹,
(2) 打开vs2015的命令提示符工具(看你的vs版本),并进入build路径(ctrl+c后在命令框右键即可粘贴)
输入如下命令,设置各个库的包含路径和动态库的路径
cmake -g "nmake makefiles" -dcmake_build_type=release -dfreetype_library="e:/qt5.9.5/userfile/20231005/xpdf-4.04/freetype/freetype.dll" -dfreetype_dir="e:/qt5.9.5/userfile/20231005/xpdf-4.04/freetype-2.12.0" -dpng_png_include_dir="e:/qt5.9.5/userfile/20231005/xpdf-4.04/libpng-1.6.35" -dpng_library="e:/qt5.9.5/userfile/20231005/xpdf-4.04/libpng/libpng16.dll" -dzlib_library="e:/qt5.9.5/userfile/20231005/xpdf-4.04/zlib/zlibwapi.dll" -dzlib_include_dir="e:/qt5.9.5/userfile/20231005/xpdf-4.04/zlib" ..
我的结果如下:
e:\qt5.9.5\userfile\20231005\xpdf-4.04\build>cmake -g "nmake makefiles" -dcmake_build_type=release -dfreetype_library="e:/qt5.9.5/userfile/20231005/xpdf-4.04/freetype/freetype.dll" -dfreetype_dir="e:/qt5.9.5/userfile/20231005/xpdf-4.04/freetype-2.12.0" -dpng_png_include_dir="e:/qt5.9.5/userfile/20231005/xpdf-4.04/libpng-1.6.35" -dpng_library="e:/qt5.9.5/userfile/20231005/xpdf-4.04/libpng/libpng16.dll" -dzlib_library="e:/qt5.9.5/userfile/20231005/xpdf-4.04/zlib/zlibwapi.dll" -dzlib_include_dir="e:/qt5.9.5/userfile/20231005/xpdf-4.04/zlib" ..
-- the c compiler identification is msvc 19.0.24215.1
-- the cxx compiler identification is msvc 19.0.24215.1
-- check for working c compiler: f:/vs2015/vc/bin/cl.exe
-- check for working c compiler: f:/vs2015/vc/bin/cl.exe -- works
-- detecting c compiler abi info
-- detecting c compiler abi info - done
-- detecting c compile features
-- detecting c compile features - done
-- check for working cxx compiler: f:/vs2015/vc/bin/cl.exe
-- check for working cxx compiler: f:/vs2015/vc/bin/cl.exe -- works
-- detecting cxx compiler abi info
-- detecting cxx compiler abi info - done
-- detecting cxx compile features
-- detecting cxx compile features - done
-- looking for mkstemp
-- looking for mkstemp - not found
-- looking for mkstemps
-- looking for mkstemps - not found
-- looking for popen
-- looking for popen - not found
-- performing test have_std_sort
-- performing test have_std_sort - success
-- looking for fseeko
-- looking for fseeko - not found
-- looking for fseek64
-- looking for fseek64 - not found
-- looking for _fseeki64
-- looking for _fseeki64 - found
-- found freetype (old-style includes): e:/qt5.9.5/userfile/20231005/xpdf-4.04/freetype/freetype.dll
-- found zlib: e:/qt5.9.5/userfile/20231005/xpdf-4.04/zlib/zlibwapi.dll
-- found png: e:/qt5.9.5/userfile/20231005/xpdf-4.04/libpng/libpng16.dll (found version "1.6.35")
-- qt5 found
-- looking for pthread.h
-- looking for pthread.h - not found
-- found threads: true
-- configuring done
-- generating done
-- build files have been written to: e:/qt5.9.5/userfile/20231005/xpdf-4.04/build
2.6.2 开始编译
在命令框输入:
nmake
我失败在如下这一步(2023-10-05)
三、xpdf库在windows上新建qt工程
如需源码,请上闲鱼,搜索:科技代码小卖部
3.1 新建一个新工程
在qtcreator下新建工程,如下图xpdfdemo20231006new4_04
3.2 拷贝头文件
(1)拷贝freetype库的头文件到新工程目录下
在freetype库的源码下,拷贝整个include文件夹到新工程目录。
(2)拷贝freetype库的库文件(lib和dll)到新工程目录下
在新工程目录下新建lib文件夹,拷贝编译后的freetype库文件到该路径下(编译方法见前面的2.2小节)
(3)拷贝xpdf的头文件到新工程目录下
将xpdf库源码中的相关头文件拷贝到新工程目录下,新建一个aconf.h文件,将下面的代码拷贝到里面(源码里只有aconf.h.in,需要编译才能生成aconf.h,如想自行编译,见2.6小节,在build文件夹下编译后会生成aconf.h文件)
/*
* aconf.h
*
* this file is modified by cmake.
*
* copyright 2002-2015 glyph & cog, llc
*/
#ifndef aconf_h
#define aconf_h
#include <aconf2.h>
/*
* use a4 paper size instead of letter for postscript output.
*/
#define a4_paper 1
/*
* do not allow text selection.
*/
#define no_text_select 0
/*
* include support for opi comments.
*/
#define opi_support 1
/*
* enable multithreading support.
*/
#define multithreaded 0
/*
* enable c++ exceptions.
*/
#define use_exceptions 1
/*
* use fixed point (instead of floating point) arithmetic.
*/
#define use_fixedpoint 1
/*
* enable support for cmyk output.
*/
#define splash_cmyk 1
/*
* enable support for devicen output.
*/
#define splash_devicen 1
/*
* enable support for highlighted regions.
*/
#define highlighted_regions 1
/*
* full path for the system-wide xpdfrc file.
*/
//@system_xpdfrc_define@
/*
* directory to use for the ${datadir} variable in the xpdfrc config
* file.
*/
//@xpdfrc_datadir_define@
/*
* various include files and functions.
*/
#define have_mkstemp 1
#define have_mkstemps 1
#define have_popen 1
#define have_std_sort 1
#define have_fseeko 0
#define have_fseek64 0
#define have_fseeki64 1
#define _file_offset_bits 64
#define _large_files 1
#define _largefile_source 1
/*
* this is defined if using freetype 2.
*/
#define have_freetype_h 1
/*
* this is defined if using d-type 4.
*/
#define have_dtype4_h 0
/*
* this is defined if using libpaper.
*/
#define have_paper_h 0
/*
* this is defined if using libfontconfig.
*/
#define have_fontconfig 0
/*
* defined if the splash library is avaiable.
*/
#define have_splash 0
/*
* defined if using lcms2.
*/
#define have_lcms 0
/*
* defined for evaluation mode.
*/
#define eval_mode 1
/*
* defined when building the closed source xpdfreader binary.
*/
#define building_xpdfreader 0
#endif
(4)拷贝xpdf例程到新工程中
将xpdf库源码中的xpdf-qt文件夹拷贝到新工程目录下。
3.3 配置pro文件
删除core和gui模块,添加qt库network,printsupport和axcontainer。如下图所示
添加头文件和库文件的检索路径
3.4 添加已有文件到新工程
在header上右键,选择add existing directory,勾选所有需要导入的文件,批量完成导入。
在默认的基础上,把2个rc资源文件也勾选上,如下图所示。
3.5 删除多余文件
由于例程没有用qt界面编辑器,且自带main函数,因此需要将原新建工程的以下文件删除:
main.cpp
mainwindow.h
mainwindow.cpp
mainwindow.ui
删除后的工程结构如下图所示。
3.6 修改源码
源码还有点错误,需要修改。先点击运行进行编译,会报如下的错误。
(1)修改xpdfviewer.cc
打开xpdfviewer.cc文件,添加头文件
#include <time.h>
(2)修改xpdfapp.cc
由于字符集的问题,需要将xpdfapp.cc中的void xpdfapp::readpagesfile() 函数整个换为下方的代码,进行字符转换。
char * wchar2char(const wchar_t* szunicodestring)
{
uint ncodepage = 936; //gb2312
int nlength=widechartomultibyte(ncodepage,0,szunicodestring,-1,null,0,null,null);
char* pbuffer=new char[nlength+1];
widechartomultibyte(ncodepage,0,szunicodestring,-1,pbuffer,nlength,null,null);
pbuffer[nlength]=0;
return pbuffer;
}
wchar_t * char2wchar(const char* cchar)
{
wchar_t *m_wchar;
int len = multibytetowidechar( cp_acp ,0,cchar ,strlen( cchar), null,0);
m_wchar= new wchar_t[len+1];
multibytetowidechar( cp_acp ,0,cchar,strlen( cchar),m_wchar,len);
m_wchar[len]= '\0' ;
return m_wchar;
}
void xpdfapp::readpagesfile() {
// construct the file name (first time only)
if (savedpagesfilename.isempty()) {
#ifdef _win32
wchar_t wpath[max_path];
// char path[max_path];
if (shgetfolderpath(null, csidl_appdata, null,
shgfp_type_current, wpath) != s_ok) {
return;
}
//char path[max_path];
char* path=wchar2char(wpath);
savedpagesfilename = qstring::fromlocal8bit(path);
savedpagesfilename.append("/xpdf");
createdirectory(char2wchar(savedpagesfilename.tolocal8bit().constdata()), null);
savedpagesfilename.append("/xpdf.pages");
// char path[max_path];
// if (shgetfolderpath(null, csidl_appdata, null,
// shgfp_type_current, path) != s_ok) {
// return;
// }
// savedpagesfilename = qstring::fromlocal8bit(path);
// savedpagesfilename.append("/xpdf");
// createdirectory(savedpagesfilename.tolocal8bit().constdata(), null);
// savedpagesfilename.append("/xpdf.pages");
#else
gstring *path = gethomedir();
savedpagesfilename = qstring::fromutf8(path->getcstring());
delete path;
savedpagesfilename.append("/.xpdf.pages");
#endif
}
// no change since last read, so no need to re-read
if (savedpagesfiletimestamp.isvalid() &&
qfileinfo(savedpagesfilename).lastmodified() == savedpagesfiletimestamp) {
return;
}
// mark all entries invalid
for (int i = 0; i < maxsavedpagenumbers; ++i) {
savedpagenumbers[i].filename.clear();
savedpagenumbers[i].pagenumber = 1;
}
// read the file
file *f = openfile(savedpagesfilename.toutf8().constdata(), "rb");
if (!f) {
return;
}
char buf[1024];
if (!fgets(buf, sizeof(buf), f) ||
strcmp(buf, "xpdf.pages-1\n") != 0) {
fclose(f);
return;
}
int i = 0;
while (i < maxsavedpagenumbers && fgets(buf, sizeof(buf), f)) {
int n = (int)strlen(buf);
if (n > 0 && buf[n-1] == '\n') {
buf[n-1] = '\0';
}
char *p = buf;
while (*p != ' ' && *p) {
++p;
}
if (!*p) {
continue;
}
*p++ = '\0';
savedpagenumbers[i].pagenumber = atoi(buf);
savedpagenumbers[i].filename = qstring::fromutf8(p);
++i;
}
fclose(f);
// save the timestamp
savedpagesfiletimestamp = qfileinfo(savedpagesfilename).lastmodified();
}
(3)完成后又会报如下图的错误
png.h是libpng库里的头文件,如果不需要pdf转html,转txt等功能,将把对应的.cc文件删掉。如果需要,就要编译libpng库,并导入对应的头文件。(我这里不需要这些高级的功能,就直接删掉了),需要删掉的文件如下:
htmlgen.cc
pdftopng.cc
删除后重新执行qmake命令,然后点击运行。
(4)完成后又会报如下图的错误
这是因为前面说的的高级功能,每个都有main函数并能生成各自的exe文件,从而导致main函数重复了。把他们都删除掉,需要删除的文件如下:
pdfdetach.cc
pdffonts.cc
pdfimages.cc
pdfinfo.cc
pdftohtml.cc
pdftoppm.cc
pdftops.cc
pdftotext.cc
同样,完成后重新执行qmake命令,然后点击运行。
3.6 xpdf库的使用
前面设置好,xpdf库的配置就已经完成了,已经可以开始使用了。点击运行即弹出程序。(enjoy!!!!!)
四、xpdf库的自用
如需源码,请上闲鱼,搜索:科技代码小卖部
4.1 工程构建方式
导入xpdf-qt文件时,只需要导入qtpdfcore和xpdfwidget的头文件和c文件。如下图所示
4.2 初始化pdf显示库
如下图所示,我把代码精简过了。
//*******************设置pdf文档显示的字体库路径*******************
globalparams::defaulttextencoding = "ucs-2";
globalparams = new globalparams(cfgfilearg);
qstring dir = qcoreapplication::applicationdirpath();
globalparams->setbasedir(dir.tolocal8bit().constdata());
dir += "/xpdf-t1fonts";
globalparams->setupbasefonts(dir.tolocal8bit().constdata());
//*******************初始化pdf页面类*******************
pdf = new xpdfwidget(null,qcolor(255,255,255), qcolor(169,169,169),gfalse);//参数2:pdf空白处颜色,参数3:pdf文件外颜色
pdf->enablehyperlinks(false);//是否启动超链接
pdf->setkeypassthrough(true);//是否响应按键事件
pdf->setmousepassthrough(true);//是否响应鼠标事件
connect(pdf, signal(mousewheel(qwheelevent*)),this, slot(mousewheel(qwheelevent*)));//鼠标事件
ui->stackedwidget->addwidget(pdf);//pdf页面类加载到ui界面中
ui->stackedwidget->setcurrentwidget(pdf);//设置ui界面当前显示的pdf页面类
pdf->setfocus(qt::otherfocusreason);
4.3 打开pdf文件
//按钮槽函数:打开pdf文件
void mainwindow::on_btn_openpdf_clicked()
{
//*******************打开文件前先关闭已有的文件*******************
try {
pdf->closefile();
} catch (gmemexception e) {
}
//*******************打开pdf文件*******************
qdir startdir = qdir(".");//打开文件时默认的起始路径
qstring filename = qfiledialog::getopenfilename(this, "open pdf file",
startdir.canonicalpath(),
"pdf files (*.pdf)");
//如果文件为空,直接返回
if (filename.isempty()) {
return;
}
//打开文件
int err = pdf->loadfile(filename, "");//打开文件
if (err != xpdfwidget::pdfok) {
qmessagebox::warning(null, "打开文件错误",
"打不开文件 '" + filename + "'");
}else{
pdf->zoomcentered(xpdfwidget::zoomtowidth);//调整到合适大小
}
}
4.4 缩放和滚动页面
void mainwindow::mousewheel(qwheelevent *e) {
//*******************文件打开状态才响应*******************
if (!pdf->hasopendocument()) {
return;
}
//*******************响应缩放或滚动页面*******************
qpoint delta = e->angledelta();//获取滚动的距离
//ctrl键被按下,进行缩放,否则进行滚动显示
if(e->modifiers() == qt::controlmodifier) {
if (delta.y() > 0) {
double z = pdf->getzoompercent(pdf->getmidpage());
pdf->zoomcentered(z*1.2);
} else if (delta.y() < 0) {
double z = pdf->getzoompercent(pdf->getmidpage());
pdf->zoomcentered(z*0.8);
}
}else{
//滚动显示
if (delta.y() > 0) {
pdf->getcore()->scrollupprevpage(delta.ry());
} else if (delta.y() < 0) {
pdf->getcore()->scrolldownnextpage(-delta.ry());
}
}
}
4.5 缩放到合适大小
//恢复到合适大小
void mainwindow::on_btn_openpdf_2_clicked()
{
//*******************文件打开状态才响应*******************
if (!pdf->hasopendocument()) {
return;
}
pdf->zoomcentered(xpdfwidget::zoomtowidth);
}
4.6 关闭pdf文件
void mainwindow::on_btn_closepdf_clicked()
{
//*******************打开文件前先关闭已有的文件*******************
try {
pdf->closefile();
} catch (gmemexception e) {
}
}
发表评论