Файлы шрифтов в локальном хранилище в WinRT

124

Элемент Glyphs чаще всего встречается в документах, создаваемых с использованием формата XPS (XML Paper Specification), разработанного компанией Microsoft в связи с WPF. XPS — формат документов с фиксированными страницами. Иначе говоря, все страницы документа имеют фиксированный размер и макет, как в известном формате Adobe PDF (Portable Document Format).

Файл с документом XPS по сути представляет собой ZIP-архив с файлами шрифтов, растровыми изображениями и отдельными файлами для каждой страницы документа. Каждая страница документа определяется файлом XAML с корневым элементом FixedPage (класс, не определенный в Windows Runtime), который обычно содержит элементы Path для отображения графики в форме объектов ImageBrush и элементы Glyphs для отображения текста. У элементов Glyphs атрибуту FontUri задается URI-адрес файла шрифта в пакете XPS. Все свойства элементов Glyphs уже заданы для правильного размещения текста на странице.

Программа WPF может отобразить файл XPS без особых трудностей. Программе Windows 8 для этого придется проделать более значительную работу. Она должна открыть пакет XPS и разобрать отдельные файлы FixedPage. Для каждой страницы программа должна создать различные объекты Path, ImageBrush и Glyphs в коде, не забывая о возможной компенсации всех возможностей XPS, не поддерживаемых в Windows Runtime.

В пакете XPS URI-адреса элементов Glyphs и ImageBrush ссылаются на файлы шрифтов и растровых изображений в пакете. Эти файлы необходимо скопировать в локальное хранилище приложения и внести соответствующие изменения в URI.

Программа PrivateFonts демонстрирует практическую применимость этого процесса. При первом запуске программы в третьем столбце текст выводится шрифтом по умолчанию Windows 8 (вместо закрытых шрифтов), а четвертый столбец отсутствует. При последующих запусках программы третий и четвертый столбцы совпадают с первыми двумя:

Сохранение шрифтов в локальном хранилище

Различия объясняются особенностями структуры программы. При первом запуске файлы шрифтов копируются в локальное хранилище приложения:

using System;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace PrivateFonts
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            Loaded += OnLoaded;
        }

        private async void OnLoaded(object sender, RoutedEventArgs e)
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            bool folderExists = false;

            try
            {
                StorageFolder fontsFolder = await localFolder.GetFolderAsync("Fonts");
                folderExists = true;
            }
            catch (Exception)
            {
            }

            if (!folderExists)
            {
                StorageFolder fontsFolder = await localFolder.CreateFolderAsync("Fonts");

                string[] fonts = { "Kooten.ttf", "Linds.ttf", "Miramo.ttf", "Miramob.ttf", 
                                   "Peric.ttf", "pericl.ttf", "Pesca.ttf", "Pescab.ttf" };

                foreach (string font in fonts)
                {
                    // Копирование из контента приложения в IBuffer
                    string uri = "ms-appx:///Fonts/" + font;
                    IBuffer buffer = await PathIO.ReadBufferAsync(uri);

                    // Копирование из IBuffer в локальное хранилище
                    StorageFile fontFile = await fontsFolder.CreateFileAsync(font);
                    await FileIO.WriteBufferAsync(fontFile, buffer);
                }
            }
        }
    }
}

Обработчик Loaded проверяет, существует ли в локальном хранилище каталог с именем Fonts. Если каталог не существует, он создается, после чего все файлы шрифтов копируются в него без изменения имен. (Если бы эту операцию можно было выполнить синхронно, я бы сделал это в конструкторе до вызова InitializeComponent, чтобы файлы были доступны для парсера XAML при первом выполнении программы.)

Разметка ссылок на файлы локального хранилища почти не отличается от разметки ссылок на контент программы, если не считать использования префикса ms-appdata, а также префикса local перед каталогом Fonts. Конечно, приводить всю разметку не обязательно — вы быстро поймете общий принцип:

<Page ... FontSize="36">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        
        ...

        <StackPanel Grid.Column="2">
            <TextBlock Text="Kootenay"
                       FontFamily="ms-appdata:///local/Fonts/Kooten.ttf#Kootenay" />

            <TextBlock Text="Lindsey"
                       FontFamily="ms-appdata:///local/Fonts/Linds.ttf#Lindsey" />

            <TextBlock Text="Miramonte"
                       FontFamily="ms-appdata:///local/Fonts/Miramo.ttf#Miramonte" />

            <TextBlock Text="Miramonte Bold"
                       FontFamily="ms-appdata:///local/Fonts/Miramob.ttf#Miramonte" />

            <TextBlock Text="Pericles"
                       FontFamily="ms-appdata:///local/Fonts/Peric.ttf#Pericles" />

            <TextBlock Text="Pericles Light"
                       FontFamily="ms-appdata:///local/Fonts/Pericl.ttf#Pericles" />

            <TextBlock Text="Pescadero"
                       FontFamily="ms-appdata:///local/Fonts/Pesca.ttf#Pescadero" />

            <TextBlock Text="Pescadero Bold"
                       FontFamily="ms-appdata:///local/Fonts/Pescab.ttf#Pescadero" />

            <TextBlock Text="Pescadero Bold*"
                       FontFamily="ms-appdata:///local/Fonts/Pesca.ttf#Pescadero"
                       FontWeight="Bold" />

            <TextBlock Text="Pescadero Italic*"
                       FontFamily="ms-appdata:///local/Fonts/Pesca.ttf#Pescadero"
                       FontStyle="Italic" />
        </StackPanel>

        <Grid Grid.Column="3">
            <Grid.Resources>
                <Style TargetType="Glyphs">
                    <Setter Property="FontRenderingEmSize" Value="36" />
                    <Setter Property="Fill" Value="Black" />
                    <Setter Property="OriginX" Value="0" />
                </Style>
            </Grid.Resources>
            <Glyphs UnicodeString="Kootenay"
                    FontUri="ms-appdata:///local/Fonts/Kooten.ttf"
                    OriginY="45" />

            <Glyphs UnicodeString="Lindsey"
                    FontUri="ms-appdata:///local/Fonts/Linds.ttf"
                    OriginY="90" />

            <Glyphs UnicodeString="Miramonte"
                    FontUri="ms-appdata:///local/Fonts/Miramo.ttf"
                    OriginY="135" />

            <Glyphs UnicodeString="Miramonte Bold"
                    FontUri="ms-appdata:///local/Fonts/Miramob.ttf"
                    OriginY="180" />

            <Glyphs UnicodeString="Pericles"
                    FontUri="ms-appdata:///local/Fonts/Peric.ttf"
                    OriginY="225" />

            <Glyphs UnicodeString="Pericles Light"
                    FontUri="ms-appdata:///local/Fonts/Pericl.ttf"
                    OriginY="270" />

            <Glyphs UnicodeString="Pescadero"
                    FontUri="ms-appdata:///local/Fonts/Pesca.ttf"
                    OriginY="315" />

            <Glyphs UnicodeString="Pescadero Bold"
                    FontUri="ms-appdata:///local/Fonts/Pescab.ttf"
                    OriginY="360" />

            <Glyphs UnicodeString="Pescadero Bold*"
                    FontUri="ms-appdata:///local/Fonts/Pesca.ttf"
                    StyleSimulations="BoldSimulation"
                    OriginY="405" />

            <Glyphs UnicodeString="Pescadero Italic*"
                    FontUri="ms-appdata:///local/Fonts/Pesca.ttf"
                    StyleSimulations="ItalicSimulation"
                    OriginY="450" />
        </Grid>
        
        <TextBlock Text="* искусственый"
                   Grid.ColumnSpan="4"
                   VerticalAlignment="Bottom"
                   HorizontalAlignment="Center" />
    </Grid>
</Page>

Из всех сложных форматов документов, которые могут отображаться в Windows 8, XPS безусловно является самым простым, потому что его содержимое похоже на элементы Windows Runtime, страницы уже построены, а вся графика и элементы Glyphs точно позиционированы в этих страницах. Куда больше проблем создают форматы со свободными страницами (такие, как EPUB), в которых за позиционирование слов на странице отвечает программа. Для этой работы программист должен намного лучше разбираться в метриках шрифтов.

Упрощенный способ позиционирования основан на вызове Measure() для элемента TextBlock и получении его ширины и высоты. Это позволит определить местонахождение отдельных слов в абзаце, позиции разрывов строк и разбиения абзацев между страницами. Но если потребуется выровнять несколько элементов TextBlock с разными размерами или семействами шрифтов по базовой линии, без дополнительной информации вам не обойтись.

Возможно, на этой стадии вам придется либо проанализировать внутреннее строение файлов шрифтов для извлечения метрик, либо воспользоваться средствами DirectWrite. А когда вы начнете пользоваться DirectWrite для получения метрик шрифтов, вполне возможно, вы также решите, что эта технология является лучшим инструментом для формирования макета страниц.

Пройди тесты
Лучший чат для C# программистов