我正在开发一个项目,该项目可以生成包含相当复杂的数学和科学公式的PDF。文本以Times New Roman呈现,它具有非常好的Unicode覆盖范围,但不完整。我们有一个系统来交换一个更unicode完整的字体,用于在TNR中没有字形的代码点(就像大多数“陌生人”的数学符号一样),但我似乎无法找到一种方法来查询* .ttf文件以查看是否存在给定的字形。到目前为止,我只是硬编码了一个存在代码点的查找表,但我更喜欢自动解决方案。
我在ASP.net下的网络系统中使用VB.Net,但任何编程语言/环境中的解决方案都会受到赞赏。
编辑:win32解决方案看起来很棒,但我试图解决的具体情况是在ASP.Net网络系统中。有没有办法在不将Windows API dll包含到我的网站中的情况下执行此操作?
答案 0 :(得分:10)
这是使用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你想要检查和一个Font 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
答案 1 :(得分:1)
FreeType是一个可以读取TrueType字体文件(以及其他文件)的库,可用于查询特定字形的字体。但是,FreeType是为渲染而设计的,因此使用它可能会导致您提供的代码超出此解决方案所需的代码。
不幸的是,即使在OpenType / TrueType字体的世界中,也没有一个明确的解决方案;字符到字形的映射有十几种不同的定义,具体取决于字体的类型和最初设计的平台。您可能会尝试查看Microsoft cmap table definition副本中的OpenType spec,但这并不容易阅读。
答案 2 :(得分: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;
}
答案 3 :(得分:0)
此Microsoft知识库文章可能有所帮助: http://support.microsoft.com/kb/241020
它有点过时了(最初是为Windows 95编写的),但一般原则可能仍然适用。示例代码是C ++,但由于它只是调用标准的Windows API,因此它很可能在.NET语言中工作,并且有一点点肘部油脂。
CNC中 似乎旧的95时代的API已经被微软称为“Uniscribe”的新API淘汰,它应该能够满足您的需求。
答案 4 :(得分:0)
Scott Nichols发布的代码很棒,除了一个错误:如果字形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中的main for循环更改为:
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
答案 5 :(得分:0)
我仅使用VB.Net单元测试就完成了此操作,而没有WIN32 API调用。它包含用于检查特定字符U + 2026(省略号)和U + 2409(HTab)的代码,并且还返回具有字形的字符数(以及低和高值)。我只对Monospace字体感兴趣,但是很容易更改...
if (field.equals("composer")) {//Composer search
Composer composer = new Composer();
String newstring = new StringBuilder(value).reverse().toString();
composer.setComposer(value);
tracks = stub.getByComposer(composer);
}
输出为
M N = Consolas,gtf = Consolas,[x0-xFFFC]#2488,U + 2026。
M N = Courier New,gtf = Courier New,[x20-xFFFC]#3177,U + 2026。
M N = Lucida控制台,gtf = Lucida控制台,[x20-xFB02]#644,U + 2026。
M N = Lucida Sans打字机,gtf = Lucida Sans打字机,[x20-xF002]#240,U + 2026。
M N = MingLiU-ExtB,gtf = MingLiU-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 A扩展,gtf = OCR A扩展,[x20-xF003]#248,U + 2026。
M N = SimSun,gtf = SimSun,[x20-xFFE5]#28737,U + 2026。
M N = SimSun-ExtB,gtf = SimSun-ExtB,[x20-x7F]#96。
M N = Webdings,gtf = Webdings,[x20-xF0FF]#446。