计算 Windows 中字形的位置

2023-12-19

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

WPF 可能包含一些在 GlyphRun 类属性 GlyphOffsets 中执行此操作的函数。 DirectWrite 具有 IDWriteTextAnalyzer 接口,该接口的 GetGlyphPlacements 方法可以返回 DWRITE_GLYPH_OFFSET 和许多其他复杂的脚本信息。查看 GDI 显示和打印机驱动函数,STROBJ_bEnumPositionsOnly 似乎返回一组包含此信息的 GLYPHPOS 结构。如果您发送全文进行渲染,那么 GDI 在所有情况下肯定都能正确渲染此内容,但如果您想逐个字形地进行渲染,则不会。

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

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

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


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



     _
    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
  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

计算 Windows 中字形的位置 的相关文章

  • log4net 仅在调用 XmlConfigurator.Configure() 时起作用

    我明白那个this https stackoverflow com questions 445976 log4net config in external file does not work 1479343 1479343 questio
  • 即使在 GC Collect 和 WaitForPendingFinalizers 之后,窗口对象在关闭后也未释放?

    这是一个简单的测试应用程序 可帮助了解 WPF 内存使用情况 我想了解的关键是为什么MainWindow即使在关闭并等待 GC 完成之后 仍然被引用并且它的内存没有被释放 参见下面的代码清单 文本 MainWindow Finalizer
  • 字典 API(词汇)[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有人知道一个好的 NET 字典 API 吗 我对含义不感兴趣 而是我需要能够以多种不同的方式查询单词 返
  • 并行应用程序的可变与不可变[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 在我正在编写的应用程序中 我需要编写大量基本类型 这些类型很可能是不可变的 但我想知道并行应用程序中可变类型与不可变类型的比较如何 您可以对可变
  • 运行 t4 脚本作为 resx 文件的自定义工具

    我有一个资源文件MyResource resx 我想改变MyResource Designer cs文件生成 我有一个 t4 脚本 它接受 resx 文件作为输入并给出结果转换 但是 我必须手动运行此 t4 才能使其工作 我看到 resx
  • 如何将智能感知添加到我的应用程序中?

    我们的一款产品拥有一种专有的宏语言 通过我们的 Windows 软件进行编辑 我想添加智能感知 但我不知道如何去做 至少不完全重新发明轮子 是否有任何示例代码或第 3 方包至少可以让我开始 它不一定是免费的 该应用程序使用 NET 用 C
  • 解决找不到程序集的问题 |文件未找到异常 |融合日志

    我正在尝试将我的解决方案包 wsp 部署到 SharePoint 2007 环境 WSP 包含一个功能 该功能加载功能接收器类以在运行时部署计时器作业 在部署此 WSP 时 我不断得到 特征 fb631f6c 2c46 4ab5 b7b3
  • 使用 apache PDF-Box 插入 PDF 附件的缩略图

    我有一个代码可以将文件附加到 PDF 文件 PDDocument doc new PDDocument PDPage page new PDPage doc addPage page read attachment file File fi
  • 如何获取Winforms窗体标题栏高度的大小?

    因此 如果它是工具窗口或可最小化的表单 我希望能够以编程方式获取其高度 这可能吗 如果是这样怎么办 您可以使用以下方法确定工具窗口和普通表单的标题栏高度 Rectangle screenRectangle this RectangleToS
  • 如何从 Web API 应用程序返回 PDF

    我有一个在服务器上运行的 Web API 项目 它应该从两种不同类型的源返回 PDF 实际的可移植文档文件 PDF 和存储在数据库中的 base64 字符串 我遇到的问题是将文档发送回客户端 MVC 应用程序 剩下的部分是关于所发生的一切以
  • 调整图像的亮度、对比度和伽玛值

    在 NET 中调整图像的亮度 对比度和伽玛值的简单方法是什么 c and gdi have a simple way to control the colors that are drawn It s basically a ColorMa
  • C# - 方法必须有返回类型

    我在调用 C 中的方法时遇到问题 不断收到消息 方法 计算 必须有返回类型 using System Diagnostics namespace WindowsFormsApplication1 public partial class F
  • 如何使用间隙分隔pdf中的段落?

    我有一些 pdf 文件 每页有 2 3 个段落 每个段落都由一些行间隙分隔 但是在使用 pymupdf 阅读时 我看不到段落之间有任何机器可打印的分隔符 还有其他方法吗 其他图书馆可以做到这一点吗 code import fitz from
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 枚举扩展方法

    在vs2008中 是否可以编写适用于任何枚举的扩展方法 我知道您可以针对特定枚举编写扩展方法 但我希望能够使用单个扩展方法对每个枚举进行处理 这可能吗 是的 只需针对基础进行编码Enum类型 例如 public static void So
  • 引用的程序集自动由 Visual Studio 替换

    我有 2 个项目 一个可移植类库和一个常规单元测试项目 在可移植类库中 我使用 NuGet 来引用 Microsoft BCL 可移植包 它附带 2 个程序集 System Threading Tasks dll and System Ru
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet

随机推荐