ubuntu16.04中python36使用pdfkit問題解決

背景

我這裏的需求是需要將html文件轉成pdf文件,在windows測試環境中可以正常轉換,但是在部署到ubuntu上時碰到一些問題

使用的python來處理,使用的python庫是pdfkit,pdfkit的實質是通過【wkhtmltopdf】進行轉換,所以需要先安裝wkhtmltopdf,然後代碼中可以進行轉換

問題

  • ubuntu 中安裝時報錯

  • html文件中包含中文時,無法將中文轉換出來

解決方案

ubuntu安裝時報以下錯誤

  1. 安裝【wkhtmltopdf】
apt-get install wkhtmltopdf
  1. 安裝之後進行測試發現以下問題
root@iZ2ze4dhkm7u47lku6qixxZ:~# wkhtmltopdf https://www.baidu.com baid.pdf
QXcbConnection: Could not connect to display 
Aborted

參考Linux上使用 wkhtmltopdf 將網頁轉成pdf的信息,可以看到最簡單的解決方案是,安裝【xvfb】

  1. 安裝xvfb繼續測試
apt-get install xvfb

轉換成pdf之後,中文出現亂碼問題
中文亂碼

所以需要在ubuntu安裝一個雅黑字體,參考資料

  1. 安裝字體繼續測試

下載【微軟雅黑.ttf】文件上傳到服務器[/usr/share/fonts/winFonts]

root@iZ2zedu2sj65xaim6aez5hZ:/usr/share/fonts# mkdir winFonts
root@iZ2zedu2sj65xaim6aez5hZ:/usr/share/fonts# cd winFonts/
root@iZ2zedu2sj65xaim6aez5hZ:/usr/share/fonts/winFonts# chmod 644 /usr/share/fonts/winFonts/*.ttf
root@iZ2zedu2sj65xaim6aez5hZ:/usr/share/fonts/winFonts# mkfontscale

中文亂碼解決

至此,ubuntu中使用wkhtmltopdf的問題都已經解決,後面就是關於python pdfkit使用問題的解決方案

pdfkit中是直接使用wkhtmltopdf命令進行轉換的,而前面ubuntu中安裝wkhtmltopdf後不可以直接使用,是安裝了xvfb的,所以要多pdfkit庫進行重新處理

import subprocess
import sys

try:
    # Python 2.x and 3.x support for checking string types
    assert basestring
except NameError:
    basestring = str

from pdfkit import PDFKit


class UbuntuConfiguration(object):
    def __init__(self, wkhtmltopdf='', xvfb='', meta_tag_prefix='pdfkit-'):
        self.meta_tag_prefix = meta_tag_prefix

        self.wkhtmltopdf = wkhtmltopdf
        self.xvfb = xvfb

        if not self.wkhtmltopdf:
            if sys.platform == 'win32':
                self.wkhtmltopdf = subprocess.Popen(
                    ['where', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].strip()
            else:
                self.wkhtmltopdf = subprocess.Popen(
                    ['which', 'wkhtmltopdf'], stdout=subprocess.PIPE).communicate()[0].strip()
        if not xvfb:
            # 只針對ubuntu 最簡單的解決方案,安裝wkhtmltopdf 不足以解決問題時,需要安裝xvfb-run來執行
            if sys.platform != 'win32':
                self.xvfb = subprocess.Popen(
                    ['which', 'xvfb-run'], stdout=subprocess.PIPE).communicate()[0].strip()
                # 確定是否已經安裝
                try:
                    with open(self.xvfb) as f:
                        pass
                except IOError:
                    raise IOError('No xvfb executable found: "%s"\n' % self.xvfb)

        try:
            with open(self.wkhtmltopdf) as f:
                pass
        except IOError:
            raise IOError('No wkhtmltopdf executable found: "%s"\n'
                          'If this file exists please check that this process can '
                          'read it. Otherwise please install wkhtmltopdf - '
                          'https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf' % self.wkhtmltopdf)


class UbuntuPDFKit(PDFKit):
    def __init__(self, url_or_file, type_, options=None, toc=None, cover=None,
                 css=None, cover_first=False):
        configuration = UbuntuConfiguration()
        super(UbuntuPDFKit, self).__init__(url_or_file, type_, options, toc=toc, cover=cover, css=css,
                                           cover_first=cover_first, configuration=configuration)
        self.xvfb = self.configuration.xvfb

    def _command(self, path=None):
        """
        Generator of all command parts
        """
        if self.css:
            self._prepend_css(self.css)

        yield self.xvfb

        yield self.wkhtmltopdf

        for argpart in self._genargs(self.options):
            if argpart:
                yield argpart

        if self.cover and self.cover_first:
            yield 'cover'
            yield self.cover

        if self.toc:
            yield 'toc'
            for argpart in self._genargs(self.toc):
                if argpart:
                    yield argpart

        if self.cover and not self.cover_first:
            yield 'cover'
            yield self.cover

        # If the source is a string then we will pipe it into wkhtmltopdf
        # If the source is file-like then we will read from it and pipe it in
        if self.source.isString() or self.source.isFileObj():
            yield '-'
        else:
            if isinstance(self.source.source, basestring):
                yield self.source.to_s()
            else:
                for s in self.source.source:
                    yield s

        # If output_path evaluates to False append '-' to end of args
        # and wkhtmltopdf will pass generated PDF to stdout
        if path:
            yield path
        else:
            yield '-'


def xvfb_from_file(input, output_path, options=None, toc=None, cover=None, css=None, cover_first=False):
    r = UbuntuPDFKit(input, 'file', options=options, toc=toc, cover=cover, css=css, cover_first=cover_first)
    return r.to_pdf(output_path)

主要就是對commond增加上xvfb

代碼中只定義了本地文件處理,如果有需求可以繼續定義網絡文件的方式。
至此,在python36中基本上可以使用了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章