代码之家  ›  专栏  ›  技术社区  ›  Todd Main

如何在.NET中创建子集字体?

  •  7
  • Todd Main  · 技术社区  · 14 年前

    我有一个Silverlight应用程序,需要在其中嵌入一些不常用的字体。对于我来说,只需复制TTF/OTF并用我的应用程序编译就足够简单了。然而,在许多情况下,实际使用的字符只有5-10个。在其他情况下,有些字体文件非常大( Arial Unicode MS常规 以22.1 MB为例)。我的应用程序的快速下载时间非常重要,因此优化使用的字体至关重要。

    所以,我想的是,我在表达式混合等应用程序中看到了 <Glyph/> is used to create a read-only font 你也可以选择只嵌入某些字符。在其他情况下,我看到人们使用只包含某些字符的字体作为完整字体的子集(而不使用 <字形/gt; 在Silverlight中,但只使用子集.ttf作为 <FontFamily/> 这正是我所追求的,只是我不使用表达式。

    我并不是在寻找偷偷摸摸的解决方法,比如导出到xps文件并获取.odtff文件。

    有没有一种编程方法(.net/gdi+)来创建只有特定字符的字体子集,并将其编译为.ttf/.otf?此外,这也需要适用于.ttc文件。

    5 回复  |  直到 7 年前
        1
  •  5
  •   josh3736    14 年前

    本地API CreateFontPackage 可能是你想要的。您可以传递TTF和要保留的字符列表。如果你通过 TTFCFP_SUBSET 对于 usSubsetFormat 然后,您将得到一个只包含这些字符的有效TTF。

    Here's a thread 使用一个工作示例的代码(不幸的是,在C中)。

        2
  •  5
  •   Eugene Cheverda    14 年前

    在字体的WPF中有静态和动态链接。所有这些都可以在混合中定义。对于字体的静态链接,只编译和嵌入所需的字符。通过动态链接,嵌入所有字体集。因此,请尝试为所选字体设置静态链接,并尝试是否有效。

    UPD

    尝试向您添加以下代码 .csproj 文件。这里我们包括了Tahoma字体。autofill属性设置为true表示我们将嵌入控件的仅用于程序集的字符。一组字符 <Charachters/> 标记填充点以将这些字符包含到程序集中。所有其他标签都设置为假,因为我们不需要它们。

    <ItemGroup>
        <BlendEmbeddedFont Include="Fonts\tahoma.ttf">
          <IsSystemFont>True</IsSystemFont>
          <All>False</All>
          <AutoFill>True</AutoFill>
          <Characters>dasf</Characters>
          <Uppercase>False</Uppercase>
          <Lowercase>False</Lowercase>
          <Numbers>False</Numbers>
          <Punctuation>False</Punctuation>
        </BlendEmbeddedFont>
        <BlendEmbeddedFont Include="Fonts\tahomabd.ttf">
          <IsSystemFont>True</IsSystemFont>
          <All>False</All>
          <AutoFill>True</AutoFill>
          <Characters>dasf</Characters>
          <Uppercase>False</Uppercase>
          <Lowercase>False</Lowercase>
          <Numbers>False</Numbers>
          <Punctuation>False</Punctuation>
        </BlendEmbeddedFont>
      </ItemGroup>
      <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    
      <Import Project="$(MSBuildExtensionsPath)\Microsoft\Expression\Blend\3.0\WPF\Microsoft.Expression.Blend.WPF.targets" />
    
        3
  •  4
  •   Todd Main    13 年前

    更改此问题的已接受答案,因为它是纯.NET,没有外部引用。使用.NET 4:

    Imports System.Windows.Media
    Imports System.Text.Encoding
    Imports System.Collections
    
    Public Sub CreateSubSet(sourceText As String, fontURI As Uri)
        Dim gt As FontEmbeddingManager = New FontEmbeddingManager
    
        Dim glyphTypeface As GlyphTypeface = New GlyphTypeface(fontURI)
        Dim Index As Generic.ICollection(Of UShort)
        Index = New Generic.List(Of UShort)
        Dim sourceTextBytes As Byte() = Unicode.GetBytes(sourceText)
        Dim sourceTextChars As Char() = Unicode.GetChars(sourceTextBytes)
        Dim sourceTextCharVal As Integer
        Dim glyphIndex As Integer
        For sourceTextCharPos = 0 To UBound(sourceTextChars)
            sourceTextCharVal = AscW(sourceTextChars(sourceTextCharPos))
            glyphIndex = glyphTypeface.CharacterToGlyphMap(sourceTextCharVal)
            Index.Add(glyphIndex)
        Next
        Dim filebytes() As Byte = glyphTypeface.ComputeSubset(Index)
        Using fileStream As New System.IO.FileStream("C:\Users\Me\new-subset.ttf", System.IO.FileMode.Create)
            fileStream.Write(filebytes, 0, filebytes.Length)
        End Using
    End Sub
    
        4
  •  1
  •   aghidini    7 年前

    我知道这是个老问题,但我发现很难使用 CreateFontPackage 来自C(如@josh3736的答案所述)的API,所以我想共享我的代码。

    我将API与glyphIndexs一起使用,您可以通过删除 TTFCFP_FLAGS_GLYPHLIST 旗帜。

    这是我的代码:

    public byte[] CreateSubset(byte[] inputData, IEnumerable<ushort> glyphIndices)
    {
        AllocProc allocProc = Marshal.AllocHGlobal;
        ReallocProc reallocProc = (p, c) =>
            p == IntPtr.Zero
                ? Marshal.AllocHGlobal(c)
                : Marshal.ReAllocHGlobal(p, c);
        FreeProc freeProc = Marshal.FreeHGlobal;
    
        var resultCode = CreateFontPackage(
            inputData, (uint) inputData.Length,
            out var bufferPtr,
            out _,
            out var bytesWritten,
            TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST,
            0,
            TTFMFP_SUBSET,
            0,
            TTFCFP_MS_PLATFORMID,
            TTFCFP_UNICODE_CHAR_SET,
            glyphIndices,
            (ushort)glyphIndices.Length,
            allocProc, reallocProc, freeProc, (IntPtr)0);
    
        if (resultCode != 0 || bufferPtr == IntPtr.Zero)
        {
            return null;
        }
    
        try
        {
            var buffer = new byte[bytesWritten];
            Marshal.Copy(bufferPtr, buffer, 0, buffer.Length);
            return buffer;
        }
        finally
        {
            freeProc(bufferPtr);
        }
    }
    
    internal const ushort TTFCFP_FLAGS_SUBSET = 0x0001;
    internal const ushort TTFCFP_FLAGS_COMPRESS = 0x0002;
    internal const ushort TTFCFP_FLAGS_TTC = 0x0004;
    internal const ushort TTFCFP_FLAGS_GLYPHLIST = 0x0008;
    
    internal const ushort TTFMFP_SUBSET = 0x0000;
    
    internal const ushort TTFCFP_UNICODE_PLATFORMID = 0x0000;
    internal const ushort TTFCFP_MS_PLATFORMID = 0x0003;
    
    internal const ushort TTFCFP_UNICODE_CHAR_SET = 0x0001;
    
    [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    private delegate IntPtr AllocProc(Int32 size);
    
    [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    private delegate IntPtr ReallocProc(IntPtr memBlock, IntPtr size);
    
    [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
    private delegate void FreeProc(IntPtr memBlock);
    
    [DllImport("FontSub.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
    private static extern uint CreateFontPackage(
        [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]
        byte[] puchSrcBuffer,
        uint ulSrcBufferSize,
        out IntPtr puchFontPackageBufferPtr,
        out uint pulFontPackageBufferSize,
        out uint pulBytesWritten,
        ushort usFlags,
        ushort usTtcIndex,
        ushort usSubsetFormat,
        ushort usSubsetLanguage,
        ushort usSubsetPlatform,
        ushort usSubsetEncoding,
        [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 12)]
        ushort[] pusSubsetKeepList,
        ushort usSubsetKeepListCount,
        AllocProc lpfnAllocate,
        ReallocProc lpfnReAllocate,
        FreeProc lpfnFree,
        IntPtr lpvReserved
    );
    

    我只在TTF文件中使用代码,对于TTC(字体集合),您必须更改一些内容,但是它仍然可以工作。

        5
  •  0
  •   NakedBrunch    14 年前

    FontForge http://fontforge.sourceforge.net/ )是一个允许自动格式转换的开源字体编辑器。看起来它只是Python,但可能值得一看。