vb.net - 如何正确渲染嵌入的字体?
问题描述
我下载了一个 True Type 字体,并按照本页的说明嵌入了它。
我必须设置UseCompatibleTextRendering
属性才能加载它,但它看起来很奇怪,我不知道为什么它在浏览器中看起来不错,但在应用程序中却没有。
为了清楚起见,我将字体添加到我的资源中,将其设置为嵌入式资源,我使用了这个模块:
Imports System.IO
Imports System.Reflection
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Module ExternalFontType
Public Function GetFont(aAssembly As Assembly,
strFontName As String, intFontSize As Integer,
fsFontStyle As FontStyle) As Font
Using pcolFonts As New PrivateFontCollection
Dim bFont() As Byte = ExternalFontType.bRawFontData(aAssembly, strFontName)
Dim ptrMemFont As IntPtr =
Marshal.AllocCoTaskMem(bFont.Length)
Marshal.Copy(bFont, 0, ptrMemFont, bFont.Length)
pcolFonts.AddMemoryFont(ptrMemFont, bFont.Length)
Marshal.FreeCoTaskMem(ptrMemFont)
Return New Font(pcolFonts.Families(0),
intFontSize, fsFontStyle)
End Using
End Function
Private Function bRawFontData(aAssembly As Assembly, strFontName As String) As Byte()
Using stFont As Stream =
aAssembly.GetManifestResourceStream(strFontName)
If (stFont Is Nothing) Then Throw _
New Exception(String.Format("Cannot load _
font '{0}'", strFontName))
Dim bFontBuffer() As Byte = New _
Byte(CInt(stFont.Length - 1)) {}
stFont.Read(bFontBuffer, 0, CInt(stFont.Length))
Return bFontBuffer
End Using
End Function
End Module
并将其包含在此代码中
lbl.UseCompatibleTextRendering = True
lbl.Font = ExternalFontType.GetFont(Me.GetType.Assembly, "ProyectName.FontName.ttf", 15, FontStyle.Bold)
解决方案
该代码不止一个问题:
PrivateFontCollection不能用声明声明:只要需要它指向的字体,就必须保留这个集合。
Using
它通常在使用它的类(表单)或共享类(或模块,此处)中声明为字段,然后在不再需要时将其丢弃。Marshal.FreeCoTaskMem()
不能在这里使用;在 之后调用它 是一种诱惑Marshal.AllocCoTaskMem()
,但不是在这种情况下。这可能(将)损害字体数据分配。您需要做的是处理该PrivateFontcollection
对象。框架将处理 COM事务(即使您忘记处置PrivateFontcollection
对象,它也会为您完成。不过,您应该尽量不要忘记)。不需要程序集引用:字体作为字节数组添加到项目的资源中,这就是所需要的。然后可以通过名称检索它,例如,
My.Resources.SomeFontName
或使用ResourceManager.GetObject()方法,将返回的对象转换为Byte()
:Dim fontData As Byte() = My.Resources.SomeFontName Dim fontData As Byte() = DirectCast(My.Resources.ResourceManager.GetObject("SomeFontName"), Byte())
▶ 您已经提到了这一点,但让我们再说一遍:并非所有控件都可以使用这些字体。只有可以使用 GDI+ 绘制的 Fonts 的控件才能真正使用来自 的 Fonts PrivateFontCollection
,Label 和 Button 控件就是其中之一,实际上它们都暴露了UseCompatibleTextRendering属性。例如,RichTextBox 不能。
- 如果字体创建正确,您可以使用
Graphics.DrawString()
该字体绘制字符串内容,即使您无法将其设置为控件的字体。
Private myFontCollection As PrivateFontCollection = New PrivateFontCollection()
在表单的构造器中,从项目的资源中添加字体。
这里我使用了一个辅助类,
FontManager
它公开了一个public shared
方法AddFontsFromResource()
:传递给这个方法PrivateFontCollection
和一个与字体名称相对应的资源名称列表。
该方法用可以成功安装的字体填充集合,并返回已安装的字体数量。
当然,您可以使用您喜欢的任何其他方法来引用您的字体。注意。在示例中,将三个 Font 资源添加到集合中:
{"FontFamily1Regular", "FontFamily1Italics", "OtherFontFamily"}
但两个属于同一个FontFamily
,因此PrivateFontCollection
将只包含两个元素,而不是三个。
Public Sub New()
Dim installedFontsCount = FontManager.AddFontsFromResources(myFontCollection,
{"FontFamily1Regular", "FontFamily1Italics", "OtherFontFamily"})
' The Font can set here or anywhere else
someLabel.UseCompatibleTextRendering = True
someLabel.Font = New Font(myFontCollection.Families(0), 10.5F, FontStyle.Regular)
someButton.UseCompatibleTextRendering = True
someButton.Font = New Font(myFontCollection.Families(0), 10.5F, FontStyle.Italic)
End Sub
PrivateFontCollection
当不再需要它时处理它很重要:当初始化它的表单关闭或应用程序关闭之前:
您还可以使用共享对象来引用PrivateFontCollection
可在项目中的任何地方使用的 a。在这种情况下,需要在应用程序关闭时处理该集合。
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
myFontCollection.Dispose()
End Sub
助手类:
Imports System.Drawing.Text
Imports System.Runtime.InteropServices
Public Class FontManager
Public Shared Function AddFontsFromResources(fontCollection As PrivateFontCollection, fontNames As String()) As Integer
If fontNames.Length = 0 Then Return Nothing
Dim installedFontsCount = 0
For Each fontName As String In fontNames
Try
Dim fontData As Byte() = CType(My.Resources.ResourceManager.GetObject(fontName), Byte())
If fontData Is Nothing Then Throw New InvalidOperationException()
Dim data As IntPtr = Marshal.AllocCoTaskMem(fontData.Length)
Marshal.Copy(fontData, 0, data, fontData.Length)
fontCollection.AddMemoryFont(data, fontData.Length)
installedFontsCount += 1
Catch ex As Exception
' Placeholder: Notify User/Log/Whatever
Debug.Print($"Font installation failed for {fontName}")
End Try
Next
Return installedFontsCount
End Function
End Class
C#版本:
using System.Drawing.Text;
using System.Runtime.InteropServices;
public static int AddFontsFromResources(PrivateFontCollection fontCollection, string[] fontNames)
{
int installedFontsCount = 0;
if (fontNames.Length == 0) return 0;
foreach (string fontName in fontNames) {
try {
byte[] fontData = (byte[])Properties.Resources.ResourceManager.GetObject(fontName);
var data = Marshal.AllocCoTaskMem(fontData.Length);
Marshal.Copy(fontData, 0, data, fontData.Length);
fontCollection.AddMemoryFont(data, fontData.Length);
installedFontsCount += 1;
}
catch (Exception) {
// Placeholder: Notify User/Log/Whatever
Console.WriteLine($"Font installation failed for {fontName}");
}
}
return installedFontsCount;
}
推荐阅读
- java - 在java中显示字符串的字符串数组
- javascript - 尝试为 setTimeout 淡入和淡出文本时遇到问题
- javascript - 如何使用 HTML 和 Node JS 将数据插入 MySQL?
- node.js - 从 WSS 连接(节点 js)接收数据时使用超时
- featuretools - 了解特征工具中的 PercentTrue 原始输出
- javascript - 显示/隐藏选项卡箭头以在溢出时导航其他选项卡
- reactjs - 使用 React Router 获取 TypeError
- java - Android被捡了多少次?
- c# - 如何设置 ffmpeg 管道输出?
- symfony - Symfony 遍历 ArrayCollection