asp.net - tipografia - ¿Hay alguna forma de determinar mediante programación si un archivo de fuente tiene un glifo Unicode específico?
fuentes de letras bonitas (5)
Aquí hay un pase utilizando c # y la API de Windows.
[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;
}
Luego, dado un carácter a Comprobar que quieres verificar y una Fuente de Font para probarlo en contra ...
if (!CheckIfCharInFont(toCheck, theFont) {
// not present
}
Mismo código usando 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
Estoy trabajando en un proyecto que genera archivos PDF que pueden contener fórmulas bastante complejas de matemáticas y ciencias. El texto se representa en Times New Roman, que tiene una cobertura Unicode bastante buena, pero no completa. Tenemos un sistema para intercambiar una fuente completa más Unicode para los puntos de código que no tienen un glifo en TNR (como la mayoría de los símbolos matemáticos "desconocidos"), pero parece que no puedo encontrar una manera de consultar el archivo * .ttf para ver si un glifo dado está presente. Hasta ahora, he codificado una tabla de búsqueda de los puntos de código que están presentes, pero preferiría una solución automática.
Estoy usando VB.Net en un sistema web bajo ASP.net, pero las soluciones en cualquier lenguaje de programación serán apreciadas.
Editar: La solución win32 se ve excelente, pero el caso específico que estoy tratando de resolver está en un sistema web ASP.Net. ¿Hay alguna manera de hacer esto sin incluir los DLL de Windows API en mi sitio web?
El código publicado por Scott Nichols es excelente, excepto por un error: si el id del glifo es mayor que Int16.MaxValue, arroja una OverflowException. Para solucionarlo, agregué la siguiente función:
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
Y luego cambió el bucle principal por en la función GetUnicodeRangesForFont para que se vea así:
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
Este artículo de Microsoft KB puede ayudar: http://support.microsoft.com/kb/241020
Está un poco anticuado (fue escrito originalmente para Windows 95), pero el principio general aún puede aplicarse. El código de muestra es C ++, pero dado que solo llama a las API estándar de Windows, lo más probable es que funcione también en los lenguajes .NET con un poco de esfuerzo.
-Editar- Parece que las antiguas API de 95-era han sido obsoletas por una nueva API que Microsoft llama " Uniscribe ", que debería poder hacer lo que usted necesita.
La respuesta de Scott es buena. Aquí hay otro enfoque que probablemente sea más rápido si se comprueban solo un par de cadenas por fuente (en nuestro caso 1 cadena por fuente). Pero probablemente sea más lento si está usando una fuente para verificar toneladas de texto.
[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;
}
FreeType es una biblioteca que puede leer archivos de fuentes TrueType (entre otros) y se puede utilizar para consultar la fuente de un glifo específico. Sin embargo, FreeType está diseñado para renderizar, por lo que su uso podría hacer que extraigas más código del que necesitas para esta solución.
Desafortunadamente, no existe realmente una solución clara incluso dentro del mundo de las fuentes OpenType / TrueType; la asignación de carácter a glifo tiene alrededor de una docena de definiciones diferentes según el tipo de fuente y para qué plataforma se diseñó originalmente. Puede intentar ver la definición de la tabla cmap en la copia de Microsoft de la especificación OpenType , pero no es exactamente fácil de leer.