ИКФ: высота строки, часть 1 (3-я публикация цикла «Тайны CSS2.1″)

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

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

Высота лайн-бокса

Первым делом давайте всё-таки уточним, из чего же в итоге вычисляется общая высота лайн-бокса. Общее правило таково: высота строки берётся с расчётом, чтобы заведомо вместить самый высокий ее элемент. Чем бы этот элемент ни оказался — текстом, картинкой, инлайн-блоком и т.п.

Высота картинок и инлайн-блоков определяется их свойством height (min-height) или внутренней высотой (высотой контента для инлайн-блоков). Высота обычных инлайн-боксов с текстом определяется значением свойства line-height (присвоенного инлайн-элементу или унаследованному им), а если line-height не задано — то от метрик шрифта (также заданного самому элементу или унаследованному). Анонимные инлайн-боксы получают значения line-height и свойства шрифта от своего блочного контейнера.

Высота строки также может зависеть от вертикального выравнивания ('vertical-align'инлайн-боксов внутри строки. Возможны варианты, когда итоговая высота строки будет даже больше наибольшей из высот ее элементов. Рассмотрению этих вариантов, и этого свойства в целом, мы посвятим третью часть этого цикла.

Если в строке нет ни одного инлайн-бокса (текстового или "неделимого", типа картинки), то всё вышеперечисленное для её высоты не играет никакой роли, так как в таком случае лайн-бокс будет иметь нулевую высоту. В связи с этим, предлагаю начать наши познания с обычного текста. Поехали…

Высота текста

Надо сказать, что высота текстового лайн-бокса высчитывается за счёт размера шрифта. Но дело в том, что не так всё просто, и здесь есть много нюансов. Поэтому предлагаю начать немного издалека и плавно подойти к сути.

Любой символ шрифта в вебе представлен прямоугольной площадкой определенного размера и пропорций. Основной размер шрифта определяется его кеглем (он задается свойством font-size), а пропорции — сколько тысячных долей кегля будут выступать над базовой линией, сколько будет "свисать" под нее, и сколько составит ширина каждого символа — определяется свойствами самого шрифта, его метриками. Как правило, ширина площадки у каждого символа своя (только в моноширинных шрифтах площадки разных символов в ширину одинаковы), а вертикальные метрики общие для всех символов и характеризуют весь шрифт целиком. Например, для буквы "A" в шрифте Ubuntu Titling Bold площадка будет на 0,75 кегля выступать над базовой линией, на 0,25 свисать вниз под нее и в ширину займет 0,592 кегля — т.е. ее габариты составят 1,000×0,592 кегля. Метрики шрифта хранятся где-то в файле шрифта, детали зависят от его формата и операционной системы (напр., для шрифтов TrueType в Windows высота определяется метриками usWinAscent и usWinDescent). Но, давайте по порядку.

Площадка

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

На изображении видно, что, несмотря на то, что символы разных шрифтов различаются начертанием, насыщенностью, величиной очка буквы и пропорциями, высота площадок при одинаковом кегле (в данном примере он равен 100px) у них одна и та же. Причем даже если бы некоторые символы были верхними индексами — высота их кегельных площадок осталась бы той же самой, равной высоте соседних площадок.

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

Чтобы была видна разница, приведу наглядный пример:

Размер шрифта (кегль) по-прежнему один и тот же у всех символов (те же 100px), но площадки символов разных шрифтов, различающихся по начертанию и пропорциям, теперь тоже оказались разными. По сравнению с предыдущим примером разница налицо, не правда ли?

Как определяется высота каждой площадки?

Прежде чем выяснить этот вопрос, я хотел бы познакомить вас с очень важной деталью в построении текста, которая имеет название "Базовая линия"

Базовая линия

Базовая линия (baseline) для шрифта — что-то вроде ватерлинии для корабля: она делит кегельные площадки на "надводную" и "подводную" части, и служит основой для выравнивания всех инлайн-боксов по умолчанию, независимо от размеров и соотношения этих частей.

Выглядит это примерно так:

Красная линия на изображении и есть та самая базовая линия, о которой идёт речь. И, как можно видеть, несмотря на различность в высоте площадок, все символы "сидят" прямо на ней. Обратите внимание на первую букву "g". Её нижняя часть выпирает из базовой линии. Это — нижний выносной элемент буквы.

Если присмотреться повнимательнее, то можно увидеть, что между выносным элементом и краем площадки есть еще некоторое пустое пространство. Аналогичное пространство есть и под верхним краем площадки. Это — заплечики. Они нужны для того, чтобы при построении лайн-боксов тексты из разных строк не слипались друг с другом.

А теперь вернёмся к нашим баранам.

Итак, узнав одну из главных тайн про базовую линию, разобраться с остальным будет намного легче. Поэтому вернёмся к метрикам, о которых я упомянул вначале рассказа.

Две похожие вертикальные метрики берут на себя вычисление двух частей самой площадки. Первая метрика вычисляет верхнюю часть, до базовой линии, а вторая нижнюю, после неё. Например, если брать такие метрики, как usWinAscent и usWinDescent, то первая (usWinAscent) будет вычислять высоту над базовой линией, а вторая (usWinDescent) — под.

Ради любопытства мы решили разобрать шрифт в формате svg (где каждая литера описана, как определённая кривая), чтобы посмотреть лежащие на виду метрики. Мы воспользовались генератором с fontsquirrel.com, создающим для каждого шрифта набор файлов, включая svg, и прогнали через него вот этот шрифт. Для каждой литеры указана авансовая ширина (сдвиг координаты следующего символа), а для всего шрифта указаны ascent и descent вот в таком виде: <font-face units-per-em="2048" ascent="1638" descent="-410" />. Именно эти параметры используются для деления площадок на "над базовой" / "под базовой". Над базовой получается 1638/2048, а под ней — 410/2048.

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

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

В общем мы выяснили, что высота площадки складывается из следующих вещей: Верхние заплечики + высота символа с запасом над базовой линией + высота символа с запасом под базовой линией + нижние заплечики.

Но всё-таки, какова же высота строки в итоге?

Поняв, что же из себя представляют площадки для символов и как высчитывается их высота, мы уже с лёгкостью может узнать высоту самой строки (лайн-бокса). Чуть ранее я говорил, что высота строки зависит от размера шрифта. Но, если говорить точнее, то от размера шрифта зависит высота площадок, а вот уже от высоты наибольшей площадки и зависит высота самого лайн-бокса. Причём для лайн-бокса важен не только верхний край самой высокой площадки, но и нижний — самой низкой из них. 

В нашем случае самая высокая площадка вышла у шестого символа слева — буквы "H", а самая низкая у буквы "g" в слове "height", и в итоге высота нашей строки получилась 228px. А кегль шрифта, напомним, равен всего 100px!

Другие публикации цикла

1. Введение в инлайновый контекст форматирования (ИКФ): основные понятия (1-я публикация цикла “Тайны CSS2.1″).

2Введение в инлайновый контекст форматирования (ИКФ): основные понятия (2-я публикация цикла “Тайны CSS2.1″).

4ИКФ: высота строки, часть 2 (4-я публикация цикла “Тайны CSS2.1″).

5ИКФ: высота строки, часть 3 (5-я публикация цикла “Тайны CSS2.1″).

6. ИКФ: высота строки, часть 4 (6-я публикация цикла “Тайны CSS2.1″).

7ИКФ: высота строки, часть 5 (7-я публикация цикла “Тайны CSS2.1″).

8ИКФ: Вертикальное выравнивание в строке, часть 1 (8-я публикация цикла “Тайны CSS2.1″).

9ИКФ: Вертикальное выравнивание в строке, часть 2 (9-я публикация цикла “Тайны CSS2.1″).

10. ИКФ: Вертикальное выравнивание в строке, часть 3 (10-я публикация цикла “Тайны CSS2.1″).

11ИКФ: Горизонтальное выравнивание, часть 1 (11-я публикация цикла “Тайны CSS2.1″).

12ИКФ: Горизонтальное выравнивание, часть 2 (12-я публикация цикла “Тайны CSS2.1″).

Нам необходимы ваши конструктивные коментарии в наших статьях! Пожалуйста, не проходите мимо.

P.S. Это тоже может быть интересно:

15 Комментарии

  1. reptile

    Интересный цикл статей. Нельзя ли в следующих выпусках объяснить почему макреры нумерованного списка выходят за пределы блока?

    1. SelenIT

      В планах всё есть! Закончим с инлайновым форматированием, примемся за блочное, и это будет там одним из пунктов.
      Вкратце — спецификация про list-style-position: outside (это значение по умолчанию для списков) говорит следующее:

      Бокс маркера располагается снаружи основного блочного бокса. Позиция маркера, соседствующего с float-ами, в CSS 2.1 не определена. CSS 2.1 не определяет точного местоположения бокса маркера или его очередности при отрисовке, но требует, чтобы для элементов списка со свойством 'direction', равным 'ltr', бокс маркера был слева от контента, а для элементов с 'direction', равным 'rtl' — справа от него.

      Т.е. главное, чтобы маркеры списка были за пределами блока элемента списка и соответствовали направлению текста, а остальное отдано на волю браузера и практически не поддается управлению. В CSS3 планируется ввести псевдоэлемент ::marker, которым можно будет управлять, но, насколько я в курсе, он еще в глухом черновике и нигде не поддерживается. Зато в нынешнем CSS можно вырубить дефолтные маркеры совсем (list-style-type:none) и заменить их более управляемой собственной конструкцией из псевдоэлементов ::before и CSS-счетчиков.

  2. c1n1k

    если рассказали про базовую линию, тогда следует и раскрыть «литеру», «глиф».
    А вообще статья вызывает больше вопросов. Тут вроде бы и затронута классическая типографика, но как то «вяло», словно сказали «А» и не сказали про «Б». И от этого статья получилась ни рыба, ни мясо.

    1. psywalker (Автор записи)

      На счёт "вяло" вы правы, но имхо, этого "вяло" вполне достаточно для статьи, которая не относится  напрямую к типографике. Тема "Типографика" настолько огромна, что ей посвящают целые книги и пересказывать уже написанное кем-то десять раз нет смысла. Об этом можно почитать в Вики, например.

      Но, всё-таки в части "Горизонтальное выравнивание" мы обещаем чуть больше затронуть эту тему, т.к. там она актуальнее всё же. 

      1. c1n1k

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

  3. Алексей

    Когда мы пишем font-size: 12px, то это означает высоту от базовой линии до высоты самого высокого символа шрифта, вроде Ă? (кроме случаев когда всякие загогулины вылазяют за этот размер, что может быть разрешено в самом шрифте для создания каких-то специфических эффектов, например эдакая художественная пропись).
    Это высота не включает заплечики?

  4. Алексей

    Так что же всё-таки такое кегель шрифта? Ни по Вашей иллюстрации, ни по информации в этих Ваших интернетах, это не вполне очевидно… Вот Вы назначили font-size: 100px, что же именно Вы назначили?

    1. Алексей

      Ура! Оказывается информация вполне доступна в справочнике Влада:

      https://webref.ru/css/font-size

      Высотой шрифта считается расстояние от базовой линии до верхней границы кегельной площадки! Что-то я обыскался информации на этот счёт, а она, оказывается, была перед носом!

      1. SelenIT

        Хм, там реально так написано, но это неверно! Высота шрифта — это полная высота кегельной площадки, сумма ее «надводной» и «подводной» частей, над базовой и под ней (на примере SVG-шрифта из статьи — ascent и descent соответственно). Другое дело, что в компьютерном наборе само понятие «кегельная площадка» — не более чем математическая абстракция, условность. Спецификация CSS Fonts level 3 определяет font-size так:

        For scalable fonts, the font-size is a scale factor applied to the EM unit of the font. (Note that certain glyphs may bleed outside their EM box.)

        Для масштабируемых шрифтов font-size — это масштабный коэффициент, применяемый к кеглю шрифта. (Учтите, что некоторые символы могут «вытекать» наружу своей кегельной площадки)

        Т.е., например, символы 100-пиксельного шрифта должны быть вдвое больше таких же символов этого же шрифта, но 50-пиксельного (с точностью до сглаживания и хинтинга), но символы разных шрифтов даже при одинаковом font-size в принципе могут совершенно разного размера (даже крупнее font-size!), это полностью во власти дизайнера шрифта.

        1. Алексей

          Ну в общем, масло маслянное, никакой определённости! То, что шрифты могут быть разного размера и почти всегда крупнее font-size я уже давно увидел, достаточно задать блок определённой высоты, воткнуть туда span, сделать его какого-нибудь полупрозрачного цвета, и задать ему font-size того же размера что и height блока и наблюдать, фон шрифта будет выходить за размеры блока. При этом, когда мы меняем шрифты, то можно видеть что размер спана выходит за размеры блока в разной степени, но практически всегда, хоть немного, да больше.

          https://jsfiddle.net/Launder/gmg3u1tL/2/

          Хорошо это можно отследить по поведению первой и третьей иллюстрации.

          А что означает в спецификации выражение: «certain glyphs may bleed outside their EM box»

          То есть, если пользоваться вот этой иллюстрацией:

          http://indians.ru/a-font-sizes.htm

          получается, некоторые символы могут не только вылезать за размер очка, и заполнять заплечинки, но и выходить за их пределы, то есть быть выше собственной кегельной площадки? (Я исхожу из того, что размеры кегельной площадки, для определённого шрифта, определённых размеров, величина постоянная, вне зависимости от символа, или для экзотических символов могут быть исключения?)

          1. SelenIT

            фон шрифта будет выходить за размеры блока

            Тут подвох в другом: то, что залито фоном шрифта — это не кегельные площадки! Их можно условно назвать «литерными площадками», но их высота — это дефолтный line-height (тоже «зашитый» в метрики шрифта). Например, для Times New Roman, если верить этой странице, этот дефолтный межстрочный интервал равен 2355/2048≈1.15 кегля.

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

            В металлическом наборе, конечно, не могли. Но в компьютерном — да, могут, поскольку «кегельная площадка» тут понятие чисто виртуальное, условное. По сути любой символ шрифта — разновидность векторной картинки, а «кегельная площадка» — всего лишь система координат для этой картинки. И значения координат формально не ограничены — например, какой-нибудь дизайнерский росчерк с завитком в рукописном шрифте вполне может «улететь» далеко в отрицательные числа.

            1. Алексей

              Насколько мне известно, ворд-пресс, если держать страничку долго открытой, иногда «съедает», комментарии, правда не исключено, что их потом можно найти где-нибудь в кэше. Наверное, это тот случай :-)
              Поэтому, отправлю повторно…

              Ну то, что «какой-нибудь дизайнерский росчерк с завитком в рукописном шрифте вполне может «улететь» далеко в отрицательные числа» — конечно, плюс, поскольку позволяет сосредоточится на завитке, а не на том, как завиток перенести на носитель, но то, что кегельная площадка понятие «виртуальное, условное», пожалуй, минус, поскольку как она тогда может быть «системой координат для этой картинки»?…

              Вот давайте возьмём обе наши ссылки и сравним:
              http://indians.ru/a-font-sizes.htm — моя ссылка, описание того, как, по видимому, должно быть
              http://netlib.narod.ru/library/book0039/ch09_11.htm — Ваша ссылка, описание, по видимому, того «как есть» :-)

              Самое первое — EM, размер шрифта, насколько я понимаю, от него строится всё остальное, его размер условно задали в 2048 пунктов. То есть, font-size: 30px означает что 2048 пунктов это и будет 30px. Насколько я понимаю, это чисто условное обозначение, то, от чего отталкивается дизайнер шрифта. Но какая же это площадка? Площадка — это место, на котором что-то располагается. А тут размер, относительно которого строятся площадки. По Вашей ссылке, есть размер под базовой линией и над базовой линией, и их сумма больше этого самого EM. Условно этот размер EM больше похож на размер очка или даже и того меньше…
              Далее идёт некое свободное пространство, которое, как там утверждается, может быть, а может не быть. Опять же что это? Почему оно внизу? А может оно частично внизу, частично вверху?
              Вообще, по идее, это заплечинки, но опять же, они входят в кегель! Кстати, если так посмотреть, то кегельная площадка и «литерная» совпадают, только в одном случае термин используется для металлического набора, а в другом — для компьютерного. А размер заплечинок в сумме, кстати, показывает, насколько размер кегля, больше размера очка.
              Собственно, эти же размеры, насколько я понимаю, определяют интерлиньяж для шрифта по-умолчанию. В металлическом наборе, если требовался бОльший интелиньяж, докладывали шпон, в компьютерном наборе, можно просто указать нужный line-height.
              Но, видимо, когда мы указываем line-height: 1.4em, то размер em берётся не из высоты кегельной (литерной) площадки, а просто приравнивается к численному значению EM. Поэтому, когда у нас line-height: normal, то разницу между высотой очка и заплечинками определяет текущий шрифт (правда на расстояние между строчками, похоже, оказывает ещё влияние и strut, и если его line-height больше чем у спанов, то именно он будет line-height лайн-бокса.
              Вот ссылка с иллюстрацией: https://jsfiddle.net/Launder/h0bs30q0/5/), можно увидеть, как при изменении размера шрифта блока, меняется поведение строк. Поэтому для чистоты эксперимента strut лучше обнулить).
              Так вот, то о чём Вы говорите про «дефолтный» line-height, по-идее, определяется высотой очка и сумме верхней и нижней заплечинок (по-умолчанию, насколько я понимаю, они равны, впрочем, ничего, вероятно, не мешает дизайнеру шрифта, сделать заплечинки различных размеров, если он почему-то увидит в этом смысл). По сути, получившийся результат равен высоте кегельной площадки.
              Плюс к этому, браузер добавляет свой какой-то промежуток, но связан ли этот промежуток со свойствами шрифта — не знаю:

              https://jsfiddle.net/Launder/gmg3u1tL/2/

              На втором примере этой иллюстрации, если внимательно присмотреться, то можно увидеть, что в Лисе прибавлен промежуток сверху, тоненькая пиксельная полосочка, а в Хроме — снизу. Если же мы для этого примера размер шрифта увеличим, скажем до 100px (36 строчка CSS-кода), то мы в обоих браузерах увидим полосочки как сверху так и снизу.
              Откуда они? Связаны ли они как-то свойствами шрифта? Есть ли тут влияние ОС? Насколько мы видим именно то, что задумал дизайнер шрифта? Я понимаю, что у всех у нас отличный врождённый вкус, но хочется всё-таки иметь возможность, посмотреть на шрифт глазами его создателя, а уже потом, по необходимости, вносить коррективы…

              Кстати, если полуапроши входят в кегельную плошадку, то почему тогда когда мы ставим letter-spacing: 0 (равно как и word-spacing), то расстояние между инлайн-блоками не обнуляется? В своей статье на эту тему, Вы ставили отрицательные значения. Но ведь эти отрицательные значение — хак, который подбираются вручную и шумом ветра, равновесие нарушается… Получается заплечинки входят в кегельную (литерную?) площадку, а полуапроши не входят, хотя по логике они одного поля ягода — одни для вертикальных отступов, другие для горизонтальных…

              Вот вопросы, в первом приближении, которые возникают, глядя на все эти метрики… Понимаю, что многое, я пока понимаю ещё довольно-таки примерно, но именно поэтому и хотелось бы видеть ясную и более-менее не противоречивую картинку, по которой можно ориентироваться, и понимая, где что, как-то с этим взаимодействовать…

              1. Алексей

                Получается заплечинки входят в кегельную (литерную?) площадку, а полуапроши не входят

                Тут, пожалуй, не прав — если между инлайн-блоками нет пробелов и символов переноса, то нет между ними и расстояния. А значит и полуапроши входят в площадку символа.

            2. Алексей

              Кстати,

              дефолтный line-height (тоже «зашитый» в метрики шрифта)

              косвенно подтверждает то, что если font установить в коде ниже чем line-height, то значение последнего, похоже, приравнивается к normal для назначенного шрифта.

  5. Алексей

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

    Может вместо ширины написать «размеры»?

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.