跳到主要内容

章 3.14 · 篇 II · 工艺

配置:组合页面

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

更新于 2026-10-166分钟所属章节: 3 配置enescadefritptzhjaar

简单来说

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

#页面模板

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

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和一个框架的模板就是一整页,这个框架占满版心。其余字段都是可选的:

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

#框架

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

模板有网格时,框架按它的grid单元格放置,否则按它的placement放置,后者与设计元素的定位相同:一个锚点('container'表示区域,'page'、'bleed',或用'#name'表示另一个框架或组)、一条边、一个偏移和一个尺寸。锚定到另一个框架的框架随之移动。两者都没有时,框架占满整个区域。

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

#组

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

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

#继承与变体

带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链之后应用,根模板的变体最先应用。跨页同时有两面,所以不应用任何变体。

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)返回这些警告。

#注记样式

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

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

#锚点的注记字段

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

字段类型默认值说明
widthnumber注记自身的宽度注记的行宽,以图片宽度的比例表示:较长的名称在其中折成两行。
align'start' | 'center' | 'end''center'注记的哪一侧落在锚点上。
verticalAlign'top' | 'middle' | 'bottom''middle'注记的哪条边落在锚点的高度上。
rotatenumber0角度,顺时针,绕锚点旋转:例如让河流的名称顺着河道排。
stylestring无一个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。
templateExtendsUnknownextends指向的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(之前写出的包照常加载,不受影响:旧配置中的任何内容都不改变含义)。