Существует ли способ программно определить, имеет ли файл шрифтов определенный Глиф Unicode?

Я работаю над проектом, который генерирует PDFs, который может содержать довольно сложную математику и научные формулы. Текст представляется в Times New Roman, который имеет довольно хорошее покрытие Unicode, но не завершенный. Мы имеем в распоряжении систему для загрузки большего количества Unicode полный шрифт для кодовых точек, которые не имеют глифа в TNR (как большинство "более странных" математических символов), но я, может казаться, не нахожу способ запросить *.ttf файл, чтобы видеть, присутствует ли данный глиф. До сих пор я только что трудно кодировал справочная таблица, которой присутствуют кодовые точки, но я очень предпочел бы автоматическое решение.

Я использую VB.Net в веб-системе в соответствии с ASP.net, но решения на любом языке программирования / среда ценились бы.

Править: win32 решение выглядит превосходным, но конкретный случай, который я пытаюсь решить, находится в веб-системе ASP.NET. Существует ли способ сделать это без включения окон API DLLs в мой веб-сайт?

31
задан Jaymin 18 October 2019 в 09:00
поделиться

4 ответа

Эта статья Microsoft KB может помочь: http://support.microsoft.com/kb/241020

Это немного датировано (был первоначально записан для Windows 95), но общий принцип может все еще применяться. Примером кода является C++, но так как он просто называет стандартный Windows APIs, он будет больше, чем, вероятно, работать на языках.NET также с небольшой тяжелой работой.

- Редактирование кажется, что старые API с 95 эрами были obsoleted новым API вызовы Microsoft" Uniscribe", который должен смочь сделать то, к чему Вы нуждаетесь в нем.

0
ответ дан 27 November 2019 в 22:51
поделиться

Вот передача в нем с помощью c# и 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;
}

Затем учитывая символ 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  
10
ответ дан 27 November 2019 в 22:51
поделиться

FreeType является библиотекой, которая может считать файлы шрифтов TrueType (среди других) и может использоваться для запросов шрифта для определенного глифа. Однако FreeType разработан для рендеринга, так использования, он мог бы заставить Вас вытягивать в большем количестве кода, чем Вам нужно для этого решения.

, К сожалению, нет действительно ясного решения даже в мире OpenType / шрифты TrueType; отображение символа к глифу имеет приблизительно дюжину различных определений в зависимости от типа шрифта и для какой платформы это было первоначально разработано. Вы могли бы попытаться посмотреть cmap определение таблицы в копии Microsoft спецификация OpenType, но это не точно легкое чтение.

1
ответ дан 27 November 2019 в 22:51
поделиться

Код, опубликованный Скоттом Николсом, великолепен, за исключением одной ошибки: если идентификатор глифа больше 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

А затем изменил основной цикл for в функции 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
0
ответ дан 27 November 2019 в 22:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: