# 配置：组合页面

> 组合页面的页面模板：框架、组、装饰、继承与左右页变体、图片锚点的注记样式，以及模板可能给出的警告

- HTML版本: https://postext.dev/zh/docs/configuration-composed-pages
- 最后更新: 2026-10-16
- 阅读时间: 6分钟
- 其他语言: [en](https://postext.dev/en/docs/configuration-composed-pages.md), [es](https://postext.dev/es/docs/configuration-composed-pages.md), [ca](https://postext.dev/ca/docs/configuration-composed-pages.md), [de](https://postext.dev/de/docs/configuration-composed-pages.md), [fr](https://postext.dev/fr/docs/configuration-composed-pages.md), [it](https://postext.dev/it/docs/configuration-composed-pages.md), [pt](https://postext.dev/pt/docs/configuration-composed-pages.md), [ja](https://postext.dev/ja/docs/configuration-composed-pages.md), [ar](https://postext.dev/ar/docs/configuration-composed-pages.md)

## 简单来说

本页讲页面模板。模板是在设置里画好的一个页面：一些有名字的方框，叫做框架，还规定每个框架放在哪里、最大能长到多大、是什么样子。组合页面的Markdown再按名字把内容填进这些框架，所以文字里从不写位置，设置里也从不写文字。一个模板可以从另一个模板出发，只改其中几个框架，左页和右页上还可以不一样。本页还列出写在图片上的文字所用的注记样式，以及模板可能给出的每一条警告和改正的办法。

## 页面模板

组合页面的版式来自配置中的**模板**，内容来自`::::page{template="…"}`围栏中的Markdown。模板给一些矩形命名，这些矩形叫做**框架**；Markdown按名称（即框架的槽位）把每一段内容送进对应的框架。Markdown中从不出现几何信息，模板中也从不出现正文文字。`pageTemplates`属性保存全书的模板：

```ts
const config: PostextConfig = {
  calloutStyles: [{ id: 'caja-azul' }, { id: 'tab-titulo' }],
  pageTemplates: [
    {
      id: 'tema-cajas',
      decoration: {
        elements: [
          { kind: 'image', id: 'fondo', resourceId: '{attr.fondo}', decorative: true,
            placement: { anchor: { to: 'bleed', edge: 'top-left' }, size: { width: 'fill', height: 'fill' } } },
        ],
      },
      frames: [
        { name: 'titulo', takes: 'h1', style: 'tab-titulo' },
        { name: 'texto', flow: true },
        { name: 'dinero', style: 'caja-azul', padding: { right: { value: 62, unit: 'mm' } } },
        { name: 'dinero-fotos', layer: 2, width: { value: 60, unit: 'mm' },
          placement: { anchor: { to: '#dinero', edge: 'top-right' } } },
      ],
      groups: [
        { name: 'pagina', kind: 'stack', gap: { value: 6, unit: 'mm' }, frames: ['titulo', 'texto', 'dinero'] },
      ],
    },
  ],
};
```

只设了`id`和一个框架的模板就是一整页，这个框架占满版心。其余字段都是可选的：

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `id` | `string` | 必填 | 页面围栏所请求的名称（`template="tema-cajas"`）。在所有模板中唯一：id相同的第二个模板会被忽略。 |
| `name` | `string` | 即id | 在沙盒中显示的名称。排版从不读取它。 |
| `extends` | `string` | 无 | 本模板作为起点的模板的id。见[继承与变体](https://postext.dev/zh/docs/configuration-composed-pages.md#继承与变体)。 |
| `area` | `'content' \| 'trim' \| 'bleed'` | `'content'` | 框架位置所参照的矩形。`'content'`是页面的版心，像正文一样在左右页上镜像；`'trim'`是裁切后的页面；`'bleed'`是连同出血的页面。 |
| `grid` | `{ columns, rows, columnGap?, rowGap? }` | 无 | 把区域分成单元格，框架或组就可以按单元格放置（`grid: { col: 2, row: 1, cols: 2 }`，从1起数），不必用`placement`。间距默认为`0`。 |
| `frames` | `FrameConfig[]` | `[]` | 各个框架，按模板顺序排列：先测量哪个框架、出现并列时以哪个为准，都按这个顺序。见[框架](https://postext.dev/zh/docs/configuration-composed-pages.md#框架)。 |
| `groups` | `FrameGroupConfig[]` | `[]` | 共享空间的纵向或横向框架序列。见[组](https://postext.dev/zh/docs/configuration-composed-pages.md#组)。 |
| `decoration` | `DesignSlot` | 无 | 与页面一起绘制的图像、线条和方框，使用[页眉与页脚](https://postext.dev/zh/docs/configuration-headers-footers.md#页眉与页脚)的元素语言。文本元素只放占位符（`{pageNumber}`、`{chapterTitle}`，以及表示页面围栏某个属性的`{attr.x}`）。元素可以锚定到某个框架或组（`'#dinero'`），并随之移动。装饰在框架之后排放，从不推开文字。 |
| `header`、`footer` | `DesignSlot \| false` | 继承 | 页面的书眉和页码。未设置时，页面沿用所在章的书眉和页码；`false`把它们隐藏。 |
| `spread` | `boolean` | `false` | 模板描述两个对页，排成一整张。在从左到右的书中，跨页总是从左页开始（在从右到左的书中从右页开始），需要时会在它前面加一个空白页。 |
| `gutter` | `Dimension` | `0` | 在跨页上，订口处框架要避开的带状区域，框架设了`crossGutter: true`时除外。 |
| `variants` | `{ recto?, verso? }` | 无 | 针对右页（`recto`）或左页（`verso`）的改动，写法与模板本身相同。左右指的是页面的实际位置，所以从左到右和从右到左的书都适用。 |
| `overflowTemplate` | `string` | 本模板 | 框架续排到下一页（`overflow.continueIn: 'next-page'`）时所加页面的模板。未设置时，所加的页面使用本模板，只填入续排的框架。 |
| `bodyStyle` | `string` | 无 | 页面文字的段落样式，用法与标题样式中的相同。 |
| `palette` | `string` | 无 | 页面的调色板覆盖，用法与标题样式和篇中的相同。 |
| `role` | `string` | `'composed'` | 带`pages`筛选条件的设计元素所看到的页面角色。 |

### 框架

框架是接收排好的内容的具名矩形。它的`name`就是Markdown所请求的槽位，在所属模板中唯一（组也不能使用框架的名称，因为锚点用同样的方式指向两者）。

模板有网格时，框架按它的`grid`单元格放置，否则按它的`placement`放置，后者与设计元素的定位相同：一个锚点（`'container'`表示区域，`'page'`、`'bleed'`，或用`'#name'`表示另一个框架或组）、一条边、一个偏移和一个尺寸。锚定到另一个框架的框架随之移动。两者都没有时，框架从区域的左上角开始，宽度与区域相同，高度按它的高度规则确定。内容如何灌入各框架、如何串接和裁切，见[组合页面 › 页面如何排出](https://postext.dev/zh/docs/composed-pages.md#页面如何排出)。

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `name` | `string` | 必填 | 槽位名称。 |
| `placement` | `ElementPlacement` | 整个区域 | 锚点、边、偏移和尺寸，与[元素定位](https://postext.dev/zh/docs/configuration-headers-footers.md#元素定位)相同。由网格单元格放置框架时忽略。 |
| `grid` | `{ col, row, cols?, rows? }` | 无 | 模板网格中的一个单元格，从1起数，跨`cols` × `rows`个单元格（默认1）。超出网格的单元格会被移回网格内。模板没有网格时忽略。 |
| `width`、`height` | `Dimension \| { percent } \| 'auto' \| 'fill'` | `'fill'`、`'auto'` | 固定长度、区域的百分比（0到100）、`'auto'`（内容本身的尺寸）或`'fill'`（到框架所锚定的那条边为止剩下的空间）。未设置时，框架读取`placement.size`，再取默认值：全宽，高度与内容相同。文本框架需要行宽，所以`'auto'`宽度只对只放一张图片的框架有意义（图片在框架高度下的宽度）。引擎无法使用的值（负长度、未知单位）按默认值处理。 |
| `minWidth`、`maxWidth`、`minHeight`、`maxHeight` | `Dimension` | 无 | `'auto'`或`'fill'`尺寸的限值，也是组分出或收回空间时的限值。最小值优先于最大值，超出限值的固定尺寸会被拉回限值之间（这两种情况`frameMinMax`都会警告）。 |
| `style` | `string` | 无边框 | 一个[标注框样式](https://postext.dev/zh/docs/configuration-callouts.md#标注框样式)的id：框架用标注框样式绘制（底色、边框、内边距、色条、图标、标签页签、标题）。页签上的文字来自Markdown（`:::frame{slot="dinero" label="El dinero"}`）。标注框设置中决定框在正文中放在哪里的那些（`placement`、`span`、`columns`、`width`、`fixed`……）不适用于框架。 |
| `padding` | `{ top?, right?, bottom?, left? }` | 样式的值 | 逐边覆盖样式的内边距，例如为压在方框上的照片留出空间。 |
| `layer` | `number` | `1` | 绘制顺序。装饰是第0层；负数层画在装饰下面。 |
| `rotate` | `number` | `0` | 角度，顺时针。方框和其中的内容一起旋转。 |
| `verticalAlign` | `'top' \| 'middle' \| 'bottom' \| 'justify'` | `'top'` | 框架比内容高时，内容放在什么位置。 |
| `columns`、`columnGap` | `number`、`Dimension` | `1`、样式的值 | 框架内的分栏。 |
| `exclude` | `'none' \| 'push'` | `'none'` | `'push'`：其他框架的文字绕开这个框架的矩形排。 |
| `flow` | `boolean` | `false` | 框架接收没有指定槽位的内容。 |
| `takes` | `'h1'` … `'h6'` | 无 | 框架接收该级别中第一个没有指定槽位的标题，所以页面标题只需在围栏顶部写`# Title`。 |
| `next` | `string` | 无 | 链接：放不下的内容在同一页上以该值为名的框架中续排。 |
| `overflow.continueIn` | `string` | 无 | 放不下的内容在哪里续排：`'#frame'`（本页的另一个框架，与`next`相同）、`'next-page'`（在这一页之后添加的页面上的同一个框架）或`'template:<id>'`（该模板的一页）。 |
| `overflow.mark` | `string \| false` | 随语言 | 被截断部分末尾的标记。未设置时，使用文档语言的续排词（与拆分表格相同）；`false`不加标记。 |
| `fit.shrinkText` | `boolean` | `true` | 别的办法都无法让内容放下时，可以把文字排小。 |
| `fit.minScale` | `number` | `0.9` | 文字的最小缩放比例，大于0且不超过1（更大的值按1处理）。 |
| `fit.scaleImages`、`fit.imageMinScale` | `boolean`、`number` | `true`、`0.8` | 框架中的图片是否可以缩小，以及最小缩到多少。 |
| `pin` | `boolean` | `false` | 框架从不移动，也不改变大小，用于必须与画面对齐的方框。组绕开它排列。 |
| `grow`、`shrink`、`priority` | `number` | `0` | 框架怎样分享所在组的空间：`grow`分得多余空间的一份，`shrink`允许框架让出空间，`priority`较低的框架先让出。 |
| `optional` | `boolean` | `false` | Markdown没有向框架发送任何内容时，不发出警告。 |
| `crossGutter` | `boolean` | `false` | 在跨页上，框架可以越过订口的带状区域。 |
| `writingMode`、`direction` | `'horizontal-tb' \| 'vertical-rl'`、`'ltr' \| 'rtl'` | 页面的值 | 横排页面上的竖排方框，或者反过来。 |
| `remove` | `boolean` | `false` | 在扩展另一个模板的模板中，或在变体中：去掉继承来的同名框架。 |

### 组

组是一列（从上到下）或一行（从起始到末尾）共享空间、一起移动的框架。在组内，框架自己沿组方向的位置被忽略：组按顺序排列它们，彼此之间留出`gap`。某个框架的文字变长时，其后的框架随之移动。组也是一个方框，框架和装饰可以锚定到它（`'#pagina'`）。

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `name` | `string` | 必填 | 在模板的框架和组中唯一。 |
| `kind` | `'stack' \| 'row'` | `'stack'` | 从上到下，或从起始到末尾。 |
| `frames` | `string[]` | `[]` | 组中的框架，按它们在页面上的顺序排列。一个框架最多属于一个组；不是模板中框架的名称，或已经列出过的框架，会被跳过。 |
| `gap` | `Dimension` | `0` | 组中相邻两个框架之间的距离。 |
| `placement`、`grid` | 同框架 | 其框架的外框 | 组自己的方框。未设置时，取包住其各框架声明方框的外框。 |
| `align` | `'start' \| 'center' \| 'end' \| 'space-between'` | `'start'` | 组中没有框架伸展时，多余空间放在哪里。 |
| `remove` | `boolean` | `false` | 在扩展另一个模板的模板中，或在变体中：去掉继承来的组。 |

### 继承与变体

带`extends`的模板从它所指的模板出发（后者本身也可以扩展另一个模板），只改动自己写出的部分：

- `area`、`header`或`overflowTemplate`这类字段替换父模板的值。`grid`逐键合并。`spread`、`gutter`和`role`也会继承；`id`、`name`和`extends`属于模板自己。
- `frames`和`groups`**按名称**合并：与继承项同名的条目改动它写出的字段，其余保留（`fit`、`overflow`和`padding`逐键合并；`placement`整体替换）；`remove: true`去掉该项；新名称追加在继承项之后。
- 装饰元素以同样的方式按`id`合并；id是新的或没有id的元素会被追加。

`variants.recto`和`variants.verso`的写法与模板相同，合并方式也相同，分别作用于右页和左页。它们在整条`extends`链之后应用，根模板的变体最先应用。跨页同时有两面，所以不应用任何变体。

```ts
pageTemplates: [
  { id: 'tema', frames: [{ name: 'texto', flow: true }, { name: 'margen', width: { value: 30, unit: 'mm' },
      placement: { anchor: { to: 'container', edge: 'top-left' } } }],
    variants: { recto: { frames: [{ name: 'margen', placement: { anchor: { to: 'container', edge: 'top-right' } } }] } } },
  { id: 'tema-sin-margen', extends: 'tema', frames: [{ name: 'margen', remove: true }] },
]
```

沙盒保存模板时，从不丢掉等于默认值的值：在扩展另一个模板的模板中，或在变体中，这个值会覆盖父模板的值。

在代码中，`resolvePageTemplate(config, id, { parity })`返回一个模板：继承链已合并，所在一面的变体已应用，所有默认值已填好，所有引用都已按下文警告所述处理；`validatePageTemplates(config)`返回这些警告。

### 排版实际应用的设置

排版会应用每个框架的位置和尺寸（网格、锚点、`min`/`max`、`verticalAlign`）、样式和`padding`、`columns`、`layer`、`rotate`、`exclude`、`flow`、`takes`、`next`和`overflow.continueIn: "#frame"`，以及模板的`area`、`grid`、`decoration`、`header`、`footer`和`variants`。以下设置会被读取和检查，但还没有应用：组内的空间协调（`grow`、`shrink`、`priority`、`pin`，以及组的`gap`和`align`；组内框架保持各自的位置）、`fit`、`overflow.continueIn: "next-page"`和`overflowTemplate`、`spread`和`crossGutter`、框架的`writingMode`和`direction`，以及模板的`bodyStyle`和`palette`。宽度`'auto'`按`'fill'`处理。

## 注记样式

`labelStyles`属性为写在图片上的注记命名各种外观：地图上的海洋和城镇，示意图的各个部分。注记位于图片的一个锚点上（锚点是随资源保存的一个点，与漫画的说话人相同），使用锚点或注记所指定的样式。

```ts
labelStyles: [
  { id: 'mar', fontStyle: 'italic', textTransform: 'uppercase', letterSpacing: 0.12,
    color: { hex: '#2b6ca3', model: 'hex' } },
  { id: 'ciudad', fontWeight: 600, halo: { color: { hex: '#ffffff', model: 'hex' }, width: { value: 0.6, unit: 'pt' } } },
]
```

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `id` | `string` | 必填 | 锚点和注记使用的名称。 |
| `name` | `string` | 即id | 在沙盒中显示的名称。 |
| `paragraphStyle` | `string` | 无 | 注记借用其字体设置的段落样式；下面的字段覆盖它。 |
| `fontFamily`、`fontSize`、`fontWeight` | `string`、`Dimension`、`number` | 正文的值 | 注记的字体、字号和字重。 |
| `fontStyle` | `'normal' \| 'italic'` | `'normal'` | 正体或斜体。 |
| `color` | `ColorValue` | 正文颜色 | 文字的颜色。 |
| `textTransform` | `'none' \| 'uppercase'` | `'none'` | 大写字母，地图上的海洋和国家名称常这样排。 |
| `letterSpacing` | `number` | `0` | 字母之间额外的间距，以em计。 |
| `halo` | `{ color, width }` | 无 | 文字背后的描边，让文字在繁杂的画面上也能读清。 |
| `background`、`padding` | `ColorValue`、`Dimension` | 无 | 注记背后的底板，以及文字周围留出的空间。 |
| `minScale` | `number` | `0.85` | 注记放不进其宽度时，最多可以缩小到多少。 |

### 锚点的注记字段

图片的锚点（`Resource.anchors`，漫画用来标记说话人的那些点）可以带几个供注记使用的可选字段。这些字段都是相对于图片的比例，所以在每种语言中、在图片的任何裁切下都成立。漫画忽略它们。

| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `width` | `number` | 注记自身的宽度 | 注记的行宽，以图片宽度的比例表示：较长的名称在其中折成两行。 |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | 注记的哪一侧落在锚点上。 |
| `verticalAlign` | `'top' \| 'middle' \| 'bottom'` | `'middle'` | 注记的哪条边落在锚点的高度上。 |
| `rotate` | `number` | `0` | 角度，顺时针，绕锚点旋转：例如让河流的名称顺着河道排。 |
| `style` | `string` | 无 | 一个`labelStyles`的id。注记自己指定样式时，以注记的为准。 |

## 页面模板警告

在其他地方，引擎会忽略它不认识的键。页面模板和注记样式则会被检查，每个问题都是一条配置警告，出现在`postext check`和沙盒的检查面板中。这些警告的`path`是指向出错字段的JSON指针：`/pageTemplates/3/frames/2/style`、`/pageTemplates/0/variants/recto/frames/1/next`、`/resources/4/anchors/0/style`。字段是继承来的时，指针指向写出它的模板。指出不存在的id或名称的警告，还会给出最接近的现有名称。

模板、框架、组、网格、`fit`、`overflow`、变体或注记样式中拼错的键是`unknownConfigKey`；超出某个设置取值范围的词（`area`、`verticalAlign`、`exclude`、`takes`、`kind`、`align`、`writingMode`、`direction`、`fontStyle`、`textTransform`）是`unknownConfigValue`，该设置保留默认值。没有`id`的模板，或没有`name`的框架或组，会被忽略，并在该字段上报告`unknownConfigValue`。

| 警告 | 原因 | 解决办法 |
| --- | --- | --- |
| `templateDuplicate` | 两个模板的`id`相同。使用第一个，后一个被忽略。 | 给其中一个改名，或删除副本。 |
| `templateExtendsCycle` | 沿着`extends`又回到同一个模板（`a → b → a`）。循环中的每个模板，以及扩展其中任何一个的模板，都会被丢弃：没有页面能使用它们。 | 去掉循环中某一个模板的`extends`。 |
| `templateExtendsUnknown` | `extends`指向的id没有对应的模板。该模板单独使用，不继承任何内容。 | 改正id（警告会给出最接近的id），或添加父模板。 |
| `frameDuplicate` | 同一列表中的两个框架或两个组`name`相同，或者某个组与某个框架同名。后一个条目被忽略；锚定到该名称时读取框架。 | 给每个框架和组起各自的名称。要修改继承来的框架，写一个同名的条目即可。 |
| `frameStyleUnknown` | 框架的`style`没有对应的标注框样式。框架绘制时不带方框。 | 改正id，或把该样式加到`calloutStyles`中。 |
| `frameAnchorUnknown` | 框架、组或装饰元素锚定到`'#name'`，而模板中没有这个名称的框架、组或元素；或者框架和组之间互相锚定，形成循环（也包括经过一个自己没有锚定、方框就是其框架外框的组）。该锚点按`'container'`（即区域）处理。 | 锚定到存在的框架或组，或把其中一个锚定到区域，打破循环。 |
| `frameMinMax` | 最小值大于最大值（`minHeight 40mm > maxHeight 30mm`），或者固定的`width`或`height`在限值之外。最小值优先，固定尺寸被拉回限值之间，`used`说明框架实际取的值。单位不同的长度按页面的分辨率比较，em按正文字号换算。 | 让最小值不大于最大值，或去掉与固定尺寸矛盾的那个限值。 |
| `groupFrameUnknown` | 组的`frames`列出了一个不是模板中框架的名称（可能已被子模板或变体去掉）。组跳过它。 | 改正名称，或把它从列表中去掉。 |
| `groupFrameTwice` | 一个框架列在两个组中，或在同一个组中列了两次。一个框架只属于一个组：后面的那次被跳过。 | 只把框架留在它随之移动的那个组中。 |
| `frameChainLoop` | 框架通过`next`或`overflow.continueIn: '#frame'`互相续排，形成循环（`a → b → a`）。闭合循环的那个链接被丢弃，所以链在它前面的框架处结束。 | 让链在最后一个框架处结束：去掉它的`next`，或让它续排到下一页。 |
| `overflowTargetUnknown` | 某个`next`没有指向模板中的框架；某个`overflow.continueIn`没有指向框架（`'#x'`）或模板（`'template:x'`），或者不属于`'#frame'`、`'next-page'`和`'template:<id>'`这三种形式；或者`overflowTemplate`没有指向模板。放不下的内容不会在那里续排。 | 改正名称或id，或写成三种形式之一。 |
| `labelStyleUnknown` | 图片的某个锚点指定的`style`在`labelStyles`中不存在。警告会指出该资源。该锚点上的注记采用默认外观。 | 改正锚点上的样式id，或把该样式加到`labelStyles`中。 |

自页面模板起，配置版本为15（之前写出的包照常加载，不受影响：旧配置中的任何内容都不改变含义）。
