【问题标题】:How to do page numbering in header/footer htmls with wkhtmltopdf?如何使用 wkhtmltopdf 在页眉/页脚 html 中进行页码编号?
【发布时间】:2021-02-25 21:01:59
【问题描述】:

我正在开发一个电子发票系统,我们的一项功能是生成发票的 PDF 格式并邮寄。我们有多个发票模板,以后会创建更多,所以我们决定使用 HTML 模板,生成 HTML 文档,然后将其转换为 PDF。但是我们在使用 wkhtmltopdf 时遇到了一个问题,据我所知(我已经用 Google 搜索了几天以找到解决方案),我们不能简单地既使用 HTML 作为页眉/页脚,又在其中显示页码。

在错误报告(或类似的)(http://code.google.com/p/wkhtmltopdf/issues/detail?id=140)中,我读到使用 JavaScript 可以实现此组合。但是在此页面或其他地方找不到有关如何操作的其他信息。

当然,强制使用 JavaScript 并不是那么重要,如果使用 wkhtmltopdf 一些 CSS 魔法可以发挥作用,它会和任何其他骇人听闻的解决方案一样棒。

谢谢!

【问题讨论】:

    标签: javascript css wkhtmltopdf css-paged-media


    【解决方案1】:

    实际上它比使用代码 sn-p 简单得多。您可以在命令行中添加以下参数:--footer-center [page]/[topage]

    就像 Richard 提到的,更多变量位于 the documentation 的页脚和页眉部分。

    【讨论】:

    • 没错,但是这样你就不能使用 HTML,因为我记得在这个项目上工作过。
    • 它将背景推到底部。我的页面背景中有一条底线,我需要在这条线上显示分页。但为了以这种方式显示页面,我需要添加 margin-bottom 5 参数,它会增加我的 pdf 页面高度并将底线推到下一页。
    • 已用 'footer-spacing' => -4, 'footer-font-size' => 10, 修复它
    • 只是争论的一个小例子,我花了几分钟才弄清楚它到底是不是.\'Program Files (x86)'/wkhtmltopdf/bin/wkhtmltopdf.exe --footer-center [page]/[topage] C:\faktury\f-2.html C:\-faktury\f-wk2.pdf
    【解决方案2】:

    在其他一些参数中,页码和总页码作为查询参数传递给页脚 HTML,如官方文档中所述:

    ... [页码] 参数以 GET 方式发送到页眉/页脚 html 文档。

    来源:http://wkhtmltopdf.org/usage/wkhtmltopdf.txt

    所以解决方案是使用一点 JS 来检索这些参数并将它们呈现到 HTML 模板中。这是页脚 HTML 的完整工作示例:

    <!doctype html>
    <html>
    <head>
        <meta charset="utf-8">
        <script>
            function substitutePdfVariables() {
    
                function getParameterByName(name) {
                    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
                    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
                }
    
                function substitute(name) {
                    var value = getParameterByName(name);
                    var elements = document.getElementsByClassName(name);
    
                    for (var i = 0; elements && i < elements.length; i++) {
                        elements[i].textContent = value;
                    }
                }
    
                ['frompage', 'topage', 'page', 'webpage', 'section', 'subsection', 'subsubsection']
                    .forEach(function(param) {
                        substitute(param);
                    });
            }
        </script>
    </head>
    <body onload="substitutePdfVariables()">
        <p>Page <span class="page"></span> of <span class="topage"></span></p>
    </body>
    </html>
    

    substitutePdfVariables() 在主体 onload 中被调用。然后,我们从查询字符串中获取每个支持的变量,并将所有元素中的内容替换为匹配的类名。

    【讨论】:

    • 您使用的是哪个框架?在 .NET 中,我不知道如何从 C# 生成的代码中使用它。 (需要浏览器触发)
    • 好极了。对我来说就像一个魅力!
    【解决方案3】:

    要显示页码和总页数,您可以在页脚或页眉代码中使用此 javascript sn-p:

      var pdfInfo = {};
      var x = document.location.search.substring(1).split('&');
      for (var i in x) { var z = x[i].split('=',2); pdfInfo[z[0]] = unescape(z[1]); }
      function getPdfInfo() {
        var page = pdfInfo.page || 1;
        var pageCount = pdfInfo.topage || 1;
        document.getElementById('pdfkit_page_current').textContent = page;
        document.getElementById('pdfkit_page_count').textContent = pageCount;
      }
    

    并在页面加载时调用 getPdfInfo

    当然 pdfkit_page_current 和 pdfkit_page_count 将是显示数字的两个元素。

    摘自here

    【讨论】:

    • PDFKit 只是 wkhtmltopdf 的 Ruby 包装器
    • 糟糕。很抱歉没有真正检查您的链接。
    • 大家好,我试过这个答案,但在我的情况下,我将它添加到标题空间中,但它只显示一次。就我而言,生成的 pdf 有 6 页来自 html。请帮忙
    • @Sushant,您需要在调用 wkhtmltopdf 时使用 --header-html 参数来提供将添加到每个页面开头的 url。 wkhtmltopdf.org/usage/wkhtmltopdf.txt 上有一个稍微完整的例子。
    • 我想我们在谈论 wkhtmltopdf... 为什么这里提到了 PDFKit。如果有人不使用 Ruby,那么这些代码将毫无用处。
    【解决方案4】:

    从 wkhtmltopdf 文档 (http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf-0.9.9-doc.html) 标题下的“页脚和页眉”中有一个代码 sn-p 来实现页码:

    <html><head><script>
    function subst() {
      var vars={};
      var x=document.location.search.substring(1).split('&');
      for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
      var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
      for(var i in x) {
        var y = document.getElementsByClassName(x[i]);
        for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
      }
    }
    </script></head><body style="border:0; margin: 0;" onload="subst()">
    <table style="border-bottom: 1px solid black; width: 100%">
      <tr>
        <td class="section"></td>
        <td style="text-align:right">
          Page <span class="page"></span> of <span class="topage"></span>
        </td>
      </tr>
    </table>
    </body></html>
    

    除了页码之外,还有更多可用的变量可以替换用于页眉/页脚。

    【讨论】:

    • 就其本身而言,sn-p 什么也不做。您链接到的页面包含 Javascript 代码,该代码实际上填充了 sn-p 中的spans。
    • 嘿它对我不起作用..我做同样的事情,但它在这些值中显示未定义
    • 专业提示:如果您没有使用某些信息,例如 topagewebpagesectionsubsectionsubsubsection,那么您应该删除它。我们正在生成相当大的 PDF(7,000 多页),并且在大约 1,000 页时遇到了分段错误。经过彻底的调查,它归结为删除那些未使用的变量。从那以后,我们再也没有看到分段错误。
    • @APetrov 从字面上看,只需将它们从var x=['frompage','topage','page','webpage','section','subsection','subsubsection']; 中删除,它们将不会在其后的for 循环中处理。
    【解决方案5】:

    安全的方法,即使您使用的是 XHTML(例如,使用 thymeleaf)。与其他解决方案的唯一区别是使用了 // 标签。

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8"/>
        <script>
            /*<![CDATA[*/
            function subst() {
                var vars = {};
                var query_strings_from_url = document.location.search.substring(1).split('&');
                for (var query_string in query_strings_from_url) {
                    if (query_strings_from_url.hasOwnProperty(query_string)) {
                        var temp_var = query_strings_from_url[query_string].split('=', 2);
                        vars[temp_var[0]] = decodeURI(temp_var[1]);
                    }
                }
                var css_selector_classes = ['page', 'topage'];
                for (var css_class in css_selector_classes) {
                    if (css_selector_classes.hasOwnProperty(css_class)) {
                        var element = document.getElementsByClassName(css_selector_classes[css_class]);
                        for (var j = 0; j < element.length; ++j) {
                            element[j].textContent = vars[css_selector_classes[css_class]];
                        }
                    }
                }
            }
            /*]]>*/
        </script>
    </head>
    <body onload="subst()">
        <div class="page-counter">Page <span class="page"></span> of <span class="topage"></span></div>
    </body>
    

    最后一点:如果使用 thymeleaf,请将 &lt;script&gt; 替换为 &lt;script th:inline="javascript"&gt;

    【讨论】:

      【解决方案6】:

      我的示例展示了如何在特定页面上隐藏一些文本,在这种情况下,它显示了从第 2 页开始的文本

      <span id='pageNumber'>{#pageNum}</span>
      <span id='pageNumber2' style="float:right; font-size: 10pt; font-family: 'Myriad ProM', MyriadPro;"><strong>${siniestro.numeroReclamo}</strong></span>
      <script>
          var elem = document.getElementById('pageNumber');
          document.getElementById("pageNumber").style.display = "none";
             if (parseInt(elem.innerHTML) <= 1) {
                 elem.style.display = 'none';
                 document.getElementById("pageNumber2").style.display = "none";
             }
      </script>
      

      【讨论】:

        【解决方案7】:

        来自wkhtmltopdf Docs

        为 0.12.6 更新。

        页脚和页眉:
        页眉和页脚可以添加到 分别由 --header-* 和 --footer* 参数记录。在 页眉和页脚文本字符串提供给例如--header-left, 的 以下变量将被替换。

        • [page] 替换为当前正在打印的页数
        • [frompage] 替换为要打印的第一页的编号
        • [topage] 替换为要打印的最后一页的编号
        • [webpage] 替换为正在打印的页面的 URL
        • [section] 替换为当前节的名称
        • [小节] 替换为当前小节的名称
        • [日期] 替换为系统本地格式的当前日期
        • [isodate] 替换为 ISO 8601 扩展格式的当前日期
        • [time] 替换为系统本地格式的当前时间
        • [title] 替换为当前页面对象的标题
        • [doctitle] 替换为输出文档的标题
        • [sitepage] 替换为当前正在转换的站点中的页面编号
        • [sitepages] 替换为当前站点中正在转换的页面数

        例如,指定 --header-right "Page [page] of [topage]",将产生文本 "Page x of y",其中 x 是 当前页的编号,y 是最后一页的编号, 出现在文档的左上角。

        页眉和页脚也可以随 HTML 文档一起提供。作为一个 示例一可以指定 --header-html header.html,并使用 header.html 中的以下内容:

        <!DOCTYPE html>   
        <html>
          <head><script>
            function subst() {
              var vars = {};
              var query_strings_from_url = document.location.search.substring(1).split('&');
              for (var query_string in query_strings_from_url) {
                if (query_strings_from_url.hasOwnProperty(query_string)) {
                  var temp_var = query_strings_from_url[query_string].split('=', 2);
                  vars[temp_var[0]] = decodeURI(temp_var[1]);
                }
              }
              var css_selector_classes = ['page', 'frompage', 'topage', 'webpage', 'section', 'subsection', 'date', 'isodate', 'time', 'title', 'doctitle', 'sitepage', 'sitepages'];
              for (var css_class in css_selector_classes) {
                if (css_selector_classes.hasOwnProperty(css_class)) {
                    var element = document.getElementsByClassName(css_selector_classes[css_class]);
                    for (var j = 0; j < element.length; ++j) {
                        element[j].textContent = vars[css_selector_classes[css_class]];
                    }
                }
              }   
            }
          </script></head>
          <body style="border:0; margin: 0;" onload="subst()">   
            <table style="border-bottom: 1px solid black; width: 100%">
              <tr>
                <td class="section"></td>
                <td style="text-align:right">
                  Page <span class="page"></span> of <span class="topage"></span>
                </td>
              </tr>   
            </table>
          </body>
        </html>
        

        专业提示

        如果您没有使用某些信息,例如webpagesectionsubsectionsubsubsection,那么您应该删除它们。我们正在生成相当大的 PDF,并且在大约 1,000 页时遇到了分段错误。

        经过彻底调查,最终决定删除那些未使用的变量。不,我们可以生成 7,000 多页 PDF 而不会出现分段错误。

        【讨论】:

          【解决方案8】:

          应该使用正确的 CSS Paged Media:http://www.w3.org/TR/css3-gcpm/

          我正在研究现在需要做些什么。

          【讨论】:

          • 我还查看了 CSS 分页媒体,但页眉和页脚是每页单独呈现的,因此它们不能包含任何这些信息。难题是:如何解决必须使用 CSS 在每个页面上创建和定位页眉和页脚的问题。我找不到答案,所以 wkhtmltopdf 的页眉和页脚机制看起来更简单。
          • 如果 wkhtmltopdf 不支持,这几乎不是一个答案?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-04-21
          • 2012-10-16
          • 2015-06-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-03
          相关资源
          最近更新 更多