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

如何在.NET MAUI中用SkiaSharp+HarfBuzz正确渲染阿拉伯文本?

.NET MAUI中SkiaSharp+HarfBuzz阿拉伯文本渲染错误的解决方法

问题分析

你遇到的阿拉伯文本重叠、塑形错误,核心原因是HarfBuzz的字形坐标系统与SkiaSharp的布局逻辑没有正确对齐,包括单位转换、RTL文本的位置计算、HarfBuzz属性设置顺序这几个关键点。

修正方案

以下是调整后的完整代码,关键修改点已标注:

public byte[] ImageArabic()
{
    try
    {
        int width = 400, height = 100, textSize = 40;
        string arabicText = "مرحبا بالعالم"; // 阿拉伯文本

        using var surface = SKSurface.Create(new SKImageInfo(width, height));
        var canvas = surface.Canvas;
        canvas.Clear(SKColors.White);

        // 加载阿拉伯字体
        var assembly = typeof(App).GetTypeInfo().Assembly;
        var resourceName = "PrintReceipt.Resources.Fonts.GE_SS_Two_Light.ttf";

        using Stream fontStream = assembly.GetManifestResourceStream(resourceName);
        if (fontStream == null)
            throw new FileNotFoundException($"字体文件 {resourceName} 未找到!");

        using MemoryStream memoryStream = new MemoryStream();
        fontStream.CopyTo(memoryStream);
        memoryStream.Position = 0;
        byte[] fontBytes = memoryStream.ToArray();

        using SKTypeface typeface = SKTypeface.FromStream(new MemoryStream(fontBytes));

        // 初始化HarfBuzz
        using var hbBlob = HarfBuzzSharp.Blob.FromStream(new MemoryStream(fontBytes));
        using var hbFace = new HarfBuzzSharp.Face(hbBlob, 0);
        using var hbFont = new HarfBuzzSharp.Font(hbFace);
        
        // 🔴 修改:HarfBuzz缩放与Skia文本大小对齐,匹配HarfBuzz的1/64em单位
        float scale = textSize * 64;
        hbFont.SetScale((int)scale, (int)scale);

        // 🔴 修改:先添加文本,再设置布局属性,避免GuessSegmentProperties覆盖手动配置
        using var hbBuffer = new HarfBuzzSharp.Buffer();
        hbBuffer.AddUtf16(arabicText);
        hbBuffer.Direction = HarfBuzzSharp.Direction.RightToLeft;
        hbBuffer.Script = HarfBuzzSharp.Script.Arabic;
        hbBuffer.Language = new HarfBuzzSharp.Language("ar");
        // 无需调用GuessSegmentProperties,已手动指定所有必要属性

        hbFont.Shape(hbBuffer);

        var glyphInfos = hbBuffer.GlyphInfos;
        var glyphPositions = hbBuffer.GlyphPositions;

        using var paint = new SKPaint
        {
            Color = SKColors.Black,
            IsAntialias = true,
            Typeface = typeface,
            TextSize = textSize
        };

        // 构建定位字形的TextBlob
        using var textBlobBuilder = new SKTextBlobBuilder();
        var runBuffer = textBlobBuilder.AllocatePositionedRun(typeface, textSize, glyphInfos.Length);

        // 🔴 修改:计算文本总宽度,确保RTL文本从右侧正确对齐
        float totalAdvance = 0;
        foreach (var pos in glyphPositions)
        {
            totalAdvance += pos.XAdvance / 64.0f;
        }
        float startX = width - 10 - totalAdvance; // 右侧留10px边距,文本整体左对齐到该位置
        float startY = height / 2 - paint.FontMetrics.Descent / 2; // 基于FontMetrics实现精准垂直居中

        float currentX = startX;
        for (int i = 0; i < glyphInfos.Length; i++)
        {
            runBuffer.Glyphs[i] = (ushort)glyphInfos[i].Codepoint;
            // 🔴 修改:XOffset是相对于currentX的偏移,直接累加计算位置
            float xPos = currentX + glyphPositions[i].XOffset / 64.0f;
            float yPos = startY + glyphPositions[i].YOffset / 64.0f;
            runBuffer.Positions[i] = new SKPoint(xPos, yPos);
            
            // 更新currentX到下一个字形的起始位置
            currentX += glyphPositions[i].XAdvance / 64.0f;
        }

        using var textBlob = textBlobBuilder.Build();
        canvas.DrawText(textBlob, 0, 0, paint);

        using var image = surface.Snapshot();
        using var data = image.Encode(SKEncodedImageFormat.Png, 100);
        return data.ToArray();
    }
    catch (Exception ex)
    {
        Console.WriteLine($"错误:{ex.Message}");
        return null;
    }
}

关键修改说明

  1. HarfBuzz缩放对齐:将HarfBuzz的缩放设置为textSize * 64,匹配其默认的1/64em单位,确保字形大小与SkiaSharp的textSize完全一致。
  2. 属性设置顺序调整:先添加文本到HarfBuzz Buffer,再手动设置RTL方向、阿拉伯脚本和语言,避免自动检测方法覆盖手动配置。
  3. 垂直居中优化:使用SKPaint.FontMetrics计算垂直居中位置,比简单的textSize/2更精准,避免文本上下偏移。
  4. RTL布局修正:先计算文本总宽度,再从右侧预留边距的位置开始布局,同时按HarfBuzz返回的XAdvance依次累加当前位置,彻底解决字符重叠问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:43:13