You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取指定字体下单词的实际尺寸?(Kotlin+Apache POI XSLF)

解决PPTX文本替换后的尺寸匹配问题(Kotlin + Apache POI XSLF)

一、提取嵌入字体供AWT计算精准宽度

如果PPTX中嵌入了目标字体,可通过Apache POI提取字体文件并加载到AWT中,利用FontMetrics计算文本实际宽度:

import org.apache.poi.xslf.usermodel.XMLSlideShow
import java.awt.Font
import java.awt.FontMetrics
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import java.io.ByteArrayInputStream
import java.io.FileInputStream

// 提取PPTX中嵌入的字体并返回字体名称到Font的映射
fun loadEmbeddedFonts(slideShow: XMLSlideShow): Map<String, Font> {
    val fontMap = mutableMapOf<String, Font>()
    slideShow.packagePart.package.run {
        getPartsByName(Regex("/ppt/fonts/.*\\.(ttf|otf)")).forEach { part ->
            val fontFileName = part.partName.name.split("/").last()
            val fontName = fontFileName.substringBeforeLast('.')
            val fontBytes = part.inputStream.readBytes()
            val rawFont = Font.createFont(Font.TRUETYPE_FONT, ByteArrayInputStream(fontBytes))
            fontMap[fontName] = rawFont
        }
    }
    return fontMap
}

// 计算指定字体下文本的实际宽度(单位:像素)
fun getTextWidth(text: String, font: Font): Float {
    val dummyImage = BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
    val g2d = dummyImage.graphics as Graphics2D
    val metrics = g2d.getFontMetrics(font)
    val width = metrics.stringWidth(text).toFloat()
    g2d.dispose()
    return width
}

若PPTX未嵌入字体,需匹配系统已安装的字体(注意处理字体名称的兼容问题,比如PPT中的"Calibri"对应系统同名字体)。

二、无AWT依赖的近似估算方案

如果无法加载字体,可基于字体大小和字符平均宽度做近似估算,适合对精度要求不高的场景:

import org.apache.poi.xslf.usermodel.XSLFTextRun

// 估算指定文本运行下的文本宽度(单位:点)
fun estimateTextWidth(textRun: XSLFTextRun, text: String): Double {
    val fontSize = textRun.fontSize ?: 12.0
    // 根据字体类型调整系数:等宽字体用0.5,无衬线用0.6,衬线用0.65
    val charWidthCoefficient = when (textRun.fontFamily) {
        "Courier New", "Consolas" -> 0.5
        "Arial", "Calibri" -> 0.6
        "Times New Roman" -> 0.65
        else -> 0.6
    }
    return text.length * fontSize * charWidthCoefficient
}

三、替换时的尺寸匹配逻辑

拿到原单词宽度后,从拉丁语单词列表中选择宽度最接近的目标单词:

fun pickBestMatchingLatinWord(originalWidth: Float, latinCandidates: List<String>, font: Font): String {
    return latinCandidates.minByOrNull { candidate ->
        val candidateWidth = getTextWidth(candidate, font)
        kotlin.math.abs(candidateWidth - originalWidth)
    } ?: latinCandidates.first()
}

注意事项

  • 嵌入字体是保证宽度计算精准的前提,未嵌入字体的PPT会因系统字体差异导致估算偏差
  • 带连字、字距调整的复杂字体,AWT的FontMetrics计算结果更准确
  • 处理多行文本时,需额外考虑换行和行间距对整体尺寸的影响

内容的提问来源于stack exchange,提问作者Poui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 04:32:45