如何使用自定义字体使用openHtmlToPdf?



我正在springboot中开发一个微服务,通过使用freeMarker和openHtmlToPdf库来生成pdf。我想介绍自定义字体(泰米尔语)。但是只得到####作为输出。我不知道我哪里错了。

将html转换为pdf的方法

private fun convertToPdf(htmlContent: String): ByteArrayResource {
val jsoupDocument = Jsoup.parse(htmlContent)
jsoupDocument.outputSettings().syntax(Document.OutputSettings.Syntax.html)
val xmlDocument = W3CDom().fromJsoup(jsoupDocument)
val FONT_FILE = File("resources/fonts/NotoSansTamil.ttf")
val byteArrayOutputStream = ByteArrayOutputStream()
val baseUrl = javaClass
.protectionDomain
.codeSource
.location
.toString()
PdfRendererBuilder()
.withW3cDocument(xmlDocument, baseUrl)
.useFont(FONT_FILE, "Nota Sans")
.toStream(byteArrayOutputStream)
.run()
return ByteArrayResource(byteArrayOutputStream.toByteArray())
} 
<<p>Freemarker模板/strong>

<html>
<head>
<#--    <meta charset="UTF-16">-->
<title>FreeMarker</title>
<style>
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: url(./fonts/NotoSansTamil.ttf);
}
</style>
</head>
<body>
<h1> Welcome to FreeMarker ${name} </h1>
</body>
</html>

编码有多种不同的问题。

font-familymismatch

您将字体定义为

.useFont(FONT_FILE, "Nota Sans") //font-family will be 'Nota Sans'

但是在html中使用

<style>
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: url(./fonts/NotoSansTamil.ttf);
}
</style>

说明一下这个字体叫做Noto而不是Nota.

解决方案:在html片段中始终使用Kotlin代码中定义的相同字体族名称。

File路径错误

你定义的字体完全错了。我假设你的项目布局是像Maven/Gradle建议。在这种情况下,resources文件夹应该是[project_root]/src/main/resources

val FONT_FILE = File("resources/fonts/NotoSansTamil.ttf")

如果我是对的,那么字体应该是[project_root]/src/main/resources/fonts/NotoSansTamil.ttf但是你的FONT_FILE会指向[project_root]/resources/fonts/NotoSansTamil.ttf

凌乱的基础URL

构建并传递baseUrlPdfRenderedBuilder来解析资源。

val baseUrl = javaClass
.protectionDomain
.codeSource
.location
.toString()

如果你的布局是Maven/Gradle,那么它指向[project_root]/target/classes/。它永远不会在其他机器上工作,当你把你的代码打包成一个工件时,它也不会工作。

将字体加载为文件而不是资源

val FONT_FILE = File("resources/fonts/NotoSansTamil.ttf")

这个代码片段指定了文件系统上的一个文件,但是您需要一个通常随工件一起提供的资源。

<<p>解决方案/em>
val fontUrl = object {}.javaClass
.getResource("fonts/NotoSansTamil.ttf")?.toURI()!!
val fontFile = File(fontUrl)

混合字体分辨率概念

你添加了一个字体渲染.useFont(...)和html/css侧src: url(...);这是一个坏主意,不会正常工作。

解决方案:只选择一条路。使用builder.useFont@font-face规则之一,而不是两者都使用。我建议使用builder.useFont。在这种情况下,不再需要@font-face定义和混乱的baseUrl

把所有的放在一起

修复所有前面提到的问题,下面的代码将工作。

注意:我以前从来没有写过Kotlin代码,可能它可以做得更好,优化等。

<!DOCTYPE html PUBLIC "-//OPENHTMLTOPDF//DOC XHTML Character Entities Only 1.0//EN" "">
<html lang="en">
<head>
<meta charset="UTF-16"/>
<title>FreeMarker</title>
<style>
/*
changed from @font-face
no src: passed, it will be resolved at renderer side
*/
body {
font-family: noto-sans, sans-serif;
font-style: normal;
font-weight: 400;
}
</style>
</head>
<body>
<h1> Welcome to FreeMarker!</h1>
</body>
</html>
private const val html = """ ... """ // HTML content as Stirng
fun main() {
val outFile = createTempFile("sample_", ".pdf")
// using font as resource
val fontUrl = object {}.javaClass
.getResource("fonts/NotoSansTamil.ttf")?.toURI()!!
PdfRendererBuilder()
.withHtmlContent(html, null) // baseUrl no longer passed
.useFont(File(fontUrl), "noto-sans") //matching font-family
.toStream(outFile.outputStream())
.run()
println("PDF file created at: $outFile")
getDesktop().open(outFile.toFile())
}

最新更新