> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/ru/podrobnye-stati/47-tex-tables-how-tex-calculates-spanned-column-widths.md).

# Таблицы TeX: как TeX вычисляет ширины объединённых столбцов

## Цель этой статьи

В этой статье мы исследуем, как $$\mathrm\TeX$$ вычисляет ширину столбцов таблицы, когда таблицы содержат элементы (например, заголовки таблицы), которые занимают несколько столбцов (например, с использованием $$\mathrm\TeX$$ примитивов `\omit` и `\span`). Используя базовую [«эталонную» таблицу](#reference-table) в качестве отправной точки мы создаём ряд примеров — основанных на этой эталонной таблице — изменяя различные записи, чтобы создать объединённые столбцы. Изучая эффект этих изменений, мы можем начать понимать лежащий в основе алгоритм, который $$\mathrm\TeX$$ использует для вычисления ширины объединённых столбцов.

### Использование $$\mathrm\TeX$$ не $$\mathrm\LaTeX$$

Чтобы рассмотреть и объяснить *как* $$\mathrm\TeX$$ определяет ширину объединённых столбцов, необходимо отказаться от любого из замечательных $$\mathrm\LaTeX$$ табличных пакетов и вернуться к фундаментальным, низкоуровневым (примитивным) командам создания таблиц: в частности, `\halign{...}`, `\span` и `\omit`. [Существующие $$\mathrm\TeX$$/$$\mathrm\LaTeX$$ табличные пакеты](https://ctan.org/topic/table) конечно, являются важнейшими инструментами продуктивности и предоставляют множество чрезвычайно полезных возможностей, позволяющих пользователям быстро создавать широкий спектр табличного материала с помощью $$\mathrm\LaTeX$$. Эти пакеты предоставляют необходимый «макро-каркас», построенный вокруг $$\mathrm\TeX$$низкоуровневого поведения, а их разработчики предоставляют очень полезные абстракции и слои изоляции, которые берут на себя управление лежащей в основе сложностью. Многие из этих пакетов — поистине невероятные достижения сложного $$\mathrm\TeX$$ программирования: мы все должны быть благодарны за то, что они существуют, чтобы избавить нас от необходимости использовать «сырой» $$\mathrm\TeX$$!

Фактический алгоритм, который $$\mathrm\TeX$$ использует для вычисления ширины объединённых столбцов, объясняется на странице 245 [$$\mathrm\TeX\text{book}$$](https://www.amazon.co.uk/TeXbook-Donald-E-Knuth/dp/0201134489) и, с дополнительными подробностями, в разделе 801 (страница 336) печатной книги, содержащей $$\mathrm\TeX$$исходный код [$$\mathrm\TeX\text{: The Program}$$](https://www.amazon.co.uk/Computers-Typesetting-Tex-Program-TEX/dp/0201134373). Однако для многих людей (в том числе и для меня) объяснения Кнута иногда довольно компактны и кратки и порой их трудно подробно проследить: иллюстрированные примеры всегда очень полезны.

### Да, таблицы сложны

В разделе 768 (страница 322) книги [$$\mathrm\TeX\text{: The Program}$$](https://www.amazon.co.uk/Computers-Typesetting-Tex-Program-TEX/dp/0201134373), Кнут делает интересное замечание:

> «Это своего рода чудо всякий раз, когда `\halign` и `\valign` работают, потому что они пересекают так много управляющих структур $$\mathrm\TeX$$».

Кроме того, том IV четырёхтомной книжной серии [$$\mathrm\TeX\text{ in Practice}$$](https://www.amazon.co.uk/Tex-Practice-Set-Stephan-Bechtolsheim/dp/038797296X/ref=sr_1_11?s=books\&ie=UTF8\&qid=1504256043\&sr=1-11\&keywords=TeX+in+Practice) посвящает не менее 180 страниц (сс. 199–379) созданию таблиц в $$\mathrm\TeX$$ через `\halign` и `\valign`.

Итак, можно с уверенностью отметить, что $$\mathrm\TeX$$ таблицы действительно «довольно хитрые».

### Объединение столбцов: \omit, \span и \multispan

Как отмечалось, чтобы исследовать $$\mathrm\TeX$$вычисления ширины столбцов у нас должны использовать «сырой» $$\mathrm\TeX$$; под этим подразумевается сочетание примитивных команд и одной $$\text{Plain }\mathrm\TeX$$ макрокоманды под названием `\multispan`. Хотя мы не будем использовать эти команды для непосредственной иллюстрации наших примерных таблиц (то есть полностью объясняя весь $$\mathrm\TeX$$ код), стоит кратко пояснить их:

* `\halign`: Один из двух $$\mathrm\TeX$$ примитивов (команд) для создания таблиц. Другой — это `\valign` но он используется не так широко и не будет обсуждаться в этой статье.
* `\omit`: Примитив $$\mathrm\TeX$$ (команда), которая предписывает $$\mathrm\TeX$$ игнорировать шаблон преамбулы записи таблицы.
* `\span`: Примитив $$\mathrm\TeX$$ примитив (команда), используемый для объединения двух соседних записей таблицы.
* `\multispan{n}`: Обычная $$\mathrm\TeX$$ макрокоманда для объединения `n` столбцов.

По сути, для объединения столбцов $$\mathrm\TeX$$ игнорирует соответствующее число шаблонов преамбулы таблицы и объединяет требуемое количество записей таблицы в одну запись. `\multispan{n}` работает, раскрываясь в последовательность `\omit` и `\span` токенов, необходимых для объединения `n` столбцов. Например, `\multispan{3}` раскрывается в `\omit\span\omit\span\omit`.

## Представляем нашу «эталонную» таблицу

Вот наша эталонная таблица, а затем — аннотированная версия, объясняющая элементы, использованные в её построении:

![{{{alt}}}](/files/95b454b8a6fc64e598cb2631d2011af07063f7eb)

Изменяя нашу эталонную таблицу, мы будем наблюдать, что происходит с шириной таблицы и шириной отдельных столбцов по мере добавления записей, занимающих различные столбцы. Эта эталонная таблица была создана на «сыром» $$\mathrm\TeX$$ с использованием `\halign{...}` примитиве вместе с рядом пользовательских макрокоманд, необходимых для верстки таблиц — мы не будем обсуждать эти макрокоманды, поскольку они несущественны для понимания примеров и объяснений.

Вот аннотированная версия нашей эталонной таблицы, объясняющая её особенности:

![{{{alt}}}](/files/b793dd77010a044f5e4d18dfe92a22fd70753d1d)

В нашем первом наборе примерных таблиц и в исходной эталонной таблице для всех установлено `\tabskip=0pt` так, чтобы $$\mathrm\TeX$$ не добавляет никакого пространства между нашими столбцами: по сути, они соприкасаются друг с другом. Причина этого — упростить первоначальное обсуждение и последующие вычисления — позже в статье мы снова введём ненулевой `\tabskip` клей, чтобы изучить его влияние на вычисление ширины объединённых столбцов.

Как отмечено в примечаниях, мы добавили небольшой пробел (5pt) в начале всех не объединённых записей таблицы (кроме первой строки). Этот пробел 5pt входит в общую ширину всех не объединённых записей (кроме первой строки) и был добавлен лишь для того, чтобы таблица выглядела немного менее загромождённой.

### Краткое замечание о ширине таблиц

Это `\halign{...}` команда имеет три формы:

* `\halign{...}`: установить таблицу в любую ширину, которую $$\mathrm\TeX$$ вычисляет на основе размера записей (и `\tabskip` клея);
* `\halign to *width* {...}`: предписывает $$\mathrm\TeX$$ верстать таблицу с заданной `*шириной*`;
* `\halign spread *amount*{...}`: скорректировать вычисленную ширину на `*величину*`.

Когда $$\mathrm\TeX$$ верстает таблицу, используя `\halign{...}` ему приходится считывать всю таблицу в память, чтобы выполнить различные вычисления, необходимые для её верстки. Следовательно, если вы не задали ширину с помощью `\halign to *width* {...}` вы не можете узнать окончательную ширину, пока $$\mathrm\TeX$$ не завершит её обработку (верстку). Один из способов получить ширину таблицы, созданной с помощью `\halign{...}` — сначала сверстать таблицу внутри `\vbox{...}` (например, `\setbox0=\vbox{\halign{...}}`) а затем, например, использовать `\the\wd0` чтобы получить ширину.

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

Важно отметить, что когда $$\mathrm\TeX$$ верстает таблицу, созданную с помощью `\halign{...}` любой текст внутри записей таблицы не подвергается автоматическому переносу строк: записи таблицы верстаются в *ограниченном горизонтальном режиме*— так же, как и `\hbox`. Чтобы включить перенос строк, текст записи таблицы нужно заключить внутрь `\vbox{...}` вместе с использованием подходящего значения для `\hsize` внутри этого `\vbox{...}`. Однако заметьте, что текст внутри `\noalign{...}` команды ( $$\mathrm\TeX$$ примитива), используемого в `\halign{...}` подчиняется $$\mathrm\TeX$$переносу строк у него. По сути, и как следует из названия, `\noalign{...}` позволяет $$\mathrm\TeX$$ «выбраться» из `\halign{...}` и поместить материал между строками таблицы — обычно для создания горизонтальных линий между строками таблицы.

### Не допускается: \halign{...} внутри \hbox{...}

Вы не можете *непосредственно* верстать `\halign{...}` внутри `\hbox{...}`. `\hbox{\halign{...}}` вызовет довольно запутанную ошибку:

```latex
! Missing } inserted.
<вставленный текст>
                }
<будет прочитано снова>
                   \halign
l.1 \hbox{\halign
```

#### Пояснение этой ошибки

Из-за внешнего `\hbox{...}` $$\mathrm\TeX$$ находится в *ограниченном горизонтальном режиме*; затем он обнаруживает `\halign{...}` что является *вертикальным режимом* командой. Например, если вы используете `\halign{...}` внутри абзаца, $$\mathrm\TeX$$ завершит абзац, обработает `\halign{...}` а затем продолжит с остальной частью абзаца.

При использовании внутри `\hbox{...}`, `\halign{...}` вызывает $$\mathrm\TeX$$ попытку вернуться в вертикальный режим, пытаясь принудительно закрыть текущую группу: $$\mathrm\TeX$$ сообщает «`! Пропущена }`» и выдаёт ошибку, потому что считает, что вы допустили ошибку в использовании группировки. Хотя правая фигурная скобка (`}`) может и не отсутствовать в вашем $$\mathrm\TeX$$ коде, сообщение об ошибке является симптомом того, что `\hbox{...}` «мешает» и $$\mathrm\TeX$$ делает своё «наилучшее предположение» относительно подходящего действия для решения проблемы.

## Примеры таблиц с объединёнными столбцами

Следующая последовательность графиков таблиц предоставляет ряд примеров, демонстрирующих эффект объединения столбцов таблицы: показывая, что длинные записи таблицы могут иметь неожиданные последствия для ширины определённых столбцов — а следовательно, и для ширины самой таблицы. Вопрос, который мы собираемся рассмотреть, — что делает $$\mathrm\TeX$$ делает, когда конкретная запись таблицы занимает несколько столбцов, но она «слишком широка, чтобы поместиться». Как отмечалось выше, $$\mathrm\TeX$$ действительно применяет к этой задаче вычисления ширины столбцов определённый алгоритм: следующие примеры призваны помочь сформировать «ощущение» работы этого алгоритма.

### Пример таблицы 1

В этом примере мы используем `\multispan{2}` для объединения столбцов 1 и 2 с записью, текст которой **Заголовок таблицы**:

![{{{alt}}}](/files/6d9119eb80602a712f81df4d6af1be68ce007ee9)

#### Наблюдения

* Ширина этой таблицы такая же, как у [эталонной таблицы](#reference-table): $$327.71722\text{pt}$$.
* Ширина записи, объединяющей столбцы 1 и 2, составляет $$81.04953\text{pt}$$ что меньше общей ширины записей в столбцах, которые она охватывает: $$52.56676\text{pt} + 57.06679\text{pt} = 109.63355\text{pt}$$

## Пример таблицы 2

Как и в [Пример таблицы 1](#example-table-1), этот пример также использует `\multispan{2}` для объединения столбцов 1 и 2, но здесь мы используем более длинную запись, текст которой **Немного более длинный заголовок таблицы**.

![{{{alt}}}](/files/67c37fcbc117ccea0650de0b2fb81e854c797978)

#### Наблюдения

Если вы сравните этот пример с нашим [эталонной таблицы](#reference-table) мы увидим следующее:

* Ширина этой таблицы увеличилась с $$327.71722\text{pt}$$ на $$374.37032\text{pt}$$: всего на $$46.6531\text{pt}$$.
* Ширина записи, объединяющей столбцы 1 и 2 ($$156.28664\text{pt}$$) больше общей ширины записей в столбцах, которые она охватывает: $$52.56676\text{pt} + 57.06679\text{pt} = 109.63355\text{pt}$$. Эта разница составляет $$156.28664\text{pt}-109.63355\text{pt} = 46.6531\text{pt}$$ что совпадает с величиной, на которую увеличилась ширина таблицы.
* $$\mathrm\TeX$$ изменил ширину столбца 2, чтобы обеспечить дополнительное требуемое пространство. Позже мы увидим, как $$\mathrm\TeX$$ вычисляет величину, на которую должен увеличиться столбец 2.
* Столбец 1 не затронут: его ширина не была изменена записью, объединяющей столбцы 1 и 2.

### Пример таблицы 3

В этом примере мы используем `\multispan{3}` для объединения столбцов 1–3 с записью, текст которой такой же, как у [Пример таблицы 2](#example-table-2): **Немного более длинный заголовок таблицы**.

![{{{alt}}}](/files/237afc8d8eebe6610c1c8f79e80fd2b89d59bc01)

#### Наблюдения

* Ширина этой таблицы такая же, как у [эталонной таблицы](#reference-table): $$327.71722\text{pt}$$.
* Ширина записи, объединяющей столбцы 1–3 ($$156.28664\text{pt}$$) меньше общей ширины записей в трёх столбцах, которые она охватывает: $$52.56676\text{pt} + 57.06679\text{pt} + 59.03899\text{pt} = 168.67254\text{pt}$$.
* Ни одна из ширин столбцов не была затронута записью, объединяющей столбцы 1–3.

Вы уже начинаете замечать появляющуюся закономерность?

### Пример таблицы 4

Как и в [Пример таблицы 3](#example-table-3), здесь мы используем `\multispan{3}` для объединения столбцов 1–3, но на этот раз с записью, текст которой значительно длиннее: **Значительно более длинный заголовок таблицы, который сильно растягивается**.

![{{{alt}}}](/files/21618c903f858e02bb92e15b9d540257b54e7b16)

#### Наблюдения

* По сравнению с [эталонной таблицы](#reference-table), ширина этой таблицы увеличилась с $$327.71722\text{pt}$$ на $$465.95685\text{pt}$$: увеличение на $$138.23963\text{pt}$$.
* Ширина записи, объединяющей столбцы 1–3, составляет $$306.91216\text{pt}$$.
* Общая ширина записей в трёх объединённых столбцах составляет $$52.56676\text{pt} + 57.06679\text{pt} + 59.03899\text{pt} = 168.67254\text{pt}$$.
* Разница в ширине между длинной объединяющей записью и записями в столбцах 1–3 составляет $$306.91216\text{pt}-168.67254\text{pt}=138.23962\text{pt}$$. Та же величина (с точностью до 4 знаков после запятой!), на которую увеличилась ширина таблицы.
* Только ширина столбца 3 была увеличена: ни столбец 1, ни столбец 2 не затронуты.

#### Выявляется закономерность

Если мы посмотрим на [Пример таблицы 2](#example-table-2) и [Пример таблицы 4](#example-table-4) мы увидим, что в обоих случаях именно **последний столбец в диапазоне** получил увеличение ширины, чтобы освободить место для длинной записи, охватывающей столбцы:

* В [Пример таблицы 2](#example-table-2): Длинная запись охватывала столбцы 1 и 2. Столбец 2 оказался «растянут».
* В [Пример таблицы 4](#example-table-4): Длинная запись охватывала столбцы 1–3. Столбец 3 оказался «растянут».

#### Ширина столбца 3: вырисовывается алгоритм?

Следующие вычисления дают более ясное представление о том, что $$\mathrm\TeX$$ делает. Вот что нам известно:

* Ширина длинной записи, охватывающей столбцы 1–3, составляет $$306.91216\text{pt}$$.
* Общая ширина записей в столбцах 1 и 2 составляет $$52.56676\text{pt} + 57.06679\text{pt} = 109.63355\text{pt}$$.

Какова разница между этими значениями? Она составляет $$306.91216\text{pt}-109.63355\text{pt} = 197.2786\text{pt}$$ и именно эта ширина используется для столбца 3: она непосредственно вытекает из алгоритма, используемого $$\mathrm\TeX$$.

### Пример таблицы 5

Прежде чем перейти к более сложному примеру, вот ещё один «простой» пример. Эта таблица содержит ту же длинную запись, что и [Пример таблицы 4](#example-table-4): **Значительно более длинный заголовок таблицы, который сильно растягивается**; однако на этот раз мы используем `\multispan{6}` что позволяет этой записи охватить всю таблицу. Как видите, получившаяся таблица по-прежнему имеет ту же ширину, что и наша [эталонной таблицы](#reference-table) ($$327.71722\text{pt}$$) это означает, что на столбцы не повлияла эта очень длинная запись. Очевидно, это потому, что ширина записи ($$306.91216\text{pt}$$) меньше общей ширины всех записей, которые она охватывает: $$327.71722\text{pt}$$; то есть ширины таблицы.

![{{{alt}}}](/files/055a641501879e110961e76b30e19c892cad9cb8)

### Пример таблицы 6: Немного сложнее

Здесь мы рассматриваем серию из трёх примерных таблиц (6(a)–6(c)), чтобы показать эффект двух разных записей, обе из которых охватывают столбец 5. [Пример таблицы 6(a)](#example-table-6a) и [Пример таблицы 6(b)](#example-table-6b) каждая показывает таблицу, содержащую одну запись, которая охватывает несколько столбцов вплоть до столбца 5. [Пример таблицы 6(c)](#example-table-6c) объединяет обе объединяющие записи в одну таблицу и задаёт вопрос: какая запись на самом деле определяет ширину столбца 5 и почему? Ответ приводит нас к сути алгоритма, используемого $$\mathrm\TeX$$.

#### Пример таблицы 6(a)

![{{{alt}}}](/files/2c601812ea54cc3bc16c88881321fff1f2a97858)

**Наблюдения**

* По сравнению с [эталонной таблицы](#reference-table), ширина этой таблицы увеличилась с $$327.71722\text{pt}$$ на $$371.11153\text{pt}$$: увеличение на $$43.39431\text{pt}$$.
* Ширина записи, объединяющей столбцы 3–5, составляет $$215.06683\text{pt}$$.
* Общая ширина записей в столбцах 3–5 составляет $$59.03899\text{pt} + 52.98344\text{pt} + 59.6501\text{pt} = 171.67253\text{pt}$$.
* Разница в ширине между записями, охваченными столбцами 3–5, и шириной объединяющей записи составляет $$215.06683\text{pt}-171.67253\text{pt}=43.3943\text{pt}$$: точная величина (с точностью до 4 знаков после запятой!), на которую увеличилась ширина таблицы.

#### Пример таблицы 6(b)

![{{{alt}}}](/files/c2b4ec5e3e38dcad9981d1a977eedf9b4eec95f4)

**Наблюдения**

* По сравнению с [эталонной таблицы](#reference-table), ширина этой таблицы увеличилась с $$327.71722\text{pt}$$ на $$353.3233\text{pt}$$: увеличение на $$25.60608\text{pt}$$.
* Ширина записи, объединяющей столбцы 1–5, составляет $$306.91216\text{pt}$$.
* Общая ширина записей в столбцах 1–5 составляет $$52.56676\text{pt} + 57.06679\text{pt} + 59.03899\text{pt} + 52.98344\text{pt} + 59.6501\text{pt} = 281.30608\text{pt}$$.
* Разница в ширине между записями, охваченными столбцами 1–5, и шириной объединяющей записи составляет $$306.91216\text{pt}-281.30608\text{pt}=25.60608\text{pt}$$: **note** это *меньше* чем значение, вычисленное для [Примера 6(a)](#example-table-6a), которое было $$43.3943\text{pt}$$.

#### Пример таблицы 6(c)

Здесь мы объединяем записи из примерных таблиц [6(a)](#example-table-6a) и [6(b)](#example-table-6b) в одну таблицу: что происходит?

![{{{alt}}}](/files/20da454c0362e96b4cb91cea70beb3d29464cb4d)

**Наблюдение**

* По сравнению с [эталонной таблицы](#reference-table), ширина этой таблицы увеличилась с $$327.71722\text{pt}$$ на $$371.11153\text{pt}$$: увеличение на $$43.39431\text{pt}$$. Заметим, что это в точности то же самое, что и [Пример таблицы 6(a)](#example-table-6a).

#### Что $$\mathrm\TeX$$ делает?

Чтобы понять результаты $$\mathrm\TeX$$алгоритма и процесса принятия решений, мы замечаем, что эта запись

![{{{alt}}}](/files/3c07f148f5ba09785c8bc9235596aeefc5bfec8c)

выходит за пределы записей, охватываемых $$25.60608\text{pt}$$; однако эта запись

![{{{alt}}}](/files/e4cdceeb4405b353b73a6e9a00a6974f30544d21)

выходит ещё дальше за пределы охватываемых записей: на $$43.3943\text{pt}$$. Следовательно, эта запись «выигрывает гонку», и ширина столбца 5 увеличивается на **максимум** из этих двух значений ($$43.3943\text{pt}$$). Теперь ширина столбца 5 становится $$59.6501\text{pt} + 43.3943\text{pt} = 103.0444\text{pt}$$ чтобы вместить запись, охватывающую столбцы 3–5. Наше описание точной «последовательности событий» немного упрощено, но результат именно такой, как мы описали.

## Возвращая некоторую сложность

Чтобы минимизировать сложность наших рассуждений (до сих пор), мы использовали относительно простые примеры для демонстрации принципов $$\mathrm\TeX$$алгоритма; в частности, мы установили `\tabskip=0pt`. На практике «реальные» таблицы, вероятно, будут содержать множество записей, охватывающих разные столбцы, и, конечно, будут иметь ненулевые значения `\tabskip` клея — к этой теме мы сейчас вернёмся.

### \tabskip glue и ширина объединённых столбцов

При разработке таблиц часто требуется добавление пробелов между столбцами и, конечно, $$\mathrm\TeX$$ имеет такую возможность благодаря примитивной команде под названием `\tabskip`. Эту команду можно использовать для размещения фиксированного или гибкого клея (интервала):

* перед таблицей (то есть слева от столбца 1);
* между одним или несколькими столбцами;
* после таблицы (то есть справа от последнего столбца).

Вот пример, чтобы напомнить себе:

![{{{alt}}}](/files/2c2c212d33186f08be07bccefd775cbc1ced87aa)

### Как влияет клей \tabskip на ширину объединённых столбцов?

Наличие ненулевого `\tabskip` клея между столбцами предоставляет дополнительное пространство, которое объединённые записи могут «поглотить» прежде чем $$\mathrm\TeX$$ ему нужно задуматься об увеличении ширины последнего столбца в диапазоне.

В следующем примере мы используем две таблицы, чтобы сравнить результаты объединения двух столбцов. Единственное различие между таблицами — использование `\tabskip` клея.

* В первом примере используется наша исходная «эталонная» таблица, которая, если вы помните, имеет установленное `\tabskip=0pt`.
* Во втором примере используется изменённая версия нашей [эталонной таблицы](#reference-table) (аннотированной выше), у которой установлено `\tabskip=10pt` до и после таблицы, но, что более важно, у неё установлено `\tabskip=20pt` между столбцами.

В *изменённой* эталонной таблице два объединённых столбца не влияют на ширину столбцов (и ширину таблицы), но они влияют на ширину столбца 2 (и ширину таблицы) в *исходной* [эталонной таблицы](#reference-table).

### Исходная эталонная таблица: \tabskip=0pt

Здесь мы показываем нашу исходную [эталонной таблицы](#reference-table) вместе со второй таблицей (полученной из нашей исходной [эталонной таблицы](#reference-table)), которая содержит запись «**Проверить более длинный заголовок таблицы**», охватывающую столбцы 1 и 2. Совершенно ясно, что столбец 2 (во второй таблице на схеме), а значит и вся таблица, затронуты объединёнными столбцами.

![{{{alt}}}](/files/f8fed1eaaf71ed4dee85e0357d7da7f35c34a09a)

### Изменённая эталонная таблица: \tabskip=20pt

Здесь мы показываем нашу изменённую эталонную таблицу вместе со второй таблицей (полученной из нашей изменённой эталонной таблицы), которая также содержит запись «**Проверить более длинный заголовок таблицы**», охватывающую столбцы 1 и 2. Совершенно ясно, что во второй таблице на схеме ни ширина столбца 2, ни ширина таблицы не затронуты объединёнными столбцами. В этом случае наличие `\tabskip` клея (`20pt`) между столбцами помогло «поглотить» пространство, необходимое для текста в записи, охватывающей столбцы 1 и 2:

![{{{alt}}}](/files/145ac5038aeb6c622eca0882d45487cf3872e5c1)

## Суть $$\mathrm\TeX$$алгоритма

Надеюсь, приведённый выше набор примеров помог сформировать «ощущение» того, что $$\mathrm\TeX$$ делает, чтобы вместить объединённые записи, и как $$\mathrm\TeX$$ при необходимости скорректирует ширину **последнего** леднего столбца в каждом диапазоне объединённых столбцов. Помимо ширины записей внутри отдельных объединяемых столбцов, наличие ненулевого `\tabskip` клея — важный фактор, который $$\mathrm\TeX$$ учитывает, решая, нужно ли ему корректировать какую-либо ширину столбцов. Важный момент, который следует помнить, заключается в том, что $$\mathrm\TeX$$цель — вычислить подходящую ширину для **последнего столбца** в каждом диапазоне объединённых столбцов.

### Заключительный пример таблицы: последние столбцы в диапазоне объединения

В этом заключительном примере мы снова используем нашу изменённую эталонную таблицу (с `\tabskip` склеивает значения, обсуждённые выше), чтобы вывести другую таблицу, которая содержит различные столбцы, перекрытые правилами — мы использовали правила, чтобы сделать эти перекрытия легче заметными.

Две таблицы были тщательно выровнены, чтобы показать, что в верхней таблице ни один столбец до столбца 5 не был затронут перекрытыми столбцами. Более тёмная зелёная область слева от диаграммы показывает, что столбцы 1–4 обеих таблиц по-прежнему идеально совпадают. Справа находится более светлая зелёная затенённая область, показывающая, что затронуты только столбцы 5 и 6 перекрывающими записями.

В верхней таблице перекрытия выглядят так:

* столбцы 1–5: перекрыты $$400\text{pt}$$ правилом;
* столбцы 3–5: перекрыты $$200\text{pt}$$ правилом;
* столбцы 4–6: перекрыты $$250\text{pt}$$ правилом.

![{{{alt}}}](/files/e0b2614f3efcf5e8828ce712a6ca86588bcc3159)

Ещё раз, объяснение таково: внутри серии перекрытых столбцов при необходимости корректируется только ширина последнего столбца: промежуточные столбцы не затрагиваются, и здесь это означает столбцы 1–4 — хотя, конечно, ширина столбцов 1–4 (и промежуточного `\tabskip` клея) учитывается при вычислении скорректированных ширин столбцов 5 и 6.

### Разбор $$\mathrm\TeX$$алгоритма

Мы закончим с *упрощённым* разбором «$$\mathrm\TeX$$мыслительных процессов» при вычислении ширин столбцов в перекрытых записях. Описывать $$\mathrm\TeX$$алгоритмы не всегда просто, поэтому мы позволим себе некоторую «упрощающую художественную вольность», чтобы дать обзор происходящего. Читателям, интересующимся всеми грязными подробностями, следует обратиться к разделу 801 (страница 336) печатной книги, содержащей $$\mathrm\TeX$$исходный код [$$\mathrm\TeX\text{: The Program}$$](https://www.amazon.co.uk/Computers-Typesetting-Tex-Program-TEX/dp/0201134373).

Реальные таблицы часто создаются с многократным использованием `\span` примитива (например, внутри $$\mathrm\LaTeX$$ пакетов) для построения нескольких экземпляров перекрытых столбцов внутри таблицы. Чтобы управлять этим, структуры данных (глубоко внутри $$\mathrm\TeX$$) хранят информацию (так называемые *узлы перекрытия*) которые сообщают $$\mathrm\TeX$$ о связях (перекрытиях) между элементами/столбцами таблицы. Очевидно, $$\mathrm\TeX$$ должен применять свои алгоритмы систематически и должен обработать всю таблицу, чтобы выполнить окончательные вычисления — определить ширины всех столбцов, общую ширину таблицы и, если требуется, величину, на которую используемые в таблице гибкие клеи должны растянуться или сжаться. Вполне неудивительно, что $$\mathrm\TeX$$ не может сообщить вам окончательную ширину таблицы, пока полностью не обработает `\halign{...}` команду — у него действительно очень много работы!

Отправной точкой вычисления ширины столбцов является столбец 1, потому что, разумеется, ничто не может перекрывать слева от (и *через/в*) столбец 1. $$\mathrm\TeX$$ начинает с определения ширины столбца 1, выясняя, какая запись имеет максимальную *естественная ширина*. Назовём эту максимальную ширину $$w\_1$$и если есть записи, которые перекрывают столбцы 1 и 2, назовём ширину этой записи $$w\_{12}$$ (ширина от 1 до 2). Кроме того, обозначим `\tabskip` клей между столбцами 1 и 2 как $$t\_{1}$$—заметим, что мы рассматриваем только *естественная ширина* этого `\tabskip` клея и пока игнорируем любые компоненты растяжения или сжатия, которые он может иметь. Также пусть максимальная естественная ширина всех неперекрывающих записей в столбце 2 будет $$w\_2$$.

Ключевой момент, на который следует обратить внимание, заключается в том, что $$\mathrm\TeX$$ пытается вычислить ширину столбца 2, рассматривая только те записи, где перекрытие *начинает* со столбцом 1 и *заканчивается* на столбце 2. Главное соображение для $$\mathrm\TeX$$ — это проверка $$\max(w\_{2}, w\_{12} - (w\_1+ t\_1))$$— может быть несколько записей, перекрывающих столбцы 1 и 2: некоторые могут быть узкими (малое $$w\_{12}$$), другие очень широкими (большое $$w\_{12}$$) поэтому $$\mathrm\TeX$$ ищет ту, которая оказывает наибольшее влияние (отсюда $$\max(\text{...})$$). Здесь значение $$w\_{12} -(w\_1+ t\_1)$$ — это величина, на которую запись, перекрывающая столбцы 1 и 2, «выступает» из столбца 1 в столбец 2: заметьте, что $$\mathrm\TeX$$ использует ширину столбца 1 **и** окружения `\tabskip` клея ($$t\_{1}$$) между столбцами 1 и 2. Когда $$\mathrm\TeX$$ определит, влияют ли какие-либо перекрытия от столбца 1 к 2 на ширину столбца 2, он устанавливает ширину столбца 2 в максимальное значение, которое он определил (используя описанную проверку). $$\mathrm\TeX$$ продолжает проходить по всем остальным столбцам, выполняя аналогичные проверки.

И наконец, для полноты картины, здесь мы приводим суть $$\mathrm\TeX$$алгоритма вычисления ширины столбцов (взято из документации исходного кода Кнута для $$\mathrm\TeX$$):

Пусть $$w\_{ij}$$ будет максимумом естественных ширин всех записей, которые перекрывают столбцы $$i$$ через $$j$$, включительно. Итоговые ширины столбцов задаются формулой

$$\begin{equation\*} w\_j=\max\_{1\leq i\leq j}\biggl(w\_{ij}-\sum\_{i\leq k< j}(t\_k+w\_k)\biggr) \end{equation\*}$$

где $$t\_k$$ — это естественная ширина клея tabskip между столбцами $$k$$ и $$k+1$$.

## Колофон: использование Overleaf для создания таблиц в виде SVG-графики

Все $$\mathrm\TeX$$ таблицы, представленные в этой статье, — это файлы Scalable Vector Graphics (SVG), созданные на платформе Overleaf. Аннотации (стрелки и зелёные рамки) были добавлены путём открытия SVG-графики в Inkscape — однако обратите внимание, что текст аннотаций был набран в $$\mathrm\TeX$$ как дополнительный текст, сопровождающий таблицу: в Inkscape были добавлены только стрелки и зелёные фоны. Если вам интересно узнать, как это было сделано, читайте дальше.

Серверы Overleaf используют $$\mathrm\TeX \text{ Live}$$ дистрибутив, который, помимо $$\mathrm\TeX$$-основанных движков верстки, предоставляет множество очень полезных $$\mathrm\TeX$$-связанных с ним программных инструментов и утилит. Среди них есть одна под названием [`dvisvgm`](https://dvisvgm.de) который, как следует из названия, преобразует $$\mathrm\TeX$$традиционный DVI (**D**e**V**ice **I**независимый) формат выходного файла в SVG. Среди его многочисленных [параметров командной строки](https://dvisvgm.de/Manpage/) `dvisvgm` предусмотрен параметр (`-n` или `--no-fonts`), который укажет ему преобразовать весь текст в *пути* что означает, что текст в SVG-графике рисуется с помощью линий и кривых, а не реальных шрифтов и глифов. Это может увеличить размер получающегося SVG-файла, но зато гарантирует, что SVG-графика будет чрезвычайно переносимой и почти наверняка будет хорошо работать на любом устройстве.

### Итак... как это было сделано?

В одной [предыдущей статье](https://www.overleaf.com/blog/510-using-luatex-to-run-tools-and-utilities-installed-on-overleafs-servers) Я рассказывал, как можно использовать $$\text{Lua}\mathrm\TeX$$ для запуска различных программных инструментов и утилит, установленных на серверах Overleaf — это чрезвычайно простой и удобный приём. Этот приём использовался для создания SVG-графики набранных $$\mathrm\TeX$$ таблиц, следующим образом. Из основного $$\mathrm\TeX$$ файла документа код набора каждой таблицы (созданный с использованием `\halign`) записывался в `.tex` файл. Это было достигнуто путём заключения кода таблицы между парой команд, которые я назвал `\beginscoop` и `\endscoop`. Вероятно, существует множество других способов добиться желаемого результата, но вот определения макросов, которые я использовал:

```latex
\def\cc{\catcode`\#=12\relax}
\long\def\scoop#1\endscoop{\global\fulltoks={#1}\egroup}
\def\beginscoop{\global\advance\numfigs by1\relax\bgroup\cc\scoop}
```

Используйте их так:

```latex
\beginscoop
\halign{...}
\endscoop
```

Обратите внимание, что `\endscoop` токен просто служит разделителем параметра `\scoop` макроса: $$\mathrm\TeX$$ и фактически отбрасывает `\endscoop` токен, так что нам на самом деле не нужно определять его (например, через `\def\endscoop{...}`).

Это $$\mathrm\TeX$$ код, содержащийся в `\halign{...}` сохраняется в `toks` регистр, называемый `\fulltoks`. Одна трудность, с которой я столкнулся (с $$\text{Lua}\mathrm\TeX$$) заключалась в необходимости не допустить `#` символов внутри `\halign{...}` преамбулы от «удвоения» до `##` при записи в `.tex` файл. Чтобы избежать этого, мне пришлось временно установить `\catcode`для `#` символам значение 12 перед сохранением $$\mathrm\TeX$$ кода (токенов) в `\fulltoks` токенном регистре.

Следующий шаг — записать токены, содержащиеся в `\fulltoks` в виде $$\mathrm\TeX$$ файла — поскольку я использовал $$\text{Lua}\mathrm\TeX$$ это оказалось *чрезвычайно* легко благодаря $$\text{Lua}\mathrm\TeX$$чудесному API Lua. Кратко говоря, я написал макрос под названием `\writefile{...}` который принимает в качестве параметра имя токенного регистра, токены которого вы хотите записать в файл (например, `\writefile{fulltoks}`). Внутри `\writefile{...}` макроса я использовал API Lua, чтобы получить текстовое представление `\fulltoks` токенного регистра:

```latex
\def\writefile#1{%
\directlua{
...
...
 local p=tex.toks["#1"]
...
...
}}
```

Вот снимок экрана, показывающий ещё немного `\writefile{...}` команду:

[![{{{alt}}}](/files/12d886f657d0ac6e66433353474aa974a619bc2f)](https://www.filepicker.io/api/file/ngeDmgRStGWvG044RE1A)

Язык Lua и API Lua, предоставляемый $$\text{Lua}\mathrm\TeX$$ часто могут упростить $$\mathrm\TeX$$ программные задачи, и именно благодаря этим полезным $$\text{Lua}\mathrm\TeX$$ и мощным возможностям я использую $$\mathrm\TeX$$ с \~2009 года — и остаюсь большим поклонником этого поистине замечательного $$\text{Lua}\mathrm\TeX$$ движка. Ладно, эта

реклама теперь завершается. $$\mathrm\TeX$$ Так легко получив `\fulltoks` код, хранящийся в $$\mathrm\LaTeX$$ он записывается в файл вместе с некоторым дополнительным кодом, чтобы превратить его в правильно сформированный

1. файл. Следующие шаги такие: `.tex` Обработать $$\text{pdf}\mathrm\LaTeX$$ файл, содержащий нашу таблицу, с помощью `.dvi` (в режиме DVI), чтобы он набрал таблицу и сгенерировал `dvisvgm` файл для $$\text{Lua}\mathrm\TeX$$ для обработки. Да, вы можете использовать $$\text{pdf}\mathrm\LaTeX$$для запуска [предыдущей статье](/latex/ru/podrobnye-stati/52-using-luatex-to-run-tools-and-utilities-installed-on-overleaf-s-servers.md).
2. —снова я использовал метод, описанный в `dvisvgm` И наконец, запустите `.dvi` для обработки $$\mathrm\TeX$$ файла, чтобы сгенерировать SVG-графику набранной
3. таблицы. **Чтобы получить сами SVG-графики, вы можете скачать ZIP-файл из Overleaf — не забудьте выбрать** параметр.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/ru/podrobnye-stati/47-tex-tables-how-tex-calculates-spanned-column-widths.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
