pdf2htmlEX工具的使用

记录一下我在centos6.9上编译安装pdf2htmlEX的过程

  1. 最开始想通过配置高版本yum源下载高版本依赖库,结果高版本不能下载,使用各种其他源,发现版本是一样的
  2. gcc需要4.6.3及以上,首先要编译安装gcc,过程没有报错,就是时间大概2小时
  3. 刚开始编译pdf2htmlEX时cmake报fontforge、poppler、cairo版本低,yum仓库没有高版本的,但是自己想取巧,想把Fedora编译好的pdf2htmlEX和fontforge的so拷贝过来,但是编译时都是依赖系统静态库的,导致执行时glibc库版本低,很多库版本过低问题
  4. 于是下载编译glibc,版本从2.12升级到2.18,但是我没有覆盖之前的版本,导致库连接符号需要重新设置,设置时也遇到了很多问题,最明显的就是/lib/libc-2.12.so的错误,直接导致ls ln等命令使用报错,最后放弃
  5. 最后只能下载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安装后就好了
  6. 依赖都安装好了,就在最后安装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了
  7. 最后配置下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);
        }
    }
}
posted @ 2019-10-24 16:17  爱定小闹钟  阅读(411)  评论(0编辑  收藏  举报