我正在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-family
mismatch
您将字体定义为
.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
构建并传递baseUrl
给PdfRenderedBuilder
来解析资源。
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())
}