addFontFile из ресурсов

Я добавил собственный шрифт, используя следующий код:

PrivateFontCollection pfc = new PrivateFontCollection();
pfc.AddFontFile("C:\\Path To\\YourFont.ttf");
label1.Font = new System.Drawing.Font(pfc.Families[0], 16, FontStyle.Regular);

Я добавил файл шрифта в ресурсы. Как мне добавить с addFontFile из ресурсов?

4 ответа

Решение

Если вы включили свой шрифт в ресурсы

Попробуйте эту функцию

private void addfontfrommemory()
{
 Stream fontStream = this.GetType().Assembly.GetManifestResourceStream("yourfont.ttf");

      byte[] fontdata = new byte[fontStream.Length];
      fontStream.Read(fontdata,0,(int)fontStream.Length);
      fontStream.Close();
      unsafe
      {
        fixed(byte * pFontData = fontdata)
        {
          pfc.AddMemoryFont((System.IntPtr)pFontData,fontdata.Length);
        }
      }
    }

отредактированный

Как загрузить ресурс из сборки:(YourNamespace.file.ttf)

Stream fontStream = Assembly.GetExecutingAssembly()
 .GetManifestResourceStream("WindowsFormsApplication1.SBADR.TTF");

Мой обозреватель решений:

private static void AddFontFromResource(PrivateFontCollection privateFontCollection, string fontResourceName)
{
    var fontBytes = GetFontResourceBytes(typeof (App).Assembly, fontResourceName);
    var fontData = Marshal.AllocCoTaskMem(fontBytes.Length);
    Marshal.Copy(fontBytes, 0, fontData, fontBytes.Length);
    privateFontCollection.AddMemoryFont(fontData, fontBytes.Length);
    // Marshal.FreeCoTaskMem(fontData);  Nasty bug alert, read the comment
}

private static byte[] GetFontResourceBytes(Assembly assembly, string fontResourceName)
{
    var resourceStream = assembly.GetManifestResourceStream(fontResourceName);
    if (resourceStream == null)
        throw new Exception(string.Format("Unable to find font '{0}' in embedded resources.", fontResourceName));
    var fontBytes = new byte[resourceStream.Length];
    resourceStream.Read(fontBytes, 0, (int)resourceStream.Length);
    resourceStream.Close();
    return fontBytes;
}

Это способ, которым я делаю это.

Сначала получите файл Font.ttf и, используя Visual Studio, перетащите файл в корневую папку или папку ресурсов.

В обозревателе решений щелкните правой кнопкой мыши файл и выберите Свойства. Выбрать Build Action = Content, Это отобразит файл в файлах приложения в разделе "Свойства проекта"> "Публикация"> "Файлы приложения". Вы увидите, что файл теперь можно выбрать (по умолчанию он автоматически включается).

ClickOnce теперь скопирует файл в StartupPath

Чтобы использовать его, следуйте этому примеру:

PrivateFontCollection pfc = new PrivateFontCollection();

pfc.AddFontFile(Path.Combine(Application.StartupPath, "font_name.ttf"));

textBox1.Font = new Font(pfc.Families[0], 18, FontStyle.Regular);

Предупреждение: если вы цените свое здравомыслие и этого можно избежать, загрузив этот шрифт из файла , не пытайтесь упаковать их как ресурсы. Я говорю вам сейчас: оно того не стоит.1

Ответ

[DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
private static PrivateFontCollection pfc = new PrivateFontCollection();
private static uint cFonts = 0;
private static void AddFont(byte[] fontdata)
{ 
    int fontLength;  System.IntPtr dataPointer;

    //We are going to need a pointer to the font data, so we are generating it here
    dataPointer = Marshal.AllocCoTaskMem(fontdata.Length);
            

    //Copying the fontdata into the memory designated by the pointer
    Marshal.Copy(fontdata, 0, dataPointer, (int)fontdata.Length);

    // Register the font with the system.
    AddFontMemResourceEx(dataPointer, (uint)fontdata.Length, IntPtr.Zero, ref cFonts);

    //Keep track of how many fonts we've added.
    cFonts += 1;

    //Finally, we can actually add the font to our collection
    pfc.AddMemoryFont(dataPointer, (int)fontdata.Length);
}

Хорошо, здесь есть что распаковать, но для тех, кто ищет копирование / вставку, у вас будут плохие времена без следующей строки кода. Его необходимо запустить до создания вашей первой формы и до загрузки вашего первого шрифта:2

Application.SetCompatibleTextRenderingDefault(true);

И вы можете использовать этот код, просто вызвав указанную выше функцию следующим образом:

AddFont(Properties.Resources.Roboto_Light);

Теперь, если у вас возникли проблемы с этим, вам нужно иметь дело с AddFontMemResourceExфункция, потому что ее действительно сложно правильно использовать. В основном эта функция отслеживает шрифты в памяти. Кажется, чтоpfc.AddMemoryFont на самом деле не увеличивается cFontsчто заставляет его молча не загружать каждый шрифт после первого. Этот счетчик необходимо увеличивать на единицу каждый раз при добавлении уникального семейства шрифтов. Уже добавленные варианты семейств шрифтов курсивом и полужирным шрифтом не считаются новыми семействами, аcFonts поэтому не следует увеличивать для них.

Теоретически возвращаемое значение для AddFontMemResourceEx- это указатель, указывающий на расположение ряда шрифтов, хранящихся в настоящее время в памяти. Также кажется невозможным получить реальное число, поэтому я отслеживаю вручную, увеличиваяcFonts. Если вы не можете правильно увеличить cFontsтогда этот метод не сработает. Единственный способ сказать, что это не удалось, - это увидеть, что шрифт загружен неправильно.

Резюме

Это довольно длинный ответ. Это единственное, что я нашел, что работает, но тот факт, что вам нужно перепрыгнуть через это множество обручей, чтобы добавить шрифт из ресурса, абсурден. Я не понимаю, почему функция AddFromMemory так не работает, и я уверен, что, должно быть, что-то делаю неправильно или неправильно читаю, но это все. Это был большой объем работы, и даже сейчас это не самое стабильное решение, когда дело доходит до полужирных и курсивных вариантов шрифтов. Если вы можете объяснить, что здесь происходит и почему это так странно, я хотел бы это услышать. На данный момент это единственный способ, который я знаю.

Сноски

1: Кроме того, это нарушит работу WindowsFormsDesigner. В среде WindowsFormsDesigner невозможно установить для параметра compatibilityTextRenderingDefault значение true, поэтому вы будете постоянно получать ошибки и текст не будет отображаться должным образом. Я решил эту проблему, поместив загрузку шрифта в try{}catch{}, а затем установив уловку, просто пытаясь загрузить с жестко заданного пути на моем собственном жестком диске. Поскольку загрузка из файла не требует ничего подобного, это работает как шарм.

2: Это абсолютно смешно. Теоретически,SetCompatibleTextRenderingDefault()устанавливает метод отрисовки текста по умолчанию, используемый элементами управления. Он определяет, следует ли им использовать устаревшую технику визуализации текста или новую технику визуализации текста. Поскольку (по крайней мере, в моем случае) шрифты загружаются до того, как был сгенерирован даже первый элемент управления, я совершенно не понимаю, почему это на что-то повлияет. И я знаю, что это не старые шрифты или что-то еще, потому что, если я загружу их из файла (который предположительно содержит те же данные), этот параметр не имеет значения. В этом нет никакого смысла.

Это позволит загрузить шрифт в частную коллекцию шрифтов и избежать любых ссылок на объекты и ошибок времени выполнения памяти, которые вы можете увидеть, используя приведенные выше примеры.

Из соображений производительности мы хотели загрузить шрифт только один раз и сохранить ссылки на шрифт для нескольких операций рисования между вызовами. Хитрость заключается в том, чтобы обеспечить PrivateFontCollection не выходит за рамки, если вы сохраняете ссылку на Font объект, который вы создали.

Добавьте несколько статических (общих) переменных

Private Shared _sharedFont As Font
Private Shared _sharedFontCollection As Text.PrivateFontCollection
Private Shared _sharedFontSize As Integer

Затем объявите эти функции

Private Function LoadSharedFont(ByVal fontName As String, ByVal size As Integer, ByVal style As FontStyle) As Font
    'Check if font name or size has changed, then clear cache
    If size <> _sharedFontSize Then _sharedFont = Nothing

    If _sharedFont Is Nothing Then

        'Make this shared so this variable doesnt go out of scope and is garbage collected
        _sharedFontCollection = New Text.PrivateFontCollection()
        _sharedFont = LoadFont(fontName, size, style)
        _sharedFontSize = size
    End If

    Return _sharedFont
End Function

а также

Private Function LoadFont(ByVal fontName As String, ByVal size As Integer, ByVal style As FontStyle) As Font
    Dim executingAssembly As System.Reflection.Assembly = Reflection.Assembly.GetCallingAssembly()
    Dim myNamespace As String = executingAssembly.GetName().Name.ToString()

    Try
        Using fontstream = executingAssembly.GetManifestResourceStream(myNamespace + "." + fontName)
            Dim fontBytes(CInt(fontstream.Length)) As Byte
            fontstream.Read(fontBytes, 0, CInt(fontstream.Length))

            Dim fontData As System.IntPtr = Marshal.AllocCoTaskMem(fontBytes.Length)
            Marshal.Copy(fontBytes, 0, fontData, fontBytes.Length)
            _sharedFontCollection.AddMemoryFont(fontData, fontBytes.Length)
            Marshal.FreeCoTaskMem(fontData)
        End Using

        Return New Font(_sharedFontCollection.Families(0), size, style)

    Catch ex As Exception
        'An unexpected error has occurred so return a default Font just in case.
        Return New Drawing.Font("Arial", size, FontStyle.Regular)
    End Try

End Function

Используйте следующим образом:

Dim font = LoadSharedFont("OpenSans-CondBold.ttf", 12, FontStyle.Bold)
Другие вопросы по тегам