Capítulo 3.14 · Parte II · El oficio
Configuración: páginas compuestas
Plantillas de página para las páginas compuestas: marcos, grupos, decoración, herencia y variantes de recto y verso, los estilos de rótulo de los anclajes de una imagen y los avisos que puede dar una plantilla
En pocas palabras
Esta página trata de las plantillas de página. Una plantilla es una página dibujada en los ajustes: unas cajas con nombre, llamadas marcos, con su sitio, el tamaño hasta el que pueden crecer y su aspecto. El Markdown de una página compuesta llena después esos marcos por su nombre, así que el texto nunca guarda posiciones y los ajustes nunca guardan texto. Una plantilla puede partir de otra y cambiar solo unos pocos marcos, y puede ser distinta en las páginas pares y en las impares. La página recoge también los estilos de rótulo para las palabras que se ponen sobre una imagen, y cada aviso que puede dar una plantilla con la manera de corregirlo.
#Plantillas de página
Una página compuesta es una página que toma su forma de una plantilla de página de la configuración y su contenido del Markdown de un bloque ::::page{template="…"}. La plantilla da nombre a unos rectángulos, llamados marcos; el Markdown manda cada pieza de contenido a un marco por su nombre (su hueco). La geometría nunca aparece en el Markdown, y el texto del contenido nunca aparece en la plantilla. La propiedad pageTemplates guarda las plantillas del libro:
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'] },
],
},
],
};Una plantilla que solo fija su id y un marco es una página entera con ese marco ocupando el área de texto. Todos los demás campos son opcionales:
| Propiedad | Tipo | Por defecto | Descripción |
|---|---|---|---|
id | string | obligatorio | El nombre que pide un bloque de página (template="tema-cajas"). Único entre las plantillas: una segunda con el mismo id se ignora. |
name | string | el id | El nombre que muestra el Sandbox. La composición nunca lo lee. |
extends | string | ninguno | El id de la plantilla de la que parte esta. Ver Herencia y variantes. |
area | 'content' | 'trim' | 'bleed' | 'content' | El rectángulo al que se refieren las posiciones de los marcos. 'content' es el área de texto de la página, reflejada en pares e impares como el texto corrido; 'trim' es la página al corte; 'bleed', la página con su sangrado. |
grid | { columns, rows, columnGap?, rowGap? } | ninguna | Divide el área en celdas, de modo que un marco o un grupo se puede colocar por celda (grid: { col: 2, row: 1, cols: 2 }, contando desde 1) en lugar de por placement. Los espacios valen 0 por defecto. |
frames | FrameConfig[] | [] | Los marcos, en el orden de la plantilla: el orden en que se miden y el que deshace los empates. Ver Marcos. |
groups | FrameGroupConfig[] | [] | Pilas y filas de marcos que se reparten el espacio. Ver Grupos. |
decoration | DesignSlot | ninguna | Imágenes, filetes y cajas que se pintan con la página, en el lenguaje de elementos de los encabezados y pies. Los elementos de texto solo llevan marcadores ({pageNumber}, {chapterTitle}, {attr.x} para un atributo del bloque de página). Un elemento se puede anclar a un marco o a un grupo ('#dinero') y entonces se mueve con él. La decoración se coloca después de los marcos y nunca empuja el texto. |
header, footer | DesignSlot | false | heredados | La cabecera y el folio de la página. Sin fijar, la página conserva los de su capítulo; false los oculta. |
spread | boolean | false | La plantilla describe dos páginas enfrentadas compuestas como un solo pliego. Una doble página empieza siempre en una página par en un libro de izquierda a derecha (en una impar en uno de derecha a izquierda), con una página en blanco delante cuando hace falta. |
gutter | Dimension | 0 | En una doble página, la franja junto al lomo que los marcos dejan libre, salvo que fijen crossGutter: true. |
variants | { recto?, verso? } | ninguna | Cambios para una página impar (recto) o una par (verso), escritos como la propia plantilla. El lado es el físico, así que funcionan tanto los libros de izquierda a derecha como los de derecha a izquierda. |
overflowTemplate | string | esta plantilla | La plantilla de las páginas que se añaden cuando un marco continúa en la página siguiente (overflow.). Sin fijar, la página añadida usa esta plantilla con solo los marcos que continúan llenos. |
bodyStyle | string | ninguno | Un estilo de párrafo para el texto de la página, como el que tienen los estilos de encabezado. |
palette | string | ninguna | Una paleta propia para la página, como la que tienen los estilos de encabezado y las partes. |
role | string | 'composed' | El papel de página que ven los elementos de diseño con un filtro pages. |
#Marcos
Un marco es un rectángulo con nombre que recibe contenido compuesto. Su name es el hueco que pide el Markdown, y es único en su plantilla (un grupo tampoco puede llevar el nombre de un marco, porque los anclajes nombran a los dos de la misma manera).
Un marco se coloca por su celda de grid cuando la plantilla tiene rejilla y, si no, por su placement, que funciona como el de un elemento de diseño: un anclaje ('container' es el área, 'page', 'bleed', o '#name' para otro marco o grupo), un borde, un desplazamiento y un tamaño. Un marco anclado a otro se mueve con él. Sin ninguno de los dos, el marco ocupa toda el área.
| Propiedad | Tipo | Por defecto | Descripción |
|---|---|---|---|
name | string | obligatorio | El nombre del hueco. |
placement | ElementPlacement | toda el área | Anclaje, borde, desplazamiento y tamaño, como en el posicionamiento de elementos. Se ignora cuando una celda de la rejilla coloca el marco. |
grid | { col, row, cols?, rows? } | ninguna | Una celda de la rejilla de la plantilla, contando desde 1, que abarca cols × rows celdas (1 por defecto). Una celda que se sale de la rejilla vuelve a meterse en ella. Se ignora cuando la plantilla no tiene rejilla. |
width, height | Dimension | { percent } | 'auto' | 'fill' | 'fill', 'auto' | Una longitud fija, un porcentaje del área (de 0 a 100), 'auto' (el tamaño propio del contenido) o 'fill' (lo que queda hasta el borde al que está anclado el marco). Sin fijar, el marco lee placement.size y después el valor por defecto: todo el ancho y tan alto como su contenido. Un marco de texto necesita una medida, así que un ancho 'auto' solo tiene sentido en un marco que lleva una sola imagen (el ancho de la imagen a la altura del marco). Un valor que el motor no puede usar (una longitud negativa, una unidad desconocida) se lee como el valor por defecto. |
minWidth, maxWidth, minHeight, maxHeight | Dimension | ninguno | Límites para un tamaño 'auto' o 'fill' y para el espacio que un grupo da o quita. El mínimo se impone al máximo, y un tamaño fijo fuera de ellos se lleva dentro (frameMinMax avisa de los dos casos). |
style | string | sin caja | El id de un estilo de aviso: los marcos se dibujan con estilos de aviso (fondo, borde, relleno, banda, icono, pestaña de rótulo, título). Las palabras de una pestaña vienen del Markdown (:::frame{slot="dinero" label="El dinero"}). Los ajustes de aviso que dicen dónde va una caja en el texto corrido (placement, span, columns, width, fixed…) no se aplican a un marco. |
padding | { top?, right?, bottom?, left? } | el del estilo | Sustituye el relleno del estilo lado por lado: sitio para las fotos que van encima de una caja, por ejemplo. |
layer | number | 1 | Orden de pintado. La decoración es la capa 0; una capa negativa se pinta debajo de ella. |
rotate | number | 0 | Grados, en el sentido de las agujas del reloj. La caja y su contenido giran juntos. |
verticalAlign | 'top' | 'middle' | 'bottom' | 'justify' | 'top' | Dónde se sitúa el contenido en un marco más alto que él. |
columns, columnGap | number, Dimension | 1, el del estilo | Columnas dentro del marco. |
exclude | 'none' | 'push' | 'none' | 'push': el texto de los demás marcos rodea el rectángulo de este. |
flow | boolean | false | El marco recibe el contenido que no nombra ningún hueco. |
takes | 'h1' … 'h6' | ninguno | El marco recibe el primer encabezado de ese nivel que no nombra ningún hueco, así que el título de una página es solo un # Título al principio del bloque. |
next | string | ninguno | Una cadena: el contenido que no cabe sigue en el marco de ese nombre, en la misma página. |
overflow.continueIn | string | ninguno | Dónde sigue el contenido que no cabe: '#frame' (otro marco de la página, como next), 'next-page' (el mismo marco en una página que se añade detrás de esta) o 'template:<id>' (una página de esa plantilla). |
overflow.mark | string | false | la del idioma | La marca al final de un trozo cortado. Sin fijar, la palabra de continuación del idioma del documento (la misma que usan las tablas partidas); false no pone ninguna. |
fit.shrinkText | boolean | true | Cuando nada más consigue que el contenido quepa, su texto se puede componer más pequeño. |
fit.minScale | number | 0.9 | La escala de texto más pequeña, mayor que 0 y como mucho 1 (un valor mayor se lee como 1). |
fit.scaleImages, fit.imageMinScale | boolean, number | true, 0.8 | Si las imágenes del marco se pueden componer más pequeñas, y hasta qué escala. |
pin | boolean | false | El marco nunca se mueve ni cambia de tamaño, para una caja que debe quedar alineada con la ilustración. Un grupo se coloca alrededor de él. |
grow, shrink, priority | number | 0 | Cómo comparte el marco el espacio de su grupo: grow toma una parte del espacio sobrante, shrink deja que el marco devuelva espacio, y los marcos con una priority más baja lo ceden primero. |
optional | boolean | false | Ningún aviso cuando el Markdown no manda nada al marco. |
crossGutter | boolean | false | En una doble página, el marco puede cruzar la franja del lomo. |
writingMode, direction | 'horizontal-tb' | 'vertical-rl', 'ltr' | 'rtl' | los de la página | Una caja vertical en una página horizontal, o al revés. |
remove | boolean | false | En una plantilla que extiende otra, o en una variante: quita el marco heredado con ese nombre. |
#Grupos
Un grupo es una pila (de arriba abajo) o una fila (del principio al final) de marcos que se reparten el espacio y se mueven juntos. Dentro de un grupo se ignora la posición propia de los marcos en la dirección del grupo: el grupo los coloca en orden, con gap entre ellos. Cuando crece el texto de un marco, los marcos que van detrás se desplazan. Un grupo es una caja a la que pueden anclarse marcos y decoración ('#pagina').
| Propiedad | Tipo | Por defecto | Descripción |
|---|---|---|---|
name | string | obligatorio | Único entre los marcos y grupos de la plantilla. |
kind | 'stack' | 'row' | 'stack' | De arriba abajo, o del principio al final. |
frames | string[] | [] | Los marcos del grupo, en su orden en la página. Un marco pertenece como mucho a un grupo; un nombre que no es ningún marco de la plantilla, o un marco ya listado, se salta. |
gap | Dimension | 0 | El espacio entre dos marcos del grupo. |
placement, grid | como en los marcos | la caja de sus marcos | La caja propia del grupo. Sin fijar, la caja que rodea las cajas declaradas de sus marcos. |
align | 'start' | 'center' | 'end' | 'space-between' | 'start' | Adónde va el espacio sobrante cuando ningún marco del grupo crece. |
remove | boolean | false | En una plantilla que extiende otra, o en una variante: quita el grupo heredado. |
#Herencia y variantes
Una plantilla con extends parte de la plantilla que nombra (que a su vez puede extender otra) y cambia solo lo que escribe:
- Un campo como
area,headeruoverflowTemplatesustituye al del padre.gridse combina clave a clave.spread,gutteryroletambién se heredan;id,nameyextendsson propios de la plantilla. framesygroupsse combinan por nombre: una entrada con el nombre de una heredada cambia los campos que escribe y conserva el resto (fit,overflowypaddingse combinan clave a clave;placementse sustituye entero);remove: truela quita; un nombre nuevo se añade detrás de los heredados.- Los elementos de decoración se combinan por
idde la misma manera; un elemento con un id nuevo, o sin id, se añade.
variants.recto y variants.verso se escriben como una plantilla y se combinan igual, en una página impar o en una par. Se aplican después de toda la cadena de extends, primero la variante de la raíz. Una doble página tiene los dos lados, así que no se le aplica ninguna variante.
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 }] },
]Cuando el Sandbox guarda una plantilla, nunca descarta un valor igual al valor por defecto: en una plantilla que extiende otra, o en una variante, ese valor sustituye al del padre.
Desde código, resolvePageTemplate(config, id, { parity }) devuelve una plantilla con su cadena combinada, la variante del lado aplicada, todos los valores por defecto rellenos y cada referencia resuelta como describen los avisos de abajo; validatePageTemplates(config) devuelve los avisos.
#Estilos de rótulo
La propiedad labelStyles da nombre a los aspectos de los rótulos que se ponen sobre una imagen: los mares y las ciudades de un mapa, las partes de un esquema. Un rótulo se sitúa en un anclaje de la imagen (un punto guardado con el recurso, como los hablantes de un cómic) y toma el estilo que nombra el anclaje o el rótulo.
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' } } },
]| Propiedad | Tipo | Por defecto | Descripción |
|---|---|---|---|
id | string | obligatorio | El nombre que usan los anclajes y los rótulos. |
name | string | el id | El nombre que muestra el Sandbox. |
paragraphStyle | string | ninguno | Un estilo de párrafo del que el rótulo toma la letra; los campos de abajo lo sustituyen. |
fontFamily, fontSize, fontWeight | string, Dimension, number | los del texto general | La letra del rótulo. |
fontStyle | 'normal' | 'italic' | 'normal' | Redonda o cursiva. |
color | ColorValue | el del texto general | El color de las letras. |
textTransform | 'none' | 'uppercase' | 'none' | Mayúsculas, como las que ponen los mapas a mares y países. |
letterSpacing | number | 0 | Espacio añadido entre letras, en ems. |
halo | { color, width } | ninguno | Un contorno detrás de las letras, para que se lean sobre una imagen cargada. |
background, padding | ColorValue, Dimension | ninguno | Una placa detrás del rótulo, y el espacio alrededor de su texto. |
minScale | number | 0.85 | Hasta qué tamaño se puede reducir un rótulo cuando no cabe en su ancho. |
#Campos de rótulo de un anclaje
Los anclajes de una imagen (Resource.anchors, los puntos que usan los cómics para los hablantes) admiten unos pocos campos opcionales para rótulos. Son fracciones de la imagen, así que valen en todos los idiomas y con cualquier recorte de la imagen. Los cómics los ignoran.
| Campo | Tipo | Por defecto | Descripción |
|---|---|---|---|
width | number | el propio del rótulo | La medida del rótulo, como fracción del ancho de la imagen: un nombre largo se parte en dos líneas dentro de ella. |
align | 'start' | 'center' | 'end' | 'center' | Qué lado del rótulo queda en el punto del anclaje. |
verticalAlign | 'top' | 'middle' | 'bottom' | 'middle' | Qué borde del rótulo queda a la altura del anclaje. |
rotate | number | 0 | Grados, en el sentido de las agujas del reloj, alrededor del punto del anclaje: el nombre de un río a lo largo de su curso. |
style | string | ninguno | Un id de labelStyles. Un rótulo que nombra su propio estilo lo sustituye. |
#Avisos de las plantillas de página
En el resto de la configuración, el motor ignora una clave que no conoce. Las plantillas de página y los estilos de rótulo, en cambio, se comprueban, y cada problema es un aviso de configuración en postext check y en el panel Revisión del Sandbox. El campo path de estos avisos es un puntero JSON al campo que falla: /pageTemplates/3/frames/2/style, /pageTemplates/0/variants/recto/frames/1/next, /resources/4/anchors/0/style. Cuando el campo es heredado, el puntero nombra la plantilla que lo escribió. Un aviso que nombra un id o un nombre que no existe propone además el existente que más se le parece.
Una clave mal escrita de una plantilla, un marco, un grupo, una rejilla, fit, overflow, una variante o un estilo de rótulo es un unknownConfigKey; una palabra fuera de las opciones de un ajuste (area, verticalAlign, exclude, takes, kind, align, writingMode, direction, fontStyle, textTransform) es un unknownConfigValue, y el ajuste conserva su valor por defecto. Una plantilla sin id, o un marco o un grupo sin name, se ignora con un unknownConfigValue en ese campo.
| Aviso | Causa | Solución |
|---|---|---|
templateDuplicate | Dos plantillas tienen el mismo id. Se usa la primera y la posterior se ignora. | Cambia el nombre de una de ellas, o borra la copia. |
templateExtendsCycle | Siguiendo extends se vuelve a la misma plantilla (a → b → a). Todas las plantillas del ciclo, y todas las que extienden alguna de ellas, se descartan: ninguna página puede usarlas. | Quita el extends de una plantilla del ciclo. |
templateExtendsUnknown | extends nombra un id que no tiene ninguna plantilla. La plantilla se usa sola, sin heredar nada. | Corrige el id (el aviso propone el más parecido), o añade la plantilla padre. |
frameDuplicate | Dos marcos, o dos grupos, de una misma lista tienen el mismo name, o un grupo tiene el nombre de un marco. La entrada posterior se ignora; un anclaje a ese nombre lee el marco. | Da a cada marco y a cada grupo un nombre propio. Para cambiar un marco heredado, escribe una sola entrada con su nombre. |
frameStyleUnknown | El style de un marco no nombra ningún estilo de aviso. El marco se dibuja sin caja. | Corrige el id, o añade el estilo a calloutStyles. |
frameAnchorUnknown | Un marco, un grupo o un elemento de decoración está anclado a '#name' y la plantilla no tiene ningún marco, grupo ni elemento con ese nombre; o hay marcos y grupos anclados entre sí en un ciclo (también a través de un grupo sin anclaje propio, cuya caja es la de sus marcos). El anclaje se lee como 'container', el área. | Ancla a un marco o a un grupo que exista, o rompe el ciclo anclando uno de ellos al área. |
frameMinMax | Un mínimo es mayor que su máximo (minHeight 40mm > maxHeight 30mm), o un width o un height fijo queda fuera de ellos. Se impone el mínimo, un tamaño fijo se lleva dentro de los límites y used dice qué ocupa el marco. Las longitudes en unidades distintas se comparan a la resolución de la página, y los ems al cuerpo del texto general. | Haz que el mínimo no supere al máximo, o quita el límite que contradice el tamaño fijo. |
groupFrameUnknown | Los frames de un grupo incluyen un nombre que no es ningún marco de la plantilla (quizá lo quitó una plantilla hija o una variante). El grupo se lo salta. | Corrige el nombre, o quítalo de la lista. |
groupFrameTwice | Un marco figura en dos grupos, o dos veces en uno. Un marco pertenece a un solo grupo: la aparición posterior se salta. | Deja el marco en el grupo con el que se mueve. |
frameChainLoop | Unos marcos continúan unos en otros en un ciclo a través de next o de overflow. (a → b → a). El enlace que cierra el ciclo se descarta, así que la cadena termina en el marco anterior a él. | Termina la cadena en su último marco: quítale el next, o haz que continúe en la página siguiente. |
overflowTargetUnknown | Un next no nombra ningún marco de la plantilla; un overflow.continueIn no nombra ningún marco ('#x') ni ninguna plantilla ('template:x'), o no tiene ninguna de las formas '#frame', 'next-page' y 'template:<id>'; o overflowTemplate no nombra ninguna plantilla. El contenido que no cabe no continúa allí. | Corrige el nombre o el id, o escribe una de las tres formas. |
labelStyleUnknown | Un anclaje de una imagen nombra un style que labelStyles no tiene. El aviso nombra el recurso. Los rótulos de ese anclaje toman el aspecto por defecto. | Corrige el id del estilo en el anclaje, o añade el estilo a labelStyles. |
La versión de la configuración es la 15 desde las plantillas de página (los paquetes escritos antes se siguen cargando sin cambios: nada de lo que dice una configuración anterior cambia de significado).