英文:
wkhtmltopdf not displaying footer on PDF
问题
我正在使用wkhtmltopdf库生成PDF,但是当它在服务器上托管时,无法在PDF文档中获取页脚。在我的本地系统上,它可以正常工作。我使用以下代码设置页脚:
func HTMLtoPDF(html string, someID int64, templatePath string) (*wkhtml.PDFGenerator, error) {
pdfg, err := wkhtml.NewPDFGenerator()
if err != nil {
return nil, err
}
page := wkhtml.NewPageReader(strings.NewReader(util.FilterSmartQuotesEtc(html)))
tempPath := path.Clean(fmt.Sprintf("%v/%s", templatePath, "footer.html"))
footerFilePath, err := getFooterFilePath(tempPath, someID)
if err != nil {
return nil, err
}
page.FooterHTML.Set(footerFilePath)
pdfg.AddPage(page)
pdfg.MarginTop.Set(10)
pdfg.MarginBottom.Set(10)
// Create PDF document in internal buffer
err = pdfg.Create()
if err != nil {
return nil, err
}
return pdfg, nil
}
func getFooterFilePath(templatePath string, someID int64) (string, error) {
tmpl, err := htmlTemplate.ParseFiles(templatePath)
if err != nil {
return "", err
}
var buf bytes.Buffer
data := FooterData {
SomeID: someID,
}
err = tmpl.Execute(&buf, data)
if err != nil {
return "", err
}
// Save the rendered HTML to a temporary file
tmpFile, err := ioutil.TempFile("", "footer-*.html")
if err != nil {
return "", err
}
if _, err := tmpFile.Write(buf.Bytes()); err != nil {
return "", err
}
if err := tmpFile.Close(); err != nil {
return "", err
}
return tmpFile.Name(), nil
}
我已经验证了在getFooterFilePath
函数中创建了临时页脚文件,并且我的someID
变量也被替换了。我通过记录生成的临时页脚文件并在日志中查看内容来进行了检查。
我是否做错了什么?非常感谢任何帮助。
英文:
I am using wkhtmltopdf library to generate PDF and I cannot get the footer on the PDF doc when it is hosted on the server. It works properly on my local system. I am using below code to set the footer.
func HTMLtoPDF(html string, someID int64, templatePath string) (*wkhtml.PDFGenerator, error) {
pdfg, err := wkhtml.NewPDFGenerator()
if err != nil {
return nil, err
}
page := wkhtml.NewPageReader(strings.NewReader(util.FilterSmartQuotesEtc(html)))
tempPath := path.Clean(fmt.Sprintf("%v/%s", templatePath, "footer.html"))
footerFilePath, err := getFooterFilePath(tempPath, someID)
if err != nil {
return nil, err
}
page.FooterHTML.Set(footerFilePath)
pdfg.AddPage(page)
pdfg.MarginTop.Set(10)
pdfg.MarginBottom.Set(10)
// Create PDF document in internal buffer
err = pdfg.Create()
if err != nil {
return nil, err
}
return pdfg, nil
}
func getFooterFilePath(templatePath string, someID int64) (string, error) {
tmpl, err := htmlTemplate.ParseFiles(templatePath)
if err != nil {
return "", err
}
var buf bytes.Buffer
data := FooterData {
SomeID: someID,
}
err = tmpl.Execute(&buf, data)
if err != nil {
return "", err
}
// Save the rendered HTML to a temporary file
tmpFile, err := ioutil.TempFile("", "footer-*.html")
if err != nil {
return "", err
}
if _, err := tmpFile.Write(buf.Bytes()); err != nil {
return "", err
}
if err := tmpFile.Close(); err != nil {
return "", err
}
return tmpFile.Name(), nil
}
I have verfied that the temp footer file is getting created inside getFooterFilePath function and my someID variable is also substituted. I checked this by logging the generated temp footer file and I can see the content in the logs.
Am I doing anything wrong? Any help is really appreciated
答案1
得分: 1
wkhtmltopdf
具有--replace <name> <value>
选项,用于在页眉和页脚中替换name
为value
(可重复)。这是创建动态页眉和页脚的推荐方法。希望这可以解决问题。参考链接:https://wkhtmltopdf.org/usage/wkhtmltopdf.txt。
这是示例代码:
package main
import (
"os"
"strconv"
"strings"
wkhtml "github.com/SebastiaanKlippert/go-wkhtmltopdf"
)
func HTMLtoPDF(html string, someID int64) (*wkhtml.PDFGenerator, error) {
pdfg, err := wkhtml.NewPDFGenerator()
if err != nil {
return nil, err
}
page := wkhtml.NewPageReader(strings.NewReader(html))
page.FooterHTML.Set("footer.html")
// 以下参数将传递给footer.html中的查询。
page.Replace.Set("hello", "world")
page.Replace.Set("my_id", strconv.Itoa(int(someID)))
pdfg.AddPage(page)
pdfg.MarginTop.Set(10)
pdfg.MarginBottom.Set(10)
err = pdfg.Create()
if err != nil {
return nil, err
}
return pdfg, nil
}
func main() {
pdfg, err := HTMLtoPDF(html, 123457789)
if err != nil {
panic(err)
}
err = os.WriteFile("temp.pdf", pdfg.Bytes(), 0o644)
if err != nil {
panic(err)
}
}
var html = `
<html>
<body style="font-size:100pt;">
Hello world!
</body>
</html>
`
这是footer.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 = [
// 这些值由wkhtmltopdf提供。
'page',
'frompage',
'topage',
'webpage',
'section',
'subsection',
'date',
'isodate',
'time',
'title',
'doctitle',
'sitepage',
'sitepages',
// 这些值由"replace"选项提供。
'hello',
'my_id',
];
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-top: 1px solid black; width: 100%">
<tr>
<td class="section"></td>
<td class="hello"></td>
<td class="my_id"></td>
<td style="text-align: right">
Page <span class="page"></span> of <span class="topage"></span>
</td>
</tr>
</table>
</body>
</html>
如果上述方法不起作用,或者您想找出使用临时文件方法出现了什么问题,我们可以先进行以下操作以缩小问题范围:
-
运行
strace -f /path/to/the/app > log.txt
来跟踪系统调用和信号。-f
选项使其跟踪子进程(wkhtmltopdf
在子进程中运行)。 -
上一步的日志应包含传递给
wkhtmltopdf
命令的选项。直接运行带有相同选项的wkhtmltopdf
命令,看看是否有任何区别。- 如果页脚正确呈现,则Go代码中可能有问题。可能是临时文件尚未准备好使用。
- 如果没有任何区别,由于相同的命令在您的本地机器上运行正常,这可能与环境有关。也许服务器上的
wkhtmltopdf
版本有问题;也许服务器没有所需的字体。
请提供log.txt
和直接运行wkhtmltopdf
的结果,以便我们决定下一步该怎么做。
英文:
wkhtmltopdf
has the --replace <name> <value>
option to replace name
with value
in header and footer (repeatable). This is the recommended way to create a dynamic header and footer. Hopefully it will workaround the issue. Reference: https://wkhtmltopdf.org/usage/wkhtmltopdf.txt.
Here is the demo:
package main
import (
"os"
"strconv"
"strings"
wkhtml "github.com/SebastiaanKlippert/go-wkhtmltopdf"
)
func HTMLtoPDF(html string, someID int64) (*wkhtml.PDFGenerator, error) {
pdfg, err := wkhtml.NewPDFGenerator()
if err != nil {
return nil, err
}
page := wkhtml.NewPageReader(strings.NewReader(html))
page.FooterHTML.Set("footer.html")
// the following parameters are passed to footer.html in the query.
page.Replace.Set("hello", "world")
page.Replace.Set("my_id", strconv.Itoa(int(someID)))
pdfg.AddPage(page)
pdfg.MarginTop.Set(10)
pdfg.MarginBottom.Set(10)
err = pdfg.Create()
if err != nil {
return nil, err
}
return pdfg, nil
}
func main() {
pdfg, err := HTMLtoPDF(html, 123457789)
if err != nil {
panic(err)
}
err = os.WriteFile("temp.pdf", pdfg.Bytes(), 0o644)
if err != nil {
panic(err)
}
}
var html = `
<html>
<body style="font-size:100pt;">
Hello world!
</body>
</html>
`
And here is the footer.html file:
<!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 = [
// these values are provided by wkhtmltopdf.
'page',
'frompage',
'topage',
'webpage',
'section',
'subsection',
'date',
'isodate',
'time',
'title',
'doctitle',
'sitepage',
'sitepages',
// these values are provided by the "replace" option.
'hello',
'my_id',
];
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-top: 1px solid black; width: 100%">
<tr>
<td class="section"></td>
<td class="hello"></td>
<td class="my_id"></td>
<td style="text-align: right">
Page <span class="page"></span> of <span class="topage"></span>
</td>
</tr>
</table>
</body>
</html>
If the above approach does not work or you're interested in finding out what's wrong with the temp file approach, we can do these things to narrow down the issue first:
-
run
strace -f /path/to/the/app > log.txt
to trace system calls and signals. The-f
option makes it trace child processes (wkhtmltopdf
is run in a child process). -
the log from the last step should contains the options passed to the
wkhtmltopdf
command. Run thewkhtmltopdf
command with the same options directly, see if it makes any difference.- if the footer is rendered correctly, then there is something wrong in the Go code. It could be the temp file is not ready for use yet.
- if it does not make any difference, since the same command works in your local machine, this could be an issue related to the environment. Maybe the version of
wkhtmltopdf
on the server is buggy; maybe the server does not have the required fonts.
Please provide log.txt
and the result of running wkhtmltopdf
directly, so that we can decide what to do next.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论