DirectWrite 不调整变音符号

DirectWrite not adjusting for diacritics

我目前正在调试我编写的一些 DirectWrite 代码,因为在使用非英语字符进行测试时我遇到了 运行 问题。主要是获取多个 Unicode 字符 returning 正确的索引。

编辑:经过进一步研究,我认为问题出在变音符号上,多余的字符应该以某种方式组合起来。 DWRITE_SHAPING_GLYPH_PROPERTIES 字段 isDiacritic 对最后一个 unicode 代码点执行 return 1。然而,似乎塑造过程根本没有考虑到这些。 GetGlyphPlacements returns 0 用于变音符号的提前和偏移。 LSB 大约为 -5,但这不足以偏移到正确的位置。有谁知道 DirectWrite 应该在整形过程中的哪个位置考虑变音符号以及如何考虑?

考虑这个角色:œ̃

它显示为一个字符(通过大多数文本编辑器),但有两个代码点:U+0153 U+0303

我如何在 GetGlyphs() 中说明这一点,因为它们是单独的代码点?在我的代码中,它是 returning 两个不同的索引 (177, 1123) 和一个集群 (0, 0).

这是最终呈现的内容:

这与单独呈现的两个代码点一致,但与实际字符不一致。 GetGlyphs() 编辑的实际索引数 return 为 2。

我的问题如下:

  1. 这应该是 returning 来自 GetGlyphs() 的一个索引吗?

  2. 我什至应该得到一个索引,还是两个不同的索引有一些魔力,在这个过程的某个阶段,它们被组合在字形 运行 中?

  3. 如果我应该得到一个指数,这些指数的总和是多少 process/functions?也许是我的 ScriptAnalysis 中的错误?试图缩小问题的范围。

  4. 我应该使用字符的长度而不包括代码点吗?

抱歉,我对 fonts/Unicode 和整个塑造过程的内部运作不是很了解。

这是我用于获取索引和预付款的过程的一些代码:

text_length = len(text.encode('utf-16-le')) // 2
text_buffer = create_unicode_buffer(text, text_length)

self._text_analysis.GenerateResults(self._analyzer, text_buffer, len(text_buffer))

# Formula for text buffer size from Microsoft.
max_glyph_size = int(3 * text_length / 2 + 16)

length = text_length
clusters = (UINT16 * length)()
text_props = (DWRITE_SHAPING_TEXT_PROPERTIES * length)()
indices = (UINT16 * max_glyph_size)()
glyph_props = (DWRITE_SHAPING_GLYPH_PROPERTIES * max_glyph_size)()
actual_count = UINT32()

self._analyzer.GetGlyphs(text_buffer,
                         len(text_buffer),
                         self.font.font_face,
                         False,  # sideways
                         False,  # rtl
                         self._text_analysis.script,  # scriptAnalysis
                         None,  # localName
                         None,  # numberSub
                         None,  # typo features
                         None,  # feature range length
                         0,  # feature range
                         max_glyph_size,  # max glyph size
                         clusters,  # cluster map
                         text_props,  # text props
                         indices,  # glyph indices
                         glyph_props,  # glyph pops
                         byref(actual_count)  # glyph count
                     )

advances = (FLOAT * length)()
offsets = (DWRITE_GLYPH_OFFSET * length)()
self._analyzer.GetGlyphPlacements(text_buffer,
                                  clusters,
                                  text_props,
                                  text_length,
                                  indices,
                                  glyph_props,
                                  actual_count,
                                  self.font.font_face,
                                  self.font.font_metrics.designUnitsPerEm,
                                  False, False,
                                  self._text_analysis.script,
                                  self.font.locale,
                                  None,
                                  None,
                                  0,
                                  advances,
                                  offsets)

编辑:这是渲染代码:

def render_single_glyph(self, font_face, indice, advance, offset, metrics):
    """Renders a single glyph using D2D DrawGlyphRun"""
    glyph_width, glyph_height, lsb, font_advance = metrics

    # Slicing an array turns it into a python object. Maybe a better way to keep it a ctypes value?
    new_indice = (UINT16 * 1)(indice)
    new_advance = (FLOAT * 1)(advance)

    run = self._get_single_glyph_run(font_face,
                                     self.font._real_size,
                                     new_indice,  # indice,
                                     new_advance,  # advance,
                                     pointer(offset),  # offset,
                                     False,
                                     False)


    offset_x = 0
    if lsb < 0:
        # Negative LSB: we shift the layout rect to the right
        # Otherwise we will cut the left part of the glyph
        offset_x = math.ceil(abs(lsb))

    font_height = (self.font.font_metrics.ascent + self.font.font_metrics.descent) * self.font.font_scale_ratio

    # Create new bitmap.
    self._create_bitmap(int(math.ceil(glyph_width)),
                        int(math.ceil(font_height)))

    # This offsets the characters if needed.
    point = D2D_POINT_2F(offset_x, int(math.ceil(font_height)))

    self._render_target.BeginDraw()

    self._render_target.Clear(transparent)

    self._render_target.DrawGlyphRun(point,
                                     run,
                                     self.brush,
                                     DWRITE_MEASURING_MODE_NATURAL)

    self._render_target.EndDraw(None, None)
    image = wic_decoder.get_image(self._bitmap)

    glyph = self.font.create_glyph(image)
    glyph.set_bearings(self.font.descent, offset_x, round(advance * self.font.font_scale_ratio))  # baseline, lsb, advance
    return glyph

整形过程由您的输入控制,即(文本、字体、语言环境、脚本、用户特征)。所有影响结果的因素。具体回答你的问题:

Should this be returning one indice from GetGlyphs()?

这主要由您的字体定义。

Should I even be getting one indice, or is there some magic involved with two different indices, where at some stage in the process they are combined in the glyph run?

GetGlyphs() 对单个 运行 进行操作。根据每个脚本定义的形状规则,并根据字体中定义的转换,字形可以自由形成一个簇。

If I should be getting one indice, what process/functions are these indices combined at? Perhaps a bug in my ScriptAnalysis? Trying to narrow down where the issue may be.

基本上,如果你的输入参数是正确的,你就会得到你所得到的输出,你无法真正控制它的核心。您可以做的是在 Uniscribe、CoreText (macos) 和 Chromium/Firefox (harfbuzz) 上测试相同文本和字体的输出,看看它们是否不同。

Should I be using the length of the characters and not include codepoints?

我没收到这个。