# Configuration : ressources et tableaux

> Les types de ressources et leur numérotation, les styles de tableau et de légende, les diagrammes en une seule encre et les vidéos imprimées

- Version HTML: https://postext.dev/fr/docs/configuration-resources
- Dernière mise à jour: 2026-10-10
- Temps de lecture: 5 min
- Autres langues: [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), [pt](https://postext.dev/pt/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)

## En bref

Cette page présente les réglages des figures, des tableaux, des diagrammes et des vidéos. Postext les appelle des ressources et numérote chaque genre séparément. Vous choisissez l’aspect des tableaux : leurs filets, leurs fonds, leur typographie et la façon dont ils se coupent d’une page à l’autre. Vous choisissez comment s’écrit la légende d’une figure ou d’un tableau. Vous pouvez aussi imprimer les diagrammes en une seule encre et décider de l’aspect d’une vidéo sur le papier.

## Types de ressources

Un **type de ressource** est une catégorie que vous définissez vous-même (*Figure*, *Tableau*, *Diagramme*, *Listing*…) et qui détermine comment les ressources de ce genre sont numérotées, légendées et citées. La liste se trouve dans `config.resourceTypes` ; le Sandbox la modifie dans **Maquette → Figures et tableaux → Numérotation et placement**.

Quand `config.resourceTypes` n’est pas défini, Postext fournit trois types par défaut : **Figure**, **Tableau** et **Vidéo**, chacun numéroté séparément selon `{h1}.{n}` (remis à zéro à chaque titre de niveau 1), avec des compteurs décimaux. Une liste sans type `video` (celle d’un livre enregistré avant l’arrivée des vidéos) numérote quand même les ressources vidéo de type `video` : le type Vidéo intégré leur est ajouté (`effectiveResourceTypes(config, resources)`), et `defaultVideoResourceType(locale)` le renvoie seul. Leurs noms suivent la langue du document : `config.locale`, à défaut `bodyText.hyphenation.locale`, à défaut l’anglais (voir [Langue du document](https://postext.dev/fr/docs/configuration-text.md#langue-du-document)).

Les types par défaut dépendent de la langue. La fonction exportée `defaultResourceTypes(locale = 'en')` traduit les noms des types, les libellés courts et les préfixes de légende dans la langue du document : l’anglais donne *Figure*/*Fig.* et *Table*/*Tab.* ; l’espagnol *Figura*/*Fig.* et *Tabla*/*Tabla* ; le français, l’allemand, l’italien, le portugais, le catalan et le néerlandais ont aussi les leurs (le tableau de [Langue du document](https://postext.dev/fr/docs/configuration-text.md#langue-du-document) les donne). Les étiquettes régionales comme `es-ES` se résolvent par la langue, et toute langue sans traduction se rabat sur l’anglais. Le comportement de la numérotation (`numberingTemplate: '{h1}.{n}'`, `resetOn: 'h1'`, compteurs décimaux) ne dépend pas de la langue. Chaque appel renvoie des objets neufs, que vous pouvez donc modifier librement :

```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'; // which free slot a float may take; 'here' = inline embed at the ::resource directive
  span?: 'column' | 'page' | 'side';             // one column, the full content width, or the float-only side column
  rotate?: 'ccw' | 'cw';                         // a quarter turn: a landscape table on a page of its own
  width?: number;                                // fraction (0 < width < 1) of the column or page width; default: the whole width
  align?: 'left' | 'center' | 'right';           // where a float narrower than its column sits; default 'left'
  captionSide?: boolean;                         // caption beside the figure, in the side column of a oneAndHalf layout (column floats and resources set in the text)
  columns?: number;                              // a 'column' float across this many adjacent columns (since 1.18)
  wrap?: 'none' | 'left' | 'right' | 'start' | 'end'; // text beside the resource, at that side of its column (since 1.24)
  wrapGap?: Dimension;                           // space between the wrapped resource and the text (since 1.24)
}

interface ResourceType {
  id: string;                          // stable id, referenced by Resource.typeId
  name: string;                        // singular display name, e.g. "Figure"
  namePlural?: string;                 // optional plural, e.g. "Figures"
  shortLabel: string;                  // compact label for inline refs, e.g. "Fig."
  numberingTemplate: string;           // "{h1}.{n}" or "{n}"
  resetOn: ResourceCounterReset;       // when the {n} counter resets
  counterFormat: ResourceCounterFormat;// how {n} is formatted
  captionPrefix: string;               // prepended to the caption, e.g. "Figure"
  defaultPlacement?: ResourcePlacement;// fallback placement for this type's resources
}
```

`ResourcePlacement` a la même forme que le `placement` qu’une ressource définit pour elle-même. `position` choisit le genre d’emplacement libre qu’un flottant peut prendre : `auto` (par défaut) prend le premier après le premier renvoi, `top` / `bottom` le limitent à ce genre de bande, `here` incorpore la ressource dans le texte. `span` fixe l’étendue du flottant : une colonne, toute la largeur du contenu, ou la colonne latérale réservée aux flottants d’une mise en page à une colonne et demie. `rotate` fait pivoter la ressource d’un quart de tour et en fait un flottant sur toute la largeur de la page, sur une page à lui. `width` rétrécit un flottant à une fraction de sa colonne (ou de la page, pour un flottant sur toute la largeur de la page) : un petit tableau dans une large colonne, par exemple. `align` indique où se place un flottant ainsi rétréci (à gauche par défaut, au centre ou à droite) et où se place, dans son emplacement, une image plus étroite que lui : un bitmap plus petit que la colonne, ou une image que `layout.fitFiguresToPage` a réduite. La légende et la note gardent la largeur de l’emplacement. (Jusqu’à postext 1.4, une telle image était toujours alignée à gauche.) `captionSide` place la légende à côté de la figure, dans la colonne latérale réservée aux flottants d’une mise en page à une colonne et demie (`layout.sideColumnRole: 'floats'`), à hauteur du haut de la figure (de son bas pour un flottant en pied de colonne) ; il s’applique aux flottants de colonne et, depuis postext 1.28, à une ressource placée dans le texte (`position: 'here'`, large d’une colonne, sans texte qui l’habille), dont la légende et la note se placent dans la colonne latérale, à hauteur du haut de son corps, là où cette colonne est libre à côté d’elle, et sous le corps là où elle ne l’est pas (`layout.inlineCaptionSide`, actif par défaut ; `false` réserve les légendes latérales aux flottants de colonne) ; une page sans cette colonne garde la légende sous la figure. Quand ni la ressource ni son type ne définissent de placement, le défaut intégré est `auto` / `column`. `shrink` (`'never'`, `'page'`, `'slot'`) et `minScale` (0,7 s’il n’est pas défini) réduisent une image flottante à la place dont dispose son emplacement au lieu de la repousser plus loin, et `captionMeasure: 'body'` compose la légende et la note d’une image plus étroite que son emplacement à la largeur de l’image (voir [Format du document › Placement](https://postext.dev/fr/docs/document-format.md#placement)) ; `layout.floatShrink` donne la valeur par défaut du document pour les deux premiers. `wrap` place une ressource incorporée ou un flottant d’une colonne d’un côté de sa colonne, le texte courant à côté d’elle, à `wrapGap` de distance ; `layout.wrap` contient les valeurs par défaut (voir [Format du document › Habillage](https://postext.dev/fr/docs/document-format.md#habillage-du-texte)). `citingPage` permet à un flottant `top` ou `auto` de se placer en tête de la page ou de la colonne où tombe la ligne qui le cite, au lieu de prendre le premier emplacement libre qui la suit ; `layout.floatsAtCitingPage` donne la valeur par défaut du document et `layout.maxTopFraction` la part de la colonne qu’il peut occuper (voir [Format du document › Placement](https://postext.dev/fr/docs/document-format.md#placement)).

`columns` (depuis postext 1.18) étend un flottant `span: 'column'` sur autant de colonnes adjacentes d’une page qui en compte plusieurs : une image sur deux des cinq colonnes d’un journal. Sa largeur couvre ces colonnes et les gouttières qui les séparent. Il prend la tête d’une suite de colonnes vides qui commencent au même niveau, ou le pied de la colonne qui le cite et des colonnes vides qui la suivent ; autant de colonnes que la page en compte, ou davantage, en font un flottant sur toute la largeur de la page. Il est ignoré par les étendues `'page'` et `'side'`, par une ressource pivotée (`rotate`) et par une incorporation dans le texte (`here`), et `captionSide` ne s’applique qu’à un flottant large d’une seule colonne.

| Propriété | Type | Description |
| --- | --- | --- |
| `id` | `string` | Identifiant stable référencé par le `typeId` de chaque ressource. Fixé une fois pour toutes à la création du type ; supprimer un type que des ressources référencent encore déclenche un avertissement de **type orphelin**. |
| `name` | `string` | Nom affiché, au singulier. Utilisé par le renvoi en ligne `style="full"` (par ex. `Figure 1.7`). |
| `namePlural` | `string` (facultatif) | Nom affiché au pluriel, pour les libellés de l’interface et les listes de ressources. |
| `shortLabel` | `string` | Abréviation compacte utilisée par le style de renvoi en ligne par défaut (par ex. `Fig. 1.7`). |
| `numberingTemplate` | `string` | Modèle du numéro calculé. Voir **Jetons de modèle** plus bas. Formes courantes : `{h1}.{n}` (par chapitre, par ex. `2.3`) et `{n}` (un seul compte continu). |
| `resetOn` | `ResourceCounterReset` | `'never'` donne un seul compte continu pour tout le document ; `'h1'`..`'h6'` remettent le compteur `{n}` à zéro chaque fois que paraît un titre de ce niveau (ou d’un niveau supérieur). Réglez-le sur le niveau de titre qui figure dans le modèle, par ex. `{h1}.{n}` avec `resetOn: 'h1'`. |
| `counterFormat` | `ResourceCounterFormat` | Écriture du compteur `{n}` : décimale (`1, 2, 3`), en chiffres romains minuscules ou majuscules (`i, ii` / `I, II`), ou en lettres minuscules ou majuscules (`a, b` / `A, B`). Les graphies des pages et des listes sont aussi acceptées (`'lower-roman'`, `'arabic'`… ; voir [Graphies des formats de numérotation](https://postext.dev/fr/docs/configuration-page-layout.md#graphies-des-formats-de-numérotation)) ; une valeur inconnue compte en décimal et est signalée. Les jetons de titre (`{h1}`…) s’écrivent toujours en décimal. |
| `captionPrefix` | `string` | Texte placé devant la légende de la figure ou du tableau. Le numéro calculé suit le préfixe : une légende s’écrit `{captionPrefix} {number}. {caption text}`, par ex. **Figure 1.7. Le plan d’origine.** Un type dont le `numberingTemplate` est vide n’a pas de numéro, et sa légende s’écrit `{captionPrefix}. {caption text}`. Les espaces en fin de préfixe sont supprimées, et un préfixe qui se termine déjà par `.`, `:`, `!`, `?` ou `…` (ou par leur forme pleine chasse) ne prend pas de second point : **Pl. Lignes à 0°**. |
| `defaultPlacement` | `ResourcePlacement` (facultatif) | Placement utilisé par les ressources de ce type qui ne définissent pas leur propre `placement` : `position`, `span`, `rotate`, `width`, `align`, `captionSide` et `columns`, chacun résolu séparément. Quand ni la ressource ni le type ne définissent un champ, le défaut intégré s’applique : `auto` / `column`, non pivoté, sur toute la largeur, aligné à gauche, légende sous la figure. Voir [Numérotation et renvois](https://postext.dev/fr/docs/configuration-resources.md#numérotation-et-renvois) plus bas pour l’ordre de résolution, et [Format du document › Ressources](https://postext.dev/fr/docs/document-format.md#ressources) pour l’effet de chaque valeur, ressources pivotées comprises. |
| `captionStyle` | `CaptionStyleConfig` (facultatif) | Surcharge partielle du [style de légende](https://postext.dev/fr/docs/configuration-resources.md#style-de-légende) pour les ressources de ce type. Seules les clés que vous définissez remplacent le `captionStyle` global ; tout le reste est hérité (une `color` surchargée détermine aussi les couleurs du libellé et de la note, sauf si celles-ci sont définies explicitement). Usage typique : des tableaux légendés *au-dessus*, sur une barre de couleur, tandis que les figures gardent leur légende en dessous. Les références à la palette se résolvent comme toute autre couleur. |

### Jetons de modèle

`numberingTemplate` est rendu par le même moteur que la numérotation des titres (voir [Titres](https://postext.dev/fr/docs/configuration-text.md#titres)). Il reconnaît deux genres de jetons :

- `{n}` : le compteur propre au type, écrit selon `counterFormat`. C’est la valeur qui augmente à chaque ressource et qui revient à zéro selon `resetOn`.
- `{h1}` … `{h6}` : les numéros de titre en vigueur à l’endroit du premier renvoi, en décimal sauf si le jeton nomme un style. `{h1}` est le numéro du titre de niveau 1 courant, `{h2}` celui du niveau 2, et ainsi de suite.
- `{h1:A}`, `{h2:i}`, `{n:i}` : un jeton suivi de deux-points et d’un style s’écrit dans ce style, n’importe lequel de ceux qu’accepte un modèle de titre : `1`, `01`, `A`, `a`, `I`, `i`, un nom de format de numérotation comme `upper-roman`, un jeton d’un seul caractère comme `一` ou `①`, ou un style en toutes lettres comme `Words`. Sur `{n}`, le style l’emporte sur `counterFormat` pour ce jeton. Un style inconnu compte en décimal. Depuis postext 1.28.

Tout autre texte est pris littéralement. Une barre oblique inverse échappe un `{`, un `}` ou un `\` littéral. Quand un jeton de titre n’a pas de valeur à cet endroit (par ex. `{h1}` avant tout titre de niveau 1), il disparaît avec le séparateur qui le jouxte : `{h1}.{n}` se réduit proprement au simple compteur.

Un modèle vide (`''`) n’imprime aucun numéro, mais le type compte toujours ses ressources : une légende se lit *Do. Lignes à 0°* et un `:ref` imprime le libellé seul (*Do*). Jusqu’à postext 1.4, une telle légende se lisait *Do .Lignes à 0°* et le renvoi se terminait par une espace insécable.

| Modèle | Avec `h1 = 2`, compteur = 3 | Remarques |
| --- | --- | --- |
| `{n}` | `3` | Un seul compte continu. À associer à `resetOn: 'never'`. |
| `{h1}.{n}` | `2.3` | Par chapitre. À associer à `resetOn: 'h1'`. |
| `{h1}.{h2}.{n}` | `2.0.3` | Par section. À associer à `resetOn: 'h2'`. |
| `{h1:A}.{n}` | `B.3` | En lettres, pour les annexes. À associer à `resetOn: 'h1'`. |

**Annexes.** Un livre dont les annexes sont désignées par des lettres grâce à un style de titre (`numberingTemplate: 'Appendix {1:A}'`, avec `startAt=1` sur la première) donne à leurs figures et à leurs tableaux un type à part, `{ ...table, id: 'table-a', numberingTemplate: '{h1:A}.{n}', resetOn: 'h1' }`. Un seul type sert à toutes les annexes : il imprime *Tableau A.1*, *A.2* et *B.1*, dans la légende comme dans `:ref`. Un simple `{h1}` imprimerait ici le numéro du titre en décimal (*Tableau 1.1*).

### Numérotation et renvois

Le numéro que produit un type de ressource est celui qu’imprime `:ref` et que précède le préfixe de légende. `:ref{id}` est la forme principale : le premier renvoi dans l’ordre de lecture **incorpore** la ressource, qui flotte jusqu’au premier emplacement libre après ce renvoi, à savoir le bas de la colonne qui la cite, le haut ou le bas de la colonne vide suivante, ou une bande de la page suivante (selon son placement résolu : `position: 'auto' | 'top' | 'bottom' | 'here'` et `span: 'column' | 'page' | 'side'`, plus `rotate`, `width`, `align` et `captionSide`, résolus d’abord sur la ressource, puis sur le `defaultPlacement` du type, puis selon le défaut intégré `auto` / `column` ; `'top'` / `'bottom'` limitent la recherche à ce genre d’emplacement). L’incorporation en bloc `::resource{id}` est facultative et ne sert qu’avec `placement.position: 'here'` : une incorporation dans le texte, non flottante, à un point précis du flux. La grammaire complète côté document (les deux formes, plus les options `style` et `text` de `:ref`) est décrite dans [Format du document › Ressources](https://postext.dev/fr/docs/document-format.md#ressources), y compris la façon dont l’ordre des premiers renvois détermine le compte.

Cela reproduit la numérotation des titres : de même qu’un niveau de titre porte un `numberingTemplate`, un type de ressource en porte un ; mais le compteur des ressources (`{n}`) avance à chaque premier renvoi et non à chaque titre, et `resetOn` le rattache à la hiérarchie des titres.

### Ce qui est numéroté

Une ressource est numérotée quand le texte la cite (avec `:ref` ou avec une incorporation `::resource`), dans l’ordre de ces premiers renvois, quel que soit son placement : flottante, dans le texte (`here`), dans la colonne latérale ou pivotée. Une ressource que seule une maquette dessine (un élément `image` d’une ouverture de chapitre, d’un titre courant ou d’une page de partie) ou que rien ne cite ne reçoit pas de numéro et ne fait pas avancer le compteur de son type. Ainsi, dans un essai photographique dont les planches à fond perdu sont des images d’ouverture et dont la seule planche plus petite est un flottant cité, ce flottant est la planche **I**, quel que soit le nombre de planches que les ouvertures ont montrées avant lui ; numérotez les planches d’ouverture dans leur maquette (avec un attribut comme `{attr.plate}`) et gardez le compteur pour les planches que le texte cite.

Dans un livre mis en page chapitre par chapitre (le Sandbox, `buildBundle`, ou `buildDocument` avec les compteurs que transmet `continuationAfter()`), c’est le premier renvoi de tout le livre qui compte : une ressource garde le numéro reçu dans le chapitre qui la cite en premier, et seul ce chapitre la place. Le `:ref` d’un chapitre ultérieur imprime ce numéro sans rien placer, et une incorporation `::resource` d’une ressource flottante n’y est qu’un renvoi de plus (une incorporation `here` reste composée là où elle est écrite). Un tel renvoi pointe vers la figure quand la figure se trouve dans la même sortie : un PDF du livre entier le lie à la page du chapitre précédent. Un chapitre rendu seul, en HTML ou en PDF, le compose en texte simple dans la couleur des liens, puisque sa figure n’est pas dans ce document. Un hôte qui réunit le HTML des chapitres sur une seule page passe à `renderToHtml` les ressources que les chapitres ancrent, sous la forme `refTargets`, et un tel renvoi pointe de nouveau vers la figure du chapitre précédent :

```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('');
```

**Modifié dans postext 1.5 :** jusqu’à la 1.4, chaque chapitre qui citait une figure la refaisait flotter, et le HTML de chaque `:ref` était un lien, que sa figure soit sur la page ou non.

`{h1}` est le compte continu des titres de niveau 1 : chaque H1 le fait avancer, sauf si son style de titre définit `numbered: false` (un `numberingTemplate` vide masque le numéro du titre, il n’arrête pas le compte). Un article dont le seul H1 est son titre numérote donc ses figures `1.1`, `1.2`… avec les types `{h1}.{n}` intégrés. Deux façons d’imprimer Figure 1, 2… :

- un type numéroté `{n}` avec `resetOn: 'never'` (avec `resetOn: 'h1'`, le compte reprendrait à chaque H1) ;
- un [style de titre](https://postext.dev/fr/docs/configuration-styles.md#styles-de-titre) avec `numbered: false` sur le titre, et sur tout autre H1 qui ne doit pas compter : un tel titre ne fait pas avancer `{h1}` mais le laisse tel qu’il était (vide avant le premier H1 compté, où `{h1}.{n}` se réduit au simple compteur) et ne déclenche jamais `resetOn: 'h1'`, si bien que le compte se poursuit au-delà. Après `# Introduction` et sa figure 1.1, la première figure sous un `# Appendix` non numéroté est la 1.2, pas la 2.1.

```ts
// Figure 1, 2, 3… in a single-article document
resourceTypes: defaultResourceTypes('en').map((t) => ({ ...t, numberingTemplate: '{n}', resetOn: 'never' })),
```

## Style de tableau

La propriété `tableStyle` règle la typographie et la décoration des ressources tableau : celles de tous les tableaux, sauf ceux qui choisissent un [style de tableau nommé](https://postext.dev/fr/docs/configuration-resources.md#styles-de-tableau-nommés). Les cellules du corps et celles de l’en-tête se règlent séparément. La famille, le corps et les couleurs héritent du texte courant résolu s’ils ne sont pas définis : un document sans `tableStyle` compose donc ses tableaux avec la typographie du texte courant.

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

| Propriété | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `bodyFontFamily` | `string` | police du texte courant | Famille de caractères des cellules du corps. |
| `bodyFontSize` | `Dimension` | corps du texte courant | Corps des cellules du corps. |
| `bodyColor` | `ColorValue` | couleur du texte courant | Couleur du texte des cellules du corps. |
| `headerFontFamily` | `string` | police du texte courant | Famille de caractères des cellules d’en-tête. |
| `headerFontSize` | `Dimension` | corps du texte courant | Corps des cellules d’en-tête. |
| `headerColor` | `ColorValue` | couleur du texte courant | Couleur du texte des cellules d’en-tête. |
| `headerBold` | `boolean` | `true` | Composer les cellules d’en-tête en gras. |
| `headerItalic` | `boolean` | `false` | Composer les cellules d’en-tête en italique. |
| `headerLetterSpacing` | `Dimension` | `0pt` | Approche ajoutée après chaque caractère d’une cellule d’en-tête, espaces comprises, comme le `letter-spacing` de CSS. Les valeurs positives espacent les lettres (un en-tête en capitales demande en général de `0.05em` à `0.1em`), les valeurs négatives les resserrent. Un `em` vaut le corps de l’en-tête. Les lignes d’en-tête sont mesurées avec cette approche : leurs coupures, leur centrage et leur alignement en tiennent compte, et le canvas, le HTML et le PDF la rendent à l’identique. Elle s’applique à toutes les cellules d’en-tête : les lignes d’en-tête et toute cellule marquée `isHeader`. |
| `headerTextTransform` | `'none' \| 'uppercase'` | `'none'` | Composer les cellules d’en-tête en capitales. Le texte garde sa longueur, si bien que le Sandbox fait toujours correspondre chaque lettre à la source : une lettre dont la capitale est plus longue (`ß`) reste telle quelle. Les renvois aux ressources gardent leur libellé. |
| `headerBackgroundEnabled` | `boolean` | `true` | Peindre un fond derrière la ligne d’en-tête. |
| `headerBackground` | `ColorValue` | `#f0f0f0` | Couleur de fond de la ligne d’en-tête. |
| `bodyBackgroundEnabled` | `boolean` | `false` | Peindre un fond derrière les lignes du corps. |
| `bodyBackground` | `ColorValue` | `#ffffff` | Couleur de fond des lignes du corps (peinte seulement si l’option est activée). |
| `bodyAlternateBackgroundEnabled` | `boolean` | `false` | Lignes zébrées : remplir une ligne du corps sur deux avec `bodyAlternateBackground`. Voir [lignes zébrées](https://postext.dev/fr/docs/configuration-resources.md#lignes-zébrées). |
| `bodyAlternateBackground` | `ColorValue` | `#f2f2f2` | Fond des lignes alternées du corps (peint seulement si l’option est activée). |
| `borders` | `boolean` | `true` | Tracer les bordures des cellules. |
| `borderColor` | `ColorValue` | couleur du texte courant | Couleur du trait des bordures. |
| `borderWidth` | `Dimension` | `0.75pt` | Épaisseur du trait des bordures (≈ 1 px à 96 DPI ; suit la résolution de la page). Non utilisée par `'booktabs'`, qui a ses propres épaisseurs. |
| `cellPadding` | `Dimension` | `0.375em` | Marge intérieure de chaque cellule. |
| `rules` | `'grid' \| 'horizontal' \| 'outer' \| 'none' \| 'booktabs'` | `'grid'` | Filets à tracer quand `borders` est actif : la grille complète des cellules, les filets horizontaux seuls (bords haut et bas de chaque ligne, sans verticaux), le cadre extérieur seul, aucun, ou les trois filets d’un tableau de revue scientifique (voir [filets booktabs](https://postext.dev/fr/docs/configuration-resources.md#filets-booktabs)). |
| `borderRadius` | `Dimension` | `0` | Rayon des coins du cadre extérieur du tableau. Le cadre est tracé arrondi (avec les filets `grid` ou `outer`), les fonds des cellules et le fond de l’en-tête sont découpés à sa forme (aussi avec `rules: 'none'` ou sans bordures) et les filets horizontaux sont rognés sur son contour extérieur ; les filets intérieurs restent droits. Un tableau coupé sur plusieurs pages arrondit les coins supérieurs de sa première partie et les coins inférieurs de la dernière. Limité à la moitié de la largeur et de la hauteur du tableau. Les filets `'booktabs'` restent droits (les fonds sont tout de même découpés). |
| `heavyRuleWidth` | `Dimension` | `0.08em` | Booktabs : les filets au-dessus du tableau et sous sa dernière ligne. Un `em` vaut le corps des cellules du corps. |
| `lightRuleWidth` | `Dimension` | `0.05em` | Booktabs : le filet sous les lignes d’en-tête, et les filets de groupe. |
| `spanRuleWidth` | `Dimension` | `0.03em` | Booktabs : les filets sous les cellules d’en-tête qui couvrent plusieurs colonnes. |
| `spanRules` | `'trimmed' \| 'full' \| 'none'` | `'trimmed'` | Booktabs : les filets sous les cellules d’en-tête qui couvrent plusieurs colonnes, au-dessus de la dernière ligne d’en-tête : raccourcis de `spanRuleTrim` aux deux bouts, sur toute la cellule, ou aucun. |
| `spanRuleTrim` | `Dimension` | `0.5em` | Booktabs : de combien un filet raccourci est rogné à chaque bout. |
| `groupRules` | `boolean` | `false` | Booktabs : un filet fin au-dessus de chaque ligne du corps qui ouvre un groupe. |
| `continuedFootRule` | `'bottom' \| 'light' \| 'none'` | `'light'` | Booktabs : ce qui ferme une partie d’un tableau coupé qui continue à la page suivante. |
| `overflow` | `'split' \| 'clip' \| 'hide'` | `'split'` | Sort d’un tableau plus haut que la page : le continuer sur les pages suivantes, ne garder que les lignes qui tiennent, ou l’omettre. Avec `splitInline`, vaut aussi pour un tableau placé dans le texte qui ne tient pas dans le reste de sa colonne. Voir plus bas. |
| `splitInline` | `boolean` | `true` | Appliquer aussi `overflow` aux tableaux placés `here` : un tableau dans le texte qui ne tient pas dans la place restante de sa colonne est coupé entre deux lignes et continue en tête de la colonne suivante. `false` déplace un tel tableau entier à la colonne suivante, comme jusqu’à postext 1.24 ; les configurations enregistrées par des versions antérieures dont les chapitres incorporent une ressource sont lues avec `false`. Voir [Tableaux plus hauts que la page](https://postext.dev/fr/docs/configuration-resources.md#tableaux-plus-hauts-que-la-page). Depuis postext 1.25. |
| `continuedSuffix` | `string` | `'(cont.)'` | Ajouté en italique, après une espace, à la légende de chaque partie suivante d’un tableau coupé ; un suffixe qui commence par un caractère chinois ou pleine chasse (`'（续）'`) est collé à la légende. |
| `continuesMarkerEnabled` | `boolean` | `true` | Placer une mention sous chaque partie qui continue à la page suivante. |
| `continuesMarker` | `string` | `'Continued'` / `'Continúa'` | Texte de cette mention, aligné à droite sous la partie, dans le caractère de la note (voir [style de légende](https://postext.dev/fr/docs/configuration-resources.md#style-de-légende)). La valeur par défaut suit la langue du document (voir [Langue du document](https://postext.dev/fr/docs/configuration-text.md#langue-du-document) pour les huit langues). |

Les épaisseurs de bordure restent fractionnaires : un filet de `0.5pt` est tracé comme un filet extra-fin dans le PDF et à l’écran, au lieu d’être arrondi à un pixel entier (le minimum est de 0,25 px).

### Lignes zébrées

Les longs tableaux de données se lisent plus facilement d’un bout à l’autre de la ligne quand une ligne sur deux est teintée. `bodyAlternateBackgroundEnabled` active les bandes et `bodyAlternateBackground` fixe leur couleur :

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

Les lignes se comptent à partir de la première ligne qui suit les lignes d’en-tête (`TableModel.headerRowCount`, ou les premières lignes faites de cellules d’en-tête) : cette ligne garde `bodyBackground` (ou aucun fond tant que `bodyBackgroundEnabled` est désactivé), la suivante prend le fond alterné, et ainsi de suite. Le compte suit le modèle du tableau, pas la page : un tableau coupé sur plusieurs pages garde la bande de chaque ligne sur chaque page, et une cellule fusionnée sur plusieurs lignes prend la bande de sa première ligne. Les cellules d’en-tête gardent le fond de l’en-tête, le `background` propre à une cellule l’emporte sur les deux, et une couleur liée à la palette suit la palette. Un [style de tableau nommé](https://postext.dev/fr/docs/configuration-resources.md#styles-de-tableau-nommés) définit ces deux champs comme les autres : un style peut donc être zébré sans que les autres tableaux du document le soient ; dans le Sandbox, ce sont l’interrupteur *Lignes zébrées* et sa couleur, sous **Cellules du corps**.

Dans le VDT, les cellules des lignes alternées portent `alternate: true`, et la mise en page du tableau porte `bodyAlternateBackground`. `tableCellFill(table, cell)` renvoie le fond avec lequel une cellule est peinte (le sien, celui de l’en-tête, le fond alterné ou celui du corps), c’est-à-dire ce que peignent les moteurs de rendu canvas, HTML et PDF. Les fonds voisins se rejoignent sans jointure : un navigateur à un rapport de pixels fractionnaire ou une visionneuse PDF lisse chaque fond séparément et laisserait voir la page par un filet extra-fin entre deux cellules ; les moteurs HTML et PDF peignent donc `tableCellFillRects(table)`, c’est-à-dire le fond de chaque cellule prolongé d’une bande sur chaque bord qu’elle partage avec une cellule peinte après elle, que cette cellule recouvre ensuite, et le canvas cale ses fonds sur les pixels de l’appareil.

### Filets booktabs

Les tableaux des revues et des manuels se composent d’ordinaire avec trois filets et aucun trait vertical : un filet épais au-dessus du tableau, un fin sous l’en-tête et un épais sous la dernière ligne, avec de courts filets sous les intitulés qui regroupent plusieurs colonnes (le paquet LaTeX `booktabs` : `\toprule`, `\midrule`, `\cmidrule`, `\bottomrule`). `rules: 'booktabs'` trace ce schéma :

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

- Le filet au-dessus du tableau et celui sous sa dernière ligne ont l’épaisseur `heavyRuleWidth` (`0.08em`), le filet sous les lignes d’en-tête l’épaisseur `lightRuleWidth` (`0.05em`). Un tableau sans lignes d’en-tête n’a pas de filet d’en-tête.
- Une cellule d’en-tête qui couvre plusieurs colonnes au-dessus de la dernière ligne d’en-tête reçoit sous elle un filet d’épaisseur `spanRuleWidth` (`0.03em`). Avec `spanRules: 'trimmed'` (par défaut), le filet est raccourci de `spanRuleTrim` (`0.5em`) aux deux bouts, pour que les filets sous deux intitulés voisins ne se touchent pas ; `'full'` le prolonge sur toute la cellule et `'none'` l’omet.
- `groupRules: true` ajoute un filet fin au-dessus de chaque ligne du corps qui ouvre un groupe (une seule cellule sur toute la largeur du tableau, ou une ligne de cellules d’en-tête), sauf si la ligne ouvre le tableau ou une page, où se trouve déjà le filet d’en-tête.
- Les épaisseurs se calculent sur le corps des cellules du corps (`bodyFontSize`) : un en-tête plus grand n’épaissit donc pas le filet d’en-tête. Une épaisseur de `0` supprime le filet.
- Les filets prennent `borderColor` (une couleur liée à la palette suit la palette et `:::part palette`), et `borders: false` les désactive. `borderWidth` ne s’applique pas, `borderRadius` non plus : les filets restent droits, tandis que les fonds des cellules sont toujours découpés au cadre arrondi. Les fonds d’en-tête, les lignes zébrées et le `background` propre à une cellule fonctionnent comme avec les autres schémas, sous les filets.
- Un tableau coupé sur plusieurs pages répète ses lignes d’en-tête sur chaque partie : chaque partie s’ouvre donc sur le filet épais et le filet d’en-tête. Le filet épais sous la dernière ligne ne ferme que la dernière partie ; une partie qui continue à la page suivante se termine par `continuedFootRule` : un filet fin (`'light'`, par défaut), le filet épais (`'bottom'`) ou rien (`'none'`).

La mise en page calcule les filets une seule fois. Le tableau du VDT les porte sous forme de `strokes` (`{ x1, y1, x2, y2, widthPx }`, relatifs au coin supérieur gauche du corps du tableau), et le canvas, la visionneuse HTML, le PDF et l’EPUB à mise en page fixe tracent exactement ceux-là ; dans un PDF balisé, ce sont des artefacts de mise en page. L’EPUB recomposable les écrit comme des bordures CSS sur le tableau et son en-tête, les filets raccourcis étant dessinés comme des lignes d’arrière-plan. Dans le Sandbox, **Booktabs** dans la liste *Filets* affiche ces champs et masque *Épaisseur de bordure* et *Rayon des coins*.

### Styles de tableau nommés

Un document compose rarement tous ses tableaux de la même façon : une liste de contrôle dans une grille bleu marine au cadre arrondi, une ligne d’options entourée de son seul cadre extérieur, un tableau de données en simples filets horizontaux. `tableStyles` déclare des variantes nommées, et une ressource tableau en choisit une avec `table.styleId`. Tout champ qu’un style laisse vide se lit d’abord dans `tableStyle`, puis dans le texte courant : un style n’énonce que ce qui distingue ses tableaux. Un tableau sans `styleId`, ou dont l’id n’est déclaré par aucun style, garde `tableStyle` ; un document sans `tableStyles` se rend exactement comme avant.

```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,
    },
  ],
};

// In the resources: this table is set in the "option" style.
const resource: Resource = {
  id: 'choices', typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0,
  table: { model: { rows: [/* … */] }, styleId: 'option' },
};
```

Chaque entrée accepte tous les champs de `tableStyle`, plus `id` (ce que référence `table.styleId`) et un `name` facultatif pour l’éditeur (l’id par défaut). Tout ce qu’un style peut définir s’applique tableau par tableau : typographie, fonds, bordures, filets, rayon des coins, marges intérieures et comportement en cas de débordement, avec ses chaînes de continuation. `resolveTableStylesConfig(styles, tableStyle, resolvedBodyText, locale?)` renvoie la liste résolue, `pickTableStyle(resolved, styleId)` le style dans lequel un tableau est composé, et `stripTableStylesDefaults` supprime les champs non définis (il garde un champ égal à sa valeur par défaut intégrée, qui l’emporte encore sur une valeur différente de `tableStyle`). Dans un EPUB recomposable, un style nommé est une classe posée sur le tableau (`pt-table-<id>`), mise en forme par la feuille de style du livre.

### Tableaux plus hauts que la page

Un tableau flottant qui ne tient pas sur la page neuve qui lui est offerte n’est ni comprimé ni laissé déborder : avec `overflow: 'split'` (par défaut), le moteur le coupe entre deux lignes, au dernier bord qui tient sur la page, et le continue sur les pages suivantes, autant qu’il en faut. Chaque partie suivante répète les lignes d’en-tête du tableau (`TableModel.headerRowCount`, ou à défaut les premières lignes faites de cellules d’en-tête) et reprend la légende avec `continuedSuffix` après la description : « Table 6-4. Title *(cont.)* ». Chaque partie qui continue reçoit `continuesMarker` sous elle, aligné à droite, dans le caractère de la note ; la note du tableau est réservée à la dernière partie. Une coupe ne traverse jamais une cellule fusionnée (une cellule sur plusieurs lignes passe entière à la partie suivante), et une ligne qui chapeaute les lignes du dessous (une seule cellule sur toute la largeur du tableau) est reportée à la partie suivante plutôt que laissée seule au pied d’une page. Un tableau booktabs ferme chaque partie qui continue avec `continuedFootRule` (voir [filets booktabs](https://postext.dev/fr/docs/configuration-resources.md#filets-booktabs)).

**Où finit la première partie.** Un tableau à qui s’offre la tête d’une colonne vide après son renvoi prend les lignes qui y tiennent et continue dans l’emplacement suivant. Il remplit la colonne jusqu’au pied quand il a la colonne pour lui seul : une partie qui laisserait sous elle moins de trois lignes de texte les prend aussi, plutôt que de laisser un moignon de texte. Quand la colonne porte déjà une autre bande de flottants (une figure sur toute la largeur en tête de page, par exemple), la partie s’arrête au contraire à trois lignes de texte au moins du pied, soit la place pour le texte que laisse tout flottant qui partage une colonne avec un autre : la colonne se termine ainsi par du texte sous le tableau et non par des flottants seuls. Pour qu’un long tableau descende jusqu’au pied de sa colonne, citez-le là où la page où il commence ne porte aucun autre flottant (après la page d’une figure sur toute la largeur, par exemple), ou ajustez ses lignes à la colonne.

`'clip'` garde les premières lignes qui tiennent sur la page et abandonne les autres sans le signaler (la note ferme toujours la partie) ; `'hide'` omet entièrement le tableau. Les deux ne s’appliquent qu’à un tableau plus haut qu’une page : un tableau qui tient est placé entier dans tous les modes.

**Tableaux placés dans le texte.** Un tableau placé `here` (incorporé avec `::resource`) suit les mêmes règles depuis postext 1.25 (`splitInline`, actif par défaut). Quand il ne tient pas dans la place restante de sa colonne, il est coupé entre deux lignes : la première partie garde au-dessus d’elle l’espace des flottants et au moins les lignes d’en-tête et deux lignes du corps (s’il en tient moins, tout le tableau commence dans la colonne suivante, comme avant), chaque partie suivante ouvre la colonne suivante sans espace au-dessus d’elle, composée à la largeur de cette colonne (une mise en page à une colonne et demie compose une partie dans la colonne étroite, à sa largeur), et le texte qui suit la ligne `::resource` vient après la dernière partie. La répétition de l’en-tête, la légende avec son suffixe, la mention, la note sur la dernière partie, le reliquat de trois lignes au moins et les coupes qui respectent les cellules fusionnées et les têtes de groupe sont ceux d’un tableau flottant. Un tableau de moins de cinq lignes de corps n’est jamais coupé. `'clip'` garde les premières lignes d’un tableau dans le texte plus haut qu’une colonne, composé en tête de colonne ; `'hide'` omet un tel tableau ; un tableau plus court passe entier à la colonne suivante dans les deux modes. Une page qu’ouvre un tableau dans le texte ne réserve, parmi les flottants qu’elle reçoit, que la place de sa première partie : une figure sur toute la largeur en attente se place donc en tête de cette page et le tableau continue en dessous. Les tableaux dans le texte sur une page verticale, et les tableaux à l’intérieur d’un encadré, ne sont pas coupés. `splitInline: false` déplace un tableau dans le texte entier à la colonne suivante, comme jusqu’à postext 1.24.

Les chaînes de continuation ont une valeur par défaut propre à chaque langue de document (`locale`, à défaut la langue de césure) : en anglais `(cont.)` / `Continued`, en espagnol `(cont.)` / `Continúa`, et de même en français, en allemand, en italien, en portugais, en catalan et en néerlandais (listées sous [Langue du document](https://postext.dev/fr/docs/configuration-text.md#langue-du-document)).

Le contenu des cellules est du Markdown en ligne, et un saut de ligne dans une cellule (un retour à la ligne, ou `\\` comme dans les légendes et les notes) commence un nouveau paragraphe. Un paragraphe qui commence par une puce ou un tiret (`•`, `-`, `*`, `–`) ou par un numéro (`1.`, `1)`) suivi d’une espace est composé comme un élément de liste : la marque est peinte telle qu’elle est écrite, le texte s’en écarte de l’`unorderedLists.gap` du document, les lignes suivantes s’alignent sur le texte, et deux espaces en tête imbriquent un niveau. Une cellule écrite `• Ofrece elección\n• Acomoda a personas diestras y zurdas` donne ainsi une liste de deux éléments. Une ligne faite seulement d’espaces ordinaires n’ajoute rien ; une ligne qui contient une espace insécable (U+00A0) est une ligne de la cellule, comme en CommonMark : `1\n` suivi d’une espace insécable donne une ligne de tableau haute de deux lignes de texte. Une espace insécable à la fin du texte d’une cellule garde sa largeur : `760` suivi d’une espace insécable, aligné à droite au-dessus de `(231)`, s’arrête une espace avant le bord, ce qui rapproche le 0 du 1. Les chiffres ne s’alignent exactement que là où l’espace est aussi large que la parenthèse, et dans la plupart des polices elle est plus étroite. (Jusqu’à postext 1.4, les deux étaient supprimées.)

Les largeurs de colonne appartiennent au modèle du tableau, pas au style : `TableModel.columnWidths` est une liste facultative de poids relatifs, un par colonne, normalisés au moment de la mise en page ; `[2, 1, 1]` donne à la première colonne la moitié de la largeur. Une liste absente, de mauvaise longueur, ou contenant un poids nul ou négatif se rabat sur un partage égal. L’éditeur de tableaux garde la liste en accord avec les colonnes quand on en ajoute ou en retire.

### Construire des modèles de tableau

Un `TableModel` est une grille rangée ligne par ligne, et chaque cellule est disposée selon sa place dans cette grille : `rows[r][c]` occupe la colonne `c`. Une cellule fusionnée garde donc dans la grille les cellules qu’elle couvre, chacune marquée `hiddenBy` avec sa cellule principale, contrairement à un tableau HTML, qui les omet. Les fonctions de modèle exportées par `postext` conservent cette forme ; ce sont des fonctions pures qui renvoient un nouveau modèle : `mergeCells(model, { start, end })` et `unmergeCell(model, at)`, `addRow`, `addColumn`, `removeRow`, `removeColumn`, `setCellContent`, `setCellImage`, `setCellBackground` et `setAlignment`. Les quatre fonctions de lignes et de colonnes gardent les fusions entières : une ligne ou une colonne ajoutée à l’intérieur d’un bloc fusionné l’élargit, une ajoutée avant lui le déplace, une retirée de lui le rétrécit (un bloc qui perd sa première ligne ou sa première colonne garde son contenu dans sa nouvelle cellule en haut à gauche), et chaque `hiddenBy` continue de désigner sa cellule principale.

`parseTSV(text, options?)` construit un modèle à partir de texte séparé par des tabulations, comme une plage collée depuis un tableur : les lignes se séparent aux sauts de ligne, les cellules aux tabulations, et les lignes trop courtes sont complétées pour que la grille soit rectangulaire. `headerRows` fait des premières lignes des lignes d’en-tête : leurs cellules reçoivent `isHeader` et le modèle `headerRowCount`, si bien qu’un tableau coupé sur plusieurs pages les répète.

```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] is { content: 'Part', isHeader: true }
model = mergeCells(model, { start: { row: 2, col: 1 }, end: { row: 2, col: 2 } });
// rows[2][1] gets colSpan: 2; rows[2][2] stays in the grid with hiddenBy: { row: 2, col: 1 }
```

`tableGridIssues(model)` vérifie la grille. Elle renvoie une liste vide pour un modèle sain, et sinon chaque endroit où la grille se rompt, dans l’ordre des lignes : `spanOverlap`, une cellule visible sous le `colSpan` / `rowSpan` d’une autre cellule (`coveredBy` désigne cette cellule), ce que produit une cellule couverte omise à la manière de HTML, puisque toutes les cellules suivantes se décalent sur la fusion ; et `missingCells`, une ligne qui s’arrête avant la dernière colonne sans fusion qui couvre le reste, ce qui laisse un trou.

```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 }]
```

Un tableau utilisé par le document dont la grille présente de tels problèmes est signalé dans `doc.contentWarnings` sous le nom `raggedTableGrid` (voir [Avertissements dans le document](https://postext.dev/fr/docs/configuration-programmatic-usage.md#avertissements-dans-le-document)).

## Style de légende

La propriété `captionStyle` règle les légendes des ressources (la ligne `Figure 1 — …` sous les images, les SVG et les tableaux, ou au-dessus). Le libellé numéroté et la description partagent le même caractère et le même corps (une contrainte du moteur), mais le libellé peut avoir sa propre graisse, son italique et sa couleur. La famille, le corps et la couleur héritent du texte courant s’ils ne sont pas définis. La légende peut se placer **au-dessus** de la ressource (la convention habituelle pour les tableaux) et être composée sur une **barre** de couleur qui couvre la largeur du bloc ; une **note** facultative plus petite (ligne de source, crédits : `Resource.note`) se règle avec le sous-objet `note`. Un type de ressource peut surcharger n’importe lequel de ces champs pour ses propres ressources avec `ResourceType.captionStyle` (voir [Types de ressources](https://postext.dev/fr/docs/configuration-resources.md#types-de-ressources)).

```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' },
  },
};
```

| Propriété | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `fontFamily` | `string` | police du texte courant | Famille de caractères de la légende (libellé et description). |
| `fontSize` | `Dimension` | corps du texte courant | Corps de la légende (libellé et description). |
| `color` | `ColorValue` | couleur du texte courant | Couleur du texte de la description. |
| `align` | `'left' \| 'center' \| 'right' \| 'justify' \| 'start' \| 'end'` | `'left'` | Alignement horizontal des lignes de la légende. `'justify'` étire chaque ligne sauf la dernière sur toute la largeur. Sur une barre de légende, les lignes s’alignent à l’intérieur de sa marge intérieure ; une légende latérale s’aligne dans sa propre largeur. |
| `gap` | `Dimension` | `0.75em` | Espace vertical entre la ressource et sa légende. |
| `labelBold` | `boolean` | `true` | Composer le libellé numéroté (par ex. `Figure 1`) en gras. |
| `labelItalic` | `boolean` | `false` | Composer le libellé numéroté en italique. |
| `labelColor` | `ColorValue` | `color` de la légende | Couleur du libellé numéroté. |
| `descriptionItalic` | `boolean` | `false` | Composer le texte de la description en italique. |
| `position` | `'above' \| 'below'` | `'below'` | Place de la légende. Avec `'above'`, la légende (et sa barre) vient en premier et le corps de la ressource descend de la hauteur de la légende plus `gap` ; la note passe alors sous le corps. |
| `backgroundEnabled` | `boolean` | `false` | Peindre une barre derrière la légende. La barre couvre toute la largeur du bloc et entoure les lignes de la légende, plus `padding` de chaque côté. |
| `background` | `ColorValue` | couleur principale de la palette | Couleur de fond de la barre (peinte seulement si l’option est activée). |
| `padding` | `Dimension` | `0.35em` | Marge intérieure entre le bord de la barre et le texte de la légende. Ignorée quand la barre est désactivée. |
| `note` | `object` | — | Style de la note de la ressource : voir le sous-tableau plus bas. |
| `labelNumberGap` | `string` | espace insécable ; `''` dans un document japonais | Ce qui sépare le libellé du numéro, dans la légende et dans un `:ref` en ligne : *Figure 1.7*, *Fig. 1.7*. Le chinois et le japonais les composent collés : `''` donne 图1-1, et c’est la valeur par défaut dans un document japonais (図1-1). |
| `labelSeparator` | `string` | `'. '` ; `'　'` dans un document japonais | Ce qui suit le numéro, avant la description : *Figure 1.7. Une légende*. Les légendes chinoises prennent une espace idéographique, `'　'` (图1-1　标题), et les japonaises aussi, par défaut (図1-1　東京の地図, JLReq §4.3). Un libellé sans numéro garde sa propre règle : un point, sauf si le préfixe se termine déjà par un point. |

Le sous-objet `note` règle `Resource.note`, un court texte (source, crédits, remarque) composé sous la ressource dans un corps plus petit. Il accepte la même mise en forme en ligne et les mêmes marques `:ref` que la légende, et hérite du caractère de la légende. Il se place sous la légende quand celle-ci est en dessous, et sous le corps de la ressource quand la légende est au-dessus ; sa hauteur compte dans le bloc, si bien qu’une ressource avec une note flotte d’un seul tenant.

| Propriété | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `note.fontSize` | `Dimension` | 0,85 × le corps de la légende | Corps de la note. |
| `note.color` | `ColorValue` | `color` de la légende | Couleur du texte de la note. |
| `note.italic` | `boolean` | `false` | Composer la note en italique. |
| `note.gap` | `Dimension` | `0.35em` | Espace entre la note et ce qui la précède (légende ou corps). |
| `note.align` | `'left' \| 'center' \| 'right' \| 'justify' \| 'start' \| 'end'` | `'left'` | Alignement horizontal des lignes de la note, comme `align` pour la légende. |

Les surcharges par type sont fusionnées avec `mergeCaptionStyle(resolvedCaptionStyle, override, palette?)`, exportée pour les hôtes qui ont besoin de la même résolution en dehors de la chaîne de traitement.

## Style des diagrammes

La propriété `diagramStyle` règle la couleur des diagrammes SVG incorporés (ressources `kind: 'svg'`) et les polices dans lesquelles leur texte est composé. Le **mode encre unique** est une passe de recoloration qui convertit chaque couleur d’un diagramme en une teinte d’une seule encre, pour que les figures se reproduisent fidèlement quand le document est imprimé avec une seule couleur d’accompagnement. Les **polices intégrées** incorporent dans chaque SVG les polices que nomme son texte, pour que ses libellés soient composés dans les polices du document sur le canvas, en HTML et en EPUB (voir [Polices du texte SVG](https://postext.dev/fr/docs/configuration-resources.md#polices-du-texte-svg)).

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

| Propriété | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `singleInk` | `boolean` | `false` | Recolorer chaque diagramme SVG incorporé en teintes d’une seule encre. |
| `inkColor` | `ColorValue` | Couleur principale (`#295AA3`) | L’encre. Par défaut, la couleur principale de la palette du document (liée à la palette par `paletteId: 'main-color'`) : changer cette pastille de couleur de la palette reteinte les diagrammes en même temps que les titres et le gras. |
| `inlineFonts` | `boolean` | `true` | Incorporer dans chaque SVG les polices que nomme son texte (`font-family`), sous forme d’URI de données `@font-face`, avant de l’afficher comme une image : sur le canvas, en HTML, en EPUB et dans l’image matricielle de repli du PDF. Jamais écrit dans le fichier enregistré. Une ressource s’en exclut avec `svg.inlineFonts: false` (depuis postext 1.25). |

### Fonctionnement de l’encre unique

Quand `singleInk` est activé, chaque couleur du balisage SVG est réécrite en une teinte d’`inkColor` dont l’intensité vaut **1 − luminance relative** (coefficients Rec. 709 appliqués aux canaux encodés en gamma : une approximation perceptive largement suffisante pour convertir en teintes). La conversion préserve la valeur perçue : le blanc devient le blanc du papier, le noir l’encre pleine, et les fonds clairs restent clairs quelle que soit leur teinte d’origine. Un fond jaune pâle devient une teinte pâle de l’encre ; un trait foncé s’approche de l’encre pleine.

La recoloration est faite par la fonction exportée `applySingleInkToSvg(svgText, inkHex)`, qui travaille sans DOM sur le balisage SVG, en tant que texte :

- Les littéraux hexadécimaux `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`, les fonctions `rgb()` / `rgba()` et les fonctions `hsl()` / `hsla()` sont réécrits partout où ils apparaissent : attributs de présentation, `style` en ligne, dégradés, `<defs>`. Les fonctions peuvent utiliser des canaux entiers, décimaux ou en pourcentage, et la syntaxe à virgules ou à espaces : `rgb(11.37%, 20%, 50.59%)` (tel que l’écrit Cairo) et `rgb(51 102 153 / 50%)` sont donc recolorés aussi. Une fonction teintée est réécrite en `rgb(…)` ou en `rgba(…)`.
- Les mots-clés `white` et `black` ne sont remplacés que là où ils sont des valeurs de peinture (`fill`, `stroke`, `stop-color`, `flood-color`, `color`, en attributs ou en propriétés de style en ligne), jamais dans le contenu textuel ou les libellés.
- `none`, `transparent` et `currentColor` restent intacts, tout comme les autres couleurs nommées (`red`, `steelblue`…) et le noir par défaut d’une forme ou d’un texte qui ne définit aucun remplissage. Donnez à ces éléments une couleur explicite pour qu’ils soient recolorés.
- Les canaux alpha sont conservés (les quartets de `#rgba` / `#rrggbbaa` et la composante alpha de `rgba(…)` passent tels quels ; un alpha en pourcentage est écrit comme un nombre).
- Quand `inkHex` ne peut pas être analysé, l’entrée est renvoyée inchangée.
- Le résultat porte `data-postext-single-ink="#…"` (l’encre) sur sa racine `<svg>`, et un balisage qui le porte déjà est renvoyé tel quel, quelle que soit l’encre qu’il nomme. La conversion n’est pas idempotente (une seconde passe éclaircit chaque couleur, le noir ressortant à environ deux tiers de l’encre) : une image n’est donc recolorée qu’une fois, par votre code ou par le moteur de rendu, selon celui qui la traite en premier. (Cette marque est nouvelle dans postext 1.5 ; un balisage recoloré par la 1.4 n’en a pas.)

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

const recoloured = applySingleInkToSvg(svgText, '#295AA3');
applySingleInkToSvg(recoloured, '#295AA3') === recoloured; // true: never twice
```

L’encre unique s’applique dans les trois moteurs de rendu : le moteur PDF recolore les octets SVG que lui transmet `resourceBytes` avant de les dessiner en vectoriel, et les moteurs canvas et HTML teintent les images SVG qu’ils peignent quand vous le leur demandez (voir [Encre unique sur le canvas et en HTML](https://postext.dev/fr/docs/configuration-resources.md#encre-unique-sur-le-canvas-et-en-html)) : le PDF exporté correspond ainsi à l’aperçu à l’écran.

Le résolveur et la fonction de nettoyage suivent le modèle des autres sections, avec les types `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 when everything matches the defaults
```

### Encre unique sur le canvas et en HTML

Les moteurs canvas et HTML reçoivent les images déjà décodées (`registerResourceImage`) ou sous forme d’URL (`resourceImageUrl`), et non le balisage SVG. Sur demande, ils appliquent la même conversion à ce qu’ils dessinent :

- **Canvas** (`renderPage`, `renderPageToCanvas`, `renderToCanvas`). Chaque image SVG concernée par la teinte (une figure, l’image d’une cellule de tableau, une image de maquette, une icône ou une marque d’encadré) est tramée à sa taille de placement et ses pixels sont teintés à l’encre, qu’elle ait été enregistrée comme `<img>` ou comme `ImageBitmap`. Le bitmap teinté est mis en cache comme toute trame vectorielle. Une image bitmap n’est jamais teintée.
- **HTML** (`renderToHtml`, `renderToHtmlIndexed`). Chaque `<img>` SVG reçoit `filter: url(#pt-ink-…)`, qui pointe vers un `feColorMatrix` porté par sa page : un `<svg>` de taille nulle contenant le `<filter>`, placé en premier dans la page, et inclus dans le `decorationHtml` de la page dans la sortie indexée. Chaque page le porte tant que l’encre unique s’applique, qu’elle contienne une image ou non : un hôte qui remplace les blocs un par un n’introduit jamais une image dont le filtre manque.

**Jamais teinté deux fois.** Jusqu’à postext 1.4, les moteurs canvas et HTML peignaient les images telles quelles, si bien que les hôtes recoloraient eux-mêmes le balisage avec `applySingleInkToSvg` avant de le transmettre. Les adaptateurs de paquet et le Sandbox le font encore, parce que la passe sur le balisage donne exactement les couleurs du PDF (voir le dernier paragraphe plus bas). Une image n’est donc teintée qu’une fois, selon trois règles valables dans les trois moteurs de rendu :

- **Un balisage marqué n’est pas retouché.** Le moteur PDF recolore `resourceBytes` avec `applySingleInkToSvg` : des octets SVG déjà recolorés sont donc dessinés tels quels. Sur le canvas et en HTML, une image chargée depuis une URI de données SVG dont le balisage porte la marque n’est jamais teintée non plus.
- **Désactivé sauf demande, dans postext 1.x.** Le canvas teinte une image SVG enregistrée sans indicateur propre seulement quand le rendu passe `singleInk: true` (`RenderPageOptions`), et une image enregistrée avec `registerResourceImage(id, img, { singleInk: true })` dans tout rendu. Le moteur HTML teinte quand `renderToHtml` reçoit `singleInk: true`, ou quand son résolveur `resourceImageUrl` porte `singleInk: true`. Un hôte écrit pour la 1.4, qui recolore le balisage et enregistre l’image décodée sans indicateur, garde sa sortie. La prochaine version majeure teintera par défaut.
- **`singleInk: false` n’est jamais teinté.** Derrière une URL blob ou réseau, le balisage ne peut pas être relu : une image que vous avez recolorée vous-même et décodée ainsi s’enregistre donc avec `singleInk: false`, comme le font `registerBundleImages` et le Sandbox. `bundleImageUrl(bundle)` renvoie un résolveur qui porte `singleInk: false`, et `bundleResourceBytes` transmet au PDF les octets propres du paquet, que le moteur PDF recolore une fois.

Les deux moteurs connaissent le genre de chaque image grâce au VDT : une figure et une image de cellule portent le genre de leur ressource, et un bloc d’image de maquette porte `imageKind` (`'svg'` ou `'bitmap'`), tiré de sa ressource lors de la mise en page. Pour un VDT construit avant l’existence d’`imageKind`, le canvas traite comme un SVG une image de maquette enregistrée comme source vectorielle, et le moteur HTML une image dont l’URL est une URI de données SVG ou se termine par `.svg`.

Recolorez le balisage vous-même ou laissez les moteurs teinter l’image brute, mais pas les deux :

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

// Raw SVG: tinted while diagramStyle.singleInk is on…
registerResourceImage('diagram.svg', rawImg, { singleInk: true });
// …or register it plainly and ask on each render.
registerResourceImage('diagram.svg', rawImg);
const canvas = renderPage(doc.pages[0], doc, { singleInk: true });

// Recoloured before decoding (as postext 1.4 hosts do): painted as given.
const inked = applySingleInkToSvg(svgText, ink);
registerResourceImage('diagram.svg', await decode(inked), { singleInk: false });

// The HTML backend with URLs to the raw markup.
const html = renderToHtml(doc, { resourceImageUrl: urlFor, singleInk: true });
```

`renderToHtml` tire la valeur par défaut de son option `singleInk` de l’indicateur propre au résolveur : `bundleImageUrl(bundle)` n’a donc besoin d’aucune option.

Pour chaque couleur que réécrit la passe sur le balisage (valeurs hexadécimales, `rgb()` et `hsl()`, `white` et `black` ; voir [Fonctionnement de l’encre unique](https://postext.dev/fr/docs/configuration-resources.md#fonctionnement-de-lencre-unique)), la conversion des pixels donne le même résultat, bords lissés et dégradés compris. Les deux diffèrent là où la passe sur le balisage laisse une couleur intacte : les couleurs nommées autres que `white` et `black`, `currentColor`, les formes et le texte sans remplissage (dessinés dans le noir par défaut) et les bitmaps incorporés au SVG sont teintés à l’écran mais gardent leur couleur dans le PDF. Donnez à chaque élément d’un diagramme une couleur explicite en hexadécimal, `rgb()` ou `hsl()` pour obtenir une sortie identique. Quand le canvas ne peut pas relire les pixels (une `<img>` d’une autre origine, chargée sans CORS), l’image est peinte sans teinte.

### Polices du texte SVG

Une image SVG s’affiche à travers une image : une `<img>` sur le canvas, en HTML et en EPUB. Un document image ne voit pas les polices web de la page, si bien que `<text font-family="IBM Plex Sans">` se rabattrait sur une police du système. Depuis postext 1.25, le moteur incorpore dans le balisage les polices que nomme le texte avant que l’image soit décodée ou fournie sous forme d’URL : une règle `@font-face` par fichier de police, ses octets en URI de données, dans un `<style>` placé juste après la balise racine `<svg>`. Le PDF n’a besoin de rien de tout cela : il compose le texte SVG comme du vrai texte dans ses polices incorporées (voir [Octets des ressources et originaux d’impression](https://postext.dev/fr/docs/configuration-programmatic-usage.md#octets-des-ressources-et-originaux-dimpression)).

Ce que demande le texte se lit dans `font-family`, `font-weight`, `font-style` et `font`, en attributs, dans les attributs `style` et par héritage des groupes englobants ; une règle `<style>` qui nomme une famille compte aussi. Les familles génériques (`serif`, `sans-serif`…) et le texte de `<title>` ou de `<desc>` sont exclus, et une famille que le SVG déclare lui-même avec `@font-face` n’est pas retouchée. Un segment de texte parcourt sa liste `font-family` jusqu’à la première famille qui dispose d’une police. La recoloration pour l’encre unique passe en premier, puis les polices.

Les polices viennent d’un **fournisseur** qui respecte le contrat du `PdfFontProvider` de postext-pdf, si bien qu’un seul fournisseur sert aux deux : il est appelé avec la famille, la graisse, le style et les caractères que le SVG compose dans cette police, et répond par un fichier ou plusieurs. Une famille servie en tranches unicode-range (Fontsource, Google Fonts) reçoit les tranches dont ces caractères ont besoin : un SVG aux libellés latins ne porte que le fichier `latin`. Le fournisseur par défaut lit le registre de polices du moteur : `loadBundleFonts` y enregistre les polices d’un paquet, et un hôte y enregistre les siens avec `registerFontBytes(family, weight, style, bytes, { unicodeRange })`, ou `registerFontUrl(…)` pour un fichier téléchargé la première fois qu’un SVG en a besoin. Une famille que rien n’a enregistrée est cherchée dans les règles `@font-face` des feuilles de style lisibles de la page. Une `FontFace` ajoutée à `document.fonts` à partir d’octets ne garde pas ces octets, et le moteur ne peut donc pas les relire : enregistrez aussi ces polices.

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

registerFontBytes('IBM Plex Sans', 700, 'normal', plexBoldWoff2);
await registerSvgImage('chart.svg', svgText);   // recoloured, fonts inlined, decoded, registered
const canvas = renderPage(doc.pages[0], doc);
```

Où cela se passe :

- **Canvas.** `registerSvgImage(fileId, svgText, options)` recolore (`inkHex`), intègre les polices (`fonts`, un fournisseur ; `inlineFonts: false` saute cette étape), décode et enregistre l’image comme source vectorielle, puis se résout avec le sort de chaque police. `registerBundleImages(bundle)` fait de même pour les SVG d’un paquet, en partant des polices du paquet lui-même. `prepareSvgMarkup(svgText, options)` renvoie le balisage préparé, pour un hôte qui décode de son côté.
- **HTML.** `bundleImageUrl(bundle)` sert le balisage SVG avec les polices du paquet intégrées. `renderToHtml(doc, { inlineSvgFonts: true })` les intègre dans les URI `data:` SVG que renvoie `resourceImageUrl`, à partir des polices que le registre garde en mémoire (ou `inlineSvgFonts: { fonts, maxBytes, withhold }`). Une URL d’objet ne peut pas être lue de façon synchrone : un hôte qui sert des URL blob intègre donc les polices avant de les créer.
- **EPUB.** `postext-epub` intègre les polices à partir des `fonts` du livre, puis de `svgFonts.provider`, avant d’écrire un SVG (voir [Livres EPUB](https://postext.dev/fr/docs/configuration-programmatic-usage.md#livres-epub-postext-epub)).
- **PDF.** Le texte SVG est composé comme du vrai texte dans les polices incorporées. Un `<style>` qui ne contient que des règles `@font-face` (des polices qu’un auteur a incorporées) ne fait plus se rabattre la figure sur une image matricielle. Quand une figure se rabat malgré tout (un filtre, un dégradé), son image matricielle est produite avec les polices intégrées depuis le `fontProvider` du PDF.

Les fonctions de plus bas niveau sont exportées aussi : `svgFontRequests(svgText)` liste les familles, la graisse, le style et les caractères de chaque segment ; `inlineSvgFonts(svgText, provider, options)` et `inlineSvgFontsSync(svgText, syncProvider, options)` renvoient le balisage ; `inlineSvgFontsDetailed` y ajoute un rapport sur chaque police (`inlined`, `declared`, `unavailable`, `withheld`, `tooLarge`).

| Option | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `maxBytes` | `number` | 2 Mio | Nombre maximal d’octets de police incorporés dans un SVG (avant le base64, qui ajoute un tiers). Des polices qui ensemble le dépassent ne sont pas incorporés du tout, et `svgFontsTooLarge` est signalé. |
| `formats` | `('woff2' \| 'woff' \| 'ttf' \| 'otf')[]` | les quatre | Les formats de fichier à incorporer ; les fichiers d’autres formats sont ignorés. |
| `withhold` | `(family) => boolean` | aucun | Familles à exclure d’un fichier qui sort de l’application (non redistribuables). Leur référence reste et le lecteur se rabat sur une autre police ; `onWithheld(family)` est informé de chacune. |
| `onWarning` | `(warning) => void` | aucun | Informé d’une famille sans police disponible (`svgFontUnavailable`) et du dépassement de taille (`svgFontsTooLarge`). |

**Désactivation.** `diagramStyle.inlineFonts: false` laisse chaque SVG tel qu’il est enregistré ; `svg.inlineFonts: false` sur une ressource laisse celle-ci intacte octet pour octet, pour un SVG qui porte ses propres polices ou ne doit pas changer. Un paquet écrit l’exclusion d’une ressource sous la forme `"inlineFonts": false` dans son `preset.json`.

**Licences.** L’intégration place des fichiers de police dans des images qui peuvent sortir de l’application (un export HTML, un EPUB). Elle a lieu quand une image est affichée ou exportée, jamais dans les octets enregistrés de la ressource, et `withhold` écarte les familles qu’une licence ne vous permet pas de transmettre : le générateur EPUB retient les polices marquées `redistributable: false`, et le Sandbox les familles personnalisées marquées ainsi.

## Style des vidéos

La propriété `videoStyle` règle l’impression des [ressources vidéo](https://postext.dev/fr/docs/document-format.md#vidéos) (la marque de lecture et le code QR sur l’affiche, et le lien éventuel de l’affiche vers la vidéo) et ce que leurs lecteurs proposent dans la visionneuse HTML et en 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 },
  },
};
```

| Propriété | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `playMark` | `VideoPlayMarkConfig` | voir plus bas | La marque imprimée sur l’affiche pour signaler qu’elle se lit. |
| `qr` | `VideoQrConfig` | voir plus bas | Le code QR imprimé sur l’affiche : il ouvre la page YouTube ou Vimeo de la vidéo, ou l’adresse de production d’un fichier. |
| `linkPoster` | `boolean` | `true` | Faire de l’affiche un lien vers la vidéo : une annotation de lien posée dessus dans le PDF, et un `<a>` autour d’elle en HTML et en EPUB partout où l’affiche est montrée. |
| `html` | `'player'` · `'poster'` | `'player'` | Ce que la sortie HTML compose pour une vidéo : son lecteur, ou l’affiche imprimée avec ses incrustations. |
| `player` | `VideoPlayerOptions` | voir plus bas | Les options de lecteur de toutes les vidéos ; le `video.player` propre à une vidéo s’y superpose. |

### Marque de lecture

| Propriété | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `enabled` | `boolean` | `true` | Imprimer la marque. |
| `shape` | `'circle'` · `'rounded'` · `'triangle'` | `'circle'` | Un disque avec un triangle, un rectangle arrondi avec un triangle (1,45 fois plus large que haut), ou le triangle seul, cerné de la couleur de fond. |
| `position` | `VideoOverlayPosition` | `'center'` | `'center'`, un coin (`'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`) ou le milieu d’un côté (`'top'`, `'bottom'`, `'left'`, `'right'`). Les positions sont physiques : le coin supérieur droit reste le coin supérieur droit dans un livre de droite à gauche. |
| `size` | `Dimension` | `12mm` | Hauteur de la marque ; jamais plus de 40 % du petit côté de l’affiche. |
| `inset` | `Dimension` | `4mm` | Distance aux bords de l’affiche, dans un coin ou sur un côté. |
| `color` | `ColorValue` | blanc | Le triangle. |
| `background` | `ColorValue` | Couleur principale | Le disque ou le rectangle derrière lui ; le contour du triangle quand il est seul. Lié à la palette par défaut. |
| `backgroundOpacity` | `number` | `0.9` | Opacité du fond, de 0 à 1. |

### Code QR

| Propriété | Type | Par défaut | Description |
| --- | --- | --- | --- |
| `enabled` | `boolean` | `true` | Imprimer le code. Un fichier sans adresse de production n’en reçoit pas. |
| `position` | `VideoOverlayPosition` | `'bottom-right'` | Comme pour la marque de lecture. Donnez aux deux des positions différentes. |
| `size` | `Dimension` | `18mm` | Côté du code, zone de silence comprise ; jamais plus de 45 % du petit côté de l’affiche. Les appareils photo des téléphones lisent des modules à partir d’un tiers de millimètre : une adresse de 30 caractères donne un code de 29 modules, si bien que 18 mm avec une zone de silence de 2 donnent des modules de 0,55 mm. |
| `inset` | `Dimension` | `3mm` | Distance aux bords de l’affiche. |
| `errorCorrection` | `'L'` · `'M'` · `'Q'` · `'H'` | `'M'` | Part du code qui peut être abîmée ou masquée sans empêcher la lecture : environ 7 %, 15 %, 25 % ou 30 %. Relevée d’elle-même tant que le code garde le même nombre de modules. |
| `quietZone` | `number` | `2` | Modules clairs autour du code, sur sa plaque (de 0 à 8). La plaque se détache de l’affiche : les quatre modules que la norme demande sur papier nu ne sont donc pas nécessaires. |
| `color` | `ColorValue` | noir | Les modules foncés. Gardez-les foncés sur une plaque claire : la plupart des lecteurs ne lisent pas les codes inversés. |
| `background` | `ColorValue` | blanc | La plaque. |
| `radius` | `Dimension` | `1mm` | Rayon des coins de la plaque. |

Le code est encodé par le moteur lui-même (`encodeQr(text, level)` : mode octet, UTF-8, versions 1 à 40, le masque de plus faible pénalité) et dessiné en vectoriel : le canvas remplit un seul tracé fait des suites de modules, le PDF un seul `drawSvgPath`, et le HTML un seul `<path>` avec `shape-rendering="crispEdges"` ; il reste ainsi net à toute taille d’impression.

### Options du lecteur

`VideoPlayerOptions`, dans `videoStyle.player` et dans le `video.player` de chaque vidéo. Le lecteur HTML5 d’un fichier les respecte toutes ; les lecteurs YouTube et Vimeo respectent ce que permettent leurs paramètres d’intégration.

| Propriété | Par défaut | Respectée par | Description |
| --- | --- | --- | --- |
| `controls` | `true` | YouTube, Vimeo, fichiers | Afficher les commandes du lecteur. |
| `download` | `true` | fichiers | Proposer le bouton de téléchargement du navigateur (`controlslist="nodownload"` si désactivé). Cela masque le bouton ; cela ne protège pas le fichier. YouTube et Vimeo ne proposent jamais de téléchargement. |
| `fullscreen` | `true` | YouTube, Vimeo, fichiers | Proposer le plein écran (`fs=0`, l’attribut `allowfullscreen` de l’iframe, `nofullscreen`). |
| `playbackRate` | `true` | Vimeo, fichiers | Proposer le menu de vitesse (`speed=0`, `noplaybackrate`). |
| `pictureInPicture` | `true` | Vimeo, fichiers | Proposer l’incrustation d’image (`pip=0`, `disablepictureinpicture`). |
| `remotePlayback` | `true` | fichiers | Proposer la diffusion vers un autre écran (`disableremoteplayback`). |
| `autoplay` | `false` | YouTube, Vimeo, fichiers | Démarrer la lecture d’elle-même, toujours en sourdine, comme l’exigent les navigateurs. |
| `muted` | `false` | YouTube, Vimeo, fichiers | Démarrer avec le son coupé. |
| `loop` | `false` | YouTube, Vimeo, fichiers | Reprendre depuis le début une fois arrivé à la fin. |
| `exclusive` | `true` | Folio, visionneuse HTML, EPUB (là où les scripts s’exécutent) ; fichiers | Lancer cette vidéo met en pause les autres affichées, pour qu’une seule joue à la fois. `false` la laisse jouer en même temps que les autres : les courtes boucles muettes d’une page, plusieurs en même temps. Depuis postext 1.18. |
| `preload` | `'metadata'` | fichiers | Ce que le navigateur charge avant la lecture : `'none'`, `'metadata'` ou `'auto'`. |
| `privacy` | `true` | YouTube, Vimeo | Intégrations à confidentialité renforcée : YouTube depuis `youtube-nocookie.com`, Vimeo avec `dnt=1`. |

Un EPUB ne garde que les attributs que connaît son schéma : un fichier se lit avec `controls`, `autoplay`, `muted`, `loop`, `playsinline` et `preload`, plus la marque `data-pt-alongside`, et le système de lecture décide du reste.

Une vidéo non exclusive porte `data-pt-alongside` dans la sortie HTML. `coordinateVideoPlayback(root)` fait respecter cette règle aux lecteurs placés sous `root` (l’élément qui contient la sortie de `renderToHtml`) : lancer une vidéo exclusive met en pause toutes les autres en cours de lecture, et lancer une vidéo qui joue en même temps que les autres ne met en pause que les exclusives. La fonction renvoie une fonction qui arrête l’écoute. `playsAlongside(el)` et `videosToPause(started, videos, alongside)` donnent la même règle à un hôte qui a ses propres lecteurs. Dans [Folio](https://postext.dev/fr/docs/document-format.md#les-vidéos-sur-les-pages-de-folio), une vidéo qui démarre d’elle-même et joue en même temps que les autres (`autoplay` avec `exclusive: false`) démarre, en sourdine, chaque fois que sa page apparaît, et s’arrête quand la page est tournée, plusieurs à la fois, et `loop` la reprend depuis le début. Une page ou un chapitre EPUB qui contient des vidéos à coordonner (deux ou plus, dont une exclusive) lie la même règle sous forme d’un petit script, `scripts/videos.js` (l’export `VIDEO_PLAYBACK_SCRIPT`), et le paquet déclare ce document `scripted`. Un système de lecture qui exécute les scripts fait respecter la règle aux vidéos de ce document, mais pas à celles d’une page en regard, qui est un autre document ; un système qui n’exécute pas les scripts lit chaque vidéo indépendamment, comme le font toujours les lecteurs YouTube et Vimeo.

Le résolveur et la fonction de nettoyage suivent le modèle des autres sections, avec les types `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 when all defaults
```
