pdf2htmlEX工具的使用
记录一下我在centos6.9上编译安装pdf2htmlEX的过程
- 最开始想通过配置高版本yum源下载高版本依赖库,结果高版本不能下载,使用各种其他源,发现版本是一样的
- gcc需要4.6.3及以上,首先要编译安装gcc,过程没有报错,就是时间大概2小时
- 刚开始编译pdf2htmlEX时cmake报fontforge、poppler、cairo版本低,yum仓库没有高版本的,但是自己想取巧,想把Fedora编译好的pdf2htmlEX和fontforge的so拷贝过来,但是编译时都是依赖系统静态库的,导致执行时glibc库版本低,很多库版本过低问题
- 于是下载编译glibc,版本从2.12升级到2.18,但是我没有覆盖之前的版本,导致库连接符号需要重新设置,设置时也遇到了很多问题,最明显的就是/lib/libc-2.12.so的错误,直接导致ls ln等命令使用报错,最后放弃
- 最后只能下载fontforge、poppler、cairo源码编译。fontforge使用
fontforge-pdf2htmlEX
(从github下载fontforge是总是中断,才选择作者维护的版本),fontforge-pdf2htmlEX编译时修改了CairoFontEngine.cc第425行的nullptr为NULL,fontforge-pdf2htmlEX/fontforge/ufo.cc第925行第一个参数缺失问题(在splinefont.h第2880行定义),而且编译时也有依赖报错,使用yum安装后就好了 - 依赖都安装好了,就在最后安装pdf2htmlEX时,总是报我的gcc不支持c++11新特性,找了好久没有找到原因,一直以为我编译安装gcc时的参数没有配置好导致的,于是想重新编译安装gcc,但是make时报错了,唉!
经过编译测试cpp文件,gcc是支持c++11的,于是就把判断是否支持c++11的地方注释了,可是make时有报nullptr未定义,可是这个是c++11新特性呀,一定是cmake配置时使用了老版本的gcc,于是yum卸载老版本gcc,果然cmake时就报cc、c++这两个编译器没有发现,查资料后发现cc是gcc的符号连接,c++是g++的符号连接,于是重新建立连接,指向我新安装的gcc后ok了 - 最后配置下
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
解决找不到fontforge的错误,编译顺利完成!
# 最后各版本选择
poppler-0.25.3
cairo-0.10.0
fontforge-pdf2htmlEX
pdf2htmlEX-0.14.6
# 解决找不到so库问题
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/lib64/pkgconfig:/usr/share/pkgconfig
export PKG_CONFIG_PATH=/usr/lib/pkgconfig
export LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/lib:/usr/local/lib
# remi仓库配置 https://blog.remirepo.net/pages/Config-en
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
wget https://rpms.remirepo.net/enterprise/remi-release-6.rpm
rpm -Uvh remi-release-6.rpm epel-release-latest-6.noarch.rpm
./configure --without-libzmq --without-x --without-iconv --disable-python-scripting \
--disable-python-extension
# 查看glib安装目录
[root@EB-TEST]# find /usr/ -name 'glib'
/usr/local/kevin/20171108/pkg-config-0.29/glib
/usr/local/kevin/20171108/pkg-config-0.29/glib/glib
/usr/local/kevin/software/worley/poppler-0.25.3/glib
/usr/local/kevin/db/poppler-0.20.5/glib
/usr/share/gtk-doc/html/glib
/usr/include/glib-2.0/glib
/usr/include/poppler/glib
/usr/lib64/python2.6/site-packages/gtk-2.0/glib
# 安装zlib-1.2.11
[root@EB-TEST]# make install
rm -f /usr/local/lib/libz.a
cp libz.a /usr/local/lib
chmod 644 /usr/local/lib/libz.a
cp libz.so.1.2.11 /usr/local/lib
chmod 755 /usr/local/lib/libz.so.1.2.11
rm -f /usr/local/share/man/man3/zlib.3
cp zlib.3 /usr/local/share/man/man3
chmod 644 /usr/local/share/man/man3/zlib.3
rm -f /usr/local/lib/pkgconfig/zlib.pc
cp zlib.pc /usr/local/lib/pkgconfig
chmod 644 /usr/local/lib/pkgconfig/zlib.pc
rm -f /usr/local/include/zlib.h /usr/local/include/zconf.h
cp zlib.h zconf.h /usr/local/include
chmod 644 /usr/local/include/zlib.h /usr/local/include/zconf.h
# fontforge的安装 https://github.com/fontforge/fontforge/releases?after=20150824
./bootstrap
./configure
# poppler的安装 https://poppler.freedesktop.org/releases.html
./configure --prefix=/usr --enable-xpdf-headers
# cairo的安装
# http://cairographics.org https://www.cairographics.org/releases/
# http://www.linuxfromscratch.org/blfs/view/svn/x/cairo.html
./configure --prefix=/usr --disable-static
./configure --prefix=/usr --disable-static --enable-tee
# pdf2htmlEX的安装 https://github.com/coolwanglu/pdf2htmlEX/wiki/Building
sudo yum install cmake gcc gnu-getopt java-1.8.0-openjdk libpng-devel \
fontforge-devel cairo-devel poppler-devel libspiro-devel freetype-devel \
poppler-data libjpeg-turbo-devel make
cd pdf2htmlEX
cmake . && make && sudo make install
# cmake的安装
wget https://cmake.org/files/v3.3/cmake-3.3.2.tar.gz
./configure --prefix=/usr/local/
gmake
gmake install
# 安装过程中遇到的错误
# libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, `config'.
# libtoolize: `config/ltmain.sh' is already up to date.
# libtoolize: putting libltdl files in LT_CONFIG_LTDL_DIR, `libltdl'.
# libtoolize: `COPYING.LIB' not found in `/usr/share/libtool/libltdl'
# 安装这个软件解决上边的错误
yum install libtool-ltdl-devel
# checking for GLIB... configure: error:
# Package requirements (glib-2.0 >= 2.6 gio-2.0) were not met:
# No package 'glib-2.0' 'gio-2.0' found
# 安装这个软件解决上边的错误
yum install glib2-devel
# 重新安装pango-devel解决找不到pango错误
yum install pango-devel
# 使用命令
pdf2htmlEX [options] <input.pdf> [<output.html>]
# 可用的配置项
-f,--first-page <int> 需要转换的起始页 (默认: 1)
-l,--last-page <int> 需要转换的最后一页 (默认: 2147483647)
--zoom <fp> 缩放比例
--fit-width <fp> 适合宽度 <fp> 像素
--fit-height <fp> 适合高度 <fp> 像素
--use-cropbox <int> 使用剪切框 (default: 1)
--hdpi <fp> 图像水平分辨率 (default: 144)
--vdpi <fp> 图像垂直分辨率 (default: 144)
--embed <string> 指定哪些元素应该被嵌入到输出
--embed-css <int> 将CSS文件嵌入到输出中 (default: 1)
--embed-font <int> 将字体文件嵌入到输出中 (default: 1)
--embed-image <int> 将图片文件嵌入到输出中 (default: 1)
--embed-javascript <int> 将javascript文件嵌入到输出中 (default: 1)
--embed-outline <int> 将链接嵌入到输出中 (default: 1)
--split-pages <int> 将页面分割为单独的文件 (default: 0)
--dest-dir <string> 指定目标目录 (default: ".")
--css-filename <string> 生成的css文件的文件名 (default: "")
--page-filename <string> 分割的网页名称 (default:"")
--outline-filename <string> 生成的链接文件名称 (default:"")
--process-nontext <int> 渲染图行,文字除外 (default: 1)
--process-outline <int> 在html中显示链接 (default: 1)
--printing <int> 支持打印 (default: 1)
--fallback <int> 在备用模式下输出 (default: 0)
--embed-external-font <int> 嵌入局部匹配的外部字体 (default: 1)
--font-format <string> 嵌入的字体文件后缀 (ttf,otf,woff,svg) (default: "woff")
--decompose-ligature <int> 分解连字-> fi (default:0)
--auto-hint <int> 使用fontforge的autohint上的字体时不提示 (default: 0)
--external-hint-tool <string> 字体外部提示工具 (overrides --auto-hint) (default: "")
--stretch-narrow-glyph <int> 伸展狭窄的字形,而不是填充 (default: 0)
--squeeze-wide-glyph <int> 收缩较宽的字形,而不是截断 (default: 1)
--override-fstype <int> clear the fstype bits in TTF/OTF fonts (default:0)
--process-type3 <int> convert Type 3 fonts for web (experimental) (default: 0)
--heps <fp> 合并文本的水平临界值,单位:像素(default: 1)
--veps <fp> vertical threshold for merging text, in pixels (default: 1)
--space-threshold <fp> 断字临界值 (临界值 * em) (default:0.125)
--font-size-multiplier <fp> 一个大于1的值增加渲染精度 (default: 4)
--space-as-offset <int> 把空格字符作为偏移量 (default: 0)
--tounicode <int> 如何处理ToUnicode的CMap (0=auto, 1=force,-1=ignore) (default: 0)
--optimize-text <int> 尽量减少用于文本的HTML元素的数目 (default: 0)
--bg-format <string> 指定背景图像格式 (default: "png")
-o,--owner-password <string> 所有者密码 (为了加密文件)
-u,--user-password <string> 用户密码 (为了加密文件)
--no-drm <int> 覆盖文档的 DRM 设置 (default: 0)
--clean-tmp <int> 转换后删除临时文件 (default: 1)
--data-dir <string> 指定的数据目录 (default: ".\share\pdf2htmlEX")
--debug <int> 打印调试信息 (default: 0)
-v,--version 打印版权和版本信息
-h,--help 打印使用帮助信息
--process-annotation 处理pdf注释 (default: 0)
--correct-text-visibility 隐藏不显示的内容 (default: 0)
# 转换testpdf.pdf的1~5页,且每页一个html文件
pdf2htmlEX -f 1 -l 5 --split-pages 5 --page-filename testpdf-%d.html \
--embed-css 0 --css-filename testpdf.css --embed-external-font 0 --auto-hint 1 \
--hdpi 90 --vdpi 90 --bg-format jpg --fit-width 848 --process-annotation 1 \
--correct-text-visibility 1 --dest-dir testpdf testpdf.pdf testpdf.html
# 后续使用中发现有些字体显示不全
# 拷贝字体到 /usr/share/fonts/ 下,在目录下执行命令
mkfontscale
mkfontdir # 生成字体的索引信息
fc-cache # 更新字体缓存
# yum安装字体包
yum install m17n-db-common-cjk
yum install m17n-db-chinese
在应用中调用pdf2htmlEX命令的java工具类
/**
* pdf转html工具类,使用pdf2htmlEX工具转换
*
* @author worley_qu
* @date 2017-12-08
*/
public class Pdf2HtmlUtils {
private static final Logger log = LoggerFactory.getLogger(Pdf2HtmlUtils.class);
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String TEMP_HTML = "_temp";
private static final String[] tempFileNames = { TEMP_HTML, "base.min.css", "fancy.min.css" };
public static final String HTML_EXT = ".html";
public static final String CSS_EXT = ".css";
public static final String PDF2HTMLEX_NAME = "pdf2htmlEX"; // pdf转换html命令名称
private static boolean isWindows = false;
static {
String os = System.getProperty("os.name");
if (os.toLowerCase().startsWith("win")) {
isWindows = true;
}
}
private Pdf2HtmlUtils() {
}
/**
* 把pdf文件按照页数转换为多个html文件
*
* @author worley_qu
* @date 2017-12-08
*
* @param binPath
* 转换工具pdf2htmlEX的路径
* @param extConf
* pdf转换命令扩展配置,如:--fallback 1
* @param savePath
* 转换后的html保存路径前缀
* @param pdfPath
* 需要转换的pdf文件路径
* @param htmlName
* 保存html的文件名称及目录名称(不要有中文和空格)
* @param startPage
* 从第几页开始转换
* @param splitPages
* 转换几页
* @return
*/
public static String pdfToHtml(String binPath, String extConf, String savePath, String pdfPath, String htmlName,
int startPage, int splitPages) {
if (startPage < 1 || splitPages < 1) {
return "参数错误";
}
int endPage = startPage + splitPages - 1;
StringBuilder cmd = new StringBuilder(binPath);
if (StringUtils.isNotBlank(extConf)) {
/*
* --optimize-text 1 尽量减少html元素,这个参数谨慎使用,会导致在不加载字体文件时排版错乱
* --correct-text-visibility 1 隐藏不显示的内容
* 这个参数在大部分pdf里会将图片区域的文字在背景图里显示,导致不加载字体时显示错位 如果使用了 --fallback 1
* 参数,内容全部以图片呈现,字体透明,可以解决此问题,且字体文件也不用加载了
*/
cmd.append(" ").append(extConf);
}
cmd.append(" --no-drm 1 --fit-width 848");// 适合portal展示的宽度
cmd.append(" --embed-external-font 0");// 不嵌入局部匹配的外部字体
cmd.append(" --process-annotation 1");// 处理pdf注释
cmd.append(" --bg-format jpg --hdpi 150 --vdpi 150");// 背景图片格式jpg、图片分辨率150dpi
cmd.append(" --clean-tmp 1");
cmd.append(" --first-page ").append(startPage);
cmd.append(" --last-page ").append(endPage);
cmd.append(" --split-pages ").append(splitPages);
cmd.append(" --embed-css 0");// css提取出来
cmd.append(" --css-filename ").append(htmlName).append(".css");
cmd.append(" --page-filename ").append(htmlName).append("-%d.html");
cmd.append(" --dest-dir ./").append(htmlName);// 指定html存放路径
cmd.append(" \"").append(pdfPath).append("\" ").append(TEMP_HTML);
String err = null;
try {
String[] cmdarray = null;
String[] envp = null;
if (isWindows) {
cmdarray = new String[] { "cmd", "/C", cmd.toString() };
} else {
cmdarray = new String[] { "/bin/sh", "-c", cmd.toString() };
// 如果linux有配置这个路径到ld.so.conf文件,则不需要
envp = new String[] { "LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/usr/local/lib64:$LD_LIBRARY_PATH" };
}
File fileSavePath = new File(savePath);
if (!fileSavePath.exists()) {
fileSavePath.mkdirs();
}
Process proc = Runtime.getRuntime().exec(cmdarray, envp, fileSavePath);// 指定当前执行目录
StreamGobbler stderr = new StreamGobbler(proc.getErrorStream()); // kick off stderr
stderr.start();
StreamGobbler stdout = new StreamGobbler(proc.getInputStream()); // kick off stdout
stdout.start();
int code = proc.waitFor();
log.info("{}转换html结束,code={}", htmlName, code);
deleteTempFile(savePath + htmlName);// 删除临时文件
return code == 0 ? null : stderr.getPrintStr();
} catch (IOException e) {
log.error("pdf2html IO异常:", e);
err = e.getMessage();
} catch (Exception e) {
log.error("pdf2htmlEX进程异常:", e);
err = e.getMessage();
}
return err;
}
private static void deleteTempFile(String htmlSavePath) {
for (String file : tempFileNames) {
try {
File f = new File(htmlSavePath, file);
Files.deleteIfExists(f.toPath());
} catch (IOException e) {
log.error("删除临时文件失败:", e);
}
}
log.info("成功删除{}下的临时文件", htmlSavePath);
}
}
/*
* 读取Runtime.getRuntime().exec()产生的错误流及输出流
*/
class StreamGobbler extends Thread {
private static final Logger log = LoggerFactory.getLogger(StreamGobbler.class);
InputStream is;
StringBuilder sbd = new StringBuilder();
StreamGobbler(InputStream is) {
this.is = is;
}
public String getPrintStr() {
return this.sbd.toString();
}
@Override
public void run() {
try (InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr);) {
String line = null;
while ((line = br.readLine()) != null) {// 这里要读取完,不然缓冲池填满会导致进程锁住
if (sbd.length() < 3000) { // 控制不能读太大,可能是无用信息
sbd.append(line);
}
}
} catch (Exception e) {
log.error("StreamGobbler读取流异常:", e);
}
}
}