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

qyzbxkaa  于 2023-04-10  发布在  Java
关注(0)|答案(1)|浏览(348)

我正在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())
}

自由标记模板

<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>
wnrlj8wa

wnrlj8wa1#

有多种不同的问题代码。

font-family不匹配

您将字体定义为

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

但是在hmtl框架中使用

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

只是为了记录这个字体叫诺托而不是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")

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

  • 解决方案 *
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())
}

相关问题