Шрифты в Mapbox

В Mapbox GL JS текстовые элементы являются частью слоя типа symbol, где отображение подписей, меток и аннотаций полностью зависит от шрифтов, описанных в стиле карты. В отличие от DOM-ориентированных библиотек, где текст управляется через HTML/CSS, здесь используется векторная модель с предварительно сгенерированными глифами и их подгрузкой через специализированный endpoint.

Ключевой принцип: текст не рендерится браузером напрямую — он компонуется из набора глифов (glyphs), которые заранее запрашиваются и кэшируются движком рендеринга.


Архитектура шрифтов в Mapbox GL JS

Система шрифтов состоит из нескольких уровней:

1. Font Stack (стек шрифтов) Определяет приоритетный список гарнитур, например:

"text-font": ["Roboto Regular", "Noto Sans Regular"]

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

2. Glyph Atlas (атлас глифов) Глифы запрашиваются как PBF-тайлы и представляют собой упакованные векторные данные символов.

3. Glyphs Endpoint В стиле указывается источник:

"glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf"

Определение текста в Symbol Layer

Текст задаётся через свойство layout.text-field:

map.addLayer({
  id: "city-labels",
  type: "symbol",
  source: "cities",
  layout: {
    "text-field": "{name}",
    "text-font": ["Open Sans Regular"],
    "text-size": 14
  }
});

Ключевые параметры:

  • text-field — шаблон строки или выражение
  • text-font — массив шрифтов (font stack)
  • text-size — размер в пикселях
  • text-letter-spacing — межбуквенное расстояние
  • text-line-height — высота строки

Font Stack и fallback-механизм

Font stack в Mapbox GL JS — это не просто список шрифтов, а система приоритетов для рендеринга глифов.

Пример:

"text-font": [
  "DIN Offc Pro Medium",
  "Arial Unicode MS Regular"
]

Поведение:

  • если символ есть в первом шрифте — используется он
  • если отсутствует — поиск продолжается далее
  • если символ не найден ни в одном шрифте — отображается “tofu” (□)

Практическая структура стека:

  • латиница: Roboto, Open Sans
  • кириллица: Noto Sans, Arial Unicode MS
  • CJK: Noto Sans CJK

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

Mapbox GL JS не загружает TTF/OTF напрямую в браузере. Используются подготовленные наборы глифов.

Основные способы добавления:

1. Mapbox Studio Fonts

При использовании стилей Mapbox Studio шрифты автоматически доступны через:

mapbox://fonts/{username}/{fontstack}/{range}.pbf

2. Собственные glyphs endpoint

При self-hosted стиле необходимо указать:

"glyphs": "https://example.com/fonts/{fontstack}/{range}.pbf"

Сервер обязан отдавать:

  • диапазоны Unicode (например 0–255, 256–511)
  • бинарные PBF-глифы

Ограничения формата шрифтов

В Mapbox GL JS отсутствует поддержка:

  • динамической загрузки TTF без конвертации
  • CSS @font-face
  • вариативных шрифтов (variable fonts)
  • SVG-шрифтов

Причина — необходимость предгенерации глифов для WebGL-рендеринга.


Unicode и многоязычность

Поддержка языков реализуется через комбинирование font stack и корректных glyph ranges.

Проблема:

Один шрифт редко покрывает все Unicode диапазоны.

Решение:

Использование комбинированных стеков:

"text-font": [
  "Noto Sans Regular",
  "Noto Sans Arabic Regular",
  "Noto Sans CJK JP Regular"
]

Рендеринг текста в WebGL

Mapbox GL JS использует SDF (Signed Distance Field) шрифты.

Преимущества SDF:

  • масштабирование без потери качества
  • сглаживание на GPU
  • единый формат для разных размеров

Процесс:

  1. загрузка glyph atlas
  2. разбиение текста на символы
  3. поиск глифов в атласе
  4. рендеринг через шейдер

Управление стилем текста

Базовые параметры layout:

layout: {
  "text-field": "{title}",
  "text-font": ["Open Sans Bold"],
  "text-size": 16,
  "text-transform": "uppercase",
  "text-allow-overlap": false
}

Дополнительные свойства:

  • text-anchor — привязка (center, top, bottom)
  • text-offset — смещение
  • text-rotate — поворот
  • text-max-width — перенос строк
  • text-justify — выравнивание

Динамические выражения в text-field

Mapbox GL JS поддерживает expression syntax:

"text-field": ["concat", ["get", "name"], " ", ["get", "type"]]

Возможности:

  • конкатенация строк
  • условная логика
  • форматирование чисел
  • выбор локализованных полей

Оптимизация загрузки шрифтов

Производительность зависит от:

1. Размер glyph ranges

Чем меньше диапазон — тем быстрее загрузка.

2. Кэширование

Glyph tiles кешируются браузером и WebGL контекстом.

3. Font stack минимизация

Каждый дополнительный шрифт увеличивает количество запросов.

4. Ограничение символов

Использование text-max-width и фильтрации текста уменьшает нагрузку.


Проблемы и особенности

Разрыв символов

Если glyph отсутствует:

  • символ пропускается
  • отображается fallback glyph

Неполная кириллица

Некоторые западные шрифты не содержат кириллицу, что приводит к частичному fallback.

Переполнение атласа

При большом количестве уникальных символов возможно увеличение числа запросов к glyph server.


Шрифты и слои символов

Text rendering тесно связан с icon rendering:

layout: {
  "icon-image": "marker",
  "text-field": "{label}",
  "text-font": ["Roboto Regular"],
  "text-offset": [0, 1.2]
}

Комбинация:

  • icon + text формирует единый symbol layer
  • применяется общий collision detection
  • учитывается при label placement

Локализация и языковые настройки

Для автоматического выбора языка используется:

"text-field": ["get", "name:en"]

или fallback:

"text-field": ["coalesce", ["get", "name:en"], ["get", "name"]]

Поведение при зуме

Размер текста может зависеть от масштаба:

"text-size": [
  "interpolate",
  ["linear"],
  ["zoom"],
  5, 10,
  10, 16,
  15, 22
]

Это позволяет:

  • уменьшать плотность подписей на малых масштабах
  • увеличивать читаемость при приближении

Glyph server и кастомные решения

В продакшн-архитектурах часто используется собственный glyph server:

  • генерация через fontnik
  • хранение в CDN
  • предварительная нарезка диапазонов Unicode

Это позволяет:

  • контролировать размер шрифтов
  • добавлять корпоративные гарнитуры
  • оптимизировать глобальную доставку

Производственные практики

  • использование Noto Sans как универсального fallback
  • ограничение числа font stacks до 2–3
  • минимизация уникальных символов в данных
  • контроль Unicode coverage при выборе шрифтов
  • предварительная генерация glyph tiles для всех языков проекта