python-pdfkit (wkhtmltopdf) TOCオーバーフロー 質問する

python-pdfkit (wkhtmltopdf) TOCオーバーフロー 質問する

現在、完璧な PDF を作成しています。技術的に問題はありません。ただし、目次が見苦しいです。

TOC は xsl 経由で生成され、ページの上部セクションに簡単な詳細を表示するために jinja2 に渡されます。クライアントのブランドとデザインに正確に一致するように XSL を変更しました。ただし、リストの高さは増え続けています。

現在の結果は次のとおりです(テキストがぼやけていて申し訳ありません) 新しいページの適切な場所に目次が表示されているのがわかりますが、新しいページに上余白を適用する方法はないようです。ここに画像の説明を入力してください

コード:以下は xsl です:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:outline="http://wkhtmltopdf.org/outline"
            xmlns="http://www.w3.org/1999/xhtml">
  <xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
          doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
          indent="yes" />
  <xsl:template match="outline:outline">
    <html>
      <head>
        <title>Table of Contents</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style>

      body{
        background-color: #fff;
        margin-left: 0px;
        margin-top: 0px;
        color:#1e1e1e;
        font-family: arial, verdana,sans-serif;
        font-size: 90px;
      }
      .contentSection{
        position:relative;
        height:3200px;
        width:6100px;
      }
      .profile{
        position:absolute;
        display:inline-block;
        top:200px !important;
      }


      h1 {
        text-align: left;
        font-size: 70px;
        font-family: arial;
        color: #ef882d;
      }
      li {
        border-bottom: 1px dashed rgb(45,117,183);
      }
      span {float: right;}
      li {
        list-style: none;
        margin-top:30px;
      }
      ul {
        font-size: 70px;
        font-family: arial;
        color:#2d75b7;
      }

      ul ul {font-size: 80%; padding-top:0px;}
      ul {padding-left: 0em; padding-top:0px;}
      ul ul {padding-left: 1em; padding-top:0px;}
      a {text-decoration:none; color: color:#2d75b7;}


      #topper{
        width:100%;
        border-bottom:8px solid #ef882d;
      }
      #title{
        position:absolute;
        top:60px;
        font-size:60px;
        left:150px;
        color:#666666;
      }

      h1, h2{
        font-size:60px;
        -webkit-margin-before: 0px;
        -webkit-margin-after: 0px;
        -webkit-margin-start: 0px;
        -webkit-margin-end: 0px;
      }


      #profile{
        position:static;
        -webkit-border-top-left-radius: 40px;
        -webkit-border-bottom-left-radius: 40px;
        -moz-border-radius-topleft: 40px;
        -moz-border-radius-bottomleft: 40px;
        border-top-left-radius: 40px;
        border-bottom-left-radius: 40px;
        right:-540px;
        background-color: #2d75b7;
        padding:4px;
        padding-left:60px;
        padding-right:250px;
        color:#fff;
        display:inline-block;
        margin-top:200px;
        float:right;
      }

      #room{
        padding-top: 200px;
        padding-left: 150px;
        display:inline-block;
      }
      #section{
        padding-left: 150px;
        color: #ef882d;
        text-transform: uppercase;
        font-size:60px;
        font-weight: bold;
        display:inline-block;
        margin-top: 30px;
        margin-bottom: 5px;
      }
      #area{
        padding-left: 150px;
        font-size:60px;
        color:#2d75b7;
        margin-top: 15px;
      }
      #dims{
        padding-left: 150px;
        font-size:60px;
        color:#2d75b7;
        margin-top: 15px;
      }
      #toc{
        width:50%;
        margin-top:150px;
        margin-left:300px;
      }
    </style>
    <script>
      var value = {{profile|e}};
    </script>
  </head>
  <body>
    <div class="contentSection">
      <div id="title">A title here</div>
      <div id="topper">
        <div id="profile" class="profile">{{profile|e}}</div>
        <div id="room"> {{profile|e}} </div>
        <div id="area"> Revision Date </div>
        <div id="dims"> {{area|e}} </div>
        <div id="section">Table of Contents</div>
      </div>
      <div id="toc">
        <ul><xsl:apply-templates select="outline:item/outline:item"/></ul>
      </div>
    </div>
  </body>
</html>
 </xsl:template>
  <xsl:template match="outline:item">
    <! begin LI>
    <li>
      <xsl:if test="@title!=''">
        <div>
          <a>
            <xsl:if test="@link">
              <xsl:attribute name="href"><xsl:value-of select="@link"/> . 
 </xsl:attribute>
            </xsl:if>
            <xsl:if test="@backLink">
              <xsl:attribute name="name"><xsl:value-of select="@backLink"/> .   </xsl:attribute>
            </xsl:if>
            <xsl:value-of select="@title" />
          </a>
          <span>
            <xsl:value-of select="@page" />
          </span>
        </div>
      </xsl:if>
      <ul>
        <xsl:comment>added to prevent self-closing tags in QtXmlPatterns</xsl:comment>
        <xsl:apply-templates select="outline:item"/>
      </ul>
    </li>
  </xsl:template>
</xsl:stylesheet>

私は、従来の HTML、JavaScript、およびドキュメント準備フラグを使用して、PDF の他の領域でのコンテンツのオーバーフローに対処してきました。ただし、TOC には代わりに XSL ファイルが必要です。

nth-child css でこれを実行しようとしましたが、nth-child は無視されます。

質問:

*wkhtmltopdf または python pdf-kit 内で、特に TOC 内の改ページを処理し、新しいページの上部に適切な余白を配置する方法はありますか? TOC を従来の HTML ページとして提供し、代わりに JavaScript でこれを行う方法はありますか? *

ベストアンサー1

コードレビュー

XSL (および CSS) ファイルの簡単なコードレビューを行いました。問題が解決しない場合でも、問題を再現して理解するのに役立ちます。以下は私のコメントです:

  • XSL にタイプミスがあります:<! begin LI>有効な XML タブではありません。コメントですか?

  • 私はconcat()、文字を直接追加するために XPath 関数を使用することを好みます。コードを再インデントすると、余分な空白が導入される可能性があるためです。

    そこで、次のものを置き換えました:

    <xsl:attribute name="href"><xsl:value-of select="@link"/> . </xsl:attribute>
    

    による:

    <xsl:attribute name="href">
      <xsl:value-of select="concat(@link, ' . ')"/>
    </xsl:attribute>
    
  • 必要ない場合にxs:if空の が生成されないように を追加しました:<ul>

    <xsl:if test="count(outline:item)">
      <ul>
        <xsl:comment>added to prevent self-closing tags in QtXmlPatterns</xsl:comment>
        <xsl:apply-templates select="outline:item"/>
      </ul>
    </xsl:if>
    
  • 重複または不正な形式の CSS エントリも修正し、次のものを置き換えました。

    li {
      border-bottom: 1px dashed rgb(45, 117, 183);
    }
    
    span {
      float: right;
    }
    
    li {
      list-style: none;
      margin-top: 30px;
    }
    
    ul ul {font-size: 80%; padding-top:0px;}
    ul {padding-left: 0em; padding-top:0px;}
    ul ul {padding-left: 1em; padding-top:0px;}
    a {text-decoration:none; color: color:#2d75b7;}
    

    による:

    span {
      float: right;
    }
    
    li {
      list-style: none;
      margin-top: 30px;
      border-bottom: 1px dashed rgb(45, 117, 183);
    }
    
    ul {
        font-size: 70px;
        font-family: arial;
        color: #2d75b7;
    }
    
    ul ul {
        font-size: 80%;
        padding-left: 1em;
        padding-top: 0px;
    }
    
    a {
        text-decoration: none;
        color: #2d75b7;
    }
    
    
  • XHTML をターゲットとする場合、<style>タグには必須type属性があります。属性についても同様です<script>

    <style type="text/css">...</style>
    <script type="text/javascript">...</script>
    

問題の再現

情報不足のため、バグを再現するのは少し難しかったです。 そういうことだったのだと思います。

まず、次のようなサンプル TOC ファイルを作成します。

アウトライン.xml

<?xml version="1.0" encoding="UTF-8"?>
<outline xmlns="http://wkhtmltopdf.org/outline">
  <item>
    <item title="Lorem ipsum dolor sit amet, consectetur adipiscing elit." page="2"/>
    <item title="Cras at odio ultrices, elementum leo at, facilisis nibh." page="8"/>
    <item title="Vestibulum sed libero bibendum, varius massa vitae, dictum arcu." page="19"/>
    ...
    <item title="Sed semper augue quis enim varius viverra." page="467"/>
  </item>
</outline>

このファイルには、ページ区切りを確認できる 70 個の項目が含まれています。

HTML と PDF を構築するために、(修正された) XSL ファイルを使用し、pdfkit を実行しました。

import io
import os

import pdfkit
from lxml import etree

HERE = os.path.dirname(__file__)


def layout(src_path, dst_path):
    # load the XSL
    xsl_path = os.path.join(HERE, "layout.xsl")
    xsl_tree = etree.parse(xsl_path)

    # load the XML source
    src_tree = etree.parse(src_path)

    # transform
    transformer = etree.XSLT(xsl_tree)
    dst_tree = transformer.apply(src_tree)

    # write the result
    with io.open(dst_path, mode="wb") as f:
        f.write(etree.tostring(dst_tree, encoding="utf-8", method="html"))


if __name__ == '__main__':
    layout(os.path.join(HERE, "outline.xml"), os.path.join(HERE, "outline.html"))
    pdfkit.from_file(os.path.join(HERE, "outline.html"),
                     os.path.join(HERE, "outline.pdf"),
                     options={'page-size': 'A1', 'orientation': 'landscape'})

注: ページのサイズが非常に大きいようです…

解決

おっしゃる通り、wkhtmltopdf は CSS のマージンを考慮しません。

li {
  list-style: none;
  border-bottom: 1px dashed rgb(45, 117, 183);
  margin-top: 30px;  # <-- not working after page break
}

これは通常の動作です。たとえば、ヘッダー段落 ( h1h2など) を考えてみましょう。ヘッダーには段落と次のヘッダーの間に空白を追加するために上余白を設定できますが、ヘッダーが新しいページを開始する場合は、余白を削除して、見出しをページの上余白に接するようにします。

TOC には解決策があります。paddingの代わりに ( margin) を使用できます。

li {
  border-bottom: 1px dashed rgb(45, 117, 183);
  list-style: none;
  padding-top: 30px;
}

実際には、目次コンテンツ (#toc要素) は固定されています。

#toc {
  width: 50%;
  margin-top: 150px;
  margin-left: 300px;
}

したがって、必要に応じて を減らすことができますmargin-top。たとえば、次のようになります。

#toc {
  width: 50%;
  margin-top: 120px;
  margin-left: 300px;
}

おすすめ記事