计算Windows中字形的位置 [英] Calculating the positions of glyphs in Windows

查看:70
本文介绍了计算Windows中字形的位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Windows是否有任何简单且兼容的GDI或.NET可访问子系统,可以提供字形位置字符.这里的任务是组合符号,例如阿拉伯语中的组合符号,有时具有多个组合符号链,它们相互堆叠,例如阿拉伯语Fatha +阿拉伯字母上标Alef +阿拉伯语Maddah.麻烦的是,尽管可以使用GDI GetCharacterPlacement精确地确定X位置,但是无法使用从OpenType或TrueType字体表和锚点以及一组复杂的规则派生的Y位置计算.最终,要生成具有正确格式的阿拉伯语的PDF,就需要并精确地确定Y位置.研究Microsoft Word 2013的另存为PDF"功能后,很显然,他们可以正确收集这些数据,因为研究PDF细节表明每个字符都显示在其精确位置,包括合并符号.

Is there any simple and compatible GDI or .NET accessible subsystem of Windows that will give glyph position characters. The task here is the combining symbols such as those in Arabic which sometimes have chains of multiple combining symbols stacking on top of each others such as Arabic Fatha + Arabic Letter Superscript Alef + Arabic Maddah Above. The trouble is that though the X positions can be determined precisely with GDI GetCharacterPlacement, the Y position calculations which are derived from OpenType or TrueType font tables and anchors and a complex set of rules, is not available. Ultimately, to generate a PDF with Arabic properly formatted, the Y positions are needed and precisely. Studying Microsoft Word 2013's save as PDF feature, it is clear they have a way of properly gathering this data as studying the PDF details shows that each character is displayed at its precise position including the combining symbols.

WPF可能在GlyphRun类属性GlyphOffsets中包含一些用于执行此操作的函数.DirectWrite具有IDWriteTextAnalyzer接口,该方法GetGlyphPlacements可以返回DWRITE_GLYPH_OFFSETs和许多其他复杂的脚本信息.查看GDI显示和打印机驱动器功能,STROBJ_bEnumPositionsOnly似乎返回了一组带有此信息的GLYPHPOS结构.如果您发送全文进行渲染,则GDI在所有情况下都可以正确地渲染此文本,但是如果您要逐字形地进行渲染,则GDI当然可以正确渲染.

WPF may contain some functions to do this in the GlyphRun class property GlyphOffsets. DirectWrite has the IDWriteTextAnalyzer interface which method GetGlyphPlacements can return DWRITE_GLYPH_OFFSETs and many other complex script information. Looking at the GDI Display and Printer Drive functions, STROBJ_bEnumPositionsOnly seems to return a set of GLYPHPOS structures with this information. GDI certainly renders this correctly under all circumstances if you send the full text to render but not if you want to do it glyph by glyph.

XPS对象模型中的IXpsOMGlyphs允许GetGlyphIndices调用,尽管该库不太适合,但返回一组XPS_GLYPH_INDEX可以提供horizo​​ntalOffset和verticalOffset.

IXpsOMGlyphs in the XPS Object Model allows for a GetGlyphIndices call returning a set of XPS_GLYPH_INDEX gives horizontalOffset and verticalOffset though this library is hardly appropriate.

最后,唯一合适的库看起来是Uniscribe,它使用起来很复杂,但由于Internet Explorer 5和Windows 2000而受到支持,与GDI之外的所有其他讨论(通常是Vista和更高版本或需要特殊依赖项)相反.ScriptItemize返回一个SCRIPT_STRING_ANALYSIS数组,该数组可以传递给ScriptShape,然后ScriptPlace返回一个数组或GOFFSET.实际上,Uniscribe会提供有关断字,变音符号,方向流以及复杂脚本中发生的许多其他方面的信息.我只是想知道是否有一个更简单的方法,或者这是否是执行此任务所需的最低要求,并且确切地说是合适的,因为Uniscribe似乎很难直接从.NET使用,并且合理地需要C ++包装器,因为这有很多优点.大量的结构和指针.

In the end the only appropriate library looks to be Uniscribe which is complex to use but supported since Internet Explorer 5 and Windows 2000 as opposed to all the other discussions beyond GDI which are generally Vista and later or requiring special dependencies. ScriptItemize returns an array of SCRIPT_STRING_ANALYSIS which can be passed to ScriptShape then ScriptPlace returning an array o GOFFSETs. In fact Uniscribe will given information about word breaks, diacritics, directional flow and many other aspects of what is happening in a complex script. I just wanted to know if there is a simpler method or if this is the minimum required and exact appropriate for such a task since Uniscribe appears to be extremely difficult to use directly from .NET and reasonably would need a C++ wrapper since there are a great deal of structures and pointers.

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

Update and answer: Uniscribe will not work for PDF purposes as it uses integers in GDI device units thus the accuracy is greatly compromised. Probably why Microsoft Word 2013 finally supports native PDF conversion support because ultimately DirectWrite seems to be relied upon. As mentioned below I posted both code solutions in .NET as tips on CodeProject. DirectWrite seems to be the only answer beyond designing a custom font shaping and calculation engine.

推荐答案

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

Sample Uniscribe code in .NET since it is not available on the web currently:


     _
    Public Structure GCP_RESULTS
        Public StructSize As UInteger
         _
        Public OutString As String
        Public Order As IntPtr
        Public Dx As IntPtr
        Public CaretPos As IntPtr
        Public [Class] As IntPtr
        Public Glyphs As IntPtr
        Public GlyphCount As UInteger
        Public MaxFit As Integer
    End Structure
     _
    Public Structure SCRIPT_CONTROL
        Public ScriptControlFlags As UInteger
    End Structure
     _
    Public Structure SCRIPT_STATE
        Public ScriptStateFlags As UShort
    End Structure
     _
    Public Structure SCRIPT_ANALYSIS
        Public ScriptAnalysisFlags As UShort
        Public s As SCRIPT_STATE
    End Structure
     _
    Public Structure SCRIPT_VISATTR
        Public ScriptVisAttrFlags As UShort
    End Structure
     _
    Public Structure SCRIPT_ITEM
        Public iCharPos As Integer
        Public a As SCRIPT_ANALYSIS
    End Structure
     _
    Public Structure GOFFSET
        Public du As Integer
        Public dv As Integer
    End Structure
     _
    Public Structure ABC
        Public abcA As Integer
        Public abcB As UInteger
        Public abcC As Integer
    End Structure
    Public Const E_OUTOFMEMORY As Integer = &H8007000E
    Public Const E_PENDING As Integer = &H8000000A
    Public Const USP_E_SCRIPT_NOT_IN_FONT As Integer = &H80040200
     _
    Public Shared Function GetCharacterPlacement(hdc As IntPtr,  lpString As String, nCount As Integer, nMaxExtent As Integer,  ByRef lpResults As GCP_RESULTS, dwFlags As UInteger) As UInteger
    End Function
     _
    Public Shared Function 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
    End Function
     _
    Public Shared Function 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 As Integer) As Integer
    End Function
     _
    Public Shared Function 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) As Integer
    End Function
     _
    Public Shared Function ScriptFreeCache(ByRef psc As IntPtr) As Integer
    End Function
     _
    Public Shared Function GetDC(hWnd As IntPtr) As IntPtr
    End Function
     _
    Public Shared Function ReleaseDC(hWnd As IntPtr, hdc As IntPtr) As Integer
    End Function
     _
    Private Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hObject As IntPtr) As IntPtr
    End Function
    Structure CharPosInfo
        Public Index As Integer
        Public Width As Integer
        Public PriorWidth As Integer
        Public X As Integer
        Public Y As Integer
    End Structure
    Public Shared Function GetWordDiacriticPositions(Str As String, useFont As Font) As CharPosInfo()
        Dim hdc As IntPtr
        Dim CharPosInfos As New List(Of CharPosInfo)
        hdc = GetDC(IntPtr.Zero) 'desktop device context
        Dim oldFont As IntPtr = SelectObject(hdc, useFont.ToHfont())
        Dim MaxItems As Integer = 16
        Dim Control As New SCRIPT_CONTROL With {.ScriptControlFlags = 0}
        Dim State As New SCRIPT_STATE With {.ScriptStateFlags = 1} '0 LTR, 1 RTL
        Dim Items() As SCRIPT_ITEM = Nothing
        Dim ItemCount As Integer
        Dim Result As Integer
        Do
            ReDim Items(MaxItems - 1)
            Result = ScriptItemize(Str, Str.Length, MaxItems, Control, State, Items, ItemCount)
            If Result = 0 Then
                ReDim Preserve Items(ItemCount) 'there is a dummy last item so adding one here
                Exit Do
            ElseIf Result = E_OUTOFMEMORY Then
            End If
            MaxItems *= 2
        Loop While True
        If Result = 0 Then
            'last item is dummy item pointing to end of string
            Dim Cache As IntPtr = IntPtr.Zero
            For Count = 0 To ItemCount - 2
                Dim Logs() As UShort = Nothing
                Dim Glyphs() As UShort = Nothing
                Dim VisAttrs() As SCRIPT_VISATTR = Nothing
                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 Logs(Items(Count + 1).iCharPos - Items(Count).iCharPos - 1)
                Dim dc As IntPtr = IntPtr.Zero
                Do
                    Dim GlyphsUsed As Integer
                    Result = ScriptShape(dc, Cache, Str.Substring(Items(Count).iCharPos), Items(Count + 1).iCharPos - Items(Count).iCharPos, Glyphs.Length, Items(Count).a, Glyphs, Logs, VisAttrs, GlyphsUsed)
                    If Result = 0 Then
                        ReDim Preserve Glyphs(GlyphsUsed - 1)
                        ReDim Preserve VisAttrs(GlyphsUsed - 1)
                        Exit Do
                    ElseIf Result = E_PENDING Then
                        dc = hdc
                    ElseIf Result = E_OUTOFMEMORY Then
                        ReDim Glyphs(Glyphs.Length * 2 - 1)
                        ReDim VisAttrs(VisAttrs.Length * 2 - 1)
                    ElseIf Result = USP_E_SCRIPT_NOT_IN_FONT Then
                    Else
                    End If
                Loop While True
                If Result = 0 Then
                    Dim Advances(Glyphs.Length - 1) As Integer
                    Dim Offsets(Glyphs.Length - 1) As GOFFSET
                    Dim abc As New ABC With {.abcA = 0, .abcB = 0, .abcC = 0}
                    dc = IntPtr.Zero
                    Do
                        Result = ScriptPlace(dc, Cache, Glyphs, Glyphs.Length, VisAttrs, Items(Count).a, Advances, Offsets, abc)
                        If Result  E_PENDING Then Exit Do
                        dc = hdc
                    Loop While True
                    If Result = 0 Then
                        Dim LastPriorWidth As Integer = 0
                        Dim RunStart As Integer = 0
                        For CharCount = 0 To Logs.Length - 1
                            Dim PriorWidth As Integer = 0
                            Dim RunCount As Integer = 0
                            For ResCount As Integer = Logs(CharCount) To If(CharCount = Logs.Length - 1, 0, Logs(CharCount + 1)) Step -1
                                'fDiacritic or fZeroWidth
                                If (VisAttrs(ResCount).ScriptVisAttrFlags And (32 Or 64))  0 Then
                                    CharPosInfos.Add(New CharPosInfo With {.Index = RunStart + RunCount, .PriorWidth = LastPriorWidth, .Width = Advances(ResCount), .X = Offsets(ResCount).du, .Y = Offsets(ResCount).dv})
                                End If
                                If CharCount = Logs.Length - 1 OrElse Logs(CharCount)  Logs(CharCount + 1) Then
                                    PriorWidth += Advances(ResCount)
                                    RunCount += 1
                                End If
                            Next
                            LastPriorWidth += PriorWidth
                            If CharCount = Logs.Length - 1 OrElse Logs(CharCount)  Logs(CharCount + 1) Then
                                RunStart = CharCount + 1
                            End If
                        Next
                    End If
                End If
            Next
            ScriptFreeCache(Cache)
        End If
        SelectObject(hdc, oldFont)
        ReleaseDC(IntPtr.Zero, hdc)
        Return CharPosInfos.ToArray()
    End Function

这篇关于计算Windows中字形的位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆