Используйте расширенную типографику с местными шрифтами.

Узнайте, как API локального доступа к шрифтам позволяет получить доступ к локально установленным шрифтам пользователя и получить о них низкоуровневые сведения.

Безопасные веб-шрифты

Если вы достаточно долго занимаетесь веб-разработкой, возможно, вы помните так называемые веб-безопасные шрифты . Известно, что эти шрифты доступны практически во всех экземплярах наиболее часто используемых операционных систем (а именно Windows, macOS, наиболее распространенных дистрибутивов Linux, Android и iOS). В начале 2000-х годов Microsoft даже возглавила инициативу под названием «Основные шрифты TrueType для Интернета» , которая предоставляла эти шрифты для бесплатной загрузки с целью: «Каждый раз, когда вы посещаете веб-сайт, на котором они указаны, вы увидите страницы именно так, как задумал дизайнер сайта». " . Да, сюда входят сайты, созданные в Comic Sans MS . Вот классический стек веб-безопасных шрифтов (с окончательным запасным вариантом любого шрифта sans-serif ), который может выглядеть следующим образом:

body {
  font-family: Helvetica, Arial, sans-serif;
}

Веб-шрифты

Времена, когда веб-безопасные шрифты действительно имели значение, давно прошли. Сегодня у нас есть веб-шрифты , некоторые из которых даже являются вариативными шрифтами , которые мы можем дополнительно настраивать, изменяя значения для различных открытых осей. Вы можете использовать веб-шрифты, объявив блок @font-face в начале CSS, который указывает файлы шрифтов для загрузки:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

После этого вы можете использовать собственный веб-шрифт, указав font-family , как обычно:

body {
  font-family: 'FlamboyantSansSerif';
}

Локальные шрифты как вектор отпечатков пальцев

Большинство веб-шрифтов взяты из Интернета. Однако интересный факт заключается в том, что свойство src в объявлении @font-face , помимо функции url() , также принимает функцию local() . Это позволяет загружать пользовательские шрифты (сюрприз!) локально. Если в операционной системе пользователя установлен FlamboyantSansSerif , будет использоваться локальная копия, а не загружаться:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

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

Приложение macOS Font Book показывает предварительный просмотр шрифта Google Sans.
Шрифт Google Sans установлен на ноутбуке сотрудника Google.

Злоумышленник может попытаться определить, в какой компании кто-то работает, проверив наличие большого количества известных корпоративных шрифтов, таких как Google Sans . Злоумышленник попытается отобразить текст, набор этих шрифтов, на холсте и измерить глифы. Если глифы соответствуют известной форме корпоративного шрифта, злоумышленник попал в цель. Если глифы не совпадают, злоумышленник знает, что использовался заменяющий шрифт по умолчанию, поскольку корпоративный шрифт не был установлен. Полную информацию об этой и других атаках с использованием отпечатков пальцев браузера можно найти в обзоре Laperdix et al.

Помимо фирменных шрифтов, даже список установленных шрифтов может быть идентифицирующим. Ситуация с этим вектором атаки стала настолько плохой, что недавно команда WebKit решила «включать [в список доступных шрифтов] только веб-шрифты и шрифты, поставляемые вместе с операционной системой, но не шрифты, устанавливаемые локально пользователем» . (И вот я со статьей о предоставлении доступа к локальным шрифтам.)

API доступа к локальным шрифтам

Начало этой статьи, возможно, поставило вас в негативное настроение. Можем ли мы действительно не иметь хороших вещей? Не волнуйтесь. Мы думаем, что сможем, и, возможно, все не безнадежно . Но сначала позвольте мне ответить на вопрос, который вы, возможно, задаете себе.

Зачем нам нужен API локального доступа к шрифтам, если есть веб-шрифты?

Профессиональные инструменты дизайна и графики исторически было сложно разместить в Интернете. Камнем преткновения была невозможность получить доступ и использовать все разнообразие профессионально созданных шрифтов с подсказками, которые дизайнеры установили на месте. Веб-шрифты позволяют использовать некоторые варианты использования при публикации, но не обеспечивают программный доступ к векторным формам глифов и таблицам шрифтов, используемым растеризаторами для визуализации контуров глифов. Также нет способа получить доступ к двоичным данным веб-шрифта.

  • Инструментам дизайна необходим доступ к байтам шрифта, чтобы реализовать собственную реализацию макета OpenType и позволить инструментам дизайна подключаться на более низких уровнях для таких действий, как выполнение векторных фильтров или преобразований фигур глифов.
  • Разработчики могут иметь устаревшие стеки шрифтов для своих приложений, которые они размещают в Интернете. Чтобы использовать эти стеки, им обычно требуется прямой доступ к данным шрифтов, чего веб-шрифты не предоставляют.
  • Некоторые шрифты могут не иметь лицензии на доставку через Интернет. Например, у Linotype есть лицензия на некоторые шрифты, которая включает использование только на настольных компьютерах .

API Local Font Access — это попытка решить эти проблемы. Он состоит из двух частей:

  • API перечисления шрифтов , который позволяет пользователям предоставлять доступ к полному набору доступных системных шрифтов.
  • Из каждого результата перечисления появляется возможность запрашивать низкоуровневый (байтовый) доступ к контейнеру SFNT , включающий полные данные шрифта.

Поддержка браузера

Поддержка браузера

  • Хром: 103.
  • Край: 103.
  • Firefox: не поддерживается.
  • Сафари: не поддерживается.

Источник

Как использовать API локального доступа к шрифтам

Обнаружение функций

Чтобы проверить, поддерживается ли API локального доступа к шрифтам, используйте:

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}

Перечисление локальных шрифтов

Чтобы получить список локально установленных шрифтов, вам нужно вызвать window.queryLocalFonts() . В первый раз это вызовет запрос на разрешение, которое пользователь может одобрить или отклонить. Если пользователь разрешает запрашивать свои локальные шрифты, браузер вернет массив с данными шрифтов, которые вы можете просмотреть в цикле. Каждый шрифт представлен как объект FontData с family свойств (например, "Comic Sans MS" ), fullName (например, "Comic Sans MS" ), postscriptName (например, "ComicSansMS" ) и style (например, , "Regular" ).

// Query for all available fonts and log metadata.
try {
  const availableFonts = await window.queryLocalFonts();
  for (const fontData of availableFonts) {
    console.log(fontData.postscriptName);
    console.log(fontData.fullName);
    console.log(fontData.family);
    console.log(fontData.style);
  }
} catch (err) {
  console.error(err.name, err.message);
}

Если вас интересует только подмножество шрифтов, вы также можете фильтровать их по именам PostScript, добавив параметр postscriptNames .

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

Доступ к данным SFNT

Полный доступ к SFNT доступен через метод blob() объекта FontData . SFNT — это формат файла шрифта, который может содержать другие шрифты, такие как PostScript, TrueType, OpenType, шрифты Web Open Font Format (WOFF) и другие.

try {
  const availableFonts = await window.queryLocalFonts({
    postscriptNames: ['ComicSansMS'],
  });
  for (const fontData of availableFonts) {
    // `blob()` returns a Blob containing valid and complete
    // SFNT-wrapped font data.
    const sfnt = await fontData.blob();
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    const sfntVersion = await sfnt.slice(0, 4).text();

    let outlineFormat = 'UNKNOWN';
    switch (sfntVersion) {
      case '\x00\x01\x00\x00':
      case 'true':
      case 'typ1':
        outlineFormat = 'truetype';
        break;
      case 'OTTO':
        outlineFormat = 'cff';
        break;
    }
    console.log('Outline format:', outlineFormat);
  }
} catch (err) {
  console.error(err.name, err.message);
}

Демо

Вы можете увидеть API локального доступа к шрифтам в действии в демо ниже. Обязательно ознакомьтесь также с исходным кодом . В демо демонстрируется пользовательский элемент <font-select> , реализующий локальный выбор шрифта.

Соображения конфиденциальности

Разрешение "local-fonts" , по-видимому, обеспечивает поверхность с высокой степенью отпечатков пальцев. Однако браузеры могут возвращать все, что захотят. Например, браузеры, ориентированные на анонимность, могут предоставить только набор шрифтов по умолчанию, встроенных в браузер. Аналогично, браузеры не обязаны предоставлять данные таблицы точно так, как они отображаются на диске.

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

Безопасность и разрешения

Команда Chrome разработала и реализовала API локального доступа к шрифтам, используя основные принципы, определенные в разделе «Управление доступом к мощным функциям веб-платформы» , включая пользовательский контроль, прозрачность и эргономику.

Пользовательский контроль

Доступ к шрифтам пользователя полностью находится под их контролем и не будет разрешен, пока не будет предоставлено разрешение "local-fonts" , указанное в реестре разрешений .

Прозрачность

Был ли сайту предоставлен доступ к локальным шрифтам пользователя, будет видно на информационной странице сайта .

Сохранение разрешений

Разрешение "local-fonts" будет сохраняться между перезагрузками страницы. Его можно отозвать через информационный листок сайта .

Обратная связь

Команда Chrome хочет услышать о вашем опыте работы с API локального доступа к шрифтам.

Расскажите нам о дизайне API

Что-то в API работает не так, как вы ожидали? Или вам не хватает методов или свойств, необходимых для реализации вашей идеи? У вас есть вопрос или комментарий по модели безопасности? Сообщите о проблеме спецификации в соответствующем репозитории GitHub или добавьте свои мысли к существующей проблеме.

Сообщить о проблеме с реализацией

Вы нашли ошибку в реализации Chrome? Или реализация отличается от спецификации? Сообщите об ошибке на сайте new.crbug.com . Обязательно укажите как можно больше подробностей, простые инструкции по воспроизведению и введите Blink>Storage>FontAccess в поле «Компоненты» . Glitch отлично подходит для быстрого и простого обмена репродукциями.

Показать поддержку API

Планируете ли вы использовать API локального доступа к шрифтам? Ваша публичная поддержка помогает команде Chrome расставлять приоритеты для функций и показывает другим поставщикам браузеров, насколько важно их поддерживать.

Отправьте твит @ChromiumDev, используя хэштег #LocalFontAccess , и сообщите нам, где и как вы его используете.

Благодарности

Спецификацию Local Font Access API редактировали Эмиль А. Эклунд , Алекс Рассел , Джошуа Белл и Оливье Йиптонг . Эта статья была рецензирована Джо Медли , Домиником Ретчесом и Оливье Йиптонгом . Изображение героя Бретта Джордана на Unsplash .