# Uso programmatico: pacchetti .postext

> Aprire, impaginare, disegnare e creare file .postext dal codice, i pacchetti bilingui, i file delle versioni precedenti e l’API di basso livello

- Versione HTML: https://postext.dev/it/docs/configuration-bundles
- Ultimo aggiornamento: 2026-10-16
- Tempo di lettura: 29 min
- Altre lingue: [en](https://postext.dev/en/docs/configuration-bundles.md), [es](https://postext.dev/es/docs/configuration-bundles.md), [ca](https://postext.dev/ca/docs/configuration-bundles.md), [de](https://postext.dev/de/docs/configuration-bundles.md), [fr](https://postext.dev/fr/docs/configuration-bundles.md), [pt](https://postext.dev/pt/docs/configuration-bundles.md), [zh](https://postext.dev/zh/docs/configuration-bundles.md), [ja](https://postext.dev/ja/docs/configuration-bundles.md), [ar](https://postext.dev/ar/docs/configuration-bundles.md)

## In breve

Questa pagina è per chi scrive codice. Un file .postext contiene un libro intero in un solo file: il testo, le impostazioni, i font e le immagini. La pagina mostra come aprire un file di questo tipo, impaginarlo e disegnarne le pagine. Mostra come creare un file nuovo dal tuo testo e come tenere due lingue di un libro in un solo file. Spiega anche come vengono letti i file creati da versioni precedenti di Postext.

## Pacchetti (file `.postext`)

Un **file `.postext`** è un libro intero in un solo file: un archivio zip che contiene un manifest `preset.json`, un file markdown per capitolo, i contenuti delle risorse (bitmap, SVG, master di stampa PDF) e i file dei font che la configurazione nomina. La [Sandbox](https://postext.dev/it/docs/sandbox-books-saving.md#esportazione-e-importazione) lo esporta e lo importa e la [skill per agenti](https://postext.dev/it/docs/skill.md) lo consegna. Anche il pacchetto `postext` può crearlo e aprirlo, così un libro può passare tra questi strumenti e il tuo programma senza perdere nulla.

```
my-book.postext
├── preset.json            manifest: name, locale, chapters, config, resources, fonts
├── chapters/01-dusk.md
├── chapters/02-night.md
├── resources/lantern.svg
└── fonts/ebgaramond-400-normal.woff2
```

Il manifest è descritto campo per campo nell’appendice [Formato dei pacchetti di preset](https://postext.dev/it/docs/sandbox-embedding.md#formato-dei-pacchetti-preset) della Sandbox. Un file può contenere anche `layouts.json`, il numero di pagine calcolato dalla Sandbox, così lì il libro si apre già impaginato, oppure uno per ogni edizione di un libro multilingue (`layouts.zh-Hant.json`, letto per primo). `openBundle` li ignora.

L’API è esportata da `postext` stesso e dal sottopercorso `postext/bundle`, che aggiunge le funzioni di basso livello. Importa da `postext` quando devi anche disegnare. In questo modo gli adattatori dei pacchetti e i renderer condividono un’unica istanza del modulo, il che conta su una CDN come esm.sh, dove ogni punto di ingresso è una build separata.

### Aprire un pacchetto

`openBundle` riceve i byte del file (un `Uint8Array`, un `ArrayBuffer`, oppure un `Blob` / `File` da un `<input type="file">`) e restituisce tutto ciò di cui hanno bisogno il motore e i suoi backend:

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

const bundle = await openBundle(await file.arrayBuffer(), { locale: 'es' });

bundle.chapters;   // [{ title, file, markdown }, …] in book order
bundle.config;     // PostextConfig, ready for buildDocument
bundle.resources;  // Resource[]
bundle.files;      // Map<path, Uint8Array>: every file of the bundle
```

| Campo | Che cosa contiene |
| --- | --- |
| `manifest` | Il `preset.json` convalidato. |
| `id`, `name`, `description` | Presi dal manifest. `name` e `description` sono quelli dell’edizione letta: `localized[…].name` e `description` della lingua servita quando li indica, altrimenti quelli del manifest. |
| `locale`, `locales` | La lingua in cui è stato letto il contenuto, e ogni lingua che un pacchetto bilingue contiene. `options.locale` ne sceglie una: prima il tag esatto, poi la lingua di base, poi la lingua propria del pacchetto. |
| `chapters` | `{ title, file, markdown }` per capitolo. Un capitolo senza titolo nel manifest prende il testo del suo primo titolo `#`. |
| `config` | La tavolozza di colori predefinita e i tipi di risorsa nella lingua del pacchetto, poi il `config` del manifest, poi le sostituzioni della lingua. La lingua del pacchetto è il `locale` qui sopra, quindi un pacchetto in una sola lingua riceve le proprie etichette qualunque cosa chieda `options.locale`. Un manifest che non indica nessuna lingua prende quella impostata dal suo `config` (`locale`, poi la lingua di sillabazione), altrimenti `options.locale`. `customFonts` elenca le famiglie di font del pacchetto. È la stessa configurazione con cui la Sandbox apre il pacchetto. |
| `resources` | Le risorse, con le didascalie della lingua scelta. Una dimensione assente dal manifest viene letta dal file. |
| `fonts` | Una voce per font: `{ family, weight, style, format, file, bytes }`. |
| `files` | Ogni file dell’archivio, indicizzato per percorso. |
| `thumbnail`, `canvasScope` | Il percorso dell’immagine di copertina, e come il pacchetto chiede di essere visualizzato: il `view` del manifest, con sopra il `localized[…].view` della lingua servita. |
| `start` | Il punto in cui inizia il libro, per un pacchetto che contiene una parte di un libro più lungo: lo `start` del manifest, oppure il `localized[…].start` della lingua servita. Assente per un libro che inizia dal suo principio. |
| `warnings` | Problemi non bloccanti: un file di font non supportato, un master di stampa mancante. |

Il **`fileId` di un contenuto è il suo percorso dentro il pacchetto**. `resource.svg.fileId`, `resource.bitmap.fileId` e il `fileId` di ogni variante di `customFonts` si possono cercare direttamente in `bundle.files`. `openBundle` genera un errore quando i byte non sono uno zip, quando non c’è un `preset.json` valido (alla radice o sotto un’unica cartella di primo livello), o quando manca un file che il manifest nomina.

#### Pacchetti scritti da postext 1.4 o precedenti

Ogni manifest scritto da `createBundle` e dalla Sandbox porta `configVersion: 14`: le regole di configurazione per cui è stato scritto il suo `config`. Un manifest senza questo campo è stato scritto da postext 1.4 o da una versione precedente, che impostava diciotto cose in modo diverso:

- **Interruzioni prima dei titoli** (regole 3): fino alla 1.4, un oggetto `headings` senza interruzione per l’H1 non ne aveva nessuna (vedi [Sostituzioni per livello](https://postext.dev/it/docs/configuration-headings.md#sovrascritture-per-livello)).
- **La dimensione delle formule** (regole 4): fino alla 1.4, le formule risultavano 1,131 volte più grandi di quanto indica `fontSizeScale` (vedi [Dimensione delle formule](https://postext.dev/it/docs/configuration-lists-math.md#matematica)).
- **Lo spazio sotto una risorsa in linea** (regole 5): fino alla 1.4, il testo dopo una figura o una tabella `placement.position: 'here'` riprendeva alla linea successiva della griglia, senza la distanza degli elementi flottanti sotto di essa (vedi `layout.inlineResourceGap` in [Impaginazione](https://postext.dev/it/docs/configuration-page-layout.md#impaginazione)).
- **Lo spazio intorno a una risorsa in linea dentro un riquadro** (regole 6): fino alla 1.4, una risorsa del genere stava a contatto con il testo del riquadro che la conteneva (vedi `layout.inlineResourceGapInBoxes` in [Impaginazione](https://postext.dev/it/docs/configuration-page-layout.md#impaginazione)).
- **Stili in linea nei titoli** (regole 6): fino alla 1.4, un titolo stampava le parole dei suoi segni `*italic*`, `**bold**` e altri nel proprio stile semplice (vedi `headings.inlineMarks` in [Titoli](https://postext.dev/it/docs/configuration-headings.md#titoli)).
- **La dimensione di un capolettera** (regole 6): fino alla 1.4, il `dropCap` di un testo di design senza `fontSize` era alto quanto tutti i riquadri di riga che occupa, con la sommità sopra la prima riga (vedi `dropCap` in [Elementi di testo](https://postext.dev/it/docs/configuration-headers-footers.md#elementi-di-testo)).
- **Lo spazio sotto una riga che termina con i due punti** (regole 6): fino alla 1.4, `keepColonWithList` considerava sufficiente per l’elenco una riga di spazio sotto la riga che termina con i due punti, e una prima voce di due righe che le regole di orfane e vedove tengono unita passava alla colonna successiva senza di essa (vedi `bodyText.colonListRoom`).
- **Le righe che lascia il taglio di un riquadro** (regole 6): fino alla 1.4, un riquadro diviso dentro un paragrafo o una voce di elenco poteva lasciarne una sola riga su un lato, purché ciascun lato del riquadro contenesse in tutto le sue `splitMinLines` righe (vedi `layout.boxChildSplitMinLines` in [Impaginazione](https://postext.dev/it/docs/configuration-page-layout.md#impaginazione)).
- **A capo dopo una lineetta** (regole 7): fino alla 1.4, Knuth–Plass non terminava mai una riga dopo una lineetta lunga o media composta senza spazi tra due parole (`say—that’s`), e il divisore riga per riga del testo formattato lo faceva solo tra due lettere (vedi `bodyText.breakAfterDashes` in [Testo corrente](https://postext.dev/it/docs/configuration-text.md#testo-corrente)).
- **Testo in bandiera** (regole 7): fino alla 1.4, il testo corrente in bandiera veniva composto riga per riga, riempiendo ogni riga prima della successiva, qualunque cosa dicesse `optimalLineBreaking` (vedi `bodyText.optimalRagged` in [Testo corrente](https://postext.dev/it/docs/configuration-text.md#testo-corrente)).
- **La divisione sotto un titolo** (regole 8): fino alla 1.4, il paragrafo sotto un titolo al piede di una colonna teneva lì tutte le righe che ci stavano, per quanto poche ne passassero alla colonna successiva (vedi `headings.keepWithNextSplit` in [Titoli](https://postext.dev/it/docs/configuration-headings.md#titoli)).
- **Lo spazio sotto un contenitore `:::paragraphs`** (regole 8): fino alla 1.4, lo spazio dello stile veniva messo sotto l’ultimo paragrafo prima dell’aggancio alla griglia, lo spazio superiore del blocco successivo (il `marginTop` di un titolo) veniva aggiunto sotto di esso, e la spaziatura tra paragrafi del testo veniva tralasciata (vedi `bodyText.paragraphContainerSpacing` in [Testo corrente](https://postext.dev/it/docs/configuration-text.md#testo-corrente)).
- **A capo dopo il trattino di una parola composta** (regole 8): fino alla 1.4, Knuth–Plass non terminava mai una riga giustificata dopo un trattino tra due lettere (`well-known`) in un paragrafo senza formattazione in linea, mentre lo faceva in uno con formattazione (vedi `bodyText.breakAfterHyphens` in [Testo corrente](https://postext.dev/it/docs/configuration-text.md#testo-corrente)).
- **Poesie senza separatore** (regole 9): fino alla 1.22, una poesia `:::verse` le cui righe non contenevano `||` veniva composta come emistichi singoli, ogni riga centrata (vedi `bodyText.verse.layout` in [Versi](https://postext.dev/it/docs/configuration-text.md#versi)).
- **Un rientro di prima riga accanto a un rientro sporgente** (regole 9): fino alla 1.22, l’`hangingIndent` di uno stile di paragrafo sostituiva il suo `firstLineIndent`, e la prima riga iniziava a `indent` (vedi [Stili di paragrafo](https://postext.dev/it/docs/configuration-styles.md#stili-di-paragrafo)).
- **Una barra rovesciata a fine riga** (regole 9): fino alla 1.22, una barra rovesciata che terminava una riga di un paragrafo, di una citazione o di una voce di elenco, e `\\` prima di uno spazio, venivano stampati, e le righe si univano con uno spazio (vedi `bodyText.hardLineBreaks` in [Testo corrente](https://postext.dev/it/docs/configuration-text.md#testo-corrente)).
- **Recinzioni di codice** (regole 9): fino alla 1.22, una recinzione ```` ``` ```` o `~~~` e le righe al suo interno venivano lette come Markdown: righe unite in paragrafi, una riga `#` diventava un titolo, le recinzioni venivano stampate (vedi `codeStyle.blocks` in [Listati di codice](https://postext.dev/it/docs/configuration-styles.md#listati-di-codice)).
- **Righe di versi che vanno a capo** (regole 10): nella 1.23, una riga di una poesia composta riga per riga più larga della giustezza andava a capo con la sua spaziatura naturale tra le parole, per quanto poco sbordasse (vedi `bodyText.verse.tighten` in [Versi](https://postext.dev/it/docs/configuration-text.md#versi)).

`openBundle` e `readBundle` leggono il `config` di un manifest del genere, e la configurazione `localized` di ogni lingua, tramite `migrateConfig`, che scrive esplicitamente le interruzioni impaginate dalla 1.4 e moltiplica la scala delle formule per 1,131 (dividendo per lo stesso valore i suoi margini delle formule in evidenza in `em`). Un manifest marcato da `3` a `7`, scritto da una prerelease della 1.5, riceve solo i vincoli delle regole successive al suo marchio. Con `3` sono la dimensione delle formule, la distanza in linea, i cinque vincoli delle regole 6, i due delle regole 7 e i tre delle regole 8; con `4`, la distanza in linea e i vincoli delle regole 6, 7 e 8; con `5`, i vincoli delle regole 6, 7 e 8; con `6`, i vincoli delle regole 7 e 8; con `7`, solo i vincoli delle regole 8. La divisione sotto un titolo (`pinLegacyHeadingSplit`) viene scritta come `headings.keepWithNextSplit: 'fill'` sugli `headings` che i livelli lasciano in vigore, quando un capitolo letto ha un titolo e la configurazione non indica un valore proprio, mantiene attivo `headings.keepWithNext` e non disattiva `bodyText.avoidOrphans`. Gli a capo nelle parole composte (`pinLegacyHyphenBreaks`) vengono scritti come `bodyText.breakAfterHyphens: false` sul `bodyText` in vigore, quando un capitolo letto compone un trattino tra due lettere e la configurazione né lo imposta già né disattiva `optimalLineBreaking`. Lo spazio sotto i contenitori (`pinLegacyParagraphContainerSpacing`) viene scritto come `bodyText.paragraphContainerSpacing: 'add'` sul `bodyText` in vigore, quando la configurazione dichiara uno stile di paragrafo (in `paragraphStyles` o nelle sostituzioni del visualizzatore HTML), un capitolo letto apre un contenitore `:::paragraphs` su una riga a sé e la configurazione non lo imposta già. Gli a capo dopo le lineette (`pinLegacyDashBreaks`) vengono scritti come `bodyText.breakAfterDashes: false` sul `bodyText` in vigore, quando un capitolo letto compone una lineetta lunga o media senza spazi tra due parole (prima una lettera, una cifra o una punteggiatura di chiusura, dopo una lettera, una cifra o una parentesi o virgoletta di apertura; una virgoletta prima della lineetta conta quando prima della virgoletta c’è una lettera, una cifra, una punteggiatura di chiusura o uno spazio indivisibile, come in `"no"—and`, non in `said "—Hola`; un segno in linea che tocca la lineetta o la virgoletta, come il `**` di `**riddles.**—I`, conta su entrambi i lati) e la configurazione non lo imposta già. La divisione in righe in bandiera (`pinLegacyRaggedBreaking`) viene scritta come `bodyText.optimalRagged: false` sul `bodyText` in vigore, quando la configurazione compone in bandiera una parte del testo corrente (un `textAlign` diverso da `'justify'` sul testo corrente, su uno stile di paragrafo, sul corpo di un riquadro, sul corpo di una parte o sul corpo di uno stile di sezione (`headingStyles[].bodyStyle`), oppure nelle sostituzioni del visualizzatore HTML), non lo imposta già e non disattiva `optimalLineBreaking`. La distanza nei riquadri (`pinLegacyBoxResourceGap`) viene scritta come `layout.inlineResourceGapInBoxes: false` sul `layout` in vigore, quando una risorsa è inserita su una riga a sé dentro un `:::callout` dei capitoli letti e la configurazione non lo imposta già. Il taglio dei riquadri (`pinLegacyBoxChildCut`) viene scritto come `layout.boxChildSplitMinLines: 1` sul `layout` in vigore, quando un capitolo letto apre un `:::callout` su una riga a sé e la configurazione non lo imposta già. I segni nei titoli (`pinLegacyHeadingMarks`) vengono scritti come `headings.inlineMarks: false` sugli `headings` che i livelli lasciano in vigore, quando un titolo dei capitoli letti contiene un segno (`*`, `_`, `^`, `~`, `:smallcaps[` o un link nel suo testo) e la configurazione non indica un valore proprio. I capilettera (`pinLegacyDropCapSize`) ricevono la loro dimensione della 1.4 scritta esplicitamente come `dropCap.fontSize`, ovunque si trovino: nell’unità dell’interlinea dell’elemento quando questa è una lunghezza, altrimenti nell’unità del suo corpo. Lo spazio sotto la riga con i due punti (`pinLegacyColonListRoom`) viene scritto come `bodyText.colonListRoom: 'line'` sul `bodyText` in vigore, quando un elenco dei capitoli letti segue una riga che termina con i due punti (righe vuote in mezzo ammesse) e la configurazione né indica uno spazio né disattiva `keepColonWithList`. La distanza in linea (`pinLegacyInlineGap`) viene scritta come `layout.inlineResourceGap: 'above'` sul `layout` che i livelli lasciano in vigore, quando una riga dei capitoli letti inserisce una risorsa (`::resource{id="…"}` da solo sulla sua riga, come lo legge il parser: una menzione nel testo corrente o in un segmento di codice non conta) e la configurazione non indica una distanza propria. La dimensione viene vincolata sul `math` che i livelli lasciano in vigore (il `math` proprio di una lingua sostituisce quello condiviso), e solo quando i capitoli letti contengono un `$`: un pacchetto senza formule conserva il suo `config` come è scritto. Quando né il manifest né la lingua indicano un `math`, quello in vigore è il `baseConfig` di `readBundle` (quello del lettore), e anch’esso viene vincolato, perché la 1.4 componeva le formule del pacchetto a quella dimensione: un `fontSizeScale: 1.5` di base si legge come 1,5 × 1,1312. Le interruzioni prima dei titoli della base vengono prese così come sono. Un pacchetto vecchio conserva quindi ciò che queste regole impaginavano, e `bundle.config` mostra le interruzioni, la dimensione delle formule, le distanze, i segni nei titoli, le dimensioni dei capilettera, lo spazio sotto la riga con i due punti, il taglio dei riquadri, gli a capo dopo le lineette, gli a capo nelle parole composte, la divisione in bandiera, la divisione sotto un titolo e lo spazio sotto i contenitori con cui viene impaginato. Le correzioni di impaginazione della 1.5 non hanno vincoli e si applicano a esso come a qualsiasi libro, quindi una pagina che toccano può ancora spostarsi (vedi [Dimensione delle formule](https://postext.dev/it/docs/configuration-lists-math.md#matematica) per l’elenco). Un `preset.json` scritto a mano per le regole attuali imposta `"configVersion": 14`; marcare con questo valore il manifest di un pacchetto vecchio è anche il modo, in una riga, per leggerlo con le regole attuali (un pacchetto senza versione perde allora anche il vincolo delle interruzioni prima dei titoli). Un manifest marcato `8`, scritto da postext dalla 1.5 alla 1.22, riceve solo i quattro vincoli delle regole 9, come del resto ogni manifest più vecchio: la disposizione dei versi (`pinLegacyVerseLayout`) viene scritta come `bodyText.verse.layout: 'bayt'` sul `bodyText` in vigore, quando un capitolo letto compone una poesia `:::verse` la cui recinzione non indica una disposizione e le cui righe non contengono un separatore di emistichi, e la configurazione non lo imposta già; i rientri abbinati (`pinLegacyPairedIndents`) eliminano il `firstLineIndent` di ogni stile di paragrafo (in `paragraphStyles` o nelle sostituzioni del visualizzatore HTML) che imposta anche un `hangingIndent` diverso da zero; gli a capo forzati (`pinLegacyHardBreaks`) vengono scritti come `bodyText.hardLineBreaks: false` sul `bodyText` in vigore, quando un capitolo letto termina con una barra rovesciata una riga di un paragrafo, di una citazione o di una voce di elenco e il blocco prosegue sotto, oppure compone `\\` prima di uno spazio e di altro testo (esclusi codice e formule in linea, formule in evidenza, titoli e poesie `:::verse`), e la configurazione non lo imposta già; e le recinzioni di codice (`pinLegacyCodeBlocks`) vengono scritte come `codeStyle.blocks: false`, quando un capitolo letto apre una recinzione ```` ``` ```` o `~~~` (tre o più caratteri, con al massimo tre spazi di rientro) e la configurazione non lo imposta già. Un manifest marcato `9`, scritto da postext 1.23, riceve solo il vincolo delle regole 10, come del resto ogni manifest più vecchio: le righe di versi che vanno a capo (`pinLegacyVerseTightening`) vengono scritte come `bodyText.verse.tighten: false` sul `bodyText` in vigore, quando un capitolo letto compone una poesia riga per riga (una recinzione `:::verse` che indica `layout=lines`, oppure che non indica una disposizione su righe senza separatore di emistichi mentre la configurazione non compone queste poesie come bayt) e la configurazione non lo imposta già. Un manifest marcato `10`, scritto da postext 1.24, riceve solo il vincolo delle regole 11, come del resto ogni manifest più vecchio: il bilanciamento di una griglia di caratteri (`pinLegacyGridBalancing`) viene scritto come `headings.balancing.enabled: true` sugli `headings` in vigore, quando la configurazione unita imposta `cjk.grid.enabled` nel testo orizzontale e non imposta essa stessa `enabled`, perché la 1.24 bilanciava una pagina del genere per impostazione predefinita. Le regole 11 impediscono anche gli a capo di un solo carattere nei titoli di opere, compongono i numeri cerchiati come caratteri cinesi e compongono il testo di design CJK con le regole del testo corrente (#637): un manifest marcato `10` o più vecchio riceve `cjk.titleMinChars: 1` (`pinLegacyTitleBreaks`) quando un capitolo letto compone un titolo di opera (《 〈 o `:book[`), `cjk.circledNumbers: 'western'` (`pinLegacyCircledNumbers`) quando uno compone un numero cerchiato (U+2460–U+24FF, U+2776–U+2793), e `cjk.composeDesignText: false` (`pinLegacyDesignText`) quando un capitolo letto o la configurazione stessa contiene testo CJK, ciascuno sul `cjk` in vigore e solo quando la configurazione non lo imposta già. Tagliano anche le tabelle in linea e compongono `:::columns` nel testo corrente (#634): un manifest marcato `10` o più vecchio riceve `tableStyle.splitInline: false` (`pinLegacyInlineTableSplit`) sul `tableStyle` in vigore quando un capitolo letto inserisce una risorsa, e `layout.flowColumns: false` (`pinLegacyFlowColumns`) sul `layout` in vigore quando un capitolo letto apre una recinzione `:::columns` su una riga a sé, ciascuno solo quando la configurazione non lo imposta già. Offrono inoltre a un elemento flottante la cima della colonna di un’apertura a tutta larghezza (#639): un manifest marcato `10` o più vecchio riceve `layout.floatsUnderOpener: false` (`pinLegacyOpenerHeadFloats`) sul `layout` in vigore quando la configurazione unita imposta un livello o uno stile di titolo con `span: 'page'` e un capitolo letto ha un titolo, solo quando la configurazione non lo imposta già. Le regole 12 permettono a una pagina su una griglia di caratteri di trattenere le sue righe (#717): un manifest marcato `11` o più vecchio la cui configurazione unita attiva `cjk.grid.enabled` riceve `bodyText.avoidWidows: true`, e nella regione `japan` `bodyText.avoidOrphans: true` (`pinLegacyGridBreakRules`), sul `bodyText` in vigore, ciascuno solo quando la configurazione non lo imposta già. Mantengono anche due caratteri di una nota su due righe da ciascun lato di un a capo (#720): un manifest del genere riceve `cjk.warichu.minChars: 1` (`pinLegacyNoteBreaks`) sul `cjk` in vigore quando un capitolo letto compone una nota `:warichu[` o la configurazione unita imposta `citations.notes: 'warichu'`, solo quando la configurazione non lo imposta già. E nella regione Giappone una parola occidentale prende lo spazio Han–latino accanto alle sue parentesi e ai suoi segni (#721): un manifest marcato `11` o più vecchio la cui configurazione unita si risolve nella regione Giappone riceve `cjk.latinSpacingEdge: 'letters'` (`pinLegacyLatinSpacingEdge`) sul `cjk` in vigore quando un capitolo letto contiene kana o kanji, solo quando la configurazione non lo imposta già. E una risorsa composta nel testo prende `placement.captionSide` come fa un elemento flottante di colonna (#729): un manifest marcato `11` o più vecchio la cui configurazione unita imposta `layout.layoutType: 'oneAndHalf'` riceve `layout.inlineCaptionSide: false` (`pinLegacyInlineCaptionSide`) quando un capitolo letto inserisce una risorsa, solo quando la configurazione non lo imposta già.

Le regole 12 cambiano anche il modo in cui si bilancia una colonna corta, e un manifest marcato `11` o più vecchio mantiene ciò con cui è stato impaginato. L’ordine delle leve (#707): riceve `headings.balancing.maxLinesBeforeLoose` impostato al suo `maxLinesPerHeading`, o a `4` quando non ne imposta nessuno (`pinLegacyLooseOrder`), sugli `headings` in vigore, quando un capitolo letto ha un titolo e la configurazione unita bilancia le sue colonne, consente a un titolo di occupare più di due righe e non imposta già la chiave. La riga sotto una figura su più colonne (#708): riceve `headings.balancing.stretchAfterWideFloats: 'always'` (`pinLegacyWideFloatStretch`) sugli `headings` in vigore, quando un capitolo letto inserisce o cita una risorsa o fa flottare un riquadro, e la configurazione unita bilancia le sue colonne, non disattiva `stretchAfterFloats` e non imposta già la chiave. Le interruzioni dentro i gruppi di un indice analitico (#712): riceve `index.groups.splitMinEntries: 1` (`pinLegacyIndexGroupSplit`) sull’`index` in vigore, quando un capitolo letto compone una recinzione `:::index` su una riga a sé e la configurazione non imposta già la chiave. L’unità del taglio a livello (#711): riceve `headings.balancing.trailingStep: 'grid'` (`pinLegacyTrailingStep`) sugli `headings` in vigore, quando un capitolo letto può comporre testo fuori dall’interlinea del testo corrente (una recinzione `:::`, una recinzione di codice, una citazione, un elenco, una formula in evidenza o una citazione bibliografica), e la configurazione unita bilancia le sue colonne, mantiene attivo `trailing` o `beforeSpan` e non imposta già la chiave.

Le regole 12 vanno a capo agli spazi nell’etichetta di un rimando quando l’etichetta è fatta di parole (#702). Un manifest marcato `11` o più vecchio riceve `crossRefs.breakLabels: false` (`pinLegacyRefLabelBreaks`) sul `crossRefs` in vigore, quando un capitolo letto contiene un rimando (`:ref{…}`, `\ref{…}`, `\eqref{…}` o un `@id` di pandoc-crossref) e la configurazione non indica l’impostazione: ogni etichetta resta l’unica parola con cui era stata impaginata.

Le regole 12 compongono una parola senza lettere (`46.`, `1367)`) nella scrittura del testo che la circonda (#725). Un manifest marcato `11` o più vecchio riceve `bodyText.scriptContext: false` (`pinLegacyScriptContext`) sul `bodyText` in vigore, qualunque cosa contengano i suoi capitoli, quando la configurazione non indica l’impostazione: una parola del genere viene composta da sola, come era stata impaginata. La regola agisce in un font che disegna la punteggiatura in base alla scrittura, e quali font carica un libro non si sa quando se ne legge il manifest.

Le regole 13 trasformano in un’impostazione la lingua in cui viene composto il testo (#764), e le regole 14 rendono predefinita la lingua propria del documento (#770). Un manifest marcato `13` o più vecchio riceve `bodyText.shapingLanguage: 'host'` (`pinLegacyShapingLanguage`) sul `bodyText` in vigore, qualunque cosa contengano i suoi capitoli, quando la configurazione non indica l’impostazione; uno che indica un valore lo mantiene. Il suo testo viene misurato nella lingua della pagina che ospita l’impaginazione, e in nessuna lingua in un worker, come era stato impaginato. Un manifest marcato `13` è stato scritto da postext 1.29: la 1.29.1 e la 1.29.2 impaginavano un libro che non indica un valore nella lingua dell’applicazione ospite, come fa il vincolo; la 1.29.0, il cui valore predefinito era `'document'`, lo impaginava in quella del documento con un canvas e un PDF che non seguivano la misurazione, e anch’esso si legge con `'host'`. Un manifest marcato `14` che non indica un valore viene impaginato nella lingua del documento.

Le regole 13 disegnano anche la casella di una voce di attività (`- [ ]`, `- [x]`): un quadrato disegnato dal motore, con un segno di spunta quando la voce è completata, che occupa uno spazio proprio prima del testo della voce (#765). Un manifest marcato `12` o più vecchio riceve `unorderedLists.taskBox: 'character'` (`pinLegacyTaskBox`) sull’`unorderedLists` in vigore, quando un capitolo contiene una voce di attività e la configurazione non indica l’impostazione. La sua casella resta il carattere `☐` o `☑` composto nel carattere dell’elenco, e il testo della voce inizia dopo la larghezza di quel carattere, come era stato impaginato. Quella larghezza dipendeva dal font che conteneva il carattere, nella maggior parte dei libri un font di sistema, e non si conosce quando si legge il manifest.

```ts
import { CONFIG_VERSION, migrateConfig } from 'postext/bundle';

migrateConfig({ headings: { fontFamily: 'Georgia' } }, undefined, { content: 'A book with no maths.' });
// => { headings: { fontFamily: 'Georgia', levels: [{ level: 1, breakBefore: { enabled: false } }] } }
migrateConfig({ math: { fontSizeScale: 1.2 } }, 3);
// => { math: { fontSizeScale: 1.35746…, marginTop: { value: 0.7072, unit: 'em' }, marginBottom: { value: 0.7072, unit: 'em' } },
//      layout: { inlineResourceGap: 'above', inlineResourceGapInBoxes: false, boxChildSplitMinLines: 1 },
//      headings: { inlineMarks: false, keepWithNextSplit: 'fill' }, bodyText: { colonListRoom: 'line', breakAfterDashes: false, breakAfterHyphens: false, verse: { layout: 'bayt', tighten: false }, hardLineBreaks: false }, codeStyle: { blocks: false } }
migrateConfig({ layout: { layoutType: 'single' } }, 4, { content: 'Text.\n\n::resource{id="fig"}' });
// => { layout: { layoutType: 'single', inlineResourceGap: 'above' } }
migrateConfig({ layout: { layoutType: 'single' } }, 5, { content: ':::callout\nText.\n\n::resource{id="fig"}\n:::' });
// => { layout: { layoutType: 'single', inlineResourceGapInBoxes: false, boxChildSplitMinLines: 1 } }
migrateConfig({ bodyText: { textAlign: 'left' } }, 6, { content: 'I say—that is all.' });
// => { bodyText: { textAlign: 'left', breakAfterDashes: false, optimalRagged: false } }
migrateConfig({ paragraphStyles: [{ id: 'verse' }] }, 7, { content: ':::paragraphs{style="verse"}\nA line.\n:::' });
// => { paragraphStyles: [{ id: 'verse' }], bodyText: { paragraphContainerSpacing: 'add' } }
migrateConfig({ bodyText: { fontFamily: 'Georgia' } }, 7, { content: 'A well-known tale.' });
// => { bodyText: { fontFamily: 'Georgia', breakAfterHyphens: false } }
migrateConfig(config, CONFIG_VERSION); // today's rules: `config` itself
```

`content` è il markdown che la configurazione impagina (una stringa o un elenco di capitoli). Senza di esso la dimensione delle formule viene vincolata ogni volta che la matematica è attiva, lo spazio sotto i contenitori ogni volta che la configurazione dichiara uno stile di paragrafo, e le due distanze, i segni nei titoli, lo spazio sotto la riga con i due punti, il taglio dei riquadri, gli a capo dopo le lineette, la divisione sotto un titolo e gli a capo nelle parole composte sempre, perché il motore non può sapere se il libro contiene una formula, un contenitore `:::paragraphs`, una figura in linea, un titolo con segni, un elenco introdotto dai due punti, un riquadro, una lineetta senza spazi, un titolo o una parola composta. La divisione in bandiera viene vincolata dalla sola configurazione, con o senza contenuto. Migra una configurazione salvata una sola volta e salvala di nuovo con `CONFIG_VERSION`: il vincolo delle formule moltiplica la scala, quindi una configurazione migrata due volte crescerebbe due volte. Anche la disposizione dei versi e le recinzioni di codice vengono vincolate senza contenuto, perché il libro potrebbe contenere una poesia `:::verse` senza separatore o una recinzione, e i rientri abbinati vengono vincolati dalla sola configurazione. Lo stesso vale per le righe di versi che vanno a capo della 1.23, perché il libro potrebbe comporre una poesia riga per riga.

### Impaginare e disegnare un pacchetto

Quattro funzioni collegano un pacchetto aperto al motore e ai backend:

- **`loadBundleFonts(bundle)`** registra i font del pacchetto in `document.fonts`, e i loro byte nel registro dei font del motore per le immagini SVG (`registerFontBytes`). Attendine il completamento prima di impaginare, perché l’impaginazione misura il testo con i font di cui dispone il browser. Le famiglie che il pacchetto nomina ma non contiene (Google Fonts) devi comunque caricarle tu, come per qualsiasi altro documento.
- **`registerBundleImages(bundle)`** decodifica le immagini per il backend canvas (`renderPage`, `renderToCanvas`), poster dei video compresi. **`bundleImageUrl(bundle)`** è la funzione di risoluzione `resourceImageUrl` per `renderToHtml`, e **`bundleVideoUrl(bundle)`** la sua funzione di risoluzione `resourceVideoUrl` per i file video contenuti in un pacchetto. Entrambe ricolorano le figure SVG quando `diagramStyle.singleInk` è attivo, una volta sola: ricolorano il markup e contrassegnano le immagini perché nessun backend le tinga di nuovo (vedi [Un solo inchiostro su canvas e in HTML](https://postext.dev/it/docs/configuration-resources.md#inchiostro-unico-sul-canvas-e-in-html)). Entrambe incorporano inoltre in ogni SVG i font che il suo testo nomina, prima dai font propri del pacchetto, poi dai font registrati nel motore (vedi [Font nel testo SVG](https://postext.dev/it/docs/configuration-resources.md#font-nel-testo-svg)); `registerBundleImages(bundle, { onWarning })` e `bundleImageUrl(bundle, { onWarning })` segnalano una famiglia senza font.
- **`buildBundle(bundle)`** impagina i capitoli in ordine e restituisce un `VDTDocument` per capitolo. Ogni capitolo prosegue quello precedente: contatori dei titoli e delle risorse, la parte aperta, la parità delle pagine e la numerazione delle pagine. Un capitolo che stampa l’indice (`:::toc`) o l’indice analitico (`:::index`) riceve la struttura dell’intero libro. Accetta le stesse opzioni di `buildDocument`, più `config` per sostituire la configurazione del pacchetto, `cache` per condividere una cache delle misure e `metadata` (vedi sotto).
- **`bundleResourceBytes(bundle)`** e **`bundleFontProvider(bundle, { decodeWoff2, fallback })`** sono le opzioni `resourceBytes` e `fontProvider` del `renderToPdf` di `postext-pdf`. Il fornitore di font sceglie dal pacchetto il peso più vicino dello stile richiesto. Per un font `.woff2` ha bisogno di `decompressWoff2`, e per una famiglia che il pacchetto non contiene chiama `fallback` con gli argomenti del renderer, `request` compreso, e passa avanti qualunque cosa restituisca. Un fallback che scarica solo il file `latin` di una famiglia stampa come riquadri vuoti una famiglia cinese che il pacchetto non incorpora; uno che risponde con porzioni, come `sliceFontProvider` in [Font cinesi, giapponesi e coreani](https://postext.dev/it/docs/configuration-pdf-output.md#font-cinesi-giapponesi-e-coreani), la stampa per intero.

```ts
import { openBundle, loadBundleFonts, registerBundleImages, buildBundle, renderPage,
  bundleResourceBytes, bundleFontProvider } from 'postext';
import { renderToPdf, decompressWoff2 } from 'postext-pdf';

const bundle = await openBundle(bytes);
await loadBundleFonts(bundle);
await registerBundleImages(bundle);

const docs = buildBundle(bundle);                        // one VDTDocument per chapter
const firstPage = renderPage(docs[0].pages[0], docs[0]); // a <canvas>

const pdf = await renderToPdf(docs, {                    // the whole book
  fontProvider: bundleFontProvider(bundle, { decodeWoff2: decompressWoff2, fallback: fontsource }),
  resourceBytes: bundleResourceBytes(bundle),
});
```

Per impaginare da te un singolo capitolo, passa `bundle.chapters[i].markdown`, `bundle.resources` e `bundle.config` a `buildDocument`, come faresti per qualsiasi documento.

**I metadati del libro.** Come nella Sandbox, il front matter del primo capitolo è quello del libro: `buildBundle` passa il suo `title`, il suo `author` e il resto a ogni capitolo, così le testatine `{title}` e `{author}` valgono su ogni pagina e il `doc.metadata` di ogni capitolo li contiene. Un blocco di front matter in cima a un capitolo successivo viene ignorato: viene individuato dalle sue righe `---` e svuotato senza essere analizzato, quindi un YAML che il parser rifiuterebbe non fa danni. `options.metadata` fornisce i valori che il front matter non imposta (prevale il front matter). Anche il numero di pagine del libro arriva a ogni capitolo: `{bookTotalPages}` lo stampa, mentre `{totalPages}` conta le pagine del capitolo (vedi [Numero di pagine del libro](https://postext.dev/it/docs/configuration-headers-footers.md#numero-di-pagine-del-libro)).

```ts
const docs = buildBundle(bundle, { metadata: { author: 'A. Author' } });
docs[3].metadata.title;   // the first chapter's `title:`
```

**Dove inizia il libro.** Un pacchetto può contenere una parte di una pubblicazione più lunga: le pagine da 58 a 61 di un fascicolo, il capitolo 4 di un manuale. Il suo `start` dice che cosa viene prima, nei termini della `continuation` di `buildDocument`: le pagine prima della prima (`pageIndexOffset`, che decide su quale lato cade la pagina 1, e con esso i margini speculari e le testatine pari e dispari), la numerazione delle pagine in vigore (`pageNumbering`), i contatori dei titoli (`headings`), la parte aperta (`part`) e i contatori di risorse, enunciati, note a piè di pagina e righe. `createBundle` lo scrive come `start` di `preset.json`, `openBundle` lo restituisce come `bundle.start`, e `buildBundle` impagina con esso il primo capitolo, come farebbe `buildDocument({ markdown, continuation: start }, config)`, e da lì concatena i capitoli successivi; anche `{bookTotalPages}` conta le pagine che precedono il libro. Un manifest senza `start` si legge come prima, e un lettore che non conosce il campo lo ignora. `bookPageCount` non ne fa parte: le pagine le conta il lettore. In un pacchetto con più lingue, `localized[…].start` dà a un’edizione un inizio proprio.

```ts
const { bytes } = await createBundle({
  name: 'Field notes, chapter 4',
  markdown,
  config,
  // Page 58, an even page, in chapter 4.
  start: { pageIndexOffset: 57, pageNumbering: { startAt: 58 }, headings: { h1: 3 } },
});
const bundle = await openBundle(bytes);
bundle.start;                 // { pageIndexOffset: 57, pageNumbering: { startAt: 58 }, headings: { h1: 3, h2: 0, … } }
const [doc] = buildBundle(bundle);
doc.pages[0].pageLabel;       // '58'
```

### Esempio dal vivo: aprire un pacchetto

Il pen carica dal repository un libro di esempio di due capitoli (`lantern.postext`, con un carattere proprio, una figura SVG e una tabella). Registra i font e le immagini del pacchetto, impagina il libro con `buildBundle` e disegna ogni pagina. *Make the PDF* disegna gli stessi documenti con `postext-pdf`, incorporando i font del pacchetto. Scegli un tuo file `.postext`, per esempio esportato dalla Sandbox, per vederlo allo stesso modo.

> **Esempio eseguibile: Postext · aprire un pacchetto .postext** — Apri un file .postext con postext, impagina il libro e disegnalo su canvas e in PDF. ([codice](https://github.com/drnachio/postext/tree/main/docs/examples/open-bundle))

### Creare un pacchetto

`createBundle` scrive un file `.postext` a partire da un documento: i suoi capitoli, la configurazione, le risorse e i contenuti a cui fanno riferimento.

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

const { bytes, manifest, warnings } = await createBundle({
  name: 'The Lantern',
  locale: 'en',
  chapters: [
    { markdown: '# Dusk\n\nIt is drawn in :ref{id="lantern"}.' },
    { title: 'Night', markdown: '# Night\n\n…' },
  ],
  config,
  resources: [{
    id: 'lantern', typeId: 'figure', kind: 'svg', caption: 'The lantern.',
    svg: { fileId: 'lantern.svg', width: 240, height: 150 },
    createdAt: 0, updatedAt: 0,
  }],
  files: { 'lantern.svg': svgMarkup, 'garamond-regular': fontBytes },
});
```

| Input | Significato |
| --- | --- |
| `name`, `id`, `description`, `locale` | I metadati del manifest. `id` vale per impostazione predefinita uno slug di `name`. |
| `chapters` o `markdown` | Il libro, un `{ title?, markdown }` per capitolo, oppure un singolo documento. |
| `config` | Il `PostextConfig`. I valori uguali a quelli predefiniti vengono lasciati fuori dal manifest. |
| `resources` | Le risorse. Un’immagine indica il suo contenuto con `bitmap.fileId` / `svg.fileId` (e `svg.pdfFileId` per un master di stampa). |
| `files` | I contenuti per `fileId` (un oggetto o una `Map`): le immagini a cui fanno riferimento le risorse e i file dei font a cui fanno riferimento le varianti di `config.customFonts`. I valori possono essere un `Uint8Array`, un `ArrayBuffer`, un `Blob` o una stringa (markup SVG). |
| `thumbnail` | `{ data, mime }`: un’immagine di copertina (PNG, JPEG, WebP, GIF o SVG). |
| `canvasScope` | `'book'` chiede ai visualizzatori di impaginare l’intero libro come un unico canvas. |
| `start` | Il punto in cui inizia il libro, per un pacchetto che contiene una parte di un libro più lungo: la `continuation` con cui verrebbe costruito un singolo documento. Viene scritto come `start` del manifest. |
| `mtime` | La data di modifica scritta su ogni file dell’archivio (una `Date`, un timestamp o una stringa di data). Se la ometti, è l’ora della chiamata, quindi due chiamate con lo stesso input producono byte diversi. Passa una data fissa e lo stesso input produce gli stessi byte, che puoi sottoporre a hash o confrontare. Uno zip conserva data e ora senza fuso orario, a passi di due secondi, dal 1980 al 2099, e la data viene scritta nell’ora locale della macchina. Per avere byte identici su ogni macchina, costruisci la data a partire da campi locali, come `new Date(1980, 0, 1)`: un timestamp o una stringa che termina con `Z` indica un istante, che cade in un’ora locale diversa in ogni fuso orario (`'1980-01-01T00:00:00Z'` a ovest di UTC è ancora 1979). Una data fuori da quegli anni, in ora locale, genera un errore. |
| `localized` | Altre lingue dello stesso libro, per tag di lingua: `{ es: { name?, description?, chapters?, config?, resources? } }`. Gli input qui sopra diventano allora il contenuto di `locale`, che è obbligatorio. Vedi [Pacchetti bilingui](https://postext.dev/it/docs/configuration-bundles.md#pacchetti-bilingui). |

Restituisce i `bytes` dell’archivio, il `manifest` scritto come `preset.json`, ogni file come `files` (percorso → byte) e un elenco di `warnings`. I file prendono il nome dall’id della loro risorsa (`resources/lantern.svg`) o dal nome del file del font (`fonts/…`), e i capitoli dal loro ordine e dal titolo (`chapters/01-dusk.md`). I font sono dichiarati nei `fonts` del manifest, mai dentro `config.customFonts`. Alcune cose vengono lasciate fuori, ciascuna con un avviso:
- una risorsa o un font il cui contenuto non è in `files`
- un font `.woff` (il backend PDF non può incorporarlo)
- una famiglia marcata `redistributable: false`

Nel browser, passa `bytes` a un link di download: `URL.createObjectURL(new Blob([bytes], { type: 'application/zip' }))`. In Node, scrivili con `fs.writeFile`. `createBundle` e `openBundle` non hanno bisogno di un DOM. Le dist usano percorsi di modulo senza estensione, quindi in Node puro, senza bundler, serve un hook di risoluzione. Il file `docs/examples/open-bundle/build-sample.mjs` del repository ne mostra uno in poche righe.

### Pacchetti bilingui

Un file `.postext` può contenere un libro in più lingue, e `openBundle(bytes, { locale })` lo legge in una qualsiasi di esse. `createBundle` ne scrive uno a partire da `localized`: una voce per ogni lingua aggiuntiva, ciascuna con ciò che differisce dal contenuto principale (l’input `locale`):

```ts
const { bytes, manifest } = await createBundle({
  name: 'The Lantern',
  locale: 'en',
  chapters: [{ markdown: '# Dusk\n\n…' }, { markdown: '# Night\n\n…' }],
  config,
  resources: [lanternFigure, hoursTable],
  files: { 'lantern.svg': svgEn, 'lantern-es.svg': svgEs },
  localized: {
    es: {
      name: 'El farol',
      chapters: [{ markdown: '# Anochecer\n\n…' }, { markdown: '# Noche\n\n…' }],
      config: { headings: { levels: [{ level: 1, numberingTemplate: 'Capítulo {1}' }] } },
      resources: [
        { id: 'lantern', caption: 'El farol.', svg: { fileId: 'lantern-es.svg', width: 240, height: 150 } },
        { id: 'hours', caption: 'Horas de luz.' },
      ],
    },
  },
});

const es = await openBundle(bytes, { locale: 'es' });   // Spanish chapters, config and captions
```

- **`chapters`**: il libro in quella lingua. I file dei capitoli vanno in una cartella per lingua (`chapters/en/01-dusk.md`, `chapters/es/01-anochecer.md`) e i `chapters` del manifest diventano una mappa lingua → capitoli. Una lingua senza `chapters` legge quelli principali; quando nessuna lingua ha capitoli propri, restano un unico elenco.
- **`config`**: la configurazione per quella lingua. Ogni chiave di primo livello sostituisce per intero la chiave condivisa quando il pacchetto viene letto in quella lingua, quindi `headings` qui sopra sostituisce l’intero oggetto `headings`. Le chiavi omesse, o uguali a quelle condivise, sono condivise e non vengono scritte, quindi passare la configurazione completa della lingua funziona come passare le poche chiavi che cambiano. Una chiave impostata ai valori predefiniti mentre quella condivisa non lo è (`layout: {}`) viene scritta così com’è, quindi azzera il valore condiviso. I font sono condivisi: le famiglie dei `customFonts` di una lingua si aggiungono ai `fonts` del pacchetto.
- **`resources`**: i testi delle risorse condivise, abbinati per `id`: `caption`, `note`, `altText` e il `table` di una tabella. Un’immagine che contiene parole può avere una grafica propria: `bitmap.fileId` o `svg.fileId` (e `svg.pdfFileId`) indicano un altro contenuto in `files`, scritto come `resources/es/lantern.svg`. Gli altri campi, come il tipo o la collocazione, sono condivisi. Un id che non è tra le `resources` viene lasciato fuori con un avviso, e un’immagine mancante per una lingua lascia quella lingua sull’immagine condivisa, anche in questo caso con un avviso.
- **`name`**, **`description`**: il titolo e la descrizione del libro in quella lingua, scritti come `localized[…].name` e `description` quando differiscono da quelli condivisi. `openBundle` li restituisce come `bundle.name` e `bundle.description` per quella lingua, il Sandbox elenca l’edizione con quel titolo (all’importazione e nella sua libreria) e `postext info` e `postext check` li stampano. Una voce dell’`index.json` di una sorgente di preset può copiarli in `localized` (`localized.ca.name`), così la libreria mostra ogni edizione con il suo titolo prima di aprirla. Una lingua che non li indica tiene `name` e `description` del manifest.

Il manifest elenca ogni lingua in `locales` (`['en', 'es']`), mantiene quella principale come `locale` e conserva le altre sotto `localized`. `openBundle` senza lingua legge la lingua principale.

**Quale lingua riceve chi legge.** `openBundle(bytes, { locale })` serve la lingua esatta, altrimenti la sua lingua di base (`es-MX` legge `es`), altrimenti quella principale, e `bundle.locale` indica quale ha servito. Capitoli e testi provengono sempre dalla stessa lingua. La lingua principale mantiene i testi condivisi anche quando `localized` contiene una sua variante regionale: un pacchetto `pt-PT` con una voce `pt-BR` legge le didascalie brasiliane solo per `pt-BR`, e quelle condivise per `pt-PT` e `pt`.

### Esempio dal vivo: creare un pacchetto

Il pen costruisce un libro di due capitoli con una figura SVG ed elenca i file che `createBundle` ha scritto insieme al manifest. Offre l’archivio da scaricare, poi lo riapre con `openBundle` e ne disegna la prima pagina: il giro completo in poche righe. Importa il file scaricato nella Sandbox per continuare a lavorarci lì.

> **Esempio eseguibile: Postext · creare un pacchetto .postext** — Scrivi un file .postext con createBundle di postext, scaricalo e aprilo di nuovo. ([codice](https://github.com/drnachio/postext/tree/main/docs/examples/create-bundle))

### Lavorare con i pacchetti

Dato che la Sandbox, la skill per agenti e il pacchetto `postext` leggono e scrivono tutti lo stesso file, un file `.postext` è un modo comodo per passare un libro da uno strumento all’altro:

- **Parti da un pacchetto.** Porta una pubblicazione esistente con la [skill per agenti](https://postext.dev/it/docs/skill.md), oppure progetta un libro nella [Sandbox](https://postext.dev/it/sandbox.md) ed esportalo (**Scarica (.postext)** nel menu ⋯ della sua riga nel pannello Libri). Carica il file dal tuo programma con `openBundle` per disegnarlo su canvas, in HTML o in PDF. Tieni il file come sorgente del libro: modifica i capitoli, la configurazione o le risorse nel codice e riscrivilo con `createBundle`, oppure ricaricalo semplicemente ogni volta che cambia.
- **Correggi e rifinisci nella Sandbox.** Quando qualcosa nell’output del tuo programma va sistemato (una figura che finisce sulla pagina sbagliata, uno stile di titolo, il bilanciamento delle colonne), esporta con `createBundle` ciò che il tuo programma impagina. Importa quel file nella Sandbox (*Libri → Nuovo → Apri un file .postext…*), correggi il testo, il progetto grafico o le figure con l’anteprima dal vivo, il pannello Verifiche e la vista PDF, poi esportalo di nuovo. Il tuo programma carica allora il file corretto con `openBundle`. Oppure riporta le modifiche nel tuo codice: il `config` del manifest contiene solo i valori diversi da quelli predefiniti, quindi si legge come un breve diff.

### API di basso livello

`postext/bundle` esporta anche i mattoni su cui si basano `openBundle` e `createBundle`, per le applicazioni ospiti che conservano o servono i pacchetti a modo loro (una cartella decompressa servita via HTTP, record in un database):

- **`openBundleZip(bytes)` / `zipBundle(files, { mtime })`**: il livello dell’archivio. L’apertura tollera una cartella di primo livello e ignora le voci `__MACOSX` e i file nascosti. I percorsi che escono dal pacchetto vengono rifiutati. `mtime` data i file come fa l’input di `createBundle`.
- **`readBundle(manifest, readFile, options)`** trasforma un manifest più una callback `readFile(path)` in capitoli, configurazione, risorse, immagini e font. `options` imposta la lingua, come vengono chiamati gli id dei file (`ids`), la configurazione di base (`baseConfig`, sotto quella del manifest; per impostazione predefinita la tavolozza e i tipi di risorsa di `bundleBaseConfig` nella lingua del pacchetto, che restituisce `resolveBundleConfigLocale(manifest, locale)`, e un’applicazione ospite che passa il proprio `baseConfig` dovrebbe localizzarlo in quella lingua; sotto un manifest precedente a `configVersion: 4` il suo `math` viene vincolato insieme a quello del pacchetto, sotto uno precedente alla 5 la distanza in linea del suo `layout`, sotto uno precedente alla 6 la distanza nei riquadri del suo `layout`, lo spazio sotto la riga con i due punti del suo `bodyText`, i segni in linea dei suoi `headings` e le dimensioni dei suoi capilettera, sotto uno precedente alla 7 gli a capo dopo le lineette e la divisione in bandiera del suo `bodyText`, e sotto uno precedente alla 8 la divisione sotto un titolo dei suoi `headings` e gli a capo nelle parole composte e lo spazio sotto i contenitori `:::paragraphs` del suo `bodyText`, vedi [Pacchetti scritti da postext 1.4 o precedenti](https://postext.dev/it/docs/configuration-bundles.md#pacchetti-scritti-da-postext-14-o-precedenti)) e come vengono misurate le dimensioni intrinseche. `readResolution` legge dal file la risoluzione di ogni bitmap e la mette in `bitmap.fileResolution`; vale true per impostazione predefinita quando il pacchetto imposta `layout.bitmapResolution: 'file'`.
- **`planBundle(meta, content)` / `resolveBundleFiles(plan, sources)`**: il lato della scrittura, diviso in un piano puro (nomi dei file e manifest) e nella risoluzione dei byte tramite le callback `readBlob` / `readFont`.
- **`isBundleManifest(value)`**, i selettori di lingua (`pickChapterSpecs`, `pickLocaleOverrides`, `pickBundleView`, `resolveBundleLocale`, `resolveBundleConfigLocale`), `svgSize` / `bitmapSize` / `bitmapInfo` (i pixel di una bitmap e la risoluzione che il suo file dichiara) e i tipi del formato (`BundleManifest`, `BundleResourceSpec`, `BundleFontFamilySpec`, …).
- **`CONFIG_VERSION`, `migrateConfig(config, configVersion, { content })`, `pinLegacyHeadingBreaks(config)`, `pinLegacyMathSize(config)`, `pinLegacyInlineGap(config)`, `pinLegacyBoxResourceGap(config)`, `pinLegacyHeadingMarks(config)`, `pinLegacyDropCapSize(config)`, `pinLegacyColonListRoom(config)`, `pinLegacyBoxChildCut(config)`, `pinLegacyDashBreaks(config)`, `pinLegacyRaggedBreaking(config)`, `pinLegacyHeadingSplit(config)`, `pinLegacyParagraphContainerSpacing(config)`, `pinLegacyHyphenBreaks(config)` e `LEGACY_MATH_SIZE`** (0,5 ÷ 0,442): una configurazione salvata nei termini attuali (vedi [Pacchetti scritti da postext 1.4 o precedenti](https://postext.dev/it/docs/configuration-bundles.md#pacchetti-scritti-da-postext-14-o-precedenti)). `readBundle` la applica; anche un’applicazione ospite che conserva le configurazioni a modo suo può farlo, una volta per ogni copia salvata.

La Sandbox è costruita su queste funzioni. Vi aggiunge i propri id di archiviazione e i record di pagine `layouts.json`.
