Colonnes
L'élément columns crée des mises en page à plusieurs colonnes au sein d'un document. Chaque colonne accepte n'importe quel nœud de contenu — paragraphes, en-têtes, tableaux, listes, images, canevas, voire même des mises en page de colonnes imbriquées — suivant la sémantique de contenu de flux HTML5.
Structure
{
"columns": {
"widths": [1, 1],
"gap": 20,
"rows": [
[
{ "p": "Left column content" }
],
[
{ "p": "Right column content" }
]
]
}
}
Propriétés
| Propriété | Type | Obligatoire | Description |
|---|---|---|---|
widths | number[] | Non | Poids proportionnels par colonne (ex. [3, 1] = 75/25). Choisit le mode de mise en page grille proportionnelle. |
colNumber | integer | Non | Nombre de colonnes à division égale. Choisit le mode de mise en page flux de contenu (sémantique CSS-multicol — le contenu s'écoule sur N colonnes). |
gap | number | Non | Espace entre les colonnes en points (par défaut : 10) |
rows | array[] | Oui | Tableau de tableaux de contenu, un par colonne (colonne-majeur) |
style | string | Non | Style nommé appliqué au bloc de colonnes |
Modes de mise en page
Trois branches, chacune choisie selon ce que vous avez écrit :
| Vous avez écrit | Mode de mise en page | À quoi ça sert |
|---|---|---|
widths: [3, 1] | Grille proportionnelle | Panneaux côte à côte avec des ratios explicites. Gagne si widths et colNumber sont tous les deux définis. |
colNumber: 3 | Flux de contenu | Style journal : un seul flux de contenu divisé sur N colonnes égales. |
| (aucun) | Grille égale | Colonnes égales côte à côte ; nombre de colonnes = rows.length. |
Le chemin de grille place rows[i] dans la colonne i. Le chemin de flux de contenu concatène tout ce qui est dans rows en un seul flux et permet au moteur de rendu de le diviser sur colNumber colonnes.
Largeurs des colonnes
Le tableau widths définit les poids relatifs. La largeur de page disponible (moins les marges et les espaces) est divisée selon ces ratios.
| Largeurs | Mise en page |
|---|---|
[1, 1] | Deux colonnes égales |
[2, 1] | Deux colonnes, la gauche est deux fois plus large |
[1, 2, 1] | Trois colonnes, le centre est plus large |
[1, 1, 1] | Trois colonnes égales |
[3, 1] | Deux colonnes, la gauche occupe 75 % |
widths.length détermine le nombre de colonnes. Si rows a moins d'entrées que widths.length, les colonnes finales se renderont vides. Si rows en a plus, les extras sont ignorés.
Espace entre colonnes
La valeur gap définit l'espacement horizontal entre les colonnes, mesuré en points. S'il est omis, une valeur par défaut de 10pt est utilisée. En mode grille, l'espace est divisé moitié-moitié entre les cellules adjacentes comme remplissage ; en mode flux de contenu, le moteur de rendu l'applique nativement.
Lignes
Chaque élément de rows est un tableau d'éléments de contenu rendus dans cette colonne — la structure est colonne-majeur, donc rows[0] est la pile verticale de blocs de la première colonne, rows[1] est la pile de la deuxième colonne, et ainsi de suite. En mode flux de contenu (colNumber), le partitionnement par ligne est aplati : tout le contenu devient un seul flux.
Galerie des cas limites
L'exemple ci-dessous parcourt chaque branche d'acheminement et les cas limites courants — grille égale par défaut, largeurs proportionnelles, poids de flottaison, widths décalée par rapport à rows.length, cellules vides, widths gagnant sur colNumber, mode flux de contenu, nombreuses colonnes étroites et contenu hétérogène. Les contours pointillés magenta marquent chaque bloc de colonnes pour que les limites soient visibles.
- Output
- Template
- Data
Exemples
Mise en page à deux colonnes : expéditeur et destinataire
{
"columns": {
"widths": [1, 1],
"gap": 20,
"rows": [
[
{ "p": "[b]From:[/b]" },
{ "p": "ShipForge Ltd" },
{ "p": "45 Innovation Drive" },
{ "p": "London, UK" }
],
[
{ "p": "[b]To:[/b]" },
{ "p": "Meridian Logistics" },
{ "p": "742 Evergreen Terrace" },
{ "p": "Denver, CO" }
]
]
}
}
Tableau de bord KPI à trois colonnes
{
"document": {
"styles": {
"kpiLabel": { "fontWeight": "bold", "fontSize": 10, "color": "#333333" },
"kpiValue": { "fontSize": 24, "fontWeight": "bold", "color": "#1A1A2E" },
"kpiNote": { "fontSize": 8, "color": "#999999" }
},
"content": [
{
"columns": {
"widths": [1, 1, 1],
"gap": 15,
"rows": [
[
{ "p": "Revenue", "style": "kpiLabel" },
{ "p": "$1.87M", "style": "kpiValue" },
{ "p": "Target: $1.75M", "style": "kpiNote" }
],
[
{ "p": "Customers", "style": "kpiLabel" },
{ "p": "2,340", "style": "kpiValue" },
{ "p": "Growth: +14.1%", "style": "kpiNote" }
],
[
{ "p": "Satisfaction", "style": "kpiLabel" },
{ "p": "94%", "style": "kpiValue" },
{ "p": "NPS: 72", "style": "kpiNote" }
]
]
}
}
]
}
}
Mise en page asymétrique : contenu large avec barre latérale
{
"columns": {
"widths": [3, 1],
"gap": 25,
"rows": [
[
{ "p": "[b]Shipment Details[/b]" },
{ "p": "Your package has been dispatched from the Chicago hub and is currently in transit to the Toronto distribution center. Expected delivery window is April 3-5, 2026." },
{
"table": {
"widths": [1, 2],
"rows": [
[{ "p": "[b]Tracking ID[/b]" }, { "p": "SF-20260330-001" }],
[{ "p": "[b]Weight[/b]" }, { "p": "12.4 kg" }],
[{ "p": "[b]Carrier[/b]" }, { "p": "ShipForge Express" }]
]
}
}
],
[
{ "p": "[b]Quick Links[/b]" },
{
"ul": {
"li": [
{ "p": "Track online" },
{ "p": "Contact support" },
{ "p": "File a claim" }
]
}
},
{ "p": "[barcode, qrcode, SF-20260330-001, 80|80]" }
]
]
}
}
Flux de contenu de style journal avec colNumber
Lorsque vous avez un seul long flux de contenu qui doit s'écouler sur N colonnes égales (sémantique CSS-multicol), utilisez colNumber au lieu de widths.
{
"columns": {
"colNumber": 3,
"gap": 14,
"rows": [
[
{ "h3": "Summary" },
{ "p": "The fiscal-year report consolidates revenue, customer growth, and product-line performance across all three operating regions." },
{ "p": "Year-over-year revenue growth reached 18.4%, driven primarily by enterprise renewals and expansion into the Mid-Atlantic corridor." },
{ "p": "Customer retention held at 94.2% — the strongest figure since 2024 — while net-new acquisitions outpaced churn for the eleventh consecutive quarter." }
]
]
}
}
PDF divise le flux en 3 colonnes égales avec un vrai reflux de contenu. DOCX revient à une grille à 3 cellules de largeur égale puisque le format DOCX lui-même n'a pas de primitive de colonne de flux au niveau du bloc — le résultat ressemble à l'identique pour le contenu qui s'ajuste, mais ne se recoulera pas automatiquement sur les colonnes si le contenu déborde.
Alignement haut de colonne en mode flux de contenu
Quand un paragraphe est en flux à un saut de colonne, sa continuation dans la colonne suivante s'aligne parfaitement avec le haut de la colonne — l'interligne du paragraphe n'est pas réappliqué à une continuation. L'effet visible : la première ligne d'une colonne de continuation peut s'asseoir quelques points plus haut que les colonnes qui commencent à une limite de paragraphe.
C'est le comportement attendu et conforme à la spécification CSS multicol. Si vous voulez que tous les sommets des colonnes s'alignent exactement :
- Conseil 1 — dividez uniquement entre les paragraphes. Créez des paragraphes plus courts dimensionnés de sorte que le diviseur atterrisse à une limite de paragraphe chaque fois. Le côté négatif : vous échangez la forme du contenu pour l'alignement visuel.
- Conseil 2 — conteneur de hauteur égale. Définissez une
heightexplicite sur le bloc de colonnes ; le diviseur a plus d'espace pour se rééquilibrer, réduisant les divisions à mi-paragraphe. - Conseil 3 — utilisez plutôt la grille proportionnelle. Basculez vers
widths: [1, 1, 1](mode grille). Chaquerows[i]devient sa propre colonne, donc chaque colonne commence à une limite de paragraphe par construction. Vous perdez le reflux automatique mais gagnez les sommets alignés sur les pixels.
Références de données dans les colonnes
Le contenu des colonnes supporte les références $data tout comme n'importe quel autre élément de contenu.
{
"columns": {
"widths": [1, 1],
"gap": 20,
"rows": [
[
{ "p": "[b]From:[/b] $data.company.name" },
{ "p": "$data.company.address" },
{ "p": "$data.company.city" }
],
[
{ "p": "[b]To:[/b] $data.client.companyName" },
{ "p": "$data.client.address" },
{ "p": "$data.client.city" }
]
]
}
}