# プログラムからの利用：.postextバンドル

> コードから.postextファイルを開き、組み、描画し、作成する方法、多言語のバンドル、古いバージョンのファイル、低レベルのAPI

- HTML版: https://postext.dev/ja/docs/configuration-bundles
- 最終更新: 2026-10-16
- 読了時間: 29分
- 他の言語: [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), [it](https://postext.dev/it/docs/configuration-bundles.md), [pt](https://postext.dev/pt/docs/configuration-bundles.md), [zh](https://postext.dev/zh/docs/configuration-bundles.md), [ar](https://postext.dev/ar/docs/configuration-bundles.md)

## かんたんな説明

このページは、プログラムを書く人向けです。.postextファイルには、1冊の本の文章、設定、フォント、画像がまるごと入っています。このページでは、そのファイルを開き、ページを組み、描く方法を示します。自分の文章から新しいファイルを作る方法と、2つの言語の本を1つのファイルにまとめる方法も示します。古いバージョンのPostextで作ったファイルがどう読まれるかも説明します。

## バンドル（`.postext`ファイル）

**`.postext`ファイル**は、1冊の本をまるごと収めた1つのファイルです。zipアーカイブで、`preset.json`マニフェスト、章ごとに1つのMarkdownファイル、リソースのデータ本体（ビットマップ、SVG、PDFの印刷用マスター）、設定が指定するフォントファイルを収めます。[Sandbox](https://postext.dev/ja/docs/sandbox-books-saving.md#書き出しと読み込み)がこれを書き出し・読み込みし、[エージェントスキル](https://postext.dev/ja/docs/skill.md)がこれを出力します。`postext`パッケージでも作成し、開くことができるので、本はこれらのツールと自分のプログラムの間を何も失わずに行き来できます。

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

マニフェストの各フィールドは、Sandboxの付録[プリセットバンドルの形式](https://postext.dev/ja/docs/sandbox-embedding.md#プリセットバンドルの形式)で説明しています。ファイルには`layouts.json`を含めることもできます。これはSandboxのページ数の記録で、本がSandboxで最初からページ分けされた状態で開きます。多言語の本では版ごとに1つずつ持つこともできます（`layouts.zh-Hant.json`。こちらが先に読まれます）。`openBundle`はこれらを無視します。

APIは`postext`本体と、低レベルのヘルパーを加えた`postext/bundle`サブパスから書き出されています。描画もする場合は`postext`からインポートしてください。そうすればバンドルのアダプターとレンダラーが1つのモジュールインスタンスを共有します。これは、エントリーポイントごとに別のビルドになるesm.shのようなCDNで重要です。

### バンドルを開く

`openBundle`はファイルのバイト列（`Uint8Array`、`ArrayBuffer`、または`<input type="file">`から得た`Blob` / `File`）を受け取り、エンジンとそのバックエンドが必要とするものをすべて返します。

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

| フィールド | 内容 |
| --- | --- |
| `manifest` | 検証済みの`preset.json`。 |
| `id`, `name`, `description` | マニフェストから取得した値。`name`と`description`は読み込んだ版のもので、提供する言語の`localized[…].name`と`description`があればそれを、なければマニフェストの値を使います。 |
| `locale`, `locales` | 内容を読んだロケールと、多言語のバンドルが持つすべてのロケール。`options.locale`でどれを読むかを選びます。まず完全に一致するタグ、次に基本言語、最後にバンドル自身のロケールの順に探します。 |
| `chapters` | 章ごとの`{ title, file, markdown }`。マニフェストにタイトルがない章は、最初の`#`見出しのテキストをタイトルにします。 |
| `config` | 既定のカラーパレットとバンドルの言語でのリソースタイプの上に、マニフェストの`config`、さらにロケールの上書きを重ねたもの。バンドルの言語は上の`locale`なので、1言語のバンドルは`options.locale`が何を求めても、その言語のラベルになります。言語を指定していないマニフェストでは、その`config`が設定する言語（`locale`、次にハイフネーションのロケール）を使い、それもなければ`options.locale`を使います。`customFonts`にはバンドルのフォントファミリーが並びます。Sandboxがバンドルを開くときと同じ設定です。 |
| `resources` | 選んだロケールのキャプションを付けたリソース。マニフェストにないサイズはファイルから読み取ります。 |
| `fonts` | フェイスごとに1項目：`{ family, weight, style, format, file, bytes }`。 |
| `files` | アーカイブ内のすべてのファイル。キーはそのパスです。 |
| `thumbnail`, `canvasScope` | 表紙画像のパスと、バンドルが求める表示方法。マニフェストの`view`の上に、読み込んだ言語の`localized[…].view`を重ねたものです。 |
| `start` | バンドルがもっと長い本の一部だけを収めているときの、本の始まりの位置。マニフェストの`start`、または読み込んだ言語の`localized[…].start`です。最初から始まる本にはありません。 |
| `warnings` | 致命的でない問題。対応していないフォントファイル、印刷用マスターの欠落など。 |

**データ本体の`fileId`は、バンドル内でのそのパスです**。`resource.svg.fileId`、`resource.bitmap.fileId`、`customFonts`の各バリアントの`fileId`は、そのまま`bundle.files`で引けます。`openBundle`は、バイト列がzipでない場合、有効な`preset.json`が（ルートにも、トップレベルの1つのフォルダーの下にも）ない場合、マニフェストが指定するファイルが欠けている場合に例外を投げます。

#### postext 1.4以前で書き出したバンドル

`createBundle`とSandboxが書き出すマニフェストには、必ず`configVersion: 14`が入っています。これは、その`config`がどの設定規則を前提に書かれたかを示します。これを持たないマニフェストはpostext 1.4以前で書き出されたもので、次の18点の扱いが現在と異なります。

- **見出しの改ページ**（規則3）：1.4までは、H1の改ページを持たない`headings`オブジェクトでは改ページが入りませんでした（[レベルごとの上書き](https://postext.dev/ja/docs/configuration-headings.md#レベルごとの上書き)を参照）。
- **数式のサイズ**（規則4）：1.4までは、数式が`fontSizeScale`の指定より1.131倍大きく組まれていました（[数式のサイズ](https://postext.dev/ja/docs/configuration-lists-math.md#数式)を参照）。
- **インラインのリソースの下のアキ**（規則5）：1.4までは、`placement.position: 'here'`の図や表の後のテキストが、その下にフロートのアキを取らずに次のグリッド線から再開していました（[レイアウト](https://postext.dev/ja/docs/configuration-page-layout.md#レイアウト)の`layout.inlineResourceGap`を参照）。
- **ボックスの中のインラインのリソースの周りのアキ**（規則6）：1.4までは、そうしたリソースが周りのボックスのテキストに接して置かれていました（[レイアウト](https://postext.dev/ja/docs/configuration-page-layout.md#レイアウト)の`layout.inlineResourceGapInBoxes`を参照）。
- **見出し内の文字書式**（規則6）：1.4までは、見出しの中の`*italic*`、`**bold**`などのマークが付いた語を、見出し自身の素のスタイルで印字していました（[見出し](https://postext.dev/ja/docs/configuration-headings.md#見出し)の`headings.inlineMarks`を参照）。
- **ドロップキャップのサイズ**（規則6）：1.4までは、`fontSize`のないデザインテキストの`dropCap`は、またがる行ボックス全体と同じ高さになり、上端が1行目より上に出ていました（[テキスト要素](https://postext.dev/ja/docs/configuration-headers-footers.md#テキスト要素)の`dropCap`を参照）。
- **コロンで終わる行の下の余地**（規則6）：1.4までは、`keepColonWithList`はコロンで終わる行の下に1行分の余地があればリストには十分とみなしていました。そのため、オーファンとウィドウの規則が分割しない2行の最初の項目は、コロンの行を残して次の段に送られていました（`bodyText.colonListRoom`を参照）。
- **ボックスの分割で残る行**（規則6）：1.4までは、段落やリスト項目の途中で分割したボックスは、ボックスの各側に合計で`splitMinLines`行あれば、その段落や項目の1行だけを片側に残すことがありました（[レイアウト](https://postext.dev/ja/docs/configuration-page-layout.md#レイアウト)の`layout.boxChildSplitMinLines`を参照）。
- **ダッシュでの改行**（規則7）：1.4までは、Knuth-Plassは語と語の間に前後を詰めて置いたemダッシュやenダッシュ（`say—that’s`）の後で行を終えることがなく、書式付きのテキストを1行ずつ組む改行処理は2文字の間でだけ改行していました（[本文](https://postext.dev/ja/docs/configuration-text.md#本文)の`bodyText.breakAfterDashes`を参照）。
- **行末不ぞろいのテキスト**（規則7）：1.4までは、`optimalLineBreaking`の値にかかわらず、行末不ぞろいの本文テキストを1行ずつ、各行を埋めてから次の行に進んで組んでいました（[本文](https://postext.dev/ja/docs/configuration-text.md#本文)の`bodyText.optimalRagged`を参照）。
- **見出しの下での分割**（規則8）：1.4までは、段の末尾にある見出しの下の段落は、次の段に送られる行がどれほど少なくても、そこに入るだけの行を残していました（[見出し](https://postext.dev/ja/docs/configuration-headings.md#見出し)の`headings.keepWithNextSplit`を参照）。
- **`:::paragraphs`コンテナーの下のアキ**（規則8）：1.4までは、スタイルのアキをグリッドへのスナップの前に最後の段落の下に置き、その下に次のブロックの上のアキ（見出しの`marginTop`）を加え、テキストの段落間隔は含めていませんでした（[本文](https://postext.dev/ja/docs/configuration-text.md#本文)の`bodyText.paragraphContainerSpacing`を参照）。
- **複合語のハイフンでの改行**（規則8）：1.4までは、インライン書式のない段落では、Knuth-Plassは2文字の間のハイフン（`well-known`）の後で両端そろえの行を終えることがなく、書式のある段落では終えていました（[本文](https://postext.dev/ja/docs/configuration-text.md#本文)の`bodyText.breakAfterHyphens`を参照）。
- **区切りのない詩**（規則9）：1.22までは、行に`||`のない`:::verse`の詩は各行を1つの半句として中央に置いて組んでいました（[詩](https://postext.dev/ja/docs/configuration-text.md#詩)の`bodyText.verse.layout`を参照）。
- **1行目の字下げとぶら下げインデントの併用**（規則9）：1.22までは、段落スタイルの`hangingIndent`が`firstLineIndent`に取って代わり、1行目は`indent`から始まっていました（[段落スタイル](https://postext.dev/ja/docs/configuration-styles.md#段落スタイル)を参照）。
- **行末のバックスラッシュ**（規則9）：1.22までは、段落・引用・リスト項目の行末のバックスラッシュと、後ろにスペースのある`\\`はそのまま印字され、行はスペースを挟んでつながっていました（[本文](https://postext.dev/ja/docs/configuration-text.md#本文)の`bodyText.hardLineBreaks`を参照）。
- **コードフェンス**（規則9）：1.22までは、```` ``` ````や`~~~`のフェンスとその中の行をMarkdownとして読んでいました。行は段落に結合され、`#`の行は見出しになり、フェンスは印字されていました（[コードリスト](https://postext.dev/ja/docs/configuration-styles.md#コードリスト)の`codeStyle.blocks`を参照）。
- **詩行の折り返し**（規則10）：1.23では、一行ずつ組む詩で行長より長い行は、はみ出しがわずかでも自然な語間のまま折り返していました（[詩](https://postext.dev/ja/docs/configuration-text.md#詩)の`bodyText.verse.tighten`を参照）。

`openBundle`と`readBundle`は、そのようなマニフェストの`config`と、各ロケールの`localized`の設定を`migrateConfig`を通して読み込みます。この関数は1.4が組んだ改ページを明示的に書き出し、数式の倍率に1.131を掛けます（`em`で指定した別行立て数式のアキはこの値で割ります）。1.5のプレリリースが書いた`3`から`7`の値を持つマニフェストには、その値より後の規則の固定だけが入ります。`3`なら数式のサイズ、インラインのアキ、規則6の5つの固定、規則7の2つ、規則8の3つ。`4`ならインラインのアキと、規則6、7、8の固定。`5`なら規則6、7、8の固定。`6`なら規則7と8の固定。`7`なら規則8の固定だけです。見出しの下での分割（`pinLegacyHeadingSplit`）は、各層を重ねた結果有効になる`headings`に`headings.keepWithNextSplit: 'fill'`として書き込まれます。条件は、読み込んだ章に見出しがあり、設定が独自の値を指定しておらず、`headings.keepWithNext`を有効のままにし、`bodyText.avoidOrphans`を無効にしていないことです。複合語の改行（`pinLegacyHyphenBreaks`）は、有効な`bodyText`に`bodyText.breakAfterHyphens: false`として書き込まれます。条件は、読み込んだ章に2文字の間のハイフンがあり、設定がこの値をすでに設定しておらず、`optimalLineBreaking`も無効にしていないことです。コンテナーの下のアキ（`pinLegacyParagraphContainerSpacing`）は、有効な`bodyText`に`bodyText.paragraphContainerSpacing: 'add'`として書き込まれます。条件は、設定が段落スタイルを（`paragraphStyles`またはHTMLビューアーの上書きで）宣言し、読み込んだ章が単独の行で`:::paragraphs`コンテナーを開き、設定がこの値をすでに設定していないことです。ダッシュでの改行（`pinLegacyDashBreaks`）は、有効な`bodyText`に`bodyText.breakAfterDashes: false`として書き込まれます。条件は、読み込んだ章に語と語の間に前後を詰めて置いたemダッシュやenダッシュがあり（前に文字、数字、閉じる約物があり、後ろに文字、数字、開き括弧や開き引用符があるもの。ダッシュの前の引用符は、その引用符の前に文字、数字、閉じる約物、ノーブレークスペースがあれば数に入ります。`"no"—and`は該当し、`said "—Hola`は該当しません。ダッシュや引用符に接するインラインのマーク、たとえば`**riddles.**—I`の`**`は、どちら側にあっても数に入ります）、設定がこの値をすでに設定していないことです。行末不ぞろいの改行（`pinLegacyRaggedBreaking`）は、有効な`bodyText`に`bodyText.optimalRagged: false`として書き込まれます。条件は、設定が本文テキストのどこかを行末不ぞろいにし（本文テキスト、段落スタイル、囲みの本文、部の本文、節のスタイルの本文（`headingStyles[].bodyStyle`）、またはHTMLビューアーの上書きで、`'justify'`以外の`textAlign`を指定する）、この値をすでに設定しておらず、`optimalLineBreaking`も無効にしていないことです。ボックスの中のアキ（`pinLegacyBoxResourceGap`）は、有効な`layout`に`layout.inlineResourceGapInBoxes: false`として書き込まれます。条件は、読み込んだ章の`:::callout`の中にリソースが単独の行で埋め込まれ、設定がこの値をすでに設定していないことです。ボックスの分割（`pinLegacyBoxChildCut`）は、有効な`layout`に`layout.boxChildSplitMinLines: 1`として書き込まれます。条件は、読み込んだ章が単独の行で`:::callout`を開き、設定がこの値をすでに設定していないことです。見出しのマーク（`pinLegacyHeadingMarks`）は、各層を重ねた結果有効になる`headings`に`headings.inlineMarks: false`として書き込まれます。条件は、読み込んだ章の見出しがマーク（タイトル内の`*`、`_`、`^`、`~`、`:smallcaps[`、またはリンク）を含み、設定が独自の値を指定していないことです。ドロップキャップ（`pinLegacyDropCapSize`）は、どこにあっても1.4のサイズが`dropCap.fontSize`として書き出されます。単位は、要素の行送りが長さで指定されていればその単位、そうでなければフォントサイズの単位です。コロンで終わる行の下の余地（`pinLegacyColonListRoom`）は、有効な`bodyText`に`bodyText.colonListRoom: 'line'`として書き込まれます。条件は、読み込んだ章のリストがコロンで終わる行に続き（間に空行があってもかまいません）、設定が余地を指定しておらず、`keepColonWithList`も無効にしていないことです。インラインのアキ（`pinLegacyInlineGap`）は、各層を重ねた結果有効になる`layout`に`layout.inlineResourceGap: 'above'`として書き込まれます。条件は、読み込んだ章のある行がリソースを埋め込み（パーサーの解釈どおり、`::resource{id="…"}`が単独で行にあるもの。本文中やコードスパンでの言及は数に入りません）、設定が独自のアキを指定していないことです。サイズは、各層を重ねた結果有効になる`math`に固定されます（ロケール独自の`math`は共有のものを置き換えます）。これは読み込んだ章に`$`がある場合に限られ、数式のないバンドルの`config`は書かれたまま残ります。マニフェストもロケールも`math`を指定しない場合、有効なものは`readBundle`の`baseConfig`（読み込む側自身の設定）のもので、これも固定されます。1.4はバンドルの数式をそのサイズで組んでいたからです。基本設定の`fontSizeScale: 1.5`は1.5 × 1.1312として読まれます。基本設定の見出しの改ページはそのまま使われます。このように古いバンドルはこれらの規則で組まれた結果を保ち、`bundle.config`には、実際に組むときの改ページ、数式のサイズ、アキ、見出しのマーク、ドロップキャップのサイズ、コロンで終わる行の下の余地、ボックスの分割、ダッシュでの改行、複合語の改行、行末不ぞろいの改行、見出しの下での分割、コンテナーの下のアキが表示されます。1.5のレイアウトの修正には固定がなく、ほかの本と同じく古いバンドルにも適用されるため、それらが関わるページは動くことがあります（一覧は[数式のサイズ](https://postext.dev/ja/docs/configuration-lists-math.md#数式)を参照）。現在の規則に合わせて手で書く`preset.json`には`"configVersion": 14`を指定します。古いバンドルのマニフェストにこの値を書き込むのも、現在の規則で読み込むための1行で済む方法です（その場合、バージョンのないバンドルは見出しの改ページの固定も失います）。`8`と記されたマニフェスト（postext 1.5〜1.22が書いたもの）は規則9の4つの固定だけを受け、それより古いものも同様に受けます。詩の組み方（`pinLegacyVerseLayout`）は、読んだ章に開始行で組み方を指定せず行に半句の区切りもない`:::verse`の詩があり、設定がまだ決めていなければ、有効な`bodyText`に`bodyText.verse.layout: 'bayt'`として書き込まれます。組になった字下げ（`pinLegacyPairedIndents`）は、0でない`hangingIndent`も決めている段落スタイル（`paragraphStyles`またはHTMLビューアーの上書き設定のもの）すべてから`firstLineIndent`を外します。強制改行（`pinLegacyHardBreaks`）は、読んだ章に、段落・引用・リスト項目の行末がバックスラッシュで終わって次の行に続く箇所か、後ろにスペースと文字が続く`\\`があり（インラインのコードと数式、別行立ての数式、見出し、`:::verse`の詩は除く）、設定がまだ決めていなければ、有効な`bodyText`に`bodyText.hardLineBreaks: false`として書き込まれます。コードフェンス（`pinLegacyCodeBlocks`）は、読んだ章が```` ``` ````か`~~~`のフェンス（3文字以上、行頭からのインデントは3スペースまで）を開き、設定がまだ決めていなければ、`codeStyle.blocks: false`として書き込まれます。`9`が付いたマニフェスト（postext 1.23で書き出したもの）には規則10の固定だけが適用され、それより古いものにも適用されます。詩行の折り返し（`pinLegacyVerseTightening`）は、読んだ章が詩を一行ずつ組んでいて（`:::verse`のフェンスが`layout=lines`を指定しているか、組み方を指定せず各行に半句の区切りがなく、設定がそのような詩をバイトとして組んでいない場合）、設定がまだ決めていなければ、有効な`bodyText`に`bodyText.verse.tighten: false`として書き込まれます。`10`が付いたマニフェスト（postext 1.24で書き出したもの）には規則11の固定だけが適用され、それより古いものにも適用されます。文字グリッドの段末そろえ（`pinLegacyGridBalancing`）は、統合後の設定が横組みで`cjk.grid.enabled`を指定し、`enabled`自体は指定していなければ、有効な`headings`に`headings.balancing.enabled: true`として書き込まれます。1.24はそのようなページを既定でそろえていたためです。規則11では、書名を1字目の後で分割しないこと、丸数字を漢字として組むこと、CJKのデザインテキストを本文の規則で組むことも加わった（#637）。`10`以前のマニフェストは、読み込んだ章に書名（《、〈、`:book[`）があれば`cjk.titleMinChars: 1`（`pinLegacyTitleBreaks`）、丸数字（U+2460〜U+24FF、U+2776〜U+2793）があれば`cjk.circledNumbers: 'western'`（`pinLegacyCircledNumbers`）、読み込んだ章か設定そのものにCJKのテキストがあれば`cjk.composeDesignText: false`（`pinLegacyDesignText`）を、それぞれ有効な`cjk`に受け取る。いずれも設定がその項目をまだ指定していない場合に限る。規則11では、インラインの表を切ることと、本文中で`:::columns`を組むことも加わりました（#634）。`10`以前のマニフェストは、読み込んだ章がリソースを埋め込んでいれば有効な`tableStyle`に`tableStyle.splitInline: false`（`pinLegacyInlineTableSplit`）を、読み込んだ章が単独の行で`:::columns`フェンスを開いていれば有効な`layout`に`layout.flowColumns: false`（`pinLegacyFlowColumns`）を受け取ります。どちらも、設定がその項目をまだ指定していない場合に限ります。規則11では、ページ幅の章扉のある段の先頭もフロートの空き位置になりました（#639）。`10`以前のマニフェストは、マージした設定が`span: 'page'`の見出しレベルまたは見出しスタイルを設定し、読み込んだ章に見出しがあれば、有効な`layout`に`layout.floatsUnderOpener: false`（`pinLegacyOpenerHeadFloats`）を受け取ります。これも、設定がその項目をまだ指定していない場合に限ります。規則12では、文字グリッド上のページがグリッドの数える行数を収めるようになりました（#717）。`11`以前のマニフェストは、マージした設定が`cjk.grid.enabled`をオンにしていれば、有効な`bodyText`に`bodyText.avoidWidows: true`を、`japan`地域ではさらに`bodyText.avoidOrphans: true`を受け取ります（`pinLegacyGridBreakRules`）。どちらも、設定がその項目をまだ指定していない場合に限ります。規則12では、割注の途中の改行位置の前後に2字ずつ残すようにもなりました（#720）。こうしたマニフェストは、読み込んだ章に`:warichu[`の注があるか、マージした設定が`citations.notes: 'warichu'`を指定していれば、有効な`cjk`に`cjk.warichu.minChars: 1`（`pinLegacyNoteBreaks`）を受け取ります。これも、設定がその項目をまだ指定していない場合に限ります。規則12では、日本地域で欧文の語の括弧や記号の隣にも和欧間のアキが入るようになりました（#721）。`11`以前のマニフェストは、マージした設定が日本地域に解決され、読み込んだ章に仮名か漢字があれば、有効な`cjk`に`cjk.latinSpacingEdge: 'letters'`（`pinLegacyLatinSpacingEdge`）を受け取ります。これも、設定がその項目をまだ指定していない場合に限ります。本文中に置いたリソースも、段幅のフロートと同じく`placement.captionSide`に従うようになりました（#729）。`11`以前のマニフェストは、マージした設定が`layout.layoutType: 'oneAndHalf'`を指定し、読み込んだ章がリソースを埋め込んでいれば、`layout.inlineCaptionSide: false`（`pinLegacyInlineCaptionSide`）を受け取ります。これも、設定がその項目をまだ指定していない場合に限ります。

規則12では、短い段のそろえ方も変わりました。`11`以前のマニフェストは、組んだときのやり方を保ちます。調整手段の順序（#707）：読み込んだ章に見出しがあり、マージした設定が段をそろえる設定で、見出し1つに2行を超えて足すことを許し、この項目をまだ指定していなければ、有効な`headings`に`headings.balancing.maxLinesBeforeLoose`を受け取ります。値はその設定自身の`maxLinesPerHeading`で、指定がなければ`4`です（`pinLegacyLooseOrder`）。複数の段にまたがる図の下の行（#708）：読み込んだ章がリソースを埋め込むか参照する、または囲みをフロートにしていて、マージした設定が段をそろえる設定で、`stretchAfterFloats`をオフにしておらず、この項目をまだ指定していなければ、有効な`headings`に`headings.balancing.stretchAfterWideFloats: 'always'`（`pinLegacyWideFloatStretch`）を受け取ります。索引のグループ内での段の改まり（#712）：読み込んだ章に、1行を占める`:::index`のフェンスがあり、設定がこの項目をまだ指定していなければ、有効な`index`に`index.groups.splitMinEntries: 1`（`pinLegacyIndexGroupSplit`）を受け取ります。高さをそろえて切る単位（#711）：読み込んだ章が本文の行送りから外れた本文を組む可能性があり（`:::`のフェンス、コードフェンス、引用、リスト、別行立ての数式、文献の引用）、マージした設定が段をそろえる設定で、`trailing`か`beforeSpan`をオンのままにしており、この項目をまだ指定していなければ、有効な`headings`に`headings.balancing.trailingStep: 'grid'`（`pinLegacyTrailingStep`）を受け取ります。

規則12では、ラベルが語である参照を、ラベルのスペースで改行します（#702）。`11`以前のマニフェストは、読み込んだ章に参照（`:ref{…}`、`\ref{…}`、`\eqref{…}`、pandoc-crossrefの`@id`）があり、設定がこの項目を指定していなければ、有効な`crossRefs`に`crossRefs.breakLabels: false`を受け取ります（`pinLegacyRefLabelBreaks`）。どのラベルも、組んだときと同じ1語のままです。

規則12では、文字を含まない語（`46.`、`1367)`）を周りの文字の文字体系で組みます（#725）。`11`以前のマニフェストは、設定がこの項目を指定していなければ、章の内容にかかわらず、有効な`bodyText`に`bodyText.scriptContext: false`（`pinLegacyScriptContext`）を受け取ります。こうした語は、レイアウトしたときと同じく単独で組まれます。この規則は約物を文字体系ごとに描き分けるフォントで働きますが、マニフェストを読む時点では本がどのフォントを読み込むか分からないためです。

規則13では、テキストを組む言語が設定項目になり（#764）、規則14では、文書自身の言語がその既定値になります（#770）。`13`以前が付いたマニフェストは、設定がこの項目を指定していなければ、章の内容にかかわらず、有効な`bodyText`に`bodyText.shapingLanguage: 'host'`（`pinLegacyShapingLanguage`）を受け取ります。値を指定しているマニフェストは、その値のままです。そのテキストは、レイアウトしたときと同じく、レイアウトをホストしているページの言語で計測され、ワーカーでは言語なしで計測されます。`13`が付いたマニフェストはpostext 1.29が書き出したものです。1.29.1と1.29.2は、値を指定していない本をホストの言語でレイアウトしていて、固定はそれと同じ結果になります。既定値が`'document'`だった1.29.0は、そのような本を文書の言語でレイアウトしましたが、CanvasとPDFは計測に従っていませんでした。この本も`'host'`で読まれます。`14`が付いていて値を指定していないマニフェストは、文書の言語でレイアウトされます。

規則13では、タスク項目（`- [ ]`、`- [x]`）のボックスもエンジンが描きます（#765）。四角い枠線で、完了した項目にはチェックが入り、項目のテキストの前に独自の幅を取ります。`12`以前のマニフェストは、章にタスク項目があり、設定がこの項目を指定していなければ、有効な`unorderedLists`に`unorderedLists.taskBox: 'character'`（`pinLegacyTaskBox`）を受け取ります。ボックスはリストのフォントで組んだ文字`☐`または`☑`のままで、項目のテキストは、レイアウトしたときと同じく、その文字の幅のあとから始まります。この幅はその文字を持つフォント（多くの本ではシステムのフォント）が返していたもので、マニフェストを読む時点では分かりません。

```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`は、その設定で組むMarkdownです（文字列または章のリスト）。これがない場合、数式のサイズは数式が有効なら常に、コンテナーの下のアキは設定が段落スタイルを宣言していれば常に固定されます。2つのアキ、見出しのマーク、コロンで終わる行の下の余地、ボックスの分割、ダッシュでの改行、見出しの下での分割、複合語の改行は無条件に固定されます。本に数式、`:::paragraphs`コンテナー、インラインの図、マークを含む見出し、コロンで導入するリスト、ボックス、詰めて置いたダッシュ、見出し、複合語があるかどうかを、エンジンが判断できないためです。行末不ぞろいの改行は、内容の有無にかかわらず設定だけで固定されます。保存した設定は一度だけ移行し、`CONFIG_VERSION`で保存し直してください。数式の固定は倍率に掛け算をするので、2回移行した設定は2回大きくなります。これがないときは詩の組み方とコードフェンスも固定されます。本に区切りのない`:::verse`の詩やフェンスがあるかもしれないからです。組になった字下げは設定だけを見て固定されます。1.23の詩行の折り返しも固定されます。本に一行ずつ組む詩があるかもしれないからです。

### バンドルを組んで描画する

開いたバンドルをエンジンとバックエンドにつなぐヘルパーは4つあります。

- **`loadBundleFonts(bundle)`**：バンドルのフェイスを`document.fonts`に登録し、SVGの画像のために、そのバイト列をエンジンのフォントレジストリにも登録します（`registerFontBytes`）。レイアウトはブラウザーが持つフォントでテキストを計測するので、組む前にその完了を待ってください。バンドルが指定していても収めていないファミリー（Google Fonts）は、ほかの文書と同じく自分で読み込む必要があります。
- **`registerBundleImages(bundle)`**：canvasバックエンド（`renderPage`、`renderToCanvas`）のために画像をデコードします。動画のポスターも含みます。`renderToHtml`のリゾルバーは2つあり、**`bundleImageUrl(bundle)`**（`resourceImageUrl`）は画像を、**`bundleVideoUrl(bundle)`**（`resourceVideoUrl`）はバンドルが収める動画ファイルを解決します。どちらも`diagramStyle.singleInk`が有効な場合はSVGの図を一度だけ色替えします。マークアップを色替えし、画像に印を付けて、どのバックエンドも重ねて色を付けないようにします（[キャンバスとHTMLでの単色刷り](https://postext.dev/ja/docs/configuration-resources.md#キャンバスとhtmlでの単色刷り)を参照）。どちらも、各SVGに文字が指定するフェイスを、まずバンドル自身のフォントから、次にエンジンに登録されたフェイスから埋め込みます（[SVGの文字のフォント](https://postext.dev/ja/docs/configuration-resources.md#svgの文字のフォント)を参照）。`registerBundleImages(bundle, { onWarning })`と`bundleImageUrl(bundle, { onWarning })`は、フェイスのないファミリーを報告します。
- **`buildBundle(bundle)`**：章を順に組み、章ごとに1つの`VDTDocument`を返します。各章は前の章を引き継ぎます。見出しとリソースのカウンター、開いている部、ページの奇偶、ノンブルです。目次（`:::toc`）や索引（`:::index`）を印字する章は、本全体のアウトラインを受け取ります。`buildDocument`と同じオプションに加えて、バンドルの設定を上書きする`config`、計測キャッシュを共有する`cache`、`metadata`（後述）を受け付けます。
- **`bundleResourceBytes(bundle)`**、**`bundleFontProvider(bundle, { decodeWoff2, fallback })`**：`postext-pdf`の`renderToPdf`の`resourceBytes`オプションと`fontProvider`オプションです。フォントプロバイダーは、求められたスタイルのうち最も近いウェイトをバンドルから選びます。`.woff2`のフェイスには`decompressWoff2`が必要です。バンドルが収めていないファミリーについては、`request`を含むレンダラーの引数で`fallback`を呼び、その戻り値をそのまま渡します。ファミリーの`latin`ファイルだけを取得するフォールバックでは、バンドルが埋め込んでいない中国語のファミリーが空の四角として印字されます。[中国語・日本語・韓国語のフォント](https://postext.dev/ja/docs/configuration-pdf-output.md#中国語日本語韓国語のフォント)の`sliceFontProvider`のようにスライスで応えるフォールバックなら、すべて印字されます。

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

1つの章だけを自分で組むには、ほかの文書と同じく、`bundle.chapters[i].markdown`、`bundle.resources`、`bundle.config`を`buildDocument`に渡します。

**本のメタデータ**。Sandboxと同じく、最初の章のフロントマターが本のメタデータになります。`buildBundle`はその`title`、`author`などをすべての章に渡すので、`{title}`や`{author}`の柱はどのページにも入り、どの章の`doc.metadata`にもそれらが入ります。2章目以降の先頭にあるフロントマターのブロックは無視されます。`---`の行で見つけられ、解析されずに空白に置き換えられるので、パーサーが受け付けないYAMLでも害はありません。`options.metadata`は、フロントマターが設定しない値を補います（フロントマターが優先されます）。本のページ数もすべての章に届きます。`{bookTotalPages}`はそれを印字し、`{totalPages}`は章のページを数えます（[本全体のページ数](https://postext.dev/ja/docs/configuration-headers-footers.md#本全体のページ数)を参照）。

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

**本の始まりの位置**。バンドルは、もっと長い出版物の一部だけを収めることができます。ある号の58〜61ページや、教科書の第4章などです。`start`は、その前に何があるかを、`buildDocument`の`continuation`と同じ項目で示します。最初のページより前のページ数（`pageIndexOffset`。1ページ目が左右どちらの面になるかを決め、それに応じて左右対称のマージンと奇数・偶数ページの柱も決まります）、その時点のページ番号の付け方（`pageNumbering`）、見出しのカウンター（`headings`）、開いたままの部（`part`）、そしてリソース、番号付きの項目、脚注、行番号のカウンターです。`createBundle`はこれを`preset.json`の`start`に書き込み、`openBundle`は`bundle.start`として返します。`buildBundle`は最初の章をこれでレイアウトし（`buildDocument({ markdown, continuation: start }, config)`と同じ結果になります）、続く章をそこからつなげます。`{bookTotalPages}`は本より前のページも数えます。`start`のないマニフェストはこれまでどおりに読まれ、この項目を知らない読み取り側は無視します。`bookPageCount`は含まれません。ページ数は読み取り側が数えます。複数の言語を収めたバンドルでは、`localized[…].start`でその版だけの始まりを指定できます。

```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'
```

### ライブサンプル：バンドルを開く

このCodePenのサンプルは、リポジトリーから2章の見本の本（独自の書体、SVGの図、表を持つ`lantern.postext`）を読み込みます。バンドルのフォントと画像を登録し、`buildBundle`で本を組んで、全ページを描画します。「Make the PDF」ボタンは、同じ文書を`postext-pdf`で描画し、バンドルのフォントを埋め込みます。Sandboxから書き出したものなど、自分の`.postext`ファイルを選べば、同じように表示されます。

> **実行できる例: Postext · .postextバンドルを開く** — postextで.postextファイルを開き、本を組んでcanvasとPDFに描画します。 ([ソースコード](https://github.com/drnachio/postext/tree/main/docs/examples/open-bundle))

### バンドルを作成する

`createBundle`は、文書（章、設定、リソース、それらが参照するデータ本体）から`.postext`ファイルを書き出します。

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

| 入力 | 意味 |
| --- | --- |
| `name`, `id`, `description`, `locale` | マニフェストのメタデータ。`id`の既定値は`name`のスラッグです。 |
| `chapters` or `markdown` | 本の内容。章ごとに1つの`{ title?, markdown }`、または1つの文書です。 |
| `config` | `PostextConfig`。既定値と同じ値はマニフェストに書き出しません。 |
| `resources` | リソース。画像は`bitmap.fileId` / `svg.fileId`（印刷用マスターは`svg.pdfFileId`）でデータ本体を指定します。 |
| `files` | `fileId`ごとのデータ本体（オブジェクトまたは`Map`）。リソースが参照する画像と、`config.customFonts`のバリアントが参照するフォントファイルです。値は`Uint8Array`、`ArrayBuffer`、`Blob`、文字列（SVGのマークアップ）のいずれかです。 |
| `thumbnail` | `{ data, mime }`：表紙画像（PNG、JPEG、WebP、GIF、SVG）。 |
| `canvasScope` | `'book'`にすると、本全体を1つのキャンバスとして組むようビューアーに求めます。 |
| `start` | バンドルがもっと長い本の一部だけを収めているときの、本の始まりの位置。単独の文書をビルドするときに渡す`continuation`と同じものです。マニフェストの`start`として書き込まれます。 |
| `mtime` | アーカイブのすべてのファイルに書き込む更新日時（`Date`、タイムスタンプ、日付文字列のいずれか）。省略すると呼び出した時刻になるので、同じ入力でも2回の呼び出しで異なるバイト列になります。固定の日付を渡せば同じ入力から同じバイト列が得られ、ハッシュを取ったり比較したりできます。zipが保持する日時はタイムゾーンを持たず、2秒刻みで、1980年から2099年までです。日付はマシンのローカル時刻で書き込まれます。どのマシンでも一致するバイト列を得るには、`new Date(1980, 0, 1)`のようにローカル時刻の各フィールドから日付を作ります。タイムスタンプや`Z`で終わる文字列は特定の瞬間を指すので、タイムゾーンごとに異なるローカル時刻になります（`'1980-01-01T00:00:00Z'`はUTCより西では1979年のままです）。ローカル時刻でこの範囲の外にある日付は例外になります。 |
| `localized` | 同じ本の別の言語。ロケールタグごとに`{ es: { name?, description?, chapters?, config?, resources? } }`の形で指定します。この場合、上の入力は、必須の`locale`の内容になります。[多言語のバンドル](https://postext.dev/ja/docs/configuration-bundles.md#多言語のバンドル)を参照してください。 |

戻り値は、アーカイブの`bytes`、`preset.json`として書き出した`manifest`、すべてのファイルを収めた`files`（パス → バイト列）、`warnings`のリストです。ファイル名はリソースのid（`resources/lantern.svg`）またはフォントのファイル名（`fonts/…`）から、章のファイル名は順番とタイトル（`chapters/01-dusk.md`）から付けます。フォントはマニフェストの`fonts`で宣言し、`config.customFonts`の中には決して書きません。次のものは、それぞれ警告を出して除外します。
- データ本体が`files`にないリソースやフォントのフェイス
- `.woff`のフェイス（PDFバックエンドが埋め込めないため）
- `redistributable: false`が付いたファミリー

ブラウザーでは、`bytes`をダウンロードリンクに渡します：`URL.createObjectURL(new Blob([bytes], { type: 'application/zip' }))`。Nodeでは`fs.writeFile`で書き込みます。`createBundle`と`openBundle`にDOMは不要です。配布物のモジュールパスには拡張子がないので、バンドラーを使わない素のNodeではresolveフックが必要です。リポジトリーの`docs/examples/open-bundle/build-sample.mjs`に、数行で書いた例があります。

### 多言語のバンドル

`.postext`ファイルは本を複数の言語で収めることができ、`openBundle(bytes, { locale })`はそのどの言語でも読み込めます。`createBundle`は`localized`からこれを書き出します。追加のロケールごとに1項目で、それぞれ主となる内容（`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`**：その言語での本。章のファイルはロケールごとに1つのフォルダーに入り（`chapters/en/01-dusk.md`、`chapters/es/01-anochecer.md`）、マニフェストの`chapters`はロケール → 章のマップになります。`chapters`のないロケールは主言語の章を読みます。独自の章を持つロケールが1つもなければ、章は1つのリストのままです。
- **`config`**：その言語の設定。そのロケールでバンドルを読むと、トップレベルの各キーが共有のキーを丸ごと置き換えます。上の例の`headings`は`headings`オブジェクト全体を置き換えます。省略したキーや共有のものと同じキーは共有され、書き出されません。そのため、ロケールの設定全体を渡しても、変わる数個のキーだけを渡しても同じように動きます。共有のキーが既定値でないのにロケール側を既定値にしたキー（`layout: {}`）は指定どおり書き出されるので、共有の値をリセットします。フォントは共有です。ロケールの`customFonts`のファミリーはバンドルの`fonts`に加わります。
- **`resources`**：共有のリソースの文言で、`id`で対応づけます。`caption`、`note`、`altText`、表の`table`です。文字を含む画像は独自のアートワークを持てます。`bitmap.fileId`や`svg.fileId`（と`svg.pdfFileId`）で`files`内の別のデータ本体を指定すると、`resources/es/lantern.svg`として書き出されます。タイプや配置などほかのフィールドは共有です。`resources`にないidは警告を出して除外し、ロケールの画像が欠けている場合も、警告を出してそのロケールは共有の画像を使います。
- **`name`**、**`description`**：その言語での書名と説明。共有の値と異なるときに`localized[…].name`と`description`として書き出されます。その言語で読むと`openBundle`はこれを`bundle.name`と`bundle.description`として返し、Sandboxは読み込み時とライブラリでその版をこの書名で表示し、`postext info`と`postext check`もこれを出力します。プリセット配信元の`index.json`のエントリーに`localized`（`localized.ca.name`）として写しておくと、ライブラリは開く前から各版をそれぞれの書名で示します。指定のない言語はマニフェストの`name`と`description`を使います。

マニフェストは`locales`にすべての言語を並べ（`['en', 'es']`）、主言語を`locale`に残し、それ以外を`localized`に格納します。ロケールを指定しない`openBundle`は主言語を読みます。

**読み手が得る言語**。`openBundle(bytes, { locale })`は、完全に一致するロケール、なければその基本言語（`es-MX`なら`es`）、それもなければ主言語を返し、どれを返したかを`bundle.locale`が示します。章と文言は常に同じ言語から取られます。`localized`が主言語の地域変種を持っていても、主言語は共有の文言を保ちます。`pt-BR`の項目を持つ`pt-PT`のバンドルは、`pt-BR`に対してだけブラジルのキャプションを読み、`pt-PT`と`pt`には共有のキャプションを読みます。

### ライブサンプル：バンドルを作成する

このCodePenのサンプルは、SVGの図を持つ2章の本を作り、`createBundle`が書き出したファイルとマニフェストを一覧表示します。アーカイブをダウンロードできるようにしてから、`openBundle`で開き直して最初のページを描画します。数行で往復のすべてを行うサンプルです。ダウンロードしたファイルをSandboxで読み込めば、そこで作業を続けられます。

> **実行できる例: Postext · .postextバンドルを作成する** — postextのcreateBundleで.postextファイルを書き出し、ダウンロードして開き直します。 ([ソースコード](https://github.com/drnachio/postext/tree/main/docs/examples/create-bundle))

### バンドルの活用

Sandbox、エージェントスキル、`postext`パッケージはどれも同じファイルを読み書きするので、`.postext`ファイルは本をツールからツールへ渡す手軽な手段になります。

- **バンドルから始める**。既存の出版物を[エージェントスキル](https://postext.dev/ja/docs/skill.md)で移植するか、[Sandbox](https://postext.dev/ja/sandbox.md)で本をデザインして書き出します（**本**パネルの、その本の行の⋯メニューにある**ダウンロード（.postext）**）。そのファイルをプログラムから`openBundle`で読み込み、canvas、HTML、PDFに描画します。ファイルを本のソースとして手元に置き、章、設定、リソースをコードで編集して`createBundle`で書き戻すか、変わるたびに読み込み直します。
- **Sandboxでデバッグし、微調整する**。プログラムの出力に手直しが必要なとき（図が違うページに入る、見出しのスタイル、段末そろえなど）は、プログラムが組んだものを`createBundle`で書き出します。そのファイルをSandboxで読み込み（**本 → 新規 → .postextファイルを開く…**）、ライブプレビュー、**検査**パネル、PDFビューを見ながらテキスト、デザイン、図を直して、もう一度書き出します。プログラムは修正されたファイルを`openBundle`で読み込みます。あるいは、変わった部分をコードに書き戻します。マニフェストの`config`には既定値と異なる値だけが入っているので、短い差分として読めます。

### 低レベルAPI

`postext/bundle`は、`openBundle`と`createBundle`の土台になる部品も書き出しています。バンドルを独自の方法で保存したり配信したりするホスト（HTTPで配信する展開済みのディレクトリー、データベースのレコードなど）向けです。

- **`openBundleZip(bytes)` / `zipBundle(files, { mtime })`**：アーカイブの層。開くときはトップレベルのフォルダーを許容し、`__MACOSX`の項目とドットファイルを無視します。バンドルの外に出るパスは拒否します。`mtime`は`createBundle`の入力と同じようにファイルの日時を決めます。
- **`readBundle(manifest, readFile, options)`**：マニフェストと`readFile(path)`コールバックから、章、設定、リソース、画像、フォントを読み込みます。`options`では、ロケール、ファイルidの命名方法（`ids`）、基本設定（`baseConfig`。マニフェストの設定の下に置かれます。既定ではバンドルの言語での`bundleBaseConfig`のパレットとリソースタイプで、その言語は`resolveBundleConfigLocale(manifest, locale)`が返します。独自の`baseConfig`を渡すホストは、それをこの言語にローカライズしてください。`configVersion: 4`より古いマニフェストではその`math`がバンドルのものと一緒に固定され、5より古ければ`layout`のインラインのアキ、6より古ければ`layout`のボックスの中のアキ、`bodyText`のコロンで終わる行の下の余地、`headings`のインラインのマーク、ドロップキャップのサイズ、7より古ければ`bodyText`のダッシュでの改行と行末不ぞろいの改行、8より古ければ`headings`の見出しの下での分割と、`bodyText`の複合語の改行および`:::paragraphs`コンテナーの下のアキが固定されます。[postext 1.4以前で書き出したバンドル](https://postext.dev/ja/docs/configuration-bundles.md#postext-14以前で書き出したバンドル)を参照）、固有のサイズの計測方法を指定します。`readResolution`は各ビットマップの解像度をファイルから読み取り、`bitmap.fileResolution`に入れます。バンドルが`layout.bitmapResolution: 'file'`を指定しているときは、既定でtrueです。
- **`planBundle(meta, content)` / `resolveBundleFiles(plan, sources)`**：書き出し側。純粋な計画（ファイル名とマニフェスト）と、`readBlob` / `readFont`コールバックによるバイト列の解決に分かれています。
- **`isBundleManifest(value)`**、ロケールの選択関数（`pickChapterSpecs`、`pickLocaleOverrides`、`pickBundleView`、`resolveBundleLocale`、`resolveBundleConfigLocale`）、`svgSize` / `bitmapSize` / `bitmapInfo`（ビットマップのピクセル数と、ファイルに記載された解像度）、形式の型（`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)`、`LEGACY_MATH_SIZE`**（0.5 ÷ 0.442）：保存した設定を現在の規則で表したものにします（[postext 1.4以前で書き出したバンドル](https://postext.dev/ja/docs/configuration-bundles.md#postext-14以前で書き出したバンドル)を参照）。`readBundle`はこれを適用します。設定を独自の方法で保存するホストも、保存したコピーごとに1回ずつ適用できます。

Sandboxはこれらの上に作られています。Sandboxは独自の保存用idと`layouts.json`のページ記録を加えます。
