Is there a way to programmatically determine if a font file has a specific Unicode Glyph?

前端 未结 6 928
花落未央
花落未央 2020-11-27 04:19

I\'m working on a project that generates PDFs that can contain fairly complex math and science formulas. The text is rendered in Times New Roman, which has pretty good Unic

6条回答
  •  忘掉有多难
    2020-11-27 04:59

    Here's a pass at it using c# and the 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 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 fontRanges = new List();
        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 ranges = GetUnicodeRangesForFont(font);
        bool isCharacterPresent = false;
        foreach (FontRange range in ranges)
        {
            if (intval >= range.Low && intval <= range.High)
            {
                isCharacterPresent = true;
                break;
            }
        }
        return isCharacterPresent;
    }
    

    Then, given a char toCheck that you want to check and a Font theFont to test it against...

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

    Same code using VB.Net

     _
    Public Shared Function GetFontUnicodeRanges(ByVal hds As IntPtr, ByVal lpgs As IntPtr) As UInteger
    End Function  
    
     _
    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  
    

提交回复
热议问题