python-pdfkit(wkhtmltopdf)TOC溢出

Rad*_*dio 27 python xslt xsl-fo wkhtmltopdf python-pdfkit

我目前正在创建一个非常好的PDF.它在技术上没有任何问题.但是,TOC很难看.

TOC是通过xsl生成的,xsl通过jinja2传递给页面顶部的简单细节.我修改了XSL以精确匹配客户的品牌和设计.然而,该名单的高度不断增加.

这是当前的结果(很抱歉模糊文本)你可以看到toc在新页面上的正确位置拾取,但似乎没有办法在新页面上应用上边距: 在此输入图像描述

代码: 这是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>
Run Code Online (Sandbox Code Playgroud)

我使用传统的HTML,JavaScript和文档就绪标记处理了PDF其他区域的内容溢出.但是,TOC需要一个XSL文件.

我尝试用nth-child css nth-child被忽略了.

问题:

*在wkhtmltopdf或python pdf-kit中是否有办法专门处理TOC中的分页符,并在新页面上放置更好的页边距?有没有办法提供TOC作为传统的html页面,以便我可以用javaScript来做到这一点?*

Lau*_*RTE 3

代码审查

\n\n

我在您的 XSL(和 CSS)文件中进行了快速代码审查。\n​​即使它不能\xe2\x80\x99 解决您的问题,它也有助于重现和理解它。\n以下是我的评论:

\n\n
    \n
  • 您的 XSL 有一个拼写错误:<! begin LI>不是有效的 XML 选项卡。是评论吗?

  • \n
  • 我更喜欢使用concat()XPath 函数直接附加字符。因为,如果重新缩进代码,可能会引入额外的空格。

    \n\n

    所以,我更换了:

    \n\n
    <xsl:attribute name="href"><xsl:value-of select="@link"/> . </xsl:attribute>\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    经过:

    \n\n
    <xsl:attribute name="href">\n  <xsl:value-of select="concat(@link, \' . \')"/>\n</xsl:attribute>\n
    Run Code Online (Sandbox Code Playgroud)
  • \n
  • 我添加了一个以防止在不需要时xs:if生成空:<ul>

    \n\n
    <xsl:if test="count(outline:item)">\n  <ul>\n    <xsl:comment>added to prevent self-closing tags in QtXmlPatterns</xsl:comment>\n    <xsl:apply-templates select="outline:item"/>\n  </ul>\n</xsl:if>\n
    Run Code Online (Sandbox Code Playgroud)
  • \n
  • 我还修复了重复或格式错误的 CSS 条目,我替换了:

    \n\n
    li {\n  border-bottom: 1px dashed rgb(45, 117, 183);\n}\n\nspan {\n  float: right;\n}\n\nli {\n  list-style: none;\n  margin-top: 30px;\n}\n\nul ul {font-size: 80%; padding-top:0px;}\nul {padding-left: 0em; padding-top:0px;}\nul ul {padding-left: 1em; padding-top:0px;}\na {text-decoration:none; color: color:#2d75b7;}\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    经过:

    \n\n
    span {\n  float: right;\n}\n\nli {\n  list-style: none;\n  margin-top: 30px;\n  border-bottom: 1px dashed rgb(45, 117, 183);\n}\n\nul {\n    font-size: 70px;\n    font-family: arial;\n    color: #2d75b7;\n}\n\nul ul {\n    font-size: 80%;\n    padding-left: 1em;\n    padding-top: 0px;\n}\n\na {\n    text-decoration: none;\n    color: #2d75b7;\n}\n\n
    Run Code Online (Sandbox Code Playgroud)
  • \n
  • 如果您以 XHTML 为目标,则该<style>标记具有强制type属性。该<script>属性的注释相同。

    \n\n
    <style type="text/css">...</style>\n<script type="text/javascript">...</script>\n
    Run Code Online (Sandbox Code Playgroud)
  • \n
\n\n

重现问题

\n\n

由于缺乏信息,重现您的错误有点困难。所以我猜是的。

\n\n

首先,我创建一个示例 TOC 文件,如下所示:

\n\n

大纲.xml

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

该文件包含 70 个项目,以便我可以看到分页符。

\n\n

为了构建 HTML 和 PDF,我使用了您的(固定的)XSL 文件并运行 pdfkit:

\n\n
import io\nimport os\n\nimport pdfkit\nfrom lxml import etree\n\nHERE = os.path.dirname(__file__)\n\n\ndef layout(src_path, dst_path):\n    # load the XSL\n    xsl_path = os.path.join(HERE, "layout.xsl")\n    xsl_tree = etree.parse(xsl_path)\n\n    # load the XML source\n    src_tree = etree.parse(src_path)\n\n    # transform\n    transformer = etree.XSLT(xsl_tree)\n    dst_tree = transformer.apply(src_tree)\n\n    # write the result\n    with io.open(dst_path, mode="wb") as f:\n        f.write(etree.tostring(dst_tree, encoding="utf-8", method="html"))\n\n\nif __name__ == \'__main__\':\n    layout(os.path.join(HERE, "outline.xml"), os.path.join(HERE, "outline.html"))\n    pdfkit.from_file(os.path.join(HERE, "outline.html"),\n                     os.path.join(HERE, "outline.pdf"),\n                     options={\'page-size\': \'A1\', \'orientation\': \'landscape\'})\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意:您的页面大小看起来非常巨大\xe2\x80\xa6

\n\n

解决方案

\n\n

你是对的,wkhtmltopdf没有考虑CSS中的边距:

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

这是正常行为,例如考虑标题段落(h1h2等)。\n标题可以有上边距,以便在段落和后面的标题之间添加空白,\n但是,如果标题开始一个新的页面我们想要去掉边距,并使标题接触页面的上边距。

\n\n

对于您的 TOC,有一个解决方案。您可以使用padding(而不是margin):

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

实际上,TOC内容(#toc元素)是固定的:

\n\n
li {\n  border-bottom: 1px dashed rgb(45, 117, 183);\n  list-style: none;\n  padding-top: 30px;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,您可以减少margin-top以满足您的需要,例如:

\n\n
#toc {\n  width: 50%;\n  margin-top: 150px;\n  margin-left: 300px;\n}\n
Run Code Online (Sandbox Code Playgroud)\n