# Configuração: recursos e tabelas

> Os tipos de recurso e sua numeração, os estilos de tabela e de legenda, os diagramas em uma só tinta e os vídeos impressos

- Versão HTML: https://postext.dev/pt/docs/configuration-resources
- Última atualização: 2026-10-10
- Tempo de leitura: 5 min
- Outros idiomas: [en](https://postext.dev/en/docs/configuration-resources.md), [es](https://postext.dev/es/docs/configuration-resources.md), [ca](https://postext.dev/ca/docs/configuration-resources.md), [zh](https://postext.dev/zh/docs/configuration-resources.md), [ja](https://postext.dev/ja/docs/configuration-resources.md), [ar](https://postext.dev/ar/docs/configuration-resources.md)

## Em poucas palavras

Esta página reúne os ajustes de figuras, tabelas, diagramas e vídeos. O Postext os chama de recursos e numera cada tipo separadamente. Você define a aparência das tabelas: fios, fundos, tipografia e como elas se dividem entre páginas. Define como se escreve a legenda de uma figura ou de uma tabela. Também pode imprimir os diagramas em uma só tinta e escolher como um vídeo aparece no papel.

## Tipos de recurso

Um **tipo de recurso** é uma categoria que você mesmo define (*Figura*, *Tabela*, *Diagrama*, *Listagem*…) e que determina como os recursos daquele tipo são numerados, legendados e referenciados. A lista fica em `config.resourceTypes`; no Sandbox, ela é editada em **Design → Figuras e tabelas → Numeração e posicionamento**.

Quando `config.resourceTypes` não está definido, o Postext traz três tipos padrão embutidos: **Figura**, **Tabela** e **Vídeo**, cada um numerado separadamente como `{h1}.{n}` (reiniciando a cada título de nível 1) com contadores decimais. Uma lista sem o tipo `video` (a de um livro salvo antes de existirem vídeos) continua numerando os recursos de vídeo com tipo `video`: o tipo embutido Vídeo é acrescentado para eles (`effectiveResourceTypes(config, resources)`), e `defaultVideoResourceType(locale)` devolve esse tipo isoladamente. Os nomes seguem o idioma do documento: `config.locale`, senão `bodyText.hyphenation.locale`, senão o inglês (veja [Idioma do documento](https://postext.dev/pt/docs/configuration-text.md#idioma-do-documento)).

Os tipos padrão embutidos acompanham o idioma. A função exportada `defaultResourceTypes(locale = 'en')` traduz os nomes dos tipos, os rótulos curtos e os prefixos de legenda para o idioma do documento: o inglês gera *Figure*/*Fig.* e *Table*/*Tab.*; o espanhol gera *Figura*/*Fig.* e *Tabla*/*Tabla*; francês, alemão, italiano, português, catalão e holandês também têm os seus (a tabela em [Idioma do documento](https://postext.dev/pt/docs/configuration-text.md#idioma-do-documento) traz todos). Etiquetas regionais como `es-ES` são resolvidas pelo idioma, e qualquer idioma sem tradução recai no inglês. O comportamento da numeração (`numberingTemplate: '{h1}.{n}'`, `resetOn: 'h1'`, contadores decimais) não depende do idioma. Cada chamada devolve objetos novos, então você pode modificar o resultado à vontade:

```ts
import { defaultResourceTypes } from 'postext';

const types = defaultResourceTypes('es');
// => [{ id: 'figure', name: 'Figura', shortLabel: 'Fig.', captionPrefix: 'Figura',
//       numberingTemplate: '{h1}.{n}', resetOn: 'h1', counterFormat: 'decimal', … },
//     { id: 'table',  name: 'Tabla',  shortLabel: 'Tabla', captionPrefix: 'Tabla', … },
//     { id: 'video',  name: 'Vídeo',  shortLabel: 'Vídeo', captionPrefix: 'Vídeo', … }]
```

```ts
type ResourceCounterFormat =
  | 'decimal'
  | 'roman-lower'
  | 'roman-upper'
  | 'alpha-lower'
  | 'alpha-upper';

type ResourceCounterReset = 'never' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';

interface ResourcePlacement {
  position?: 'auto' | 'top' | 'bottom' | 'here'; // que espaço livre um flutuante pode ocupar; 'here' = inserção em linha na diretiva ::resource
  span?: 'column' | 'page' | 'side';             // uma coluna, a largura total do conteúdo ou a coluna lateral só de flutuantes
  rotate?: 'ccw' | 'cw';                         // um quarto de volta: uma tabela em paisagem numa página só para ela
  width?: number;                                // fração (0 < width < 1) da largura da coluna ou da página; padrão: a largura toda
  align?: 'left' | 'center' | 'right';           // onde fica um flutuante mais estreito que a coluna; padrão 'left'
  captionSide?: boolean;                         // legenda ao lado da figura, na coluna lateral de uma diagramação oneAndHalf (só flutuantes de coluna)
  columns?: number;                              // um flutuante 'column' ocupando esse número de colunas vizinhas (desde 1.18)
  wrap?: 'none' | 'left' | 'right' | 'start' | 'end'; // texto ao lado do recurso, naquele lado da coluna (desde 1.24)
  wrapGap?: Dimension;                           // espaço entre o recurso contornado e o texto (desde 1.24)
}

interface ResourceType {
  id: string;                          // id estável, referenciado por Resource.typeId
  name: string;                        // nome de exibição no singular, ex. "Figure"
  namePlural?: string;                 // plural opcional, ex. "Figures"
  shortLabel: string;                  // rótulo compacto para referências no texto, ex. "Fig."
  numberingTemplate: string;           // "{h1}.{n}" ou "{n}"
  resetOn: ResourceCounterReset;       // quando o contador {n} reinicia
  counterFormat: ResourceCounterFormat;// como {n} é formatado
  captionPrefix: string;               // anteposto à legenda, ex. "Figure"
  defaultPlacement?: ResourcePlacement;// posicionamento de reserva para os recursos deste tipo
}
```

`ResourcePlacement` tem a mesma forma que um recurso define no seu próprio `placement`. `position` escolhe o tipo de espaço livre que um flutuante pode ocupar: `auto` (o padrão) fica com o primeiro depois da primeira referência, `top` / `bottom` o restringem a esse tipo de faixa, `here` insere o recurso em linha. `span` define a extensão do flutuante: uma coluna, a largura total do conteúdo ou a coluna lateral só de flutuantes de uma diagramação de coluna e meia. `rotate` gira o recurso um quarto de volta e o transforma em flutuante de página inteira, numa página só para ele. `width` estreita o flutuante a uma fração da coluna (ou da página, num flutuante de página inteira), por exemplo uma tabela pequena numa coluna larga. `align` diz onde esse flutuante mais estreito fica (à esquerda por padrão, centralizado ou à direita) e também onde fica uma imagem mais estreita que o seu espaço: um bitmap menor que a coluna, ou uma imagem que `layout.fitFiguresToPage` reduziu. A legenda e a nota mantêm a medida do espaço. (Até o postext 1.4, uma imagem assim sempre ficava alinhada à esquerda.) `captionSide` põe a legenda ao lado da figura, na coluna lateral só de flutuantes de uma diagramação de coluna e meia (`layout.sideColumnRole: 'floats'`), alinhada com o topo da figura (com a base, num flutuante inferior); vale apenas para flutuantes de coluna, e numa página sem essa coluna a legenda continua embaixo da figura. Quando nem o recurso nem o seu tipo definem um posicionamento, o padrão embutido é `auto` / `column`. `shrink` (`'never'`, `'page'`, `'slot'`) e `minScale` (0.7 quando não definido) reduzem uma imagem flutuante ao espaço da sua posição em vez de levá-la adiante, e `captionMeasure: 'body'` compõe a legenda e a nota de uma imagem mais estreita que o seu espaço na largura da imagem (veja [Formato do documento › Posicionamento](https://postext.dev/pt/docs/document-format.md#posicionamento)); `layout.floatShrink` dá o padrão do documento para os dois primeiros. `wrap` põe uma inserção em linha ou um flutuante de uma coluna de um lado da coluna com o texto composto ao lado, a `wrapGap` de distância; `layout.wrap` guarda os padrões (veja [Formato do documento › Contorno de texto](https://postext.dev/pt/docs/document-format.md#contorno-de-texto)). `citingPage` deixa um flutuante `top` ou `auto` encabeçar a página ou a coluna onde cai a linha que o cita em vez de ocupar o primeiro espaço livre depois dela; `layout.floatsAtCitingPage` dá o padrão do documento e `layout.maxTopFraction` a fração da coluna que ele pode ocupar (veja [Formato do documento › Posicionamento](https://postext.dev/pt/docs/document-format.md#posicionamento)).

`columns` (desde o postext 1.18) estende um flutuante `span: 'column'` por esse número de colunas vizinhas numa página de várias colunas: uma foto ocupando duas das cinco colunas de um jornal. A sua medida são essas colunas mais as medianizes entre elas. Ele ocupa o topo de uma sequência de colunas vazias que começam na mesma altura, ou o pé da coluna que o cita e das colunas vazias seguintes; com tantas colunas quantas a página tem, ou mais, vira um flutuante de página inteira. É ignorado nos spans `'page'` e `'side'`, num recurso girado (`rotate`) e numa inserção em linha (`here`), e `captionSide` só vale para um flutuante de uma coluna de largura.

| Propriedade | Tipo | Descrição |
| --- | --- | --- |
| `id` | `string` | Identificador estável referenciado pelo `typeId` de cada recurso. É definido uma única vez, quando o tipo é criado; excluir um tipo que ainda é referenciado por recursos gera um aviso de **tipo órfão**. |
| `name` | `string` | Nome de exibição no singular. Usado pela referência no texto `style="full"` (ex.: `Figure 1.7`). |
| `namePlural` | `string` (opcional) | Nome de exibição no plural, para rótulos da interface e listas de recursos. |
| `shortLabel` | `string` | Abreviação compacta usada pelo estilo padrão de referência no texto (ex.: `Fig. 1.7`). |
| `numberingTemplate` | `string` | Modelo do número calculado. Veja **Tokens do modelo** abaixo. As formas comuns são `{h1}.{n}` (por capítulo, ex.: `2.3`) e `{n}` (uma contagem corrida única). |
| `resetOn` | `ResourceCounterReset` | `'never'` dá uma contagem corrida no documento inteiro; `'h1'`..`'h6'` reiniciam o contador `{n}` sempre que aparece um título daquele nível (ou de um nível superior). Ajuste-o ao nível de título que aparece no modelo, ex.: `{h1}.{n}` com `resetOn: 'h1'`. |
| `counterFormat` | `ResourceCounterFormat` | Como o contador `{n}` é impresso: decimal (`1, 2, 3`), romano minúsculo/maiúsculo (`i, ii` / `I, II`) ou alfabético minúsculo/maiúsculo (`a, b` / `A, B`). As grafias de páginas e listas também são aceitas (`'lower-roman'`, `'arabic'`…; veja [Grafias dos formatos de numeração](https://postext.dev/pt/docs/configuration-page-layout.md#grafias-dos-formatos-de-numeração)); um valor desconhecido conta em decimal e é reportado. Os tokens de título (`{h1}`…) sempre saem em decimal. |
| `captionPrefix` | `string` | Texto anteposto à legenda da figura ou da tabela. O número calculado vem depois do prefixo: uma legenda sai como `{captionPrefix} {number}. {caption text}`, ex.: **Figura 1.7. A planta original.** Um tipo com `numberingTemplate` vazio não tem número, e a sua legenda fica `{captionPrefix}. {caption text}`. Os espaços no fim do prefixo são descartados, e um prefixo que já termina em `.`, `:`, `!`, `?` ou `…` (ou na forma de largura total de um deles) não recebe um segundo ponto: **Pr. Linhas a 0°**. |
| `defaultPlacement` | `ResourcePlacement` (opcional) | Posicionamento usado pelos recursos deste tipo que não definem o próprio `placement`: `position`, `span`, `rotate`, `width`, `align`, `captionSide` e `columns`, cada um resolvido separadamente. Quando nem o recurso nem o tipo definem um campo, vale o padrão embutido: `auto` / `column`, sem giro, largura total, alinhado à esquerda, legenda embaixo da figura. Veja [Numeração e referências](https://postext.dev/pt/docs/configuration-resources.md#numeração-e-referências) abaixo para a cadeia de resolução e [Formato do documento › Recursos](https://postext.dev/pt/docs/document-format.md#recursos) para o que cada valor faz, inclusive nos recursos girados. |
| `captionStyle` | `CaptionStyleConfig` (opcional) | Substituição parcial do [estilo de legenda](https://postext.dev/pt/docs/configuration-resources.md#estilo-de-legenda) para os recursos deste tipo. Só as chaves que você define substituem o `captionStyle` global; todo o resto é herdado (uma `color` substituída também define as cores do rótulo e da nota, a menos que elas sejam definidas explicitamente). Uso típico: tabelas com legenda *em cima*, sobre uma barra colorida, enquanto as figuras mantêm a legenda embaixo. Referências à paleta são resolvidas como qualquer outra cor. |

### Tokens do modelo

`numberingTemplate` é processado pelo mesmo mecanismo da numeração de títulos (veja [Títulos](https://postext.dev/pt/docs/configuration-text.md#títulos)). Ele reconhece dois tipos de token:

- `{n}`: o contador do tipo, formatado conforme `counterFormat`. É o valor que aumenta a cada recurso e reinicia de acordo com `resetOn`.
- `{h1}` … `{h6}`: os números de título vigentes no ponto da primeira referência, sempre em decimal. `{h1}` é o número do título de nível 1 atual, `{h2}` o de nível 2, e assim por diante.

Qualquer outro texto é literal. Uma barra invertida escapa um `{`, `}` ou `\` literal. Quando um token de título não tem valor naquele ponto (ex.: `{h1}` antes de qualquer título de nível 1), ele desaparece junto com o separador vizinho; assim, `{h1}.{n}` se reduz ao contador sozinho.

Um modelo vazio (`''`) não imprime número, embora o tipo continue contando os seus recursos: a legenda fica *Do. Linhas a 0°* e um `:ref` imprime só o rótulo (*Do*). Até o postext 1.4, essa legenda saía *Do .Linhas a 0°* e a referência terminava num espaço não separável.

| Modelo | Com `h1 = 2`, contador = 3 | Observações |
| --- | --- | --- |
| `{n}` | `3` | Uma contagem corrida única. Combine com `resetOn: 'never'`. |
| `{h1}.{n}` | `2.3` | Por capítulo. Combine com `resetOn: 'h1'`. |
| `{h1}.{h2}.{n}` | `2.0.3` | Por seção. Combine com `resetOn: 'h2'`. |

### Numeração e referências

O número que um tipo de recurso produz é o que `:ref` imprime e o que vem depois do prefixo da legenda. `:ref{id}` é a forma principal: a primeira referência na ordem de leitura **incorpora** o recurso, que flutua para o primeiro espaço livre depois dela, seja o pé da coluna que o cita, o topo ou o pé da próxima coluna vazia, ou uma faixa da página seguinte (conforme o posicionamento resolvido: `position: 'auto' | 'top' | 'bottom' | 'here'` e `span: 'column' | 'page' | 'side'`, mais `rotate`, `width`, `align` e `captionSide`, resolvidos primeiro no recurso, depois no `defaultPlacement` do tipo e por fim no padrão embutido `auto` / `column`; `'top'` / `'bottom'` restringem a busca a esse tipo de espaço). A inserção em bloco `::resource{id}` é opcional e só é necessária para `placement.position: 'here'`, uma inserção em linha, sem flutuar, num ponto exato do fluxo. A gramática completa do lado do documento (as duas formas, mais as opções `style` e `text` de `:ref`) está documentada em [Formato do documento › Recursos](https://postext.dev/pt/docs/document-format.md#recursos), inclusive como a ordem das primeiras referências define a contagem.

Isso espelha a numeração de títulos: assim como um nível de título tem um `numberingTemplate`, um tipo de recurso também tem, mas o contador do recurso (`{n}`) avança a cada primeira referência, e não a cada título, e `resetOn` o amarra de volta à hierarquia de títulos.

### O que é numerado

Um recurso é numerado quando o texto o referencia (com `:ref` ou com uma inserção `::resource`), na ordem dessas primeiras referências, qualquer que seja o seu posicionamento: flutuante, em linha (`here`), na coluna lateral ou girado. Um recurso que só um design desenha (um elemento `image` de uma abertura de capítulo, de um cabeço ou de uma página de parte) ou que nada referencia não recebe número nem faz avançar o contador do seu tipo. Assim, num ensaio fotográfico cujas pranchas sangradas são imagens das aberturas e cuja única prancha menor é um flutuante citado, esse flutuante é a prancha **I**, por mais pranchas que as aberturas tenham mostrado antes; numere as pranchas das aberturas no próprio design delas (com um atributo como `{attr.plate}`) e deixe o contador para as pranchas que o texto cita.

Num livro diagramado capítulo a capítulo (o Sandbox, `buildBundle`, ou `buildDocument` com os contadores que `continuationAfter()` repassa), vale a primeira referência do livro inteiro: o recurso mantém o número que recebeu no capítulo que o cita primeiro, e só esse capítulo o posiciona. O `:ref` de um capítulo posterior imprime esse número e não posiciona nada, e ali uma inserção `::resource` de um recurso flutuante é só mais uma referência (uma inserção em linha `here` continua sendo composta onde está escrita). Essa referência vira link para a figura quando a figura está na mesma saída: um PDF do livro inteiro aponta para a página do capítulo anterior. Um capítulo renderizado sozinho, em HTML ou em PDF, a compõe como texto simples na cor dos links, já que a figura não está naquele documento. Um host que junta o HTML dos capítulos numa só página passa a `renderToHtml` os recursos que os capítulos ancoram, como `refTargets`, e essa referência volta a apontar para a figura do capítulo anterior:

```ts
import { anchoredResourceIds, buildBundle, renderToHtml } from 'postext';

const docs = buildBundle(bundle);
const refTargets = new Set(docs.flatMap((d) => [...anchoredResourceIds(d)]));
const html = docs.map((d) => renderToHtml(d, { refTargets })).join('');
```

**Mudou no postext 1.5:** até a 1.4, cada capítulo que referenciava uma figura a fazia flutuar de novo, e o HTML de todo `:ref` era um link, estivesse a figura na página ou não.

`{h1}` é a contagem corrida dos títulos de nível 1: todo H1 a faz avançar, a menos que o seu estilo de título defina `numbered: false` (um `numberingTemplate` vazio esconde o número do título, mas não interrompe a contagem). Por isso, um artigo cujo único H1 é o título numera as figuras como `1.1`, `1.2`… com os tipos embutidos `{h1}.{n}`. Há duas maneiras de imprimir Figura 1, 2…:

- um tipo numerado `{n}` com `resetOn: 'never'` (com `resetOn: 'h1'` a contagem recomeçaria a cada H1);
- um [estilo de título](https://postext.dev/pt/docs/configuration-styles.md#estilos-de-título) com `numbered: false` no título do artigo e em qualquer outro H1 que não deva contar: um título assim não faz `{h1}` avançar, mas o deixa como estava (vazio antes do primeiro H1 contado, onde `{h1}.{n}` se reduz ao contador sozinho) e nunca dispara `resetOn: 'h1'`, de modo que a contagem passa direto por ele. Depois de `# Introduction` e da sua Figura 1.1, a primeira figura sob um `# Appendix` sem número é a 1.2, e não a 2.1.

```ts
// Figura 1, 2, 3… num documento de um só artigo
resourceTypes: defaultResourceTypes('en').map((t) => ({ ...t, numberingTemplate: '{n}', resetOn: 'never' })),
```

## Estilo de tabela

A propriedade `tableStyle` controla a tipografia e a decoração dos recursos de tabela: de todas as tabelas, a menos que uma delas escolha um [estilo de tabela nomeado](https://postext.dev/pt/docs/configuration-resources.md#estilos-de-tabela-nomeados). As células do corpo e as de cabeçalho têm estilos independentes. Família, tamanho e cores herdam o texto corrido resolvido quando não são definidos, então um documento sem `tableStyle` compõe as tabelas com a tipografia do texto corrido.

```ts
const config: PostextConfig = {
  tableStyle: {
    headerBold: true,
    headerBackground: { hex: '#f0f0f0', model: 'hex' },
    borders: true,
    borderWidth: { value: 0.75, unit: 'pt' },
  },
};
```

| Propriedade | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `bodyFontFamily` | `string` | fonte do texto corrido | Família tipográfica das células do corpo. |
| `bodyFontSize` | `Dimension` | tamanho do texto corrido | Tamanho da fonte das células do corpo. |
| `bodyColor` | `ColorValue` | cor do texto corrido | Cor do texto das células do corpo. |
| `headerFontFamily` | `string` | fonte do texto corrido | Família tipográfica das células de cabeçalho. |
| `headerFontSize` | `Dimension` | tamanho do texto corrido | Tamanho da fonte das células de cabeçalho. |
| `headerColor` | `ColorValue` | cor do texto corrido | Cor do texto das células de cabeçalho. |
| `headerBold` | `boolean` | `true` | Compor as células de cabeçalho em negrito. |
| `headerItalic` | `boolean` | `false` | Compor as células de cabeçalho em itálico. |
| `headerLetterSpacing` | `Dimension` | `0pt` | Espaçamento entre letras (tracking) depois de cada caractere de uma célula de cabeçalho, espaços incluídos, como o `letter-spacing` do CSS. Valores positivos afastam as letras (um cabeçalho em maiúsculas costuma pedir de `0.05em` a `0.1em`), valores negativos as aproximam. Um `em` é o tamanho do cabeçalho. As linhas do cabeçalho são medidas com esse valor, então quebram, centralizam e alinham já com o tracking, e canvas, HTML e PDF o pintam igual. Vale para todas as células de cabeçalho: as linhas de cabeçalho e qualquer célula marcada como `isHeader`. |
| `headerTextTransform` | `'none' \| 'uppercase'` | `'none'` | Compõe as células de cabeçalho em maiúsculas. O texto mantém o comprimento, para que o Sandbox continue associando cada letra ao texto-fonte: uma letra cuja maiúscula é mais longa (`ß`) fica como está. As referências a recursos mantêm o seu rótulo. |
| `headerBackgroundEnabled` | `boolean` | `true` | Pinta um fundo atrás da linha de cabeçalho. |
| `headerBackground` | `ColorValue` | `#f0f0f0` | Cor de fundo da linha de cabeçalho. |
| `bodyBackgroundEnabled` | `boolean` | `false` | Pinta um fundo atrás das linhas do corpo. |
| `bodyBackground` | `ColorValue` | `#ffffff` | Cor de fundo das linhas do corpo (só é pintada quando ativada). |
| `bodyAlternateBackgroundEnabled` | `boolean` | `false` | Linhas zebradas: preenche uma a cada duas linhas do corpo com `bodyAlternateBackground`. Veja [linhas zebradas](https://postext.dev/pt/docs/configuration-resources.md#linhas-zebradas). |
| `bodyAlternateBackground` | `ColorValue` | `#f2f2f2` | Fundo das linhas alternadas do corpo (só é pintado quando ativado). |
| `borders` | `boolean` | `true` | Desenha as bordas das células. |
| `borderColor` | `ColorValue` | cor do texto corrido | Cor do traço das bordas. |
| `borderWidth` | `Dimension` | `0.75pt` | Espessura do traço das bordas (≈1 px a 96 DPI; acompanha o DPI da página). `'booktabs'` não o usa: tem as suas próprias espessuras. |
| `cellPadding` | `Dimension` | `0.375em` | Margem interna de todas as células. |
| `rules` | `'grid' \| 'horizontal' \| 'outer' \| 'none' \| 'booktabs'` | `'grid'` | Quais fios traçar quando `borders` está ativado: a grade completa das células, só os fios horizontais (borda superior e inferior de cada linha, sem verticais), só a moldura externa, nenhum ou os três fios de uma tabela de revista (veja [fios booktabs](https://postext.dev/pt/docs/configuration-resources.md#fios-booktabs)). |
| `borderRadius` | `Dimension` | `0` | Raio dos cantos da moldura externa da tabela. A moldura é traçada arredondada (com os fios `grid` ou `outer`), os fundos das células e o fundo do cabeçalho são recortados por ela (também com `rules: 'none'` ou com as bordas desativadas) e os fios horizontais são aparados no seu contorno externo; os fios internos continuam retos. Uma tabela dividida entre páginas arredonda os cantos superiores da primeira parte e os inferiores da última. Limitado à metade da largura e da altura da tabela. Os fios `'booktabs'` continuam retos (os preenchimentos são recortados). |
| `heavyRuleWidth` | `Dimension` | `0.08em` | Booktabs: os fios acima da tabela e abaixo da última linha. Um `em` é o corpo das células. |
| `lightRuleWidth` | `Dimension` | `0.05em` | Booktabs: o fio sob as linhas de cabeçalho e os fios de grupo. |
| `spanRuleWidth` | `Dimension` | `0.03em` | Booktabs: os fios sob as células de cabeçalho que abrangem várias colunas. |
| `spanRules` | `'trimmed' \| 'full' \| 'none'` | `'trimmed'` | Booktabs: os fios sob as células de cabeçalho que abrangem várias colunas, acima da última linha de cabeçalho: encurtados nas duas pontas em `spanRuleTrim`, de ponta a ponta da célula ou nenhum. |
| `spanRuleTrim` | `Dimension` | `0.5em` | Booktabs: quanto um fio de agrupamento encurtado perde em cada ponta. |
| `groupRules` | `boolean` | `false` | Booktabs: um fio fino acima de cada linha do corpo que abre um grupo. |
| `continuedFootRule` | `'bottom' \| 'light' \| 'none'` | `'light'` | Booktabs: o que fecha a parte de uma tabela dividida que continua na página seguinte. |
| `overflow` | `'split' \| 'clip' \| 'hide'` | `'split'` | O que acontece com uma tabela mais alta que a página: continuar nas páginas seguintes, manter só as linhas que cabem ou deixá-la de fora. Com `splitInline`, também com uma tabela colocada no texto que não cabe no resto da sua coluna. Veja abaixo. |
| `splitInline` | `boolean` | `true` | Aplica `overflow` também às tabelas posicionadas `here`: uma tabela em linha que não cabe no espaço que resta na sua coluna é cortada entre linhas e continua no alto da coluna seguinte. `false` leva essa tabela inteira para a coluna seguinte, como até o postext 1.24; as configurações guardadas por versões anteriores cujos capítulos incorporam um recurso são lidas com `false`. Veja [Tabelas mais altas que a página](https://postext.dev/pt/docs/configuration-resources.md#tabelas-mais-altas-que-a-página). Desde o postext 1.25. |
| `continuedSuffix` | `string` | `'(cont.)'` | Acrescentado em itálico, depois de um espaço, à legenda de cada parte que continua uma tabela dividida; um sufixo que começa com um caractere chinês ou de largura total (`'（续）'`) fica colado à legenda. |
| `continuesMarkerEnabled` | `boolean` | `true` | Coloca um aviso embaixo de cada parte que continua na página seguinte. |
| `continuesMarker` | `string` | `'Continued'` / `'Continúa'` | Texto desse aviso, alinhado à direita embaixo da parte, na tipografia das notas (veja [estilo de legenda](https://postext.dev/pt/docs/configuration-resources.md#estilo-de-legenda)). O padrão acompanha o idioma do documento (veja [Idioma do documento](https://postext.dev/pt/docs/configuration-text.md#idioma-do-documento) para os oito idiomas). |

As espessuras das bordas mantêm as frações: um fio de `0.5pt` é traçado como filete no PDF e na tela, em vez de ser arredondado para um pixel inteiro (o mínimo é 0,25 px).

### Linhas zebradas

Tabelas longas de dados ficam mais fáceis de acompanhar na horizontal quando uma linha a cada duas é tingida. `bodyAlternateBackgroundEnabled` ativa as faixas e `bodyAlternateBackground` define a cor delas:

```ts
const config: PostextConfig = {
  tableStyle: {
    bodyBackgroundEnabled: true,
    bodyBackground: { hex: '#ffffff', model: 'hex' },
    bodyAlternateBackgroundEnabled: true,
    bodyAlternateBackground: { hex: '#eef3fa', model: 'hex' },
  },
};
```

As linhas são contadas a partir da primeira linha depois das linhas de cabeçalho (`TableModel.headerRowCount`, ou as linhas iniciais formadas por células de cabeçalho): essa linha fica com `bodyBackground` (ou sem fundo, enquanto `bodyBackgroundEnabled` estiver desativado), a seguinte recebe o fundo alternado, e assim por diante. A contagem segue o modelo da tabela, não a página; assim, uma tabela dividida entre páginas mantém a faixa de cada linha em todas as páginas, e uma célula mesclada entre linhas recebe a faixa da sua primeira linha. As células de cabeçalho mantêm o fundo do cabeçalho, o `background` próprio de uma célula prevalece sobre os dois, e uma cor ligada à paleta acompanha a paleta. Um [estilo de tabela nomeado](https://postext.dev/pt/docs/configuration-resources.md#estilos-de-tabela-nomeados) define os dois campos como qualquer outro, então um estilo pode ser zebrado sem que as outras tabelas do documento sejam; no Sandbox, eles são a chave *Linhas zebradas* e a sua cor, em **Células do corpo**.

Na VDT, as células das linhas alternadas levam `alternate: true`, e o layout da tabela leva `bodyAlternateBackground`. `tableCellFill(table, cell)` devolve o fundo com que uma célula é pintada (o próprio, o do cabeçalho, o alternado ou o do corpo), que é o que os renderizadores de canvas, HTML e PDF pintam. Fundos vizinhos se encontram sem emenda: um navegador com proporção de pixels fracionária ou um leitor de PDF suaviza cada fundo separadamente e deixaria a página aparecer num filete entre duas células, por isso os renderizadores de HTML e PDF pintam `tableCellFillRects(table)` (o fundo de cada célula com uma tira sobre cada borda que ela compartilha com uma célula pintada depois, que então a cobre), e o canvas ajusta os fundos aos pixels do dispositivo.

### Fios booktabs

As tabelas de revistas e livros didáticos costumam ser compostas com três fios e nenhuma linha vertical: um grosso acima da tabela, um fino sob o cabeçalho e outro grosso sob a última linha, com fios curtos sob os cabeçalhos que agrupam várias colunas (o pacote `booktabs` do LaTeX: `\toprule`, `\midrule`, `\cmidrule`, `\bottomrule`). `rules: 'booktabs'` traça esse padrão:

```ts
const config: PostextConfig = {
  tableStyle: {
    rules: 'booktabs',
    borderColor: { hex: '#000000', model: 'hex' },
    headerBackgroundEnabled: false,
  },
};
```

- O fio acima da tabela e o fio sob a última linha têm a espessura `heavyRuleWidth` (`0.08em`); o fio sob as linhas de cabeçalho, `lightRuleWidth` (`0.05em`). Uma tabela sem linhas de cabeçalho não tem fio de cabeçalho.
- Uma célula de cabeçalho que abrange várias colunas acima da última linha de cabeçalho recebe embaixo um fio de espessura `spanRuleWidth` (`0.03em`). Com `spanRules: 'trimmed'` (o padrão) o fio é encurtado em `spanRuleTrim` (`0.5em`) nas duas pontas, para que os fios de dois cabeçalhos vizinhos não se toquem; `'full'` o estende de ponta a ponta da célula e `'none'` o omite.
- `groupRules: true` acrescenta um fio fino acima de cada linha do corpo que abre um grupo (uma única célula de ponta a ponta da tabela, ou uma linha de células de cabeçalho), exceto quando a linha abre a tabela ou uma página, onde já está o fio de cabeçalho.
- As espessuras são calculadas sobre o corpo das células (`bodyFontSize`), de modo que um cabeçalho de corpo maior não engrossa o seu fio. Uma espessura `0` omite esse fio.
- Os fios usam `borderColor` (uma cor ligada à paleta segue a paleta e `:::part palette`), e `borders: false` os desliga. `borderWidth` não se aplica, nem `borderRadius`: os fios continuam retos, enquanto os preenchimentos das células continuam recortados pela moldura arredondada. Os preenchimentos do cabeçalho, as linhas zebradas e o `background` próprio de uma célula funcionam como nos outros padrões, sob os fios.
- Uma tabela dividida entre páginas repete as linhas de cabeçalho em cada parte, então toda parte começa com o fio grosso e o fio de cabeçalho. O fio grosso sob a última linha fecha só a última parte; uma parte que continua na página seguinte termina com `continuedFootRule`: um fio fino (`'light'`, o padrão), o grosso (`'bottom'`) ou nenhum (`'none'`).

A diagramação calcula os fios uma única vez. A tabela do VDT os leva como `strokes` (`{ x1, y1, x2, y2, widthPx }`, relativos ao canto superior esquerdo do corpo da tabela), e a tela, o visualizador HTML, o PDF e o EPUB de layout fixo traçam exatamente esses; num PDF etiquetado são artefatos de layout. O EPUB refluível os escreve como bordas CSS da tabela e do seu cabeçalho, com os fios encurtados desenhados como linhas de fundo. No Sandbox, **Booktabs** no seletor *Fios* mostra esses campos e esconde *Espessura da borda* e *Raio dos cantos*.

### Estilos de tabela nomeados

Um documento raramente compõe todas as tabelas iguais: uma lista de verificação numa grade azul-marinho com moldura arredondada, uma linha de opções emoldurada só pela borda externa, uma tabela de dados com simples fios horizontais. `tableStyles` declara variantes nomeadas, e um recurso de tabela escolhe uma delas com `table.styleId`. Cada campo que um estilo deixa sem definir é lido primeiro de `tableStyle` e depois do texto corrido, então um estilo declara só o que distingue as suas tabelas. Uma tabela sem `styleId`, ou com um id que nenhum estilo declara, mantém `tableStyle`; um documento sem `tableStyles` sai exatamente como antes.

```ts
const config: PostextConfig = {
  tableStyle: {
    borderColor: { hex: '#163a76', model: 'hex' },
    borderWidth: { value: 1.3, unit: 'pt' },
    borderRadius: { value: 10, unit: 'pt' },
  },
  tableStyles: [
    {
      id: 'option',
      name: 'Option row',
      rules: 'outer',
      borderColor: { hex: '#7a9cc6', model: 'hex' },
      borderWidth: { value: 1, unit: 'pt' },
      borderRadius: { value: 8, unit: 'pt' },
      headerBackgroundEnabled: false,
    },
  ],
};

// Nos recursos: esta tabela é composta no estilo "option".
const resource: Resource = {
  id: 'choices', typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0,
  table: { model: { rows: [/* … */] }, styleId: 'option' },
};
```

Cada entrada aceita todos os campos de `tableStyle`, mais `id` (o que `table.styleId` referencia) e um `name` opcional para o editor (por padrão, o id). Tudo o que um estilo pode definir vale por tabela: tipografia, fundos, bordas, fios, raio dos cantos, margem interna e o comportamento de transbordamento com os seus textos de continuação. `resolveTableStylesConfig(styles, tableStyle, resolvedBodyText, locale?)` devolve a lista resolvida, `pickTableStyle(resolved, styleId)` devolve o estilo em que uma tabela é composta, e `stripTableStylesDefaults` descarta os campos não definidos (mantém um campo igual ao seu padrão embutido, que ainda substitui um valor diferente de `tableStyle`). Num EPUB refluível, um estilo nomeado é uma classe da tabela (`pt-table-<id>`), estilizada pela folha de estilos do livro.

### Tabelas mais altas que a página

Uma tabela flutuante que não cabe na página nova que lhe é oferecida não é espremida nem transborda: com `overflow: 'split'` (o padrão), o motor a corta entre linhas, na última borda que cabe na página, e a continua nas páginas seguintes, quantas forem necessárias. Cada parte de continuação repete as linhas de cabeçalho da tabela (`TableModel.headerRowCount`, ou as linhas iniciais formadas por células de cabeçalho quando ele não está definido) e traz a legenda de novo, com `continuedSuffix` depois da descrição: “Tabela 6-4. Título *(cont.)*”. Toda parte que continua recebe `continuesMarker` embaixo, alinhado à direita, na tipografia das notas; a nota da tabela fica para a última parte. Um corte nunca atravessa uma célula mesclada (uma célula com rowspan passa inteira para a parte seguinte), e uma linha que encabeça as linhas abaixo dela (uma única célula ocupando a tabela toda) é levada para a parte seguinte, em vez de ficar sozinha no pé de uma página. Uma tabela booktabs fecha cada parte que continua com `continuedFootRule` (veja [fios booktabs](https://postext.dev/pt/docs/configuration-resources.md#fios-booktabs)).

**Onde termina a primeira parte.** Uma tabela à qual é oferecido o topo de uma coluna vazia depois da sua referência fica com as linhas que cabem ali e continua no espaço seguinte. Ela preenche a coluna até o pé quando tem a coluna só para si: uma parte que deixaria menos de três linhas de texto embaixo dela fica também com essas linhas, em vez de deixar um toco de texto. Quando a coluna já tem outra faixa de flutuantes (uma figura de página inteira no topo da página, por exemplo), a parte para pelo menos três linhas de texto antes do pé, o espaço para texto que qualquer flutuante deixa quando divide a coluna com outro, de modo que a coluna termina com algum texto embaixo da tabela e não só com flutuantes. Para que uma tabela longa vá até o pé da coluna, cite-a num ponto em que a página onde ela começa não tenha outro flutuante (depois da página de uma figura de página inteira, por exemplo), ou ajuste as linhas dela à coluna.

`'clip'` mantém as linhas iniciais que cabem na página e descarta as outras sem aviso (a nota continua fechando a parte); `'hide'` deixa a tabela de fora por completo. Os dois só valem quando a tabela é mais alta que uma página: uma tabela que cabe é posicionada inteira em qualquer modo.

**Tabelas colocadas no texto.** Uma tabela posicionada `here` (incorporada com `::resource`) segue as mesmas regras desde o postext 1.25 (`splitInline`, ativado por padrão). Quando não cabe no espaço que resta na sua coluna, é cortada entre linhas: a primeira parte mantém o vão de flutuante acima dela e pelo menos as linhas de cabeçalho e duas linhas do corpo (com menos, a tabela inteira começa na coluna seguinte, como antes); cada parte seguinte abre a coluna seguinte sem vão acima dela, composta na largura dessa coluna (num layout de coluna e meia, uma parte que cai na coluna estreita é composta na largura dela), e o texto depois da linha `::resource` vem depois da última parte. A repetição do cabeçalho, a legenda com o sufixo, o marcador, a nota na última parte, o mínimo de três linhas na parte final e os cortes que respeitam as células mescladas e as linhas que encabeçam um grupo são os de uma tabela flutuante. Uma tabela com menos de cinco linhas do corpo nunca é cortada. `'clip'` mantém as linhas iniciais de uma tabela em linha mais alta que uma coluna, posicionada no alto de uma coluna; `'hide'` deixa essa tabela de fora; uma mais baixa passa inteira para a coluna seguinte nos dois modos. Uma página que uma tabela em linha abre só reserva, livre dos flutuantes que recebe, o espaço da primeira parte, de modo que uma figura de página inteira que estava à espera encabeça essa página e a tabela continua embaixo dela. As tabelas em linha numa página vertical e as tabelas dentro de um boxe não são cortadas. `splitInline: false` leva uma tabela em linha inteira para a coluna seguinte, como até o postext 1.24.

Os textos de continuação têm padrão por idioma do documento (`locale`, senão o idioma de hifenização): inglês `(cont.)` / `Continued`, espanhol `(cont.)` / `Continúa`, e da mesma forma em francês, alemão, italiano, português, catalão e holandês (listados em [Idioma do documento](https://postext.dev/pt/docs/configuration-text.md#idioma-do-documento)).

O conteúdo das células é markdown em linha, e uma quebra de linha dentro de uma célula (uma nova linha, ou `\\` como nas legendas e notas) começa um novo parágrafo. Um parágrafo que começa com um marcador ou travessão (`•`, `-`, `*`, `–`) ou com um número (`1.`, `1)`) seguido de espaço é composto como item de lista: o marcador é pintado como foi escrito, o texto se pendura nele com o `unorderedLists.gap` do documento, as linhas seguintes se alinham com o texto, e dois espaços iniciais aninham um nível. Assim, uma célula escrita como `• Ofrece elección\n• Acomoda a personas diestras y zurdas` sai como uma lista de dois itens. Uma linha só com espaços comuns não acrescenta nada; uma linha com um espaço não separável (U+00A0) é uma linha da célula, como no CommonMark, então `1\n` seguido de um espaço não separável faz a linha da tabela ter duas linhas de altura. Um espaço não separável no fim do texto de uma célula mantém a sua largura: `760` e um espaço não separável, alinhados à direita sobre `(231)`, terminam um espaço antes da borda, o que aproxima o 0 do 1. Os algarismos só se alinham exatamente onde o espaço é tão largo quanto o parêntese, e na maioria das fontes ele é mais estreito. (Até o postext 1.4, ambos eram descartados.)

As larguras das colunas pertencem ao modelo da tabela, não ao estilo: `TableModel.columnWidths` é uma lista opcional de pesos relativos, um por coluna, normalizados na hora do layout; `[2, 1, 1]` dá à primeira coluna metade da largura. Uma lista ausente, de comprimento errado ou com um peso não positivo recai na divisão em partes iguais. O editor de tabelas mantém a lista alinhada quando colunas são adicionadas ou removidas.

### Construção de modelos de tabela

Um `TableModel` é uma grade organizada por linhas, e cada célula é diagramada pela sua posição nela: `rows[r][c]` fica na coluna `c`. Por isso, uma célula mesclada mantém na grade as células que cobre, cada uma marcada com `hiddenBy` apontando para a sua célula principal, ao contrário de uma tabela HTML, que as omite. As funções de modelo exportadas por `postext` preservam essa forma; são funções puras que devolvem um modelo novo: `mergeCells(model, { start, end })` e `unmergeCell(model, at)`, `addRow`, `addColumn`, `removeRow`, `removeColumn`, `setCellContent`, `setCellImage`, `setCellBackground` e `setAlignment`. As quatro funções de linhas e colunas mantêm as mesclagens inteiras: uma linha ou coluna adicionada dentro de um bloco mesclado o alarga, uma adicionada antes dele o desloca, uma removida dele o encolhe (um bloco que perde a primeira linha ou coluna mantém o conteúdo na sua nova célula superior esquerda), e todo `hiddenBy` continua apontando para a sua célula principal.

`parseTSV(text, options?)` monta um modelo a partir de texto separado por tabulações (um intervalo colado de uma planilha): as linhas se dividem nas quebras de linha, as células nas tabulações, e as linhas curtas são completadas para que a grade fique retangular. `headerRows` transforma as linhas iniciais em linhas de cabeçalho: as células delas recebem `isHeader` e o modelo recebe `headerRowCount`, então uma tabela dividida entre páginas as repete.

```ts
import { parseTSV, mergeCells } from 'postext';

let model = parseTSV('Part\tQty\tNote\nBolt\t4\tM6\nNut\t8\t', { headerRows: 1 });
// model.headerRowCount === 1; model.rows[0][0] é { content: 'Part', isHeader: true }
model = mergeCells(model, { start: { row: 2, col: 1 }, end: { row: 2, col: 2 } });
// rows[2][1] recebe colSpan: 2; rows[2][2] continua na grade com hiddenBy: { row: 2, col: 1 }
```

`tableGridIssues(model)` verifica a grade. Devolve uma lista vazia para um modelo correto e, caso contrário, todos os pontos onde a grade se quebra, na ordem das linhas: `spanOverlap`, uma célula visível sob o `colSpan` / `rowSpan` de outra célula (`coveredBy` indica essa célula), que é o que acontece quando se omite uma célula coberta à maneira do HTML, já que todas as células seguintes deslizam para cima da mesclagem; e `missingCells`, uma linha que termina antes da última coluna sem uma mesclagem cobrindo o resto, o que deixa um buraco.

```ts
import { tableGridIssues } from 'postext';

tableGridIssues({
  rows: [
    [{ content: 'A', colSpan: 2 }, { content: 'C' }],
    [{ content: '1' }, { content: '2' }, { content: '3' }],
  ],
});
// => [{ kind: 'spanOverlap', row: 0, col: 1, coveredBy: { row: 0, col: 0 } },
//     { kind: 'missingCells', row: 0, col: 2 }]
```

Uma tabela usada no documento cuja grade tenha esses problemas é reportada em `doc.contentWarnings` como `raggedTableGrid` (veja [Avisos no documento](https://postext.dev/pt/docs/configuration-programmatic-usage.md#avisos-no-documento)).

## Estilo de legenda

A propriedade `captionStyle` controla as legendas dos recursos (a linha `Figure 1 — …` embaixo, ou em cima, de imagens, SVGs e tabelas). O rótulo numerado e a descrição usam a mesma fonte e o mesmo tamanho (uma limitação do motor), mas o rótulo pode ter peso, itálico e cor próprios. Família, tamanho e cor herdam o texto corrido quando não são definidos. A legenda pode ficar **acima** do recurso (a convenção usual nas tabelas) e ser composta sobre uma **barra** colorida da largura do bloco; uma **nota** opcional, menor (linha de fonte, créditos: `Resource.note`), recebe estilo pelo subobjeto `note`. Um tipo de recurso pode substituir qualquer um desses campos para os seus próprios recursos com `ResourceType.captionStyle` (veja [Tipos de recurso](https://postext.dev/pt/docs/configuration-resources.md#tipos-de-recurso)).

```ts
const config: PostextConfig = {
  captionStyle: {
    align: 'center',
    labelBold: true,
    labelColor: { hex: '#295AA3', model: 'hex' },
    descriptionItalic: true,
    position: 'above',
    backgroundEnabled: true,
    padding: { value: 0.35, unit: 'em' },
    note: { italic: true, align: 'left' },
  },
};
```

| Propriedade | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `fontFamily` | `string` | fonte do texto corrido | Família tipográfica da legenda (rótulo e descrição). |
| `fontSize` | `Dimension` | tamanho do texto corrido | Tamanho da fonte da legenda (rótulo e descrição). |
| `color` | `ColorValue` | cor do texto corrido | Cor do texto da descrição. |
| `align` | `'left' \| 'center' \| 'right' \| 'justify' \| 'start' \| 'end'` | `'left'` | Alinhamento horizontal das linhas da legenda. `'justify'` estende todas as linhas, menos a última, à largura total. Numa barra de legenda, as linhas se alinham dentro da margem interna dela; uma legenda lateral se alinha dentro da sua própria largura. |
| `gap` | `Dimension` | `0.75em` | Espaço vertical entre o recurso e a legenda. |
| `labelBold` | `boolean` | `true` | Compõe o rótulo numerado (ex.: `Figure 1`) em negrito. |
| `labelItalic` | `boolean` | `false` | Compõe o rótulo numerado em itálico. |
| `labelColor` | `ColorValue` | `color` da legenda | Cor do rótulo numerado. |
| `descriptionItalic` | `boolean` | `false` | Compõe o texto da descrição em itálico. |
| `position` | `'above' \| 'below'` | `'below'` | Onde fica a legenda. Com `'above'`, a legenda (e a sua barra) vem primeiro, e o corpo do recurso desce a altura da legenda mais `gap`; a nota então vai embaixo do corpo. |
| `backgroundEnabled` | `boolean` | `false` | Pinta uma barra atrás da legenda. A barra ocupa toda a largura do bloco e envolve as linhas da legenda mais `padding` de cada lado. |
| `background` | `ColorValue` | cor principal da paleta | Cor de preenchimento da barra (só é pintada quando ativada). |
| `padding` | `Dimension` | `0.35em` | Margem interna entre a borda da barra e o texto da legenda. Ignorada quando a barra está desativada. |
| `note` | `object` | — | Estilo da nota do recurso; veja a subtabela abaixo. |
| `labelNumberGap` | `string` | espaço não separável; `''` num documento em japonês | O que fica entre o rótulo e o número, na legenda e num `:ref` no texto: *Figura 1.7*, *Fig. 1.7*. O chinês e o japonês os compõem colados: `''` gera 图1-1 e é o padrão num documento em japonês (図1-1). |
| `labelSeparator` | `string` | `'. '`; `'　'` num documento em japonês | O que vem depois do número, antes da descrição: *Figura 1.7. Uma legenda*. As legendas em chinês levam um espaço ideográfico, `'　'` (图1-1　标题), e as japonesas também, por padrão (図1-1　東京の地図, JLReq §4.3). Um rótulo sem número mantém a sua própria regra: um ponto, a menos que o prefixo já termine em um. |

O subobjeto `note` define o estilo de `Resource.note`, um trecho curto (fonte, créditos, uma observação) composto embaixo do recurso num tamanho menor. Ele aceita a mesma formatação em linha e as mesmas marcas `:ref` da legenda e herda a fonte da legenda. Fica embaixo da legenda quando a legenda está embaixo, e embaixo do corpo do recurso quando a legenda está em cima; a sua altura conta para o bloco, então um recurso com nota flutua como uma unidade.

| Propriedade | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `note.fontSize` | `Dimension` | 0,85 × tamanho da legenda | Tamanho da fonte da nota. |
| `note.color` | `ColorValue` | `color` da legenda | Cor do texto da nota. |
| `note.italic` | `boolean` | `false` | Compõe a nota em itálico. |
| `note.gap` | `Dimension` | `0.35em` | Espaço entre a nota e o que vem antes dela (legenda ou corpo). |
| `note.align` | `'left' \| 'center' \| 'right' \| 'justify' \| 'start' \| 'end'` | `'left'` | Alinhamento horizontal das linhas da nota, como `align` na legenda. |

As substituições por tipo são mescladas com `mergeCaptionStyle(resolvedCaptionStyle, override, palette?)`, exportada para os hosts que precisam da mesma resolução fora do pipeline.

## Estilo de diagramas

A propriedade `diagramStyle` controla a cor dos diagramas SVG incorporados (recursos `kind: 'svg'`) e as fontes em que o texto deles é composto. O **modo de tinta única** é uma passada de recoloração que converte cada cor de um diagrama numa retícula de uma única tinta, para que as figuras se reproduzam fielmente quando o documento é impresso com uma só cor especial. As **fontes incorporadas** colocam em cada SVG as variantes que o seu texto nomeia, para que os rótulos saiam nas fontes do documento no canvas, no HTML e no EPUB (veja [Fontes no texto dos SVG](https://postext.dev/pt/docs/configuration-resources.md#fontes-no-texto-dos-svg)).

```ts
const config: PostextConfig = {
  diagramStyle: {
    singleInk: true,
    inkColor: { hex: '#295AA3', model: 'hex' },
  },
};
```

| Propriedade | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `singleInk` | `boolean` | `false` | Recolore todos os diagramas SVG incorporados em retículas de uma única tinta. |
| `inkColor` | `ColorValue` | Cor principal (`#295AA3`) | A tinta. Por padrão é a cor principal da paleta do documento (ligada à paleta por `paletteId: 'main-color'`), de modo que trocar essa amostra de cor da paleta tinge de novo os diagramas junto com os títulos e os trechos em negrito. |
| `inlineFonts` | `boolean` | `true` | Incorpora em cada SVG as variantes que o seu texto nomeia (`font-family`), como URIs de dados em `@font-face`, antes que ele seja exibido como imagem: no canvas, no HTML, no EPUB e na rasterização de reserva do PDF. Nunca é gravado no arquivo armazenado. Um recurso fica de fora com `svg.inlineFonts: false` (desde o postext 1.25). |

### Como funciona a tinta única

Com `singleInk` ativado, cada cor do código SVG é reescrita como uma retícula de `inkColor` cuja intensidade é **1 − luminância relativa** (coeficientes Rec. 709 aplicados aos canais com codificação gama, uma aproximação perceptual mais que suficiente para mapear retículas). O mapeamento preserva o valor percebido: o branco vira o branco do papel, o preto vira a tinta cheia, e os preenchimentos claros continuam claros, seja qual for o matiz original. Um fundo amarelo-claro vira uma retícula clara da tinta; um traço escuro se aproxima da tinta cheia.

A recoloração é feita pela função exportada `applySingleInkToSvg(svgText, inkHex)`, que trabalha sem DOM, sobre o código SVG como texto:

- Os literais hexadecimais `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`, as funções `rgb()` / `rgba()` e as funções `hsl()` / `hsla()` são reescritos onde quer que apareçam: atributos de apresentação, `style` em linha, gradientes, `<defs>`. As funções podem usar canais inteiros, decimais ou percentuais e a sintaxe com vírgulas ou com espaços, então `rgb(11.37%, 20%, 50.59%)` (como o Cairo escreve) e `rgb(51 102 153 / 50%)` também são recoloridos. Uma função tingida é reescrita como `rgb(…)` ou `rgba(…)`.
- As palavras-chave `white` e `black` só são substituídas onde aparecem como valores de pintura (`fill`, `stroke`, `stop-color`, `flood-color`, `color`, como atributos ou propriedades de estilo em linha), nunca dentro do conteúdo de texto ou de rótulos.
- `none`, `transparent` e `currentColor` ficam intactos, assim como as outras cores nomeadas (`red`, `steelblue`…) e o preto padrão de uma forma ou texto que não define preenchimento. Dê a esses elementos uma cor explícita para que sejam recoloridos.
- Os canais alfa são preservados (os dígitos de `#rgba` / `#rrggbbaa` e os componentes alfa de `rgba(…)` passam sem mudança; um alfa percentual é escrito como número).
- Quando `inkHex` não pode ser interpretado, a entrada é devolvida sem mudança.
- O resultado leva `data-postext-single-ink="#…"` (a tinta) no `<svg>` raiz, e um código que já o leva é devolvido como está, seja qual for a tinta indicada. O mapeamento não é idempotente (uma segunda passada clareia todas as cores, e o preto sai com cerca de dois terços da tinta), por isso uma imagem é recolorida uma única vez, seja pelo seu código, seja pelos renderizadores, o que chegar primeiro. (A marca é nova no postext 1.5; o código recolorido pela 1.4 não a tem.)

```ts
import { applySingleInkToSvg } from 'postext';

const recoloured = applySingleInkToSvg(svgText, '#295AA3');
applySingleInkToSvg(recoloured, '#295AA3') === recoloured; // true: nunca duas vezes
```

A tinta única vale nos três renderizadores: o renderizador de PDF recolore os bytes SVG que `resourceBytes` lhe entrega antes de desenhá-los como vetores, e os renderizadores de canvas e HTML tingem as imagens SVG que pintam quando você pede (veja [Tinta única no canvas e no HTML](https://postext.dev/pt/docs/configuration-resources.md#tinta-única-no-canvas-e-no-html)), de modo que o PDF exportado corresponde à visualização na tela.

O resolvedor e o redutor seguem o padrão das outras seções, junto com os tipos `DiagramStyleConfig` / `ResolvedDiagramStyleConfig`:

```ts
import {
  DEFAULT_DIAGRAM_STYLE_CONFIG,
  resolveDiagramStyleConfig,
  stripDiagramStyleDefaults,
  applySingleInkToSvg,
} from 'postext';
import type { DiagramStyleConfig, ResolvedDiagramStyleConfig } from 'postext';

const resolved = resolveDiagramStyleConfig(config.diagramStyle);
// => { singleInk: false, inkColor: { hex: '#295AA3', model: 'hex', paletteId: 'main-color' } }

const minimal  = stripDiagramStyleDefaults(config.diagramStyle);
// => undefined quando tudo coincide com os padrões
```

### Tinta única no canvas e no HTML

Os renderizadores de canvas e de HTML recebem as imagens já decodificadas (`registerResourceImage`) ou como URLs (`resourceImageUrl`), e não o código SVG. Quando você pede, eles aplicam o mesmo mapeamento ao que desenham:

- **Canvas** (`renderPage`, `renderPageToCanvas`, `renderToCanvas`). Cada imagem SVG a que a tinta se aplica (uma figura, a imagem de uma célula de tabela, uma imagem de design, o ícone ou marcador de um boxe) é rasterizada no tamanho em que foi posicionada, e os seus pixels são tingidos com a tinta, tenha ela sido registrada como `<img>` ou como `ImageBitmap`. O bitmap tingido vai para o cache como qualquer rasterização vetorial. Uma imagem bitmap nunca é tingida.
- **HTML** (`renderToHtml`, `renderToHtmlIndexed`). Cada `<img>` SVG recebe `filter: url(#pt-ink-…)`, que aponta para um `feColorMatrix` levado pela sua página: um `<svg>` de tamanho zero com o `<filter>`, colocado no início da página e, na saída indexada, parte do `decorationHtml` da página. Toda página o leva enquanto a tinta única estiver valendo, tenha ou não uma imagem, para que um host que atualiza os blocos um a um nunca traga uma imagem sem o seu filtro.

**Nunca tingida duas vezes.** Até o postext 1.4, os renderizadores de canvas e de HTML pintavam as imagens como as recebiam, então os hosts recoloriam o código por conta própria com `applySingleInkToSvg` antes de entregá-lo. Os adaptadores de pacote e o Sandbox ainda fazem isso, porque a passada sobre o código dá exatamente as cores do PDF (veja o último parágrafo abaixo). Por isso, uma imagem é tingida uma única vez, segundo três regras que valem nos três renderizadores:

- **Código marcado não é mexido.** O renderizador de PDF recolore `resourceBytes` com `applySingleInkToSvg`, então bytes SVG já recoloridos são desenhados como estão. No canvas e no HTML, uma imagem carregada de uma URI de dados SVG cujo código leva a marca também nunca é tingida.
- **Desligada, a menos que seja pedida, no postext 1.x.** O canvas tinge uma imagem SVG registrada sem indicação própria só quando a renderização passa `singleInk: true` (`RenderPageOptions`), e uma registrada com `registerResourceImage(id, img, { singleInk: true })` em qualquer renderização. O renderizador de HTML tinge quando `renderToHtml` recebe `singleInk: true`, ou quando o seu resolvedor `resourceImageUrl` leva `singleInk: true`. Um host escrito para a 1.4, que recolore o código e registra a imagem decodificada sem indicação, mantém a sua saída. A próxima versão principal vai tingir por padrão.
- **`singleInk: false` nunca é tingida.** Por trás de uma URL blob ou de rede, o código não pode ser lido de volta; por isso, uma imagem que você mesmo recoloriu e decodifica assim é registrada com `singleInk: false`, como fazem `registerBundleImages` e o Sandbox. `bundleImageUrl(bundle)` devolve um resolvedor que leva `singleInk: false`, e `bundleResourceBytes` entrega ao PDF os bytes do próprio pacote, que o renderizador de PDF recolore uma vez.

Os dois renderizadores sabem o tipo de cada imagem pela VDT: uma figura e uma imagem de célula levam o tipo do seu recurso, e um bloco de imagem de design leva `imageKind` (`'svg'` ou `'bitmap'`), tirado do seu recurso no momento do layout. Numa VDT construída antes de existir `imageKind`, o canvas trata como SVG uma imagem de design registrada como fonte vetorial, e o renderizador de HTML, uma cuja URL é uma URI de dados SVG ou termina em `.svg`.

Ou você recolore o código, ou deixa os renderizadores tingirem a imagem original, nunca os dois:

```ts
import { applySingleInkToSvg, registerResourceImage, renderPage, renderToHtml } from 'postext';

// SVG original: tingido enquanto diagramStyle.singleInk estiver ativado…
registerResourceImage('diagram.svg', rawImg, { singleInk: true });
// …ou registre-o sem indicação e peça em cada renderização.
registerResourceImage('diagram.svg', rawImg);
const canvas = renderPage(doc.pages[0], doc, { singleInk: true });

// Recolorido antes de decodificar (como fazem os hosts do postext 1.4): pintado como está.
const inked = applySingleInkToSvg(svgText, ink);
registerResourceImage('diagram.svg', await decode(inked), { singleInk: false });

// O renderizador de HTML com URLs para o código original.
const html = renderToHtml(doc, { resourceImageUrl: urlFor, singleInk: true });
```

`renderToHtml` tira o seu padrão de `singleInk` da indicação do próprio resolvedor, então `bundleImageUrl(bundle)` não precisa de opção.

Para cada cor que a passada sobre o código reescreve (valores hexadecimais, `rgb()` e `hsl()`, `white` e `black`; veja [Como funciona a tinta única](https://postext.dev/pt/docs/configuration-resources.md#como-funciona-a-tinta-única)), o mapeamento por pixel dá o mesmo resultado, bordas suavizadas e gradientes incluídos. Os dois diferem onde a passada sobre o código deixa uma cor intacta: cores nomeadas além de `white` e `black`, `currentColor`, formas e textos sem preenchimento (desenhados no preto padrão) e bitmaps embutidos no SVG são tingidos na tela, mas mantêm a sua cor no PDF. Dê a cada elemento de um diagrama uma cor explícita em hexadecimal, `rgb()` ou `hsl()` para obter uma saída idêntica. Quando o canvas não consegue ler os pixels de volta (um `<img>` de outra origem, carregado sem CORS), a imagem é pintada sem tingir.

### Fontes no texto dos SVG

Uma imagem SVG é exibida por meio de uma imagem: um `<img>` no canvas, no HTML e no EPUB. Um documento de imagem não enxerga as fontes web da página, então `<text font-family="IBM Plex Sans">` cairia numa fonte do sistema. Desde o postext 1.25, o motor incorpora na marcação as variantes que o texto nomeia antes de a imagem ser decodificada ou entregue como URL: uma regra `@font-face` por arquivo de fonte, com os bytes numa URI de dados, num `<style>` logo depois da tag `<svg>` raiz. O PDF não precisa de nada disso: ele compõe o texto dos SVG como texto real nas suas fontes incorporadas (veja [Bytes de recursos e matrizes de impressão](https://postext.dev/pt/docs/configuration-programmatic-usage.md#bytes-de-recursos-e-matrizes-de-impressão)).

O que o texto pede é lido de `font-family`, `font-weight`, `font-style` e `font`, como atributos, dentro de atributos `style` e herdado dos grupos que o envolvem; uma regra de `<style>` que nomeia uma família também conta. As famílias genéricas (`serif`, `sans-serif`…) e o texto dentro de `<title>` ou `<desc>` ficam de fora, e uma família que o próprio SVG declara com `@font-face` não é mexida. Cada trecho percorre a sua lista `font-family` até a primeira família que tenha uma variante. A recoloração da tinta única vem primeiro; depois, as fontes.

As variantes vêm de um **provedor** com o contrato do `PdfFontProvider` do postext-pdf, de modo que um único provedor serve aos dois: ele é chamado com a família, o peso e o estilo, além dos caracteres que o SVG compõe nessa variante, e responde com um arquivo ou com vários. Uma família servida em fatias por unicode-range (Fontsource, Google Fonts) é respondida com as fatias de que esses caracteres precisam, então um SVG com rótulos latinos leva só o arquivo `latin`. O provedor padrão lê o registro de fontes do motor: `loadBundleFonts` registra ali as variantes de um pacote, e um host registra as suas com `registerFontBytes(family, weight, style, bytes, { unicodeRange })`, ou com `registerFontUrl(…)` para um arquivo baixado na primeira vez que um SVG precisa dele. Uma família que ninguém registrou é procurada nas regras `@font-face` das folhas de estilo legíveis da página. Uma `FontFace` adicionada a `document.fonts` a partir de bytes não guarda esses bytes, então o motor não consegue lê-los de volta: registre essas variantes também.

```ts
import { registerFontBytes, registerSvgImage, renderPage } from 'postext';

registerFontBytes('IBM Plex Sans', 700, 'normal', plexBoldWoff2);
await registerSvgImage('chart.svg', svgText);   // recolorido, fontes incorporadas, decodificado, registrado
const canvas = renderPage(doc.pages[0], doc);
```

Onde isso acontece:

- **Canvas.** `registerSvgImage(fileId, svgText, options)` recolore (`inkHex`), incorpora as fontes (`fonts`, um provedor; `inlineFonts: false` pula essa etapa), decodifica e registra a imagem como fonte vetorial, e resolve com o que aconteceu a cada variante. `registerBundleImages(bundle)` faz o mesmo com os SVGs de um pacote, a partir das variantes do próprio pacote primeiro. `prepareSvgMarkup(svgText, options)` devolve a marcação preparada para um host que decodifica por conta própria.
- **HTML.** `bundleImageUrl(bundle)` serve a marcação SVG com as variantes do pacote incorporadas. `renderToHtml(doc, { inlineSvgFonts: true })` incorpora as fontes nas URIs `data:` de SVG que `resourceImageUrl` devolve, a partir das variantes que o registro guarda na memória (ou `inlineSvgFonts: { fonts, maxBytes, withhold }`). Uma URL de objeto não pode ser lida de forma síncrona, então um host que serve URLs blob incorpora as fontes antes de criá-las.
- **EPUB.** O `postext-epub` incorpora a partir das `fonts` do livro e, depois, de `svgFonts.provider`, antes de gravar um SVG (veja [Livros EPUB](https://postext.dev/pt/docs/configuration-programmatic-usage.md#livros-epub-postext-epub)).
- **PDF.** O texto dos SVG é composto como texto real nas fontes incorporadas. Um `<style>` que contém só regras `@font-face` (variantes que o autor incorporou) já não faz a figura cair numa rasterização. Quando uma figura cai mesmo nela (um filtro, um gradiente), a rasterização é feita com as variantes incorporadas a partir do `fontProvider` do PDF.

As funções de nível mais baixo também são exportadas: `svgFontRequests(svgText)` lista as famílias, o peso, o estilo e os caracteres de cada trecho; `inlineSvgFonts(svgText, provider, options)` e `inlineSvgFontsSync(svgText, syncProvider, options)` devolvem a marcação; `inlineSvgFontsDetailed` acrescenta um relatório de cada variante (`inlined`, `declared`, `unavailable`, `withheld`, `tooLarge`).

| Opção | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `maxBytes` | `number` | 2 MiB | O máximo de bytes de fonte incorporados num SVG (antes do base64, que acrescenta um terço). Variantes que juntas passam desse limite não são incorporadas, e `svgFontsTooLarge` é informado. |
| `formats` | `('woff2' \| 'woff' \| 'ttf' \| 'otf')[]` | os quatro | Os formatos de arquivo a incorporar; arquivos de outros formatos são ignorados. |
| `withhold` | `(family) => boolean` | nenhum | Famílias a deixar fora de um arquivo que sai do app (não redistribuíveis). A referência a elas fica e o leitor usa uma fonte substituta; `onWithheld(family)` é avisado de cada uma. |
| `onWarning` | `(warning) => void` | nenhum | Avisado de uma família sem variante (`svgFontUnavailable`) e do limite de tamanho (`svgFontsTooLarge`). |

**Como desativar.** `diagramStyle.inlineFonts: false` deixa todos os SVGs como estão armazenados; `svg.inlineFonts: false` num recurso deixa esse SVG intacto, byte a byte, para um SVG que traz as próprias variantes ou que não pode mudar. Um pacote grava a exclusão do recurso como `"inlineFonts": false` no seu `preset.json`.

**Licenças.** A incorporação põe arquivos de fonte dentro de imagens que podem sair do app (uma exportação HTML, um EPUB). Ela acontece quando uma imagem é exibida ou exportada, nunca nos bytes armazenados do recurso, e `withhold` deixa de fora as famílias cuja licença não permite repassá-las: o gravador de EPUB retém as variantes marcadas com `redistributable: false`, e o Sandbox, as famílias personalizadas marcadas assim.

## Estilo de vídeo

A propriedade `videoStyle` define como os [recursos de vídeo](https://postext.dev/pt/docs/document-format.md#vídeos) são impressos (a marca de reprodução e o código QR sobre o pôster, e se o pôster é um link para o vídeo) e o que os players oferecem no visualizador HTML e no EPUB.

```ts
const config: PostextConfig = {
  videoStyle: {
    playMark: { shape: 'rounded', position: 'top-left', size: { value: 10, unit: 'mm' } },
    qr: { position: 'bottom-right', size: { value: 20, unit: 'mm' }, errorCorrection: 'Q' },
    player: { download: false, privacy: true },
  },
};
```

| Propriedade | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `playMark` | `VideoPlayMarkConfig` | veja abaixo | A marca impressa sobre o pôster para indicar que ele se reproduz. |
| `qr` | `VideoQrConfig` | veja abaixo | O código QR impresso sobre o pôster: abre a página do vídeo no YouTube ou no Vimeo, ou o endereço de produção de um arquivo. |
| `linkPoster` | `boolean` | `true` | Transforma o pôster num link para o vídeo: uma anotação de link sobre ele no PDF e um `<a>` em volta dele no HTML e no EPUB, onde quer que o pôster apareça. |
| `html` | `'player'` · `'poster'` | `'player'` | O que a saída HTML compõe para um vídeo: o seu player, ou o pôster impresso com as suas sobreposições. |
| `player` | `VideoPlayerOptions` | veja abaixo | As opções de player de todos os vídeos; o `video.player` de cada vídeo se sobrepõe a elas. |

### Marca de reprodução

| Propriedade | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `enabled` | `boolean` | `true` | Imprime a marca. |
| `shape` | `'circle'` · `'rounded'` · `'triangle'` | `'circle'` | Um disco com um triângulo, um retângulo arredondado com um triângulo (1,45 vez mais largo que alto) ou só o triângulo, contornado na cor de fundo. |
| `position` | `VideoOverlayPosition` | `'center'` | `'center'`, um canto (`'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`) ou o meio de um lado (`'top'`, `'bottom'`, `'left'`, `'right'`). As posições são físicas: o canto superior direito é o canto superior direito também num livro da direita para a esquerda. |
| `size` | `Dimension` | `12mm` | Altura da marca; nunca mais de 40% do lado menor do pôster. |
| `inset` | `Dimension` | `4mm` | Distância das bordas do pôster, num canto ou num lado. |
| `color` | `ColorValue` | branco | O triângulo. |
| `background` | `ColorValue` | Cor principal | O disco ou retângulo atrás dele; o contorno do triângulo quando ele está sozinho. Ligada à paleta por padrão. |
| `backgroundOpacity` | `number` | `0.9` | Opacidade do fundo, de 0 a 1. |

### Código QR

| Propriedade | Tipo | Padrão | Descrição |
| --- | --- | --- | --- |
| `enabled` | `boolean` | `true` | Imprime o código. Um arquivo sem endereço de produção fica sem código. |
| `position` | `VideoOverlayPosition` | `'bottom-right'` | Como na marca de reprodução. Dê posições diferentes às duas. |
| `size` | `Dimension` | `18mm` | Lado do código com a sua zona de silêncio; nunca mais de 45% do lado menor do pôster. As câmeras de celular leem módulos a partir de um terço de milímetro: um endereço de 30 caracteres gera um código de 29 módulos, então 18 mm com uma zona de silêncio de 2 dão módulos de 0,55 mm. |
| `inset` | `Dimension` | `3mm` | Distância das bordas do pôster. |
| `errorCorrection` | `'L'` · `'M'` · `'Q'` · `'H'` | `'M'` | Quanto do código pode estar danificado ou coberto e ainda ser lido: cerca de 7%, 15%, 25% ou 30%. É elevado automaticamente enquanto o código mantiver o mesmo número de módulos. |
| `quietZone` | `number` | `2` | Módulos claros em volta do código, sobre a sua placa (0–8). A placa se destaca do pôster, então os quatro módulos que a norma pede sobre papel livre não são necessários. |
| `color` | `ColorValue` | preto | Os módulos escuros. Mantenha-os escuros sobre uma placa clara: a maioria dos leitores não lê códigos invertidos. |
| `background` | `ColorValue` | branco | A placa. |
| `radius` | `Dimension` | `1mm` | Raio dos cantos da placa. |

O código é gerado pelo próprio motor (`encodeQr(text, level)`: modo byte, UTF-8, versões 1 a 40, a máscara com a menor penalidade) e desenhado como vetores: o canvas preenche um único caminho com as sequências de módulos, o PDF usa um `drawSvgPath` e o HTML um `<path>` com `shape-rendering="crispEdges"`, então ele fica nítido em qualquer tamanho de impressão.

### Opções do player

`VideoPlayerOptions`, em `videoStyle.player` e no `video.player` de cada vídeo. O player HTML5 de um arquivo respeita todas; os players do YouTube e do Vimeo respeitam o que os seus parâmetros de incorporação permitem.

| Propriedade | Padrão | Respeitada por | Descrição |
| --- | --- | --- | --- |
| `controls` | `true` | YouTube, Vimeo, arquivos | Mostra os controles do player. |
| `download` | `true` | arquivos | Oferece o botão de download do navegador (`controlslist="nodownload"` quando desativado). Ele esconde o botão; não protege o arquivo. O YouTube e o Vimeo nunca oferecem download. |
| `fullscreen` | `true` | YouTube, Vimeo, arquivos | Oferece a tela cheia (`fs=0`, o `allowfullscreen` do iframe, `nofullscreen`). |
| `playbackRate` | `true` | Vimeo, arquivos | Oferece o menu de velocidade (`speed=0`, `noplaybackrate`). |
| `pictureInPicture` | `true` | Vimeo, arquivos | Oferece picture-in-picture (`pip=0`, `disablepictureinpicture`). |
| `remotePlayback` | `true` | arquivos | Oferece transmitir para outra tela (`disableremoteplayback`). |
| `autoplay` | `false` | YouTube, Vimeo, arquivos | Começa a tocar sozinho, sempre sem som, como os navegadores exigem. |
| `muted` | `false` | YouTube, Vimeo, arquivos | Começa com o som desligado. |
| `loop` | `false` | YouTube, Vimeo, arquivos | Volta a tocar desde o início ao terminar. |
| `exclusive` | `true` | Folio, visualizador HTML, EPUB (onde há scripts); arquivos | Iniciar este vídeo pausa os outros que estão à vista, então só um toca por vez. `false` deixa que ele toque junto com os outros: os clipes silenciosos em loop de uma página, vários ao mesmo tempo. Desde o postext 1.18. |
| `preload` | `'metadata'` | arquivos | Quanto o navegador carrega antes de tocar: `'none'`, `'metadata'` ou `'auto'`. |
| `privacy` | `true` | YouTube, Vimeo | Incorporações com privacidade reforçada: YouTube a partir de `youtube-nocookie.com`, Vimeo com `dnt=1`. |

Um EPUB mantém só os atributos que o seu esquema conhece: um arquivo toca com `controls`, `autoplay`, `muted`, `loop`, `playsinline` e `preload`, mais a marca `data-pt-alongside`, e o sistema de leitura decide o resto.

Um vídeo que não é exclusivo leva `data-pt-alongside` na saída HTML. `coordinateVideoPlayback(root)` faz os players sob `root` (o elemento que contém a saída de `renderToHtml`) seguirem a regra: iniciar um vídeo exclusivo pausa todos os outros que estiverem tocando, e iniciar um que toca junto pausa só os exclusivos. Devolve uma função que para de escutar. `playsAlongside(el)` e `videosToPause(started, videos, alongside)` dão a mesma regra a um host com players próprios. No [Folio](https://postext.dev/pt/docs/document-format.md#vídeos-nas-páginas-do-folio), um vídeo que toca sozinho e junto com os outros (`autoplay` com `exclusive: false`) começa, sem som, cada vez que a sua página entra em vista e para quando a página é virada, vários ao mesmo tempo, e `loop` o faz tocar de novo desde o início. Uma página ou capítulo de EPUB com vídeos a coordenar (dois ou mais, um deles exclusivo) vincula a mesma regra como um pequeno script, `scripts/videos.js` (a exportação `VIDEO_PLAYBACK_SCRIPT`), e o pacote declara esse documento como `scripted`. Um sistema de leitura que executa scripts faz os vídeos desse documento seguirem a regra, mas não os de uma página oposta, que é outro documento; um que não executa scripts toca cada vídeo por conta própria, como os players do YouTube e do Vimeo sempre fazem.

O resolvedor e o redutor seguem o padrão das outras seções, junto com os tipos `VideoStyleConfig` / `ResolvedVideoStyleConfig`:

```ts
import {
  DEFAULT_VIDEO_STYLE_CONFIG,
  DEFAULT_VIDEO_PLAYER_OPTIONS,
  resolveVideoStyleConfig,
  resolveVideoPlayerOptions,
  stripVideoStyleDefaults,
} from 'postext';

const resolved = resolveVideoStyleConfig(config.videoStyle);
const player = resolveVideoPlayerOptions(resource.video?.player, resolved.player);
const minimal = stripVideoStyleDefaults(config.videoStyle); // undefined quando tudo é padrão
```
