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

使用PdfDocument导出长文本PDF的分页问题求助

Android PDF导出:长富文本分页解决方案

针对你遇到的长富文本溢出页面、无法分页的问题,直接给你可行的实现方案:

1. 判断文本是否超出页面可用空间

首先明确页面剩余可用高度:即当前页面中,从当前绘制起始Y坐标(mYPosOnPage)到页面底部边距的距离。假设你定义了cTopMargin(上边距)、cBottomMargin(下边距)、mPageHeight(页面总高度),计算方式如下:

val remainingPageHeight = mPageHeight - cBottomMargin - mYPosOnPage

然后通过StaticLayout的height属性获取整个文本需要的总高度,和剩余高度对比即可判断是否溢出:

val spanText = Html.fromHtml(longHtmlText, Html.FROM_HTML_MODE_COMPACT)
val fullLayout = StaticLayout.Builder
    .obtain(spanText, 0, spanText.length, textPaint, mPageWidth - 2 * cLeftRightMargin)
    .setAlignment(Layout.Alignment.ALIGN_NORMAL)
    .setLineSpacing(0f, 1.0f)
    .build()

// 文本总高度超过页面剩余可用高度,即为溢出
val isOverflow = fullLayout.height > remainingPageHeight

2. 截断文本并分页绘制

如果文本溢出,需要精准找到截断位置,分页面绘制剩余内容:

步骤1:找到当前页可容纳的最后一行

利用StaticLayout.getLineForVertical()方法,传入剩余可用高度,得到当前页能显示的最后一行索引:

val lastVisibleLine = fullLayout.getLineForVertical(remainingPageHeight)

步骤2:获取文本截断索引

通过getLineEnd()获取该行对应的文本结束位置,注意要判断是否是文本的最后一行(避免无意义截断):

val endIndex = fullLayout.getLineEnd(lastVisibleLine)
val isTextFullyDisplayed = lastVisibleLine == fullLayout.lineCount - 1

步骤3:绘制当前页内容

创建仅包含当前页内容的StaticLayout,绘制到当前页面:

val currentPageText = spanText.subSequence(0, endIndex)
val currentLayout = StaticLayout.Builder
    .obtain(currentPageText, 0, currentPageText.length, textPaint, mPageWidth - 2 * cLeftRightMargin)
    .setAlignment(Layout.Alignment.ALIGN_NORMAL)
    .setLineSpacing(0f, 1.0f)
    .build()

canvas.save()
canvas.translate(cLeftRightMargin.toFloat(), mYPosOnPage.toFloat())
currentLayout.draw(canvas)
canvas.restore()

步骤4:处理剩余文本到下一页

如果还有剩余文本,创建新的PDF页面,重复上述流程:

if (!isTextFullyDisplayed) {
    // 剩余未绘制的文本
    val remainingText = spanText.subSequence(endIndex, spanText.length)
    // 新建PDF页面
    val nextPageInfo = PdfDocument.PageInfo.Builder(mPageWidth, mPageHeight, currentPageNum + 1).create()
    val nextPage = pdfDocument.startPage(nextPageInfo)
    val nextCanvas = nextPage.canvas
    // 新页面从顶部边距开始绘制
    val nextYPos = cTopMargin

    // 复用绘制逻辑处理剩余文本
    val remainingLayout = StaticLayout.Builder
        .obtain(remainingText, 0, remainingText.length, textPaint, mPageWidth - 2 * cLeftRightMargin)
        .setAlignment(Layout.Alignment.ALIGN_NORMAL)
        .setLineSpacing(0f, 1.0f)
        .build()

    nextCanvas.save()
    nextCanvas.translate(cLeftRightMargin.toFloat(), nextYPos.toFloat())
    remainingLayout.draw(nextCanvas)
    nextCanvas.restore()

    pdfDocument.finishPage(nextPage)
}

关于TextPaint初始化的说明

TextPaint的初始化是必要的,StaticLayout完全依赖它计算文本的尺寸、样式(字体大小、颜色、字重等),如果省略,会使用系统默认值,导致绘制效果不符合预期,建议保留当前的初始化代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 08:33:39