0

是否有任何简单且兼容的 GDI 或 .NET 可访问的 Windows 子系统将提供字形位置字符。这里的任务是组合符号,例如阿拉伯语中的组合符号,有时会有多个组合符号链堆叠在一起,例如阿拉伯法塔 + 阿拉伯字母上标 Alef + 阿拉伯 Maddah 上方。问题是,虽然 X 位置可以通过 GDI GetCharacterPlacement 精确确定,但从 OpenType 或 TrueType 字体表和锚点以及一组复杂的规则中得出的 Y 位置计算不可用。最终,要生成格式正确的阿拉伯语 PDF,需要精确的 Y 位置。学习 Microsoft Word 2013 的另存为 PDF 功能,

WPF 可能在 GlyphRun 类属性 GlyphOffsets 中包含一些函数来执行此操作。DirectWrite 有 IDWriteTextAnalyzer 接口,GetGlyphPlacements 方法可以返回 DWRITE_GLYPH_OFFSETs 和许多其他复杂的脚本信息。查看 GDI Display 和 Printer Drive 函数,STROBJ_bEnumPositionsOnly 似乎返回了一组带有此信息的 GLYPHPOS 结构。如果您将全文发送到渲染,GDI 肯定会在所有情况下正确渲染,但如果您想逐个字形地进行渲染,则不会。

XPS 对象模型中的 IXpsOMGlyphs 允许 GetGlyphIndices 调用返回一组 XPS_GLYPH_INDEX 给出水平偏移和垂直偏移,尽管这个库几乎不合适。

最后,唯一合适的库看起来是 Uniscribe,它使用起来很复杂,但自 Internet Explorer 5 和 Windows 2000 以来就受支持,而不是 GDI 之外的所有其他讨论,通常是 Vista 和更高版本或需要特殊依赖项。ScriptItemize 返回一个 SCRIPT_STRING_ANALYSIS 数组,该数组可以传递给 ScriptShape,然后 ScriptPlace 返回一个数组或 GOFFSET。事实上,Uniscribe 将提供有关分词、变音符号、方向流和复杂脚本中发生的许多其他方面的信息。我只是想知道是否有更简单的方法,或者这是否是此类任务所需的最低要求和完全合适的方法,因为 Uniscribe 似乎很难直接从 .NET 使用,并且合理地需要 C++ 包装器,因为有一个很棒的处理结构和指针。

更新和回答:Uniscribe 不适用于 PDF 目的,因为它在 GDI 设备单元中使用整数,因此准确性大大降低。这可能是 Microsoft Word 2013 最终支持原生 PDF 转换支持的原因,因为最终似乎依赖 DirectWrite。如下所述,我在 CodeProject 上发布了 .NET 中的两种代码解决方案作为提示。除了设计自定义字体整形和计算引擎之外,DirectWrite 似乎是唯一的答案。

4

1 回答 1

1

.NET 中的示例 Uniscribe 代码,因为它目前在 Web 上不可用:

     _
    公共结构 GCP_RESULTS
        公共 StructSize 作为 UInteger
         _
        公共 OutString 作为字符串
        公共秩序作为 IntPtr
        公共 Dx 作为 IntPtr
        公共 CaretPos 作为 IntPtr
        公共 [类] 作为 IntPtr
        公共字形作为 IntPtr
        公共 GlyphCount 作为 UInteger
        公共 MaxFit 作为整数
    末端结构
     _
    公共结构 SCRIPT_CONTROL
        公共 ScriptControlFlags 作为 UInteger
    末端结构
     _
    公共结构 SCRIPT_STATE
        公共 ScriptStateFlags 为 UShort
    末端结构
     _
    公共结构 SCRIPT_ANALYSIS
        公共脚本分析标记为 UShort
        公共作为 SCRIPT_STATE
    末端结构
     _
    公共结构 SCRIPT_VISATTR
        公共 ScriptVisAttrFlags 为 UShort
    末端结构
     _
    公共结构 SCRIPT_ITEM
        公共 iCharPos 作为整数
        公开 a 作为 SCRIPT_ANALYSIS
    末端结构
     _
    公共结构 GOFFSET
        Public du As Integer
        公共 dv 作为整数
    末端结构
     _
    公共结构 ABC
        公共 abcA 作为整数
        公共 abcB 作为 UInteger
        公共 abcC 作为整数
    末端结构
    公共常量 E_OUTOFMEMORY As Integer = &H8007000E
    公共常量 E_PENDING 作为整数 = &H8000000A
    公共常量 USP_E_SCRIPT_NOT_IN_FONT As Integer = &H80040200
     _
    公共共享函数 GetCharacterPlacement(hdc As IntPtr, lpString As String, nCount As Integer, nMaxExtent As Integer, ByRef lpResults As GCP_RESULTS, dwFlags As UInteger) As UInteger
    结束功能
     _
    公共共享函数 ScriptItemize( wcInChars As String, cInChars As Integer, cMaxItems As Integer, psControl As SCRIPT_CONTROL, psState As SCRIPT_STATE, pItems() As SCRIPT_ITEM, ByRef pcItems As Integer) As Integer
    结束功能
     _
    公共共享函数 ScriptShape(hdc As IntPtr, ByRef psc As IntPtr, wcChars As String, cChars As Integer, cMaxGlyphs As Integer, ByRef psa As SCRIPT_ANALYSIS, wOutGlyphs() As UShort, wLogClust() As UShort, psva() As SCRIPT_VISATTR, ByRef cGlyphs 作为整数)作为整数
    结束功能
     _
    公共共享函数 ScriptPlace(hdc As IntPtr, ByRef psc As IntPtr, wGlyphs() As UShort, cGlyphs As Integer, psva() As SCRIPT_VISATTR, ByRef psa As SCRIPT_ANALYSIS, iAdvance() As Integer, pGoffset() As GOFFSET, ByRef pABC As ABC) 作为整数
    结束功能
     _
    公共共享函数 ScriptFreeCache(ByRef psc As IntPtr) As Integer
    结束功能
     _
    公共共享函数 GetDC(hWnd As IntPtr) As IntPtr
    结束功能
     _
    公共共享函数 ReleaseDC(hWnd As IntPtr, hdc As IntPtr) As Integer
    结束功能
     _
    私有共享函数 SelectObject(ByVal hdc As IntPtr, ByVal hObject As IntPtr) As IntPtr
    结束功能
    结构 CharPosInfo
        公共索引为整数
        公共宽度为整数
        公共 PriorWidth 作为整数
        公共 X 作为整数
        公共 Y 作为整数
    末端结构
    公共共享函数 GetWordDiacriticPositions(Str As String, useFont As Font) As CharPosInfo()
        将 hdc 调暗为 IntPtr
        将 CharPosInfos 调暗为新列表(CharPosInfo 的)
        hdc = GetDC(IntPtr.Zero) '桌面设备上下文
        暗淡 oldFont As IntPtr = SelectObject(hdc, useFont.ToHfont())
        将 MaxItems 调暗为整数 = 16
        使用 {.ScriptControlFlags = 0} 将控件调暗为新的 SCRIPT_CONTROL
        昏暗状态为新的 SCRIPT_STATE 与 {.ScriptStateFlags = 1} '0 LTR, 1 RTL
        Dim Items() As SCRIPT_ITEM = 无
        将 ItemCount 调暗为整数
        将结果暗淡为整数
        做
            ReDim 项目(MaxItems - 1)
            结果 = ScriptItemize(Str, Str.Length, MaxItems, Control, State, Items, ItemCount)
            如果结果 = 0 那么
                ReDim Preserve Items(ItemCount) '有一个虚拟的最后一个项目,所以在这里添加一个
                退出做
            ElseIf 结果 = E_OUTOFMEMORY 然后
            万一
            最大项目数 *= 2
        为真时循环
        如果结果 = 0 那么
            '最后一项是指向字符串结尾的虚拟项
            将缓存调暗为 IntPtr = IntPtr.Zero
            对于 Count = 0 到 ItemCount - 2
                Dim Logs() As UShort = 无
                Dim Glyphs() As UShort = Nothing
                将 VisAttrs() 调暗为 SCRIPT_VISATTR = 无
                ReDim Glyphs((Items(Count + 1).iCharPos - Items(Count).iCharPos) * 3 \ 2 + 16 - 1)
                ReDim VisAttrs((Items(Count + 1).iCharPos - Items(Count).iCharPos) * 3 \ 2 + 16 - 1)
                ReDim 日志(项目(计数 + 1).iCharPos - 项目(计数).iCharPos - 1)
                Dim dc As IntPtr = IntPtr.Zero
                做
                    暗淡字形用作整数
                    结果 = ScriptShape(dc, Cache, Str.Substring(Items(Count).iCharPos), Items(Count + 1).iCharPos - Items(Count).iCharPos, Glyphs.Length, Items(Count).a, Glyphs, Logs , VisAttrs, GlyphsUsed)
                    如果结果 = 0 那么
                        ReDim 保留字形(GlyphsUsed - 1)
                        ReDim 保留 VisAttrs(GlyphsUsed - 1)
                        退出做
                    ElseIf 结果 = E_PENDING 那么
                        直流 = 直流
                    ElseIf 结果 = E_OUTOFMEMORY 然后
                        ReDim Glyphs(Glyphs.Length * 2 - 1)
                        ReDim VisAttrs(VisAttrs.Length * 2 - 1)
                    ElseIf 结果 = USP_E_SCRIPT_NOT_IN_FONT 然后
                    别的
                    万一
                为真时循环
                如果结果 = 0 那么
                    Dim Advances(Glyphs.Length - 1) 作为整数
                    暗淡偏移(字形。长度 - 1)作为 GOFFSET
                    将 abc 调暗为具有 {.abcA = 0, .abcB = 0, .abcC = 0} 的新 ABC
                    dc = IntPtr.Zero
                    做
                        结果 = ScriptPlace(dc, Cache, Glyphs, Glyphs.Length, VisAttrs, Items(Count).a, Advances, Offsets, abc)
                        If Result E_PENDING Then Exit Do
                        直流 = 直流
                    为真时循环
                    如果结果 = 0 那么
                        将 LastPriorWidth 调暗为整数 = 0
                        将 RunStart 调暗为整数 = 0
                        对于 CharCount = 0 到 Logs.Length - 1
                            Dim PriorWidth As Integer = 0
                            将 RunCount 调暗为整数 = 0
                            For ResCount As Integer = Logs(CharCount) To If(CharCount = Logs.Length - 1, 0, Logs(CharCount + 1)) 步骤 -1
                                'fDiacritic 或 fZeroWidth
                                If (VisAttrs(ResCount).ScriptVisAttrFlags And (32 or 64)) 0 Then
                                    CharPosInfos.Add(使用 {.Index = RunStart + RunCount、.PriorWidth = LastPriorWidth、.Width = Advances(ResCount)、.X = Offsets(ResCount).du、.Y = Offsets(ResCount).dv} 的新 CharPosInfo)
                                万一
                                If CharCount = Logs.Length - 1 OrElse Logs(CharCount) Logs(CharCount + 1) Then
                                    PriorWidth += Advances(ResCount)
                                    运行计数 += 1
                                万一
                            下一个
                            LastPriorWidth += PriorWidth
                            If CharCount = Logs.Length - 1 OrElse Logs(CharCount) Logs(CharCount + 1) Then
                                RunStart = CharCount + 1
                            万一
                        下一个
                    万一
                万一
            下一个
            ScriptFreeCache(缓存)
        万一
        选择对象(hdc,oldFont)
        ReleaseDC(IntPtr.Zero, hdc)
        返回 CharPosInfos.ToArray()
    结束功能
于 2014-11-19T21:59:21.610 回答