在 WPF 中精确定位字形文本

Precisely locating glyph text in WPF

我正在为 Windows 编写一个化学分子编辑器。因为它必须在 Word 加载项中使用,所以我只能使用 WPF 来呈现结构。除了一个小问题外,这工作得很好。

我使用 GlyphRuns 渲染原子标签,它们总是稍微向右移动。如果您查看屏幕截图,您会看到前导空格,尤其是 H2N 和 Hg 原子标签。为什么?白色背景是当你得到字形的轮廓几何形状时得到的运行。

GlyphRun class 的文档非常糟糕,以至于我看不到要修改哪些属性才能将文本精确定位到我想要的位置。因此,欢迎任何尝试的建议。

更新:有人要求我提供样本。代码很复杂,但并非无缘无故,所以我将其精简以专注于要点:

    public void MeasureAtCenter(Point center)
        {
            GlyphInfo = GlyphUtils.GetGlyphsAndInfo(Text, PixelsPerDip, out GlyphRun groupGlyphRun, center, _glyphTypeface, TypeSize);
            //compensate the main offset vector for any descenders
            Vector mainOffset = GlyphUtils.GetOffsetVector(groupGlyphRun, AtomShape.SymbolSize) + new Vector(0.0, -MaxBaselineOffset) + new Vector(-FirstBearing(groupGlyphRun), 0.0);



            TextRun = groupGlyphRun;
            TextMetrics = new AtomTextMetrics
            {
                BoundingBox = groupGlyphRun.GetBoundingBox(center + mainOffset),
                Geocenter = center,
                TotalBoundingBox = groupGlyphRun.GetBoundingBox(center + mainOffset),
                OffsetVector = mainOffset
            };
        }

  public static GlyphInfo GetGlyphs(string symbolText, GlyphTypeface glyphTypeFace, double size)
        {
            ushort[] glyphIndexes = new ushort[symbolText.Length];
            double[] advanceWidths = new double[symbolText.Length];
            double[] uprightBaselineOffsets = new double[symbolText.Length];
            double totalWidth = 0;

            for (int n = 0; n < symbolText.Length; n++)
            {
                ushort glyphIndex = glyphTypeFace.CharacterToGlyphMap[symbolText[n]];
                glyphIndexes[n] = glyphIndex;

                double width = glyphTypeFace.AdvanceWidths[glyphIndex] * size;
                advanceWidths[n] = width;

                double ubo = glyphTypeFace.DistancesFromHorizontalBaselineToBlackBoxBottom[glyphIndex] * size;
                uprightBaselineOffsets[n] = ubo;
                totalWidth += width;
            }
            return new GlyphInfo { AdvanceWidths = advanceWidths, Indexes = glyphIndexes, Width = totalWidth, UprightBaselineOffsets = uprightBaselineOffsets };
        }

        public static GlyphUtils.GlyphInfo GetGlyphsAndInfo(string symbolText, float pixelsPerDip, out GlyphRun hydrogenGlyphRun, Point point, GlyphTypeface glyphTypeFace, double symbolSize)
        {
            //measure the H atom first
            var glyphInfo = GlyphUtils.GetGlyphs(symbolText, glyphTypeFace, symbolSize);
            hydrogenGlyphRun = GlyphUtils.GetGlyphRun(glyphInfo, glyphTypeFace,
                symbolSize, pixelsPerDip, point);
            //work out exactly how much we should offset from the center to get to the bottom left
            return glyphInfo;
        }

 public static Vector GetOffsetVector(GlyphRun glyphRun, double symbolSize)
    {
        Rect rect = glyphRun.ComputeInkBoundingBox();

        //Vector offset = (rect.BottomLeft - rect.TopRight) / 2;
        Vector offset = new Vector(-rect.Width / 2, glyphRun.GlyphTypeface.CapsHeight * symbolSize / 2);
        return offset;
    }

确实GlyphRun class is a lot of work to use. I would suggest working with FormattedText objects instead. If there are performance issues, you can consider converting the FormattedText to Geometry once and reusing that. The MSDN docs provide a comparison of the different approaches