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

Google Slides API自定义文本适配文本框函数结果不一致求助

自定义文本框字体适配函数结果不一致的问题排查与优化

Google Slides API的AutoFit功能无法正常运行,我自行开发了计算文本框适配字体大小的函数,但结果始终不稳定,以下是当前开发中的函数:

function calculateTextFit($boxWidth, $boxHeight, $text, $fontSizeStartPT, $ScaleX, $ScaleY)
{
    // (AgendaSlide, TitleSlide 1.2)
    $fontAdjustmentFactor = $fontSizeStartPT > 26 ? 1.2 : 1.1;

    $minFontSize = $this->ptToPixels(8) * $ScaleY;
    $maxFontSize = $this->ptToPixels($fontSizeStartPT) * $ScaleY;
    $lineSpacing = $this->ptToPixels(1.4) * $ScaleY;

    $boxWidth = $this->emuToPixels($boxWidth) * $ScaleX; // Масштабування по X
    $boxHeight = $this->emuToPixels($boxHeight) * $ScaleY; // Масштабування по Y

    $areaSize = $boxWidth * $boxHeight;
    preg_match_all('/(\n|\r|\n\n)/', $text, $matches);
    $totalLineBreaks = count($matches[0]);

    // is this an Asian language (Chinese, Korean, Japanese)?
    $hasAsianCharacters = preg_match('/[\x{4E00}-\x{9FFF}\x{3040}-\x{30FF}\x{AC00}-\x{D7AF}\x{31F0}-\x{31FF}]/u', $text);
    $textLen = mb_strlen($text);

    for ($fontSize = $maxFontSize; $fontSize >= $minFontSize; $fontSize--) {

        $fontWidth = $hasAsianCharacters ? $fontSize : $fontSize * $fontAdjustmentFactor ;
        $textSize = $textLen * $fontWidth * $fontSize * $lineSpacing;
        $textSize = $textSize + ($totalLineBreaks * $lineSpacing * $boxWidth);

        if ($textSize <= $areaSize) {
            $newFontSize = $this->pixelsToPt($fontSize);
            return $newFontSize < 5 ? 5 : $newFontSize;
        }
    }

    return $this->pixelsToPt($minFontSize);
}

问题根源分析

  1. 文本尺寸计算过于理想化:用文本长度×字体宽×字体高×行间距的方式完全忽略了实际排版逻辑——英文单词会自动换行、不同字符宽度差异极大(比如i和w)、换行符对应的是新行还是新段落,这些都会导致实际占用空间和估算值偏差巨大。
  2. 换行符统计错误:当前正则会把\n\n拆成两个换行符统计,但在Slides里连续换行是段落分隔,对应的是更大的间距,不是普通行间距,这样会高估额外占用的空间。
  3. 字体宽度因子太粗糙:仅靠初始字体大小>26来区分调整因子,没考虑不同字体的固有宽度差异(比如Arial和Times New Roman的字符宽度就不一样),也没考虑英文文本的平均字符宽度更接近0.5-0.6倍字体大小,不是固定的1.1或1.2。
  4. 单位转换与缩放顺序问题:先把EMU转像素再乘缩放,可能引入精度误差,应该先处理缩放再转像素;另外行间距应该是字体大小的倍数,不是固定像素值,否则字体大小变化时行间距逻辑会混乱。

优化后的函数实现

function calculateTextFit($boxWidth, $boxHeight, $text, $fontSizeStartPT, $ScaleX, $ScaleY)
{
    // 不同字体的平均宽度因子,可根据实际使用字体调整
    $fontWidthFactors = [
        'asian' => 1.0,
        'english' => 0.55 // 英文平均字符宽度约为字体大小的0.55倍
    ];
    $minFontSizePT = 5;
    $maxFontSizePT = $fontSizeStartPT;
    $lineSpacingRatio = 1.4; // 行间距为字体大小的1.4倍

    // 先处理缩放,再转换单位为像素
    $scaledBoxWidthEMU = $boxWidth * $ScaleX;
    $scaledBoxHeightEMU = $boxHeight * $ScaleY;
    $boxWidthPx = $this->emuToPixels($scaledBoxWidthEMU);
    $boxHeightPx = $this->emuToPixels($scaledBoxHeightEMU);

    // 区分普通换行(新行)和段落分隔(空行)
    $paragraphs = preg_split('/\n\s*\n/', $text);
    $totalParagraphs = count($paragraphs);
    $totalLineBreaks = 0;
    $totalTextLength = 0;
    $hasAsianCharacters = false;

    foreach ($paragraphs as $para) {
        $para = trim($para);
        if (empty($para)) continue;
        $totalTextLength += mb_strlen($para);
        // 统计段落内的换行
        $totalLineBreaks += substr_count($para, "\n") + substr_count($para, "\r");
        // 检查是否包含亚洲字符
        if (!$hasAsianCharacters && preg_match('/[\x{4E00}-\x{9FFF}\x{3040}-\x{30FF}\x{AC00}-\x{D7AF}\x{31F0}-\x{31FF}]/u', $para)) {
            $hasAsianCharacters = true;
        }
    }

    $fontWidthFactor = $hasAsianCharacters ? $fontWidthFactors['asian'] : $fontWidthFactors['english'];
    $minFontSizePx = $this->ptToPixels($minFontSizePT);
    $maxFontSizePx = $this->ptToPixels($maxFontSizePT);

    // 从最大字体开始向下尝试
    for ($fontSizePx = $maxFontSizePx; $fontSizePx >= $minFontSizePx; $fontSizePx--) {
        $charWidthPx = $fontSizePx * $fontWidthFactor;
        $lineHeightPx = $fontSizePx * $lineSpacingRatio;
        $paragraphSpacingPx = $lineHeightPx * 1.5; // 段落间距设为行高的1.5倍,可调整

        // 计算每个段落的行数
        $totalLines = 0;
        foreach ($paragraphs as $para) {
            $para = trim($para);
            if (empty($para)) continue;
            // 段落内的每行文本长度(考虑手动换行)
            $lines = preg_split('/\n|\r/', $para);
            foreach ($lines as $line) {
                $lineLen = mb_strlen($line);
                if ($lineLen === 0) continue;
                // 计算该行需要的实际行数(自动换行)
                $requiredLines = ceil(($lineLen * $charWidthPx) / $boxWidthPx);
                $totalLines += $requiredLines;
            }
        }

        // 总占用高度 = 总行数×行高 + (段落数-1)×段落间距
        $totalTextHeightPx = ($totalLines * $lineHeightPx) + (($totalParagraphs - 1) * $paragraphSpacingPx);

        // 检查是否适配文本框高度
        if ($totalTextHeightPx <= $boxHeightPx) {
            return $this->pixelsToPt($fontSizePx);
        }
    }

    return $minFontSizePT;
}

优化点说明

  • 更精准的排版逻辑:拆分段落和行,计算每个段落的自动换行行数,考虑段落间距和行间距的实际比例。
  • 字符宽度因子优化:针对亚洲字符和英文分别设置更符合实际的宽度因子,可根据使用的具体字体微调。
  • 单位与缩放顺序调整:先缩放EMU单位的尺寸,再转换为像素,减少精度损失。
  • 换行与段落区分:区分普通换行和段落分隔,分别计算对应的空间占用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 16:40:56