Как создать набор шрифтов в .NET? - PullRequest
7 голосов
/ 14 июля 2010

У меня есть приложение Silverlight, в которое мне нужно встроить несколько менее распространенных шрифтов. Мне достаточно просто скопировать TTF / OTF и скомпилировать его с моим приложением.Однако во многих случаях используются только 5-10 символов.В других случаях некоторые файлы шрифтов невероятно велики (например, Arial Unicode MS Regular - 22,1 МБ).Быстрое время загрузки моего приложения действительно важно, поэтому оптимизация используемых шрифтов имеет первостепенное значение.

Итак, я думал о том, что я видел в таких приложениях, как Expression Blend, где <Glyph/> используется для создания шрифта только для чтения , и вы также можете просто выбрать встраивание только определенных символов.В других случаях я видел, как люди использовали шрифты, которые содержали только определенные символы, как подмножество полного шрифта (и не использовали <Glyph/> в Silverlight, а просто использовали подмножество .TTF как <FontFamily/>Это то, что мне нужно, за исключением того, что я не использую выражения.

Я не ищу хитрых обходных путей, таких как экспорт в файл XPS и захват файла .odtff.

Существует ли программный способ (.NET / GDI +) для создания подмножества шрифта только с определенными символами и его компиляции в формат .TTF / .OTF?Кроме того, это должно работать и для файлов .TTC.

Ответы [ 5 ]

5 голосов
/ 26 июля 2010

В WPF для шрифтов есть статическое и динамическое связывание.Все это можно определить в Blend.При статическом связывании шрифтов только необходимые символы компилируются и встраиваются в вашу сборку.При динамическом связывании весь набор шрифтов встроен.Поэтому попробуйте установить статическое связывание для выбранных шрифтов и попробуйте, если оно работает.

UPD

Попробуйте добавить следующий код в ваш файл .csproj.Здесь мы в том числе шрифты Tahoma.Свойство AutoFill со значением true говорит о том, что мы будем встраивать в сборку только используемые символы наших элементов управления.Набор символов в теге <Charachters/> заполняет точку для включения этих символов в сборку.Все остальные теги установлены в false, потому что они нам не нужны.

<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" />
5 голосов
/ 26 июля 2010

Нативный API CreateFontPackage может быть тем, что вы ищете.Вы можете передать TTF и список символов для сохранения.Если вы передадите TTFCFP_SUBSET для usSubsetFormat, вы получите рабочий TTF только с этими символами.

Вот поток , который выглядит как код рабочего примера(в Си, к сожалению).

4 голосов
/ 04 июля 2011

Изменение принятого ответа на этот, так как он чистый .NET без внешних ссылок. Использует .NET 4.0:

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
1 голос
/ 19 сентября 2017

Я знаю, что это старый вопрос, но мне было очень трудно использовать CreateFontPackage API из C # (как упоминалось в ответе @ josh3736), поэтому я решил поделиться своим кодом.

Я используюAPI с glyphIndices, вы можете использовать его непосредственно с символами, удалив флаг 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 (наборов шрифтов) вы должны изменить несколько вещей, но, тем не менее, это должно работать.

0 голосов
/ 26 июля 2010

FontForge (http://fontforge.sourceforge.net/) - это редактор шрифтов с открытым исходным кодом, который допускает автоматическое преобразование форматов. Похоже, что это только Python, но его стоит проверить.

...