有没有一种方法以编程方式确定是否字体文件具有特定的Unicode雕文?有没有一种方法以编程方式确定是

2019-05-13 17:07发布

我正在生成,它可以包含相当复杂的数学和科学公式的PDF文件的项目。 该文本是宋体,其中有相当不错的Unicode的覆盖,但不完整的呈现。 我们有一个系统,在代码点的多个Unicode完整的字体没有字形的TNR交换(像极了“陌生人”的数学符号,),但我似乎无法找到一种方法来查询看到* .TTF文件如果给定的标志符号出现。 到目前为止,我只是硬编码的查询表,其中的代码点是存在的,但我更喜欢一个自动解决方案。

我在ASP.net下一个网络系统使用VB.Net,但在任何编程语言/环境的解决方案,将不胜感激。

编辑:在Win32解决方案看起来很好,但我试图解决的特定情况下是一个ASP.Net Web系统。 有没有办法做到这一点,而不包括Windows API的dll文件到我的网站?

Answer 1:

下面是使用C#和Windows API的它一通。

[DllImport("gdi32.dll")]
public static extern uint GetFontUnicodeRanges(IntPtr hdc, IntPtr lpgs);

[DllImport("gdi32.dll")]
public extern static IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

public struct FontRange
{
    public UInt16 Low;
    public UInt16 High;
}

public List<FontRange> GetUnicodeRangesForFont(Font font)
{
    Graphics g = Graphics.FromHwnd(IntPtr.Zero);
    IntPtr hdc = g.GetHdc();
    IntPtr hFont = font.ToHfont();
    IntPtr old = SelectObject(hdc, hFont);
    uint size = GetFontUnicodeRanges(hdc, IntPtr.Zero);
    IntPtr glyphSet = Marshal.AllocHGlobal((int)size);
    GetFontUnicodeRanges(hdc, glyphSet);
    List<FontRange> fontRanges = new List<FontRange>();
    int count = Marshal.ReadInt32(glyphSet, 12);
    for (int i = 0; i < count; i++)
    {
        FontRange range = new FontRange();
        range.Low = (UInt16)Marshal.ReadInt16(glyphSet, 16 + i * 4);
        range.High = (UInt16)(range.Low + Marshal.ReadInt16(glyphSet, 18 + i * 4) - 1);
        fontRanges.Add(range);
    }
    SelectObject(hdc, old);
    Marshal.FreeHGlobal(glyphSet);
    g.ReleaseHdc(hdc);
    g.Dispose();
    return fontRanges;
}

public bool CheckIfCharInFont(char character, Font font)
{
    UInt16 intval = Convert.ToUInt16(character);
    List<FontRange> ranges = GetUnicodeRangesForFont(font);
    bool isCharacterPresent = false;
    foreach (FontRange range in ranges)
    {
        if (intval >= range.Low && intval <= range.High)
        {
            isCharacterPresent = true;
            break;
        }
    }
    return isCharacterPresent;
}

然后,因为要检查一个char toCheck和字体theFont来测试它反对...

if (!CheckIfCharInFont(toCheck, theFont) {
    // not present
}

使用相同的代码VB.Net

<DllImport("gdi32.dll")> _
Public Shared Function GetFontUnicodeRanges(ByVal hds As IntPtr, ByVal lpgs As IntPtr) As UInteger
End Function  

<DllImport("gdi32.dll")> _
Public Shared Function SelectObject(ByVal hDc As IntPtr, ByVal hObject As IntPtr) As IntPtr
End Function  

Public Structure FontRange
    Public Low As UInt16
    Public High As UInt16
End Structure  

Public Function GetUnicodeRangesForFont(ByVal font As Font) As List(Of FontRange)
    Dim g As Graphics
    Dim hdc, hFont, old, glyphSet As IntPtr
    Dim size As UInteger
    Dim fontRanges As List(Of FontRange)
    Dim count As Integer

    g = Graphics.FromHwnd(IntPtr.Zero)
    hdc = g.GetHdc()
    hFont = font.ToHfont()
    old = SelectObject(hdc, hFont)
    size = GetFontUnicodeRanges(hdc, IntPtr.Zero)
    glyphSet = Marshal.AllocHGlobal(CInt(size))
    GetFontUnicodeRanges(hdc, glyphSet)
    fontRanges = New List(Of FontRange)
    count = Marshal.ReadInt32(glyphSet, 12)

    For i = 0 To count - 1
        Dim range As FontRange = New FontRange
        range.Low = Marshal.ReadInt16(glyphSet, 16 + (i * 4))
        range.High = range.Low + Marshal.ReadInt16(glyphSet, 18 + (i * 4)) - 1
        fontRanges.Add(range)
    Next

    SelectObject(hdc, old)
    Marshal.FreeHGlobal(glyphSet)
    g.ReleaseHdc(hdc)
    g.Dispose()

    Return fontRanges
End Function  

Public Function CheckIfCharInFont(ByVal character As Char, ByVal font As Font) As Boolean
    Dim intval As UInt16 = Convert.ToUInt16(character)
    Dim ranges As List(Of FontRange) = GetUnicodeRangesForFont(font)
    Dim isCharacterPresent As Boolean = False

    For Each range In ranges
        If intval >= range.Low And intval <= range.High Then
            isCharacterPresent = True
            Exit For
        End If
    Next range
    Return isCharacterPresent
End Function  


Answer 2:

FreeType的是,可以读取TrueType字体文件(等等),并且可以用于查询的字体特定字形库。 然而,FreeType的是专为渲染,因此使用它可能会导致你在更多的代码比拉你需要为这个解决方案。

不幸的是,这不是真的连中的OpenType / TrueType字体的世界有一个清醒的解决方案; 字符到字形映射大约有十几个不同的定义取决于字体类型和什么平台最初设计的。 你可以尝试一下CMAP表定义在微软的拷贝OpenType字体规范 ,但它不太容易阅读。



Answer 3:

斯科特的回答是好。 这是另一种方法,这种方法可能更快,如果检查每种字体(在我们每个字体的情况下1串)只是一对夫妇的字符串。 但是,如果你正在使用一种字体,检查一吨的文本可能较慢。

    [DllImport("gdi32.dll", EntryPoint = "CreateDC", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CreateDC(string lpszDriver, string lpszDeviceName, string lpszOutput, IntPtr devMode);

    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool DeleteDC(IntPtr hdc);

    [DllImport("Gdi32.dll")]
    private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

    [DllImport("Gdi32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetGlyphIndices(IntPtr hdc, [MarshalAs(UnmanagedType.LPWStr)] string lpstr, int c,
                                              Int16[] pgi, int fl);

    /// <summary>
    /// Returns true if the passed in string can be displayed using the passed in fontname. It checks the font to 
    /// see if it has glyphs for all the chars in the string.
    /// </summary>
    /// <param name="fontName">The name of the font to check.</param>
    /// <param name="text">The text to check for glyphs of.</param>
    /// <returns></returns>
    public static bool CanDisplayString(string fontName, string text)
    {
        try
        {
            IntPtr hdc = CreateDC("DISPLAY", null, null, IntPtr.Zero);
            if (hdc != IntPtr.Zero)
            {
                using (Font font = new Font(new FontFamily(fontName), 12, FontStyle.Regular, GraphicsUnit.Point))
                {
                    SelectObject(hdc, font.ToHfont());
                    int count = text.Length;
                    Int16[] rtcode = new Int16[count];
                    GetGlyphIndices(hdc, text, count, rtcode, 0xffff);
                    DeleteDC(hdc);

                    foreach (Int16 code in rtcode)
                        if (code == 0)
                            return false;
                }
            }
        }
        catch (Exception)
        {
            // nada - return true
            Trap.trap();
        }
        return true;
    }


Answer 4:

此Microsoft知识库文章可以帮助: http://support.microsoft.com/kb/241020

这是日(最初是为Windows 95的)一点,但总的原则仍可能适用。 示例代码是C ++,但由于它只是调用标准的Windows API,它会超过在.NET语言可能工作,以及一点点苦劳。

CNC中看来,老95年代的API已经被新的API,微软称之为“过时的Uniscribe ”,它应该能够做到你需要什么。



Answer 5:

发表斯科特·尼科尔斯的代码是伟大的,但有一个缺陷:如果字形ID比Int16.MaxValue更大,它抛出一个OverflowException异常。 为了解决这个问题,我增加了以下功能:

Protected Function Unsign(ByVal Input As Int16) As UInt16
    If Input > -1 Then
        Return CType(Input, UInt16)
    Else
        Return UInt16.MaxValue - (Not Input)
    End If
End Function

然后改变主要在功能GetUnicodeRangesForFont看起来像这样循环:

For i As Integer = 0 To count - 1
    Dim range As FontRange = New FontRange
    range.Low = Unsign(Marshal.ReadInt16(glyphSet, 16 + (i * 4)))
    range.High = range.Low + Unsign(Marshal.ReadInt16(glyphSet, 18 + (i * 4)) - 1)
    fontRanges.Add(range)
Next


Answer 6:

我曾经只是一个VB.Net单元测试,并没有WIN32 API调用来完成这一点。 它包括代码以检查特定的字符U + 2026(省略号)U + 2409(HTAB),并且还返回具有字形字符(和低值和高值)的#。 我只是在等宽字体感兴趣,但很容易的改变......

    Dim fnt As System.Drawing.Font, size_M As Drawing.Size, size_i As Drawing.Size, size_HTab As Drawing.Size, isMonospace As Boolean
    Dim ifc = New Drawing.Text.InstalledFontCollection
    Dim bm As Drawing.Bitmap = New Drawing.Bitmap(640, 64), gr = Drawing.Graphics.FromImage(bm)
    Dim tf As Windows.Media.Typeface, gtf As Windows.Media.GlyphTypeface = Nothing, ok As Boolean, gtfName = ""

    For Each item In ifc.Families
        'TestContext_WriteTimedLine($"N={item.Name}.")
        fnt = New Drawing.Font(item.Name, 24.0)
        Assert.IsNotNull(fnt)

        tf = New Windows.Media.Typeface(item.Name)
        Assert.IsNotNull(tf, $"item.Name={item.Name}")

        size_M = System.Windows.Forms.TextRenderer.MeasureText("M", fnt)
        size_i = System.Windows.Forms.TextRenderer.MeasureText("i", fnt)
        size_HTab = System.Windows.Forms.TextRenderer.MeasureText(ChrW(&H2409), fnt)
        isMonospace = size_M.Width = size_i.Width
        Assert.AreEqual(size_M.Height, size_i.Height, $"fnt={fnt.Name}")

        If isMonospace Then

            gtfName = "-"
            ok = tf.TryGetGlyphTypeface(gtf)
            If ok Then
                Assert.AreEqual(True, ok, $"item.Name={item.Name}")
                Assert.IsNotNull(gtf, $"item.Name={item.Name}")
                gtfName = $"{gtf.FamilyNames(Globalization.CultureInfo.CurrentUICulture)}"

                Assert.AreEqual(True, gtf.CharacterToGlyphMap().ContainsKey(AscW("M")), $"item.Name={item.Name}")
                Assert.AreEqual(True, gtf.CharacterToGlyphMap().ContainsKey(AscW("i")), $"item.Name={item.Name}")

                Dim t = 0, nMin = &HFFFF, nMax = 0
                For n = 0 To &HFFFF
                    If gtf.CharacterToGlyphMap().ContainsKey(n) Then
                        If n < nMin Then nMin = n
                        If n > nMax Then nMax = n
                        t += 1
                    End If
                Next
                gtfName &= $",[x{nMin:X}-x{nMax:X}]#{t}"

                ok = gtf.CharacterToGlyphMap().ContainsKey(AscW(ChrW(&H2409)))
                If ok Then
                    gtfName &= ",U+2409"
                End If
                ok = gtf.CharacterToGlyphMap().ContainsKey(AscW(ChrW(&H2026)))
                If ok Then
                    gtfName &= ",U+2026"
                End If
            End If

            Debug.WriteLine($"{IIf(isMonospace, "*M*", "")} N={fnt.Name}, gtf={gtfName}.")
            gr.Clear(Drawing.Color.White)
            gr.DrawString($"Mi{ChrW(&H2409)} {fnt.Name}", fnt, New Drawing.SolidBrush(Drawing.Color.Black), 10, 10)
            bm.Save($"{fnt.Name}_MiHT.bmp")
        End If
    Next

输出是

M×N个=索拉,GTF =索拉,[X0-xFFFC]#2488,U + 2026。

M×N个=信使新建,GTF =信使新建,[X20-xFFFC]#3177,U + 2026。

M×N个=龙力控制台,GTF =龙力控制台,[X20-xFB02]#644,U + 2026。

M×N个=龙力三世打字机,GTF =龙力三世打字机,[X20-xF002]#240,U + 2026。

M×N个=名流-EXTB,GTF =名流-EXTB,[X0-x2122]#212。

M×N个= MingLiU_HKSCS-EXTB,GTF = MingLiU_HKSCS-EXTB,[X0-x2122]#212。

M×N个= MS哥特,GTF = MS哥特式,[X0-xFFEE]#15760,U + 2026。

M×N个= NSimSun,GTF = NSimSun,[X20-xFFE5]#28737,U + 2026。

M×N个= OCR甲扩展,GTF = OCR甲扩展,[X20-xF003]#248,U + 2026。

M×N个=宋体,GTF =宋体,[X20-xFFE5]#28737,U + 2026。

M×N个=宋体-EXTB,GTF =宋体-EXTB,[X20-0x7F部分]#96。

M×N个= Webdings,GTF = Webdings,[X20-xF0FF]#446。



文章来源: Is there a way to programmatically determine if a font file has a specific Unicode Glyph?