fix: 高亮规则 fontPath 排版测量与绘制字体不一致导致行首行尾标点错位
排版阶段始终用默认字体测宽和断行,但绘制阶段才切换高亮规则字体, 导致不同字体的标点 advance 差异引起首尾符号错位。 - TextLine.addColumn(): 把 fontPath.isNotEmpty() 纳入 onlyTextColumn 判断, 防止仅改字体的规则被 fastDrawTextLine 跳过 - TextColumn: 新增 internal getTypeface() 让排版阶段复用 typeface 缓存 - TextChapterLayout: 在 applyHighlightRules 后用 remeasureWithHighlightFonts() 按字符/cluster 使用对应 fontPath 的 TextPaint 重新测宽, 确保 ZhLayout 断行、desiredWidth、TextColumn.start/end 全部基于正确字体度量
This commit is contained in:
@@ -82,7 +82,7 @@ data class TextLine(
|
||||
fun addColumn(column: BaseColumn) {
|
||||
if (column !is TextColumn) {
|
||||
onlyTextColumn = false
|
||||
} else if (column.textColor != null || column.bgColor != null || column.underlineMode != 0 || column.bgImage.isNotEmpty()) {
|
||||
} else if (column.textColor != null || column.bgColor != null || column.underlineMode != 0 || column.bgImage.isNotEmpty() || column.fontPath.isNotEmpty()) {
|
||||
onlyTextColumn = false
|
||||
}
|
||||
column.textLine = this
|
||||
|
||||
@@ -110,15 +110,19 @@ data class TextColumn(
|
||||
}
|
||||
|
||||
private fun getCustomTypeface(): Typeface? {
|
||||
if (fontPath.isEmpty()) return null
|
||||
return typefaceCache.getOrPut(fontPath) {
|
||||
loadTypeface(fontPath)
|
||||
}
|
||||
return getTypeface(fontPath)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val typefaceCache = HashMap<String, Typeface?>()
|
||||
|
||||
internal fun getTypeface(fontPath: String): Typeface? {
|
||||
if (fontPath.isEmpty()) return null
|
||||
return typefaceCache.getOrPut(fontPath) {
|
||||
loadTypeface(fontPath)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadTypeface(fontPath: String): Typeface? {
|
||||
return runCatching {
|
||||
when {
|
||||
|
||||
@@ -924,6 +924,10 @@ class TextChapterLayout(
|
||||
val charStyles = applyHighlightRules(text, isTitle)
|
||||
val widthsArray = allocateFloatArray(text.length)
|
||||
textPaint.getTextWidthsCompat(text, widthsArray)
|
||||
// 用高亮规则字体重新测量字符宽度,确保排版和绘制使用同一套字体
|
||||
if (charStyles != null) {
|
||||
remeasureWithHighlightFonts(text, charStyles, textPaint, widthsArray)
|
||||
}
|
||||
val layout = if (useZhLayout) {
|
||||
val (words, widths) = measureTextSplit(text, widthsArray)
|
||||
val indentSize = if (isFirstLine) paragraphIndent.length else 0
|
||||
@@ -1359,6 +1363,35 @@ class TextChapterLayout(
|
||||
return code == 8203 || code == 8204 || code == 8205 || code == 8288
|
||||
}
|
||||
|
||||
/**
|
||||
* 对有自定义字体的高亮字符重新测量宽度,确保排版和绘制使用同一套字体度量。
|
||||
*/
|
||||
private fun remeasureWithHighlightFonts(
|
||||
text: String,
|
||||
charStyles: Array<CharStyle?>,
|
||||
textPaint: TextPaint,
|
||||
widthsArray: FloatArray
|
||||
) {
|
||||
val originalTypeface = textPaint.typeface
|
||||
var i = 0
|
||||
while (i < text.length) {
|
||||
val fontPath = charStyles[i]?.fontPath.orEmpty()
|
||||
if (fontPath.isEmpty()) { i++; continue }
|
||||
// 找连续使用同一字体的区间
|
||||
val segStart = i
|
||||
i++
|
||||
while (i < text.length && charStyles[i]?.fontPath.orEmpty() == fontPath) i++
|
||||
val segEnd = i
|
||||
val typeface = TextColumn.getTypeface(fontPath) ?: continue
|
||||
textPaint.typeface = typeface
|
||||
val segment = text.substring(segStart, segEnd)
|
||||
val segWidths = FloatArray(segEnd - segStart)
|
||||
textPaint.getTextWidthsCompat(segment, segWidths)
|
||||
segWidths.copyInto(widthsArray, segStart)
|
||||
}
|
||||
textPaint.typeface = originalTypeface
|
||||
}
|
||||
|
||||
/**
|
||||
* 对文本应用高亮规则,返回每字符的样式数组。无匹配时返回 null。
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user