postext 1.34.0 → 1.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/bundle.test.js +33 -1
- package/dist/__tests__/bundle.test.js.map +1 -1
- package/dist/__tests__/codeBlocks.test.js +88 -0
- package/dist/__tests__/codeBlocks.test.js.map +1 -1
- package/dist/__tests__/composed/fixtures/page8.d.ts +20 -0
- package/dist/__tests__/composed/fixtures/page8.d.ts.map +1 -0
- package/dist/__tests__/composed/fixtures/page8.js +159 -0
- package/dist/__tests__/composed/fixtures/page8.js.map +1 -0
- package/dist/__tests__/composed/layout.test.d.ts +2 -0
- package/dist/__tests__/composed/layout.test.d.ts.map +1 -0
- package/dist/__tests__/composed/layout.test.js +387 -0
- package/dist/__tests__/composed/layout.test.js.map +1 -0
- package/dist/__tests__/composed/slotChecks.test.js +6 -3
- package/dist/__tests__/composed/slotChecks.test.js.map +1 -1
- package/dist/__tests__/composedPaint.test.d.ts +2 -0
- package/dist/__tests__/composedPaint.test.d.ts.map +1 -0
- package/dist/__tests__/composedPaint.test.js +191 -0
- package/dist/__tests__/composedPaint.test.js.map +1 -0
- package/dist/__tests__/defaults/stripRoundTrip.test.js +3 -2
- package/dist/__tests__/defaults/stripRoundTrip.test.js.map +1 -1
- package/dist/__tests__/exports.test.js +12 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/highlightMarks.test.d.ts +2 -0
- package/dist/__tests__/highlightMarks.test.d.ts.map +1 -0
- package/dist/__tests__/highlightMarks.test.js +169 -0
- package/dist/__tests__/highlightMarks.test.js.map +1 -0
- package/dist/__tests__/tocPartLeader.test.d.ts +2 -0
- package/dist/__tests__/tocPartLeader.test.d.ts.map +1 -0
- package/dist/__tests__/tocPartLeader.test.js +133 -0
- package/dist/__tests__/tocPartLeader.test.js.map +1 -0
- package/dist/bundle/api.d.ts +8 -0
- package/dist/bundle/api.d.ts.map +1 -1
- package/dist/bundle/api.js +6 -3
- package/dist/bundle/api.js.map +1 -1
- package/dist/bundle/codec.d.ts +5 -0
- package/dist/bundle/codec.d.ts.map +1 -1
- package/dist/bundle/codec.js +4 -0
- package/dist/bundle/codec.js.map +1 -1
- package/dist/bundle/index.d.ts +2 -2
- package/dist/bundle/index.d.ts.map +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/index.js.map +1 -1
- package/dist/bundle/manifest.d.ts +14 -0
- package/dist/bundle/manifest.d.ts.map +1 -1
- package/dist/bundle/manifest.js +13 -0
- package/dist/bundle/manifest.js.map +1 -1
- package/dist/bundle/types.d.ts +7 -0
- package/dist/bundle/types.d.ts.map +1 -1
- package/dist/canvas-backend/annotations.d.ts +6 -0
- package/dist/canvas-backend/annotations.d.ts.map +1 -1
- package/dist/canvas-backend/annotations.js +39 -0
- package/dist/canvas-backend/annotations.js.map +1 -1
- package/dist/canvas-backend/blockRender.d.ts.map +1 -1
- package/dist/canvas-backend/blockRender.js +6 -1
- package/dist/canvas-backend/blockRender.js.map +1 -1
- package/dist/canvas-backend/composed.d.ts +15 -0
- package/dist/canvas-backend/composed.d.ts.map +1 -0
- package/dist/canvas-backend/composed.js +41 -0
- package/dist/canvas-backend/composed.js.map +1 -0
- package/dist/canvas-backend/index.d.ts.map +1 -1
- package/dist/canvas-backend/index.js +15 -1
- package/dist/canvas-backend/index.js.map +1 -1
- package/dist/canvas-backend/renderResourceBlock.d.ts.map +1 -1
- package/dist/canvas-backend/renderResourceBlock.js +4 -1
- package/dist/canvas-backend/renderResourceBlock.js.map +1 -1
- package/dist/composed/geometry.d.ts +108 -0
- package/dist/composed/geometry.d.ts.map +1 -0
- package/dist/composed/geometry.js +241 -0
- package/dist/composed/geometry.js.map +1 -0
- package/dist/composed/paint.d.ts +41 -0
- package/dist/composed/paint.d.ts.map +1 -0
- package/dist/composed/paint.js +83 -0
- package/dist/composed/paint.js.map +1 -0
- package/dist/composed/types.d.ts +1 -44
- package/dist/composed/types.d.ts.map +1 -1
- package/dist/composed/wrap.d.ts +52 -0
- package/dist/composed/wrap.d.ts.map +1 -0
- package/dist/composed/wrap.js +234 -0
- package/dist/composed/wrap.js.map +1 -0
- package/dist/configWarnings.js +1 -1
- package/dist/configWarnings.js.map +1 -1
- package/dist/defaults/codeStyle.d.ts.map +1 -1
- package/dist/defaults/codeStyle.js +1 -0
- package/dist/defaults/codeStyle.js.map +1 -1
- package/dist/defaults/index.d.ts +1 -0
- package/dist/defaults/index.d.ts.map +1 -1
- package/dist/defaults/index.js +9 -0
- package/dist/defaults/index.js.map +1 -1
- package/dist/defaults/markStyles.d.ts +23 -0
- package/dist/defaults/markStyles.d.ts.map +1 -0
- package/dist/defaults/markStyles.js +80 -0
- package/dist/defaults/markStyles.js.map +1 -0
- package/dist/defaults/shared.d.ts.map +1 -1
- package/dist/defaults/shared.js +12 -0
- package/dist/defaults/shared.js.map +1 -1
- package/dist/design/bidiText.d.ts +4 -1
- package/dist/design/bidiText.d.ts.map +1 -1
- package/dist/design/bidiText.js +5 -2
- package/dist/design/bidiText.js.map +1 -1
- package/dist/design/layout.d.ts +10 -0
- package/dist/design/layout.d.ts.map +1 -1
- package/dist/design/layout.js +110 -8
- package/dist/design/layout.js.map +1 -1
- package/dist/design/placeholders.d.ts +28 -2
- package/dist/design/placeholders.d.ts.map +1 -1
- package/dist/design/placeholders.js +24 -1
- package/dist/design/placeholders.js.map +1 -1
- package/dist/design/richText.d.ts +2 -0
- package/dist/design/richText.d.ts.map +1 -1
- package/dist/design/richText.js.map +1 -1
- package/dist/highlightMarks.d.ts +33 -0
- package/dist/highlightMarks.d.ts.map +1 -0
- package/dist/highlightMarks.js +209 -0
- package/dist/highlightMarks.js.map +1 -0
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +65 -8
- package/dist/html-backend.js.map +1 -1
- package/dist/htmlAnnotations.d.ts +5 -0
- package/dist/htmlAnnotations.d.ts.map +1 -1
- package/dist/htmlAnnotations.js +16 -0
- package/dist/htmlAnnotations.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/knuthPlass/richAdapter.d.ts +3 -0
- package/dist/knuthPlass/richAdapter.d.ts.map +1 -1
- package/dist/knuthPlass/richAdapter.js +3 -2
- package/dist/knuthPlass/richAdapter.js.map +1 -1
- package/dist/measure/cache.js +3 -3
- package/dist/measure/cache.js.map +1 -1
- package/dist/measure/chipEdges.d.ts +13 -1
- package/dist/measure/chipEdges.d.ts.map +1 -1
- package/dist/measure/chipEdges.js +59 -1
- package/dist/measure/chipEdges.js.map +1 -1
- package/dist/measure/links.d.ts.map +1 -1
- package/dist/measure/links.js +78 -5
- package/dist/measure/links.js.map +1 -1
- package/dist/measure/rich.d.ts +3 -0
- package/dist/measure/rich.d.ts.map +1 -1
- package/dist/measure/rich.js +19 -4
- package/dist/measure/rich.js.map +1 -1
- package/dist/parse/inlineFormatting.d.ts +14 -0
- package/dist/parse/inlineFormatting.d.ts.map +1 -1
- package/dist/parse/inlineFormatting.js +104 -5
- package/dist/parse/inlineFormatting.js.map +1 -1
- package/dist/parse/links.js +1 -1
- package/dist/parse/links.js.map +1 -1
- package/dist/parse/softBreaks.js +1 -1
- package/dist/parse/softBreaks.js.map +1 -1
- package/dist/parse/sourceMapping.d.ts.map +1 -1
- package/dist/parse/sourceMapping.js +19 -3
- package/dist/parse/sourceMapping.js.map +1 -1
- package/dist/parse/types.d.ts +24 -0
- package/dist/parse/types.d.ts.map +1 -1
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +156 -300
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/calloutLayout.d.ts +33 -0
- package/dist/pipeline/calloutLayout.d.ts.map +1 -1
- package/dist/pipeline/calloutLayout.js +91 -2
- package/dist/pipeline/calloutLayout.js.map +1 -1
- package/dist/pipeline/codeInline.d.ts +10 -1
- package/dist/pipeline/codeInline.d.ts.map +1 -1
- package/dist/pipeline/codeInline.js +96 -15
- package/dist/pipeline/codeInline.js.map +1 -1
- package/dist/pipeline/composedLayout.d.ts +88 -0
- package/dist/pipeline/composedLayout.d.ts.map +1 -0
- package/dist/pipeline/composedLayout.js +320 -0
- package/dist/pipeline/composedLayout.js.map +1 -0
- package/dist/pipeline/config.d.ts.map +1 -1
- package/dist/pipeline/config.js +2 -1
- package/dist/pipeline/config.js.map +1 -1
- package/dist/pipeline/contentWarnings.d.ts.map +1 -1
- package/dist/pipeline/contentWarnings.js +32 -1
- package/dist/pipeline/contentWarnings.js.map +1 -1
- package/dist/pipeline/floatSlots.js +1 -1
- package/dist/pipeline/floatSlots.js.map +1 -1
- package/dist/pipeline/frameLayout.d.ts +120 -0
- package/dist/pipeline/frameLayout.d.ts.map +1 -0
- package/dist/pipeline/frameLayout.js +279 -0
- package/dist/pipeline/frameLayout.js.map +1 -0
- package/dist/pipeline/headerFooter.d.ts.map +1 -1
- package/dist/pipeline/headerFooter.js +48 -3
- package/dist/pipeline/headerFooter.js.map +1 -1
- package/dist/pipeline/pageRoles.d.ts +1 -0
- package/dist/pipeline/pageRoles.d.ts.map +1 -1
- package/dist/pipeline/pageRoles.js +3 -0
- package/dist/pipeline/pageRoles.js.map +1 -1
- package/dist/pipeline/placement.js +1 -1
- package/dist/pipeline/placement.js.map +1 -1
- package/dist/pipeline/resourceLayout.d.ts.map +1 -1
- package/dist/pipeline/resourceLayout.js +5 -3
- package/dist/pipeline/resourceLayout.js.map +1 -1
- package/dist/pipeline/tabs.js +1 -1
- package/dist/pipeline/tabs.js.map +1 -1
- package/dist/types.d.ts +69 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +169 -1
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stand-in for page 8 of a primary-school social-science textbook (#855):
|
|
3
|
+
* a topic page with a tabbed title, running text with key words, three
|
|
4
|
+
* boxes with tabs (blue, green, yellow), each with its pictures beside it.
|
|
5
|
+
* The words are written for this fixture (Spanish and English); the form is
|
|
6
|
+
* a page template in the configuration, the content all in the Markdown.
|
|
7
|
+
* The pictures are solid placeholders (`fixtures/page8/*.png`).
|
|
8
|
+
*/
|
|
9
|
+
import type { PostextConfig, Resource, ResourceType } from '../../../types';
|
|
10
|
+
export declare const PAGE8_TYPES: ResourceType[];
|
|
11
|
+
/** The fixture's pictures: solid placeholders of the photos' proportions. */
|
|
12
|
+
export declare const PAGE8_RESOURCES: Resource[];
|
|
13
|
+
/** The template and styles: form only, no words but the page's folio. */
|
|
14
|
+
export declare const PAGE8_CONFIG: PostextConfig;
|
|
15
|
+
export declare function page8Fixture(locale: 'es' | 'en'): {
|
|
16
|
+
markdown: string;
|
|
17
|
+
config: PostextConfig;
|
|
18
|
+
resources: Resource[];
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=page8.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page8.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/composed/fixtures/page8.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAoG5E,eAAO,MAAM,WAAW,EAAE,YAAY,EAErC,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,eAAe,EAAE,QAAQ,EAarB,CAAC;AAElB,yEAAyE;AACzE,eAAO,MAAM,YAAY,EAAE,aAwC1B,CAAC;AAEF,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,QAAQ,EAAE,CAAA;CAAE,CAEpH"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const mm = (value) => ({ value, unit: 'mm' });
|
|
2
|
+
const pt = (value) => ({ value, unit: 'pt' });
|
|
3
|
+
const hex = (h) => ({ hex: h, model: 'hex' });
|
|
4
|
+
const MARKDOWN = {
|
|
5
|
+
es: `::::page{template="tema-cajas" fondo="banda-tema" tema="Retos del planeta"}
|
|
6
|
+
# El dinero y la vida de cada día
|
|
7
|
+
|
|
8
|
+
## Lo que compramos y vendemos
|
|
9
|
+
|
|
10
|
+
Cada familia necesita :chip[alimentos]{style="clave"}, ropa y una casa. Los consigue en tiendas y mercados, que a su vez los reciben de quienes los cultivan o los fabrican. Este ir y venir de cosas y de trabajo es la :chip[actividad económica.]{style="clave"}
|
|
11
|
+
|
|
12
|
+
- Las :chip[tiendas y empresas.]{style="clave"} Preparan productos y servicios y los ofrecen a un precio.
|
|
13
|
+
- Las :chip[familias.]{style="clave"} Pagan esos productos con lo que ganan trabajando cada mes.
|
|
14
|
+
|
|
15
|
+
:::frame{slot="dinero" label="Para qué sirve el dinero"}
|
|
16
|
+
El dinero nos deja cambiar nuestro trabajo por las cosas que necesitamos, porque todo el mundo acepta su valor.
|
|
17
|
+
|
|
18
|
+
Antes casi todo se pagaba con monedas y billetes, en **efectivo**. Hoy también pagamos con **tarjetas**, con el teléfono o con una **transferencia** desde el banco.
|
|
19
|
+
:::
|
|
20
|
+
|
|
21
|
+
::resource{id="foto-dinero" slot="dinero-fotos"}
|
|
22
|
+
|
|
23
|
+
:::frame{slot="gasto" label="En qué lo gastamos"}
|
|
24
|
+
Lo que una familia paga para vivir es su gasto. Hay varias clases:
|
|
25
|
+
|
|
26
|
+
- **Fijo:** se repite cada mes, como el alquiler o la luz.
|
|
27
|
+
- **Variable:** cambia de un mes a otro, como la comida o la ropa.
|
|
28
|
+
- **Imprevisto:** llega sin avisar, como arreglar la bicicleta.
|
|
29
|
+
:::
|
|
30
|
+
|
|
31
|
+
::resource{id="foto-gasto" slot="gasto-fotos"}
|
|
32
|
+
|
|
33
|
+
:::frame{slot="ahorro" label="Pedir prestado y ahorrar"}
|
|
34
|
+
Para comprar algo muy caro, como un coche, a veces hay que pedir un **préstamo** al banco y devolverlo poco a poco. Si gastamos menos de lo que ganamos, podemos **ahorrar** para más adelante. Un **presupuesto** ayuda a decidir cada mes cuánto guardar.
|
|
35
|
+
:::
|
|
36
|
+
|
|
37
|
+
::resource{id="foto-ahorro" slot="ahorro-fotos"}
|
|
38
|
+
::::
|
|
39
|
+
`,
|
|
40
|
+
en: `::::page{template="tema-cajas" fondo="banda-tema" tema="Planet challenges"}
|
|
41
|
+
# Money and everyday life
|
|
42
|
+
|
|
43
|
+
## What we buy and sell
|
|
44
|
+
|
|
45
|
+
Every family needs :chip[food]{style="clave"}, clothes and a home. They get them in shops and markets, which in turn get them from the people who grow or make them. This coming and going of goods and work is :chip[economic activity.]{style="clave"}
|
|
46
|
+
|
|
47
|
+
- :chip[Shops and companies.]{style="clave"} They make goods and services and offer them at a price.
|
|
48
|
+
- :chip[Families.]{style="clave"} They pay for them with what they earn at work each month.
|
|
49
|
+
|
|
50
|
+
:::frame{slot="dinero" label="What money is for"}
|
|
51
|
+
Money lets us swap our work for the things we need, because everybody accepts what it is worth.
|
|
52
|
+
|
|
53
|
+
Not long ago almost everything was paid in coins and notes, in **cash**. Today we also pay with **cards**, with a phone or with a **bank transfer**.
|
|
54
|
+
:::
|
|
55
|
+
|
|
56
|
+
::resource{id="foto-dinero" slot="dinero-fotos"}
|
|
57
|
+
|
|
58
|
+
:::frame{slot="gasto" label="What we spend it on"}
|
|
59
|
+
What a family pays to live is its spending. There are several kinds:
|
|
60
|
+
|
|
61
|
+
- **Fixed:** it comes back every month, like the rent or the electricity.
|
|
62
|
+
- **Variable:** it changes from month to month, like food or clothes.
|
|
63
|
+
- **Unexpected:** it arrives without warning, like mending the bicycle.
|
|
64
|
+
:::
|
|
65
|
+
|
|
66
|
+
::resource{id="foto-gasto" slot="gasto-fotos"}
|
|
67
|
+
|
|
68
|
+
:::frame{slot="ahorro" label="Borrowing and saving"}
|
|
69
|
+
To buy something very dear, like a car, a family may ask the bank for a **loan** and pay it back little by little. When we spend less than we earn, we can **save** for later. A **budget** helps us decide each month how much to keep.
|
|
70
|
+
:::
|
|
71
|
+
|
|
72
|
+
::resource{id="foto-ahorro" slot="ahorro-fotos"}
|
|
73
|
+
::::
|
|
74
|
+
`,
|
|
75
|
+
};
|
|
76
|
+
const box = (id, fill, ring, tabSide) => ({
|
|
77
|
+
id,
|
|
78
|
+
backgroundEnabled: true,
|
|
79
|
+
background: hex(fill),
|
|
80
|
+
border: { enabled: false },
|
|
81
|
+
borderRadius: mm(1.5),
|
|
82
|
+
padding: { top: mm(3), right: mm(4), bottom: mm(3.5), left: mm(4) },
|
|
83
|
+
label: {
|
|
84
|
+
position: tabSide,
|
|
85
|
+
background: hex('#ffffff'),
|
|
86
|
+
color: hex('#231f20'),
|
|
87
|
+
fontSize: pt(11),
|
|
88
|
+
height: mm(6.4),
|
|
89
|
+
paddingX: mm(2.5),
|
|
90
|
+
offset: mm(6.4),
|
|
91
|
+
inset: mm(0),
|
|
92
|
+
rule: { enabled: false, color: hex(ring) },
|
|
93
|
+
},
|
|
94
|
+
body: { fontSize: pt(10.5), lineHeight: pt(15) },
|
|
95
|
+
});
|
|
96
|
+
export const PAGE8_TYPES = [
|
|
97
|
+
{ id: 'foto', name: 'Foto', shortLabel: 'Foto', numberingTemplate: '', resetOn: 'never', counterFormat: 'decimal', captionPrefix: '', defaultPlacement: { position: 'here' } },
|
|
98
|
+
];
|
|
99
|
+
/** The fixture's pictures: solid placeholders of the photos' proportions. */
|
|
100
|
+
export const PAGE8_RESOURCES = [
|
|
101
|
+
['foto-dinero', 150, 100],
|
|
102
|
+
['foto-gasto', 190, 140],
|
|
103
|
+
['foto-ahorro', 115, 95],
|
|
104
|
+
['banda-tema', 230, 288],
|
|
105
|
+
].map(([id, width, height]) => ({
|
|
106
|
+
id: id,
|
|
107
|
+
typeId: 'foto',
|
|
108
|
+
kind: 'bitmap',
|
|
109
|
+
caption: '',
|
|
110
|
+
createdAt: 0,
|
|
111
|
+
updatedAt: 0,
|
|
112
|
+
bitmap: { fileId: `${id}.png`, format: 'png', width: width, height: height, resolution: 72 },
|
|
113
|
+
}));
|
|
114
|
+
/** The template and styles: form only, no words but the page's folio. */
|
|
115
|
+
export const PAGE8_CONFIG = {
|
|
116
|
+
page: { sizePreset: 'custom', width: mm(230), height: mm(288), margins: { top: mm(24), bottom: mm(22), left: mm(22), right: mm(22), mirror: true } },
|
|
117
|
+
bodyText: { fontSize: pt(12), lineHeight: pt(18) },
|
|
118
|
+
headings: {
|
|
119
|
+
balancing: { enabled: false },
|
|
120
|
+
levels: [
|
|
121
|
+
{ level: 1, fontSize: pt(22), lineHeight: pt(26), breakBefore: { enabled: false } },
|
|
122
|
+
{ level: 2, fontSize: pt(15), lineHeight: pt(20) },
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
chipStyles: [{ id: 'clave', background: hex('#ffe500'), fontWeight: 700 }],
|
|
126
|
+
resourceTypes: PAGE8_TYPES,
|
|
127
|
+
footer: { elements: [{ kind: 'text', id: 'folio', content: '{pageNumber}', fontSize: pt(10), placement: { anchor: { to: 'container', edge: 'bottom-left' } } }] },
|
|
128
|
+
calloutStyles: [
|
|
129
|
+
{ id: 'tab-titulo', backgroundEnabled: true, background: hex('#ffffff'), border: { enabled: false }, borderRadius: mm(1), padding: { top: mm(1.5), right: mm(4), bottom: mm(1.5), left: mm(4) } },
|
|
130
|
+
box('caja-azul', '#d8ecf9', '#0885bf', 'top-left'),
|
|
131
|
+
box('caja-verde', '#e6f3b4', '#66ae3e', 'top-right'),
|
|
132
|
+
box('caja-amarilla', '#fdf0c4', '#f99d1c', 'top-left'),
|
|
133
|
+
],
|
|
134
|
+
pageTemplates: [{
|
|
135
|
+
id: 'tema-cajas',
|
|
136
|
+
header: false,
|
|
137
|
+
decoration: {
|
|
138
|
+
elements: [
|
|
139
|
+
{ kind: 'image', id: 'fondo', resourceId: '{attr.fondo}', decorative: true, placement: { anchor: { to: 'bleed', edge: 'top-left' }, size: { width: 'fill', height: 'fill' } } },
|
|
140
|
+
{ kind: 'box', id: 'tarjeta', placement: { anchor: { to: 'container', edge: 'top-left' }, offset: { x: mm(-6), y: mm(-4) }, size: { width: mm(198), height: mm(250) } }, style: { backgroundColor: hex('#ffffff'), borderRadius: mm(4) } },
|
|
141
|
+
{ kind: 'text', id: 'tema', content: '{attr.tema}', fontSize: pt(9), color: hex('#ffffff'), placement: { anchor: { to: 'page', edge: 'top-right' }, offset: { x: mm(-10), y: mm(6) } } },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
frames: [
|
|
145
|
+
{ name: 'titulo', takes: 'h1', style: 'tab-titulo', width: mm(170), placement: { anchor: { to: 'container', edge: 'top-left' }, offset: { x: mm(-4), y: mm(-2) } } },
|
|
146
|
+
{ name: 'texto', flow: true, placement: { anchor: { to: '#titulo', edge: 'below' }, offset: { x: mm(4), y: mm(2) } }, width: mm(182) },
|
|
147
|
+
{ name: 'dinero', style: 'caja-azul', padding: { right: mm(62) }, placement: { anchor: { to: '#texto', edge: 'below' }, offset: { y: mm(9) } }, width: mm(186) },
|
|
148
|
+
{ name: 'dinero-fotos', layer: 2, width: mm(58), placement: { anchor: { to: '#dinero', edge: 'top-right' }, offset: { x: mm(-2), y: mm(2) } } },
|
|
149
|
+
{ name: 'gasto', style: 'caja-verde', padding: { left: mm(82) }, minHeight: mm(62), placement: { anchor: { to: '#dinero', edge: 'below' }, offset: { y: mm(12) } }, width: mm(186) },
|
|
150
|
+
{ name: 'gasto-fotos', layer: 2, width: mm(76), placement: { anchor: { to: '#gasto', edge: 'top-left' }, offset: { x: mm(3), y: mm(9) } } },
|
|
151
|
+
{ name: 'ahorro', style: 'caja-amarilla', padding: { right: mm(50) }, placement: { anchor: { to: '#gasto', edge: 'below' }, offset: { y: mm(12) } }, width: mm(186) },
|
|
152
|
+
{ name: 'ahorro-fotos', layer: 2, width: mm(44), placement: { anchor: { to: '#ahorro', edge: 'right' }, offset: { x: mm(-3) } } },
|
|
153
|
+
],
|
|
154
|
+
}],
|
|
155
|
+
};
|
|
156
|
+
export function page8Fixture(locale) {
|
|
157
|
+
return { markdown: MARKDOWN[locale], config: { ...PAGE8_CONFIG, locale }, resources: PAGE8_RESOURCES };
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=page8.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page8.js","sourceRoot":"","sources":["../../../../src/__tests__/composed/fixtures/page8.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/composed/layout.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { buildDocument } from '../../pipeline/build';
|
|
3
|
+
import { createMeasurementCache } from '../../measure';
|
|
4
|
+
import { formatWarning } from '../../pipeline/contentWarnings';
|
|
5
|
+
import { anchorOn, frameOrder, gridCellRect, placeBox, framePlaceable } from '../../composed/geometry';
|
|
6
|
+
import { normalizeFrame, resolvePageTemplate } from '../../composed/templates';
|
|
7
|
+
import { composedPaintOrder, frameBounds } from '../../composed/paint';
|
|
8
|
+
import { wrapComposedPages, FRAME_ATTR } from '../../composed/wrap';
|
|
9
|
+
import { parseMarkdownWithIssues } from '../../parse';
|
|
10
|
+
import { page8Fixture } from './fixtures/page8';
|
|
11
|
+
// Deterministic text measurement stub (no DOM in the node test env): seven
|
|
12
|
+
// px per character, whatever the face.
|
|
13
|
+
class StubCtx {
|
|
14
|
+
font = '';
|
|
15
|
+
measureText(s) {
|
|
16
|
+
return { width: s.length * 7 };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
globalThis.OffscreenCanvas = class {
|
|
20
|
+
getContext() {
|
|
21
|
+
return new StubCtx();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const mm = (value) => ({ value, unit: 'mm' });
|
|
25
|
+
const DPI = 300;
|
|
26
|
+
const px = (v) => (v / 25.4) * DPI;
|
|
27
|
+
const PAGE = {
|
|
28
|
+
page: { width: mm(200), height: mm(260), margins: { top: mm(20), bottom: mm(20), left: mm(20), right: mm(20) } },
|
|
29
|
+
headings: { balancing: { enabled: false }, levels: [{ level: 1, breakBefore: { enabled: false } }] },
|
|
30
|
+
};
|
|
31
|
+
const build = (md, templates, extra = {}) => buildDocument({ markdown: md }, { ...PAGE, ...extra, pageTemplates: templates }, createMeasurementCache());
|
|
32
|
+
const composedPage = (doc) => doc.pages.find((p) => p.composed);
|
|
33
|
+
const frame = (doc, name) => composedPage(doc).composed.frames.find((f) => f.name === name);
|
|
34
|
+
const blocksOf = (doc, f) => f.blockIds.map((id) => composedPage(doc).floats.find((b) => b.id === id));
|
|
35
|
+
const textOf = (blocks) => blocks.flatMap((b) => b.lines.map((l) => l.text)).join(' ');
|
|
36
|
+
const words = (n, w = 'word') => Array.from({ length: n }, (_, i) => `${w}${i}`).join(' ');
|
|
37
|
+
const AREA = { x: 100, y: 200, width: 1000, height: 1500 };
|
|
38
|
+
const GEO = { areas: { area: AREA, page: { x: 0, y: 0, width: 1200, height: 1800 }, bleed: { x: -30, y: -30, width: 1260, height: 1860 } }, dpi: DPI, emPx: 40 };
|
|
39
|
+
const resolved = (f, template = {}) => resolvePageTemplate({ pageTemplates: [{ id: 't', ...template, frames: [f, ...(template.frames ?? [])] }] }, 't').frames.find((x) => x.name === f.name);
|
|
40
|
+
describe('frame geometry (#855)', () => {
|
|
41
|
+
it('a frame with no placement takes the area’s top-left and width; its height follows its content', () => {
|
|
42
|
+
const f = framePlaceable(normalizeFrame({ name: 'a' }));
|
|
43
|
+
expect(placeBox(f, {}, GEO, () => undefined, () => 123)).toEqual({ x: 100, y: 200, width: 1000, height: 123 });
|
|
44
|
+
// Never past the area's foot.
|
|
45
|
+
expect(placeBox(f, {}, GEO, () => undefined, () => 9999).height).toBe(1500);
|
|
46
|
+
const filled = framePlaceable(normalizeFrame({ name: 'a', height: 'fill' }));
|
|
47
|
+
expect(placeBox(filled, {}, GEO, () => undefined, () => 1).height).toBe(1500);
|
|
48
|
+
});
|
|
49
|
+
it('anchors on the nine-point grid of the area, the trim and the bleed, with offsets', () => {
|
|
50
|
+
const f = framePlaceable(normalizeFrame({ name: 'a', width: mm(50), height: mm(20), placement: { anchor: { to: 'container', edge: 'bottom-right' }, offset: { x: mm(-5), y: mm(-5) } } }));
|
|
51
|
+
const r = placeBox(f, {}, GEO, () => undefined, () => 0);
|
|
52
|
+
expect(r.x + r.width).toBeCloseTo(1100 - px(5), 6);
|
|
53
|
+
expect(r.y + r.height).toBeCloseTo(1700 - px(5), 6);
|
|
54
|
+
const onTrim = framePlaceable(normalizeFrame({ name: 'b', width: 'fill', height: mm(10), placement: { anchor: { to: 'page', edge: 'top' } } }));
|
|
55
|
+
expect(placeBox(onTrim, {}, GEO, () => undefined, () => 0)).toMatchObject({ x: 0, y: 0, width: 1200 });
|
|
56
|
+
const onBleed = framePlaceable(normalizeFrame({ name: 'c', width: 'fill', height: 'fill', placement: { anchor: { to: 'bleed', edge: 'top-left' } } }));
|
|
57
|
+
expect(placeBox(onBleed, {}, GEO, () => undefined, () => 0)).toEqual({ x: -30, y: -30, width: 1260, height: 1860 });
|
|
58
|
+
});
|
|
59
|
+
it('sizes in percent of the area, and min/max (min wins)', () => {
|
|
60
|
+
const f = framePlaceable(normalizeFrame({ name: 'a', width: { percent: 50 }, height: { percent: 10 }, maxWidth: mm(10), minWidth: mm(20), placement: { anchor: { to: 'container', edge: 'top-left' } } }));
|
|
61
|
+
const r = placeBox(f, {}, GEO, () => undefined, () => 0);
|
|
62
|
+
expect(r.width).toBeCloseTo(px(20), 6);
|
|
63
|
+
expect(r.height).toBe(150);
|
|
64
|
+
});
|
|
65
|
+
it('hangs a frame off another with element edges, and a nine-point edge on a frame reads that point of its box', () => {
|
|
66
|
+
const ref = { x: 300, y: 400, width: 200, height: 100 };
|
|
67
|
+
expect(anchorOn('below', ref, true)).toEqual({ x: 300, y: 500, pinX: 'start', pinY: 'start' });
|
|
68
|
+
expect(anchorOn('right-of', ref, true)).toEqual({ x: 500, y: 400, pinX: 'start', pinY: 'start' });
|
|
69
|
+
expect(anchorOn('top-right', ref, true)).toEqual({ x: 500, y: 400, pinX: 'end', pinY: 'start' });
|
|
70
|
+
const f = framePlaceable(normalizeFrame({ name: 'b', width: mm(10), height: mm(10), placement: { anchor: { to: '#a', edge: 'below' }, offset: { y: mm(2) } } }));
|
|
71
|
+
const r = placeBox(f, {}, GEO, (n) => (n === 'a' ? ref : undefined), () => 0);
|
|
72
|
+
expect(r.x).toBe(300);
|
|
73
|
+
expect(r.y).toBeCloseTo(500 + px(2), 6);
|
|
74
|
+
});
|
|
75
|
+
it('places a frame in a grid cell, its own height or the cell’s', () => {
|
|
76
|
+
const template = resolvePageTemplate({ pageTemplates: [{ id: 't', grid: { columns: 2, rows: 3, columnGap: mm(0), rowGap: mm(0) }, frames: [{ name: 'a', grid: { col: 2, row: 2 } }, { name: 'b', grid: { col: 1, row: 1, cols: 2 }, height: 'auto' }] }] }, 't');
|
|
77
|
+
const cell = gridCellRect(template, template.frames[0].grid, AREA, GEO);
|
|
78
|
+
expect(cell).toEqual({ x: 600, y: 700, width: 500, height: 500 });
|
|
79
|
+
expect(placeBox(framePlaceable(template.frames[0]), template, GEO, () => undefined, () => 7)).toEqual(cell);
|
|
80
|
+
expect(placeBox(framePlaceable(template.frames[1]), template, GEO, () => undefined, () => 7)).toEqual({ x: 100, y: 200, width: 1000, height: 7 });
|
|
81
|
+
});
|
|
82
|
+
it('orders frames after what they anchor to, the frame their chain comes from, pushing frames first', () => {
|
|
83
|
+
const template = resolvePageTemplate({ pageTemplates: [{ id: 't', frames: [
|
|
84
|
+
{ name: 'caption', placement: { anchor: { to: '#photo', edge: 'below' } } },
|
|
85
|
+
{ name: 'text', next: 'more' },
|
|
86
|
+
{ name: 'more', placement: { anchor: { to: 'container', edge: 'bottom-left' } } },
|
|
87
|
+
{ name: 'photo', exclude: 'push', placement: { anchor: { to: '#text', edge: 'right-of' } } },
|
|
88
|
+
] }] }, 't');
|
|
89
|
+
const order = frameOrder(template).map((f) => f.name);
|
|
90
|
+
expect(order.indexOf('text')).toBeLessThan(order.indexOf('photo'));
|
|
91
|
+
expect(order.indexOf('photo')).toBeLessThan(order.indexOf('caption'));
|
|
92
|
+
expect(order.indexOf('text')).toBeLessThan(order.indexOf('more'));
|
|
93
|
+
// The pushing frame goes as early as its anchors allow.
|
|
94
|
+
expect(order.indexOf('photo')).toBeLessThan(order.indexOf('more'));
|
|
95
|
+
});
|
|
96
|
+
it('reads `heightSet` from what the template wrote', () => {
|
|
97
|
+
expect(framePlaceable(resolved({ name: 'a', height: 'auto' })).heightSet).toBe(true);
|
|
98
|
+
expect(framePlaceable(resolved({ name: 'a' })).heightSet).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe('regrouping a composed page into stories (#855)', () => {
|
|
102
|
+
const templates = [{ id: 't', frames: [{ name: 'title', takes: 'h1' }, { name: 'text', flow: true }, { name: 'box', next: 'box2' }, { name: 'box2' }] }];
|
|
103
|
+
const md = '::::page{template="t"}\n# Title\n\nIntro.\n\n:::frame{slot="box2" label="Two"}\nSecond.\n:::\n\n:::frame{slot="box" label="One"}\nFirst.\n:::\n\nOutro.\n::::';
|
|
104
|
+
it('one story per chain, its frames’ pieces in chain order; stories in the order they first appear', () => {
|
|
105
|
+
const { blocks, wrapped } = wrapComposedPages(parseMarkdownWithIssues(md).blocks, { pageTemplates: templates });
|
|
106
|
+
expect(wrapped).toBe(true);
|
|
107
|
+
const heads = blocks.filter((b) => b.type === 'containerStart' && b.containerAttrs?.[FRAME_ATTR]).map((b) => b.containerAttrs[FRAME_ATTR]);
|
|
108
|
+
expect(heads).toEqual(['title', 'text', 'box']);
|
|
109
|
+
const box = blocks.findIndex((b) => b.containerAttrs?.[FRAME_ATTR] === 'box');
|
|
110
|
+
expect(blocks[box].containerAttrs.label).toBe('One');
|
|
111
|
+
const inBox = blocks.slice(box + 1, blocks.findIndex((b, i) => i > box && b.type === 'containerEnd' && b.containerId === blocks[box].containerId)).map((b) => b.text);
|
|
112
|
+
expect(inBox).toEqual(['First.', 'Second.']);
|
|
113
|
+
});
|
|
114
|
+
it('leaves a page of unknown template as it is', () => {
|
|
115
|
+
const parsed = parseMarkdownWithIssues('::::page{template="nope"}\nText.\n::::').blocks;
|
|
116
|
+
expect(wrapComposedPages(parsed, { pageTemplates: templates }).wrapped).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('composed pages laid out (#855)', () => {
|
|
120
|
+
const TWO = {
|
|
121
|
+
id: 'two',
|
|
122
|
+
frames: [
|
|
123
|
+
{ name: 'title', takes: 'h1', placement: { anchor: { to: 'container', edge: 'top-left' } } },
|
|
124
|
+
{ name: 'text', flow: true, placement: { anchor: { to: '#title', edge: 'below' }, offset: { y: mm(3) } } },
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
it('owns a page of role composed; what follows starts on the next page; nothing flows on it', () => {
|
|
128
|
+
const doc = build('Before.\n\n::::page{template="two"}\n# Title\n\nBody text.\n::::\n\nAfter.', [TWO]);
|
|
129
|
+
expect(doc.pages).toHaveLength(3);
|
|
130
|
+
const page = doc.pages[1];
|
|
131
|
+
expect(page.role).toBe('composed');
|
|
132
|
+
expect(page.columns).toHaveLength(1);
|
|
133
|
+
expect(page.columns[0].blocks).toHaveLength(0);
|
|
134
|
+
expect(textOf(doc.pages[2].columns.flatMap((c) => c.blocks))).toBe('After.');
|
|
135
|
+
});
|
|
136
|
+
it('records the frames with stable ids, config paths and the blocks they hold', () => {
|
|
137
|
+
const doc = build('::::page{template="two"}\n# Title\n\nBody text.\n::::', [TWO]);
|
|
138
|
+
const composed = composedPage(doc).composed;
|
|
139
|
+
expect(composed.template).toBe('two');
|
|
140
|
+
expect(composed.configPath).toBe('/pageTemplates/0');
|
|
141
|
+
expect(composed.frames.map((f) => [f.id, f.configPath])).toEqual([['cp0:title', '/pageTemplates/0/frames/0'], ['cp0:text', '/pageTemplates/0/frames/1']]);
|
|
142
|
+
const title = frame(doc, 'title');
|
|
143
|
+
const blocks = blocksOf(doc, title);
|
|
144
|
+
expect(blocks[0].type).toBe('callout');
|
|
145
|
+
expect(blocks[0].id).toBe('cp0:title');
|
|
146
|
+
expect(textOf(blocks)).toBe('Title');
|
|
147
|
+
// Frame blocks are in `doc.blocks` with their sources (caret mapping).
|
|
148
|
+
for (const b of blocks)
|
|
149
|
+
expect(doc.blocks).toContain(b);
|
|
150
|
+
const heading = blocks.find((b) => b.type === 'heading');
|
|
151
|
+
expect(heading.sourceStart).toBeGreaterThan(0);
|
|
152
|
+
expect(title.sourceStart).toBeDefined();
|
|
153
|
+
expect(heading.containerId).toBe(blocks[0].containerId);
|
|
154
|
+
});
|
|
155
|
+
it('an auto height is the content’s height; the text frame hangs below the title', () => {
|
|
156
|
+
const doc = build('::::page{template="two"}\n# Title\n\nBody text.\n::::', [TWO]);
|
|
157
|
+
const title = frame(doc, 'title');
|
|
158
|
+
const text = frame(doc, 'text');
|
|
159
|
+
const [box, heading] = blocksOf(doc, title);
|
|
160
|
+
expect(title.rect.height).toBeCloseTo(box.bbox.height, 6);
|
|
161
|
+
expect(heading.bbox.y + heading.bbox.height).toBeLessThanOrEqual(title.rect.y + title.rect.height + 0.01);
|
|
162
|
+
expect(text.rect.y).toBeCloseTo(title.rect.y + title.rect.height + px(3), 6);
|
|
163
|
+
});
|
|
164
|
+
it('places content of a taller fixed frame as verticalAlign says', () => {
|
|
165
|
+
const at = (verticalAlign) => {
|
|
166
|
+
const doc = build('::::page{template="v"}\nOne line.\n::::', [{ id: 'v', frames: [{ name: 'f', flow: true, height: mm(50), verticalAlign, placement: { anchor: { to: 'container', edge: 'top-left' } } }] }]);
|
|
167
|
+
const f = frame(doc, 'f');
|
|
168
|
+
const para = blocksOf(doc, f).find((b) => b.type === 'paragraph');
|
|
169
|
+
return { top: para.bbox.y - f.rect.y, bottom: f.rect.y + f.rect.height - (para.bbox.y + para.bbox.height), box: blocksOf(doc, f)[0] };
|
|
170
|
+
};
|
|
171
|
+
const top = at('top');
|
|
172
|
+
const middle = at('middle');
|
|
173
|
+
const bottom = at('bottom');
|
|
174
|
+
expect(top.top).toBeCloseTo(0, 3);
|
|
175
|
+
expect(middle.top).toBeCloseTo(middle.bottom, 3);
|
|
176
|
+
expect(bottom.bottom).toBeCloseTo(0, 3);
|
|
177
|
+
// The box is drawn as tall as the frame.
|
|
178
|
+
expect(top.box.bbox.height).toBeCloseTo(px(50), 3);
|
|
179
|
+
});
|
|
180
|
+
it('chains a story from one frame to the next (`next`): nothing lost, nothing doubled', () => {
|
|
181
|
+
const story = words(120);
|
|
182
|
+
const tpl = { id: 'chain', frames: [
|
|
183
|
+
{ name: 'a', flow: true, next: 'b', width: mm(70), height: mm(30), placement: { anchor: { to: 'container', edge: 'top-left' } } },
|
|
184
|
+
{ name: 'b', width: mm(50), height: mm(150), placement: { anchor: { to: 'container', edge: 'top-right' } } },
|
|
185
|
+
] };
|
|
186
|
+
const doc = build(`::::page{template="chain"}\n${story}\n::::`, [tpl]);
|
|
187
|
+
const a = frame(doc, 'a');
|
|
188
|
+
const b = frame(doc, 'b');
|
|
189
|
+
expect(a.continuesTo).toBe(b.id);
|
|
190
|
+
expect(b.continuedFrom).toBe(a.id);
|
|
191
|
+
expect(a.overflowPx).toBe(0);
|
|
192
|
+
const aText = textOf(blocksOf(doc, a));
|
|
193
|
+
const bText = textOf(blocksOf(doc, b));
|
|
194
|
+
expect(aText.length).toBeGreaterThan(0);
|
|
195
|
+
expect(bText.length).toBeGreaterThan(0);
|
|
196
|
+
expect(`${aText} ${bText}`.replace(/-\s/g, '').split(/\s+/).join(' ')).toBe(story);
|
|
197
|
+
// Each piece is set at its own frame's measure.
|
|
198
|
+
const aLines = blocksOf(doc, a).find((x) => x.type === 'paragraph').lines;
|
|
199
|
+
const bLines = blocksOf(doc, b).find((x) => x.type === 'paragraph').lines;
|
|
200
|
+
expect(Math.max(...aLines.map((l) => l.bbox.width))).toBeGreaterThan(Math.max(...bLines.map((l) => l.bbox.width)));
|
|
201
|
+
// The second piece's box joins the first (tagging) and carries no tab.
|
|
202
|
+
expect(blocksOf(doc, b)[0].id).toBe('cp0:a-cont-1');
|
|
203
|
+
});
|
|
204
|
+
it('reads overflow.continueIn: "#frame" as next', () => {
|
|
205
|
+
const tpl = { id: 'ci', frames: [
|
|
206
|
+
{ name: 'a', flow: true, overflow: { continueIn: '#b' }, width: mm(70), height: mm(20), placement: { anchor: { to: 'container', edge: 'top-left' } } },
|
|
207
|
+
{ name: 'b', width: mm(70), height: mm(150), placement: { anchor: { to: 'container', edge: 'top-right' } } },
|
|
208
|
+
] };
|
|
209
|
+
const doc = build(`::::page{template="ci"}\n${words(100)}\n::::`, [tpl]);
|
|
210
|
+
expect(frame(doc, 'a').continuesTo).toBe('cp0:b');
|
|
211
|
+
expect(frame(doc, 'a').overflowPx).toBe(0);
|
|
212
|
+
expect(textOf(blocksOf(doc, frame(doc, 'b'))).length).toBeGreaterThan(0);
|
|
213
|
+
});
|
|
214
|
+
it('clips what no frame can hold and says so (frameOverflow), keeping it in the document', () => {
|
|
215
|
+
const story = words(200);
|
|
216
|
+
const tpl = { id: 'small', frames: [{ name: 'a', flow: true, width: mm(60), height: mm(20), placement: { anchor: { to: 'container', edge: 'top-left' } } }] };
|
|
217
|
+
const doc = build(`::::page{template="small"}\n${story}\n::::`, [tpl]);
|
|
218
|
+
const a = frame(doc, 'a');
|
|
219
|
+
expect(a.overflowPx).toBeGreaterThan(0);
|
|
220
|
+
expect(textOf(blocksOf(doc, a)).replace(/-\s/g, '').split(/\s+/).join(' ')).toBe(story);
|
|
221
|
+
const w = doc.contentWarnings.find((x) => x.kind === 'frameOverflow');
|
|
222
|
+
expect(w).toMatchObject({ kind: 'frameOverflow', template: 'small', frame: 'a', fenceIndex: 0, configPath: '/pageTemplates/0/frames/0', pageIndex: 0 });
|
|
223
|
+
expect(w.kind === 'frameOverflow' && w.overflowMm).toBeCloseTo((a.overflowPx / DPI) * 25.4, 0);
|
|
224
|
+
expect(composedPage(doc).composed.log).toEqual([{ frame: 'a', kind: 'overflow', toMm: w.overflowMm }]);
|
|
225
|
+
expect(formatWarning(w)).toContain('Frame "a" of template "small"');
|
|
226
|
+
});
|
|
227
|
+
it('wraps a frame’s text round a frame that pushes it (exclude: push)', () => {
|
|
228
|
+
const story = words(150);
|
|
229
|
+
const tpl = (exclude) => ({ id: 'push', frames: [
|
|
230
|
+
{ name: 'text', flow: true, width: mm(120), placement: { anchor: { to: 'container', edge: 'top-left' } } },
|
|
231
|
+
{ name: 'photo', exclude, layer: 2, width: mm(40), height: mm(30), placement: { anchor: { to: '#text', edge: 'top-right' } } },
|
|
232
|
+
] });
|
|
233
|
+
const lines = (doc) => blocksOf(doc, frame(doc, 'text')).filter((b) => b.type === 'paragraph').flatMap((b) => b.lines);
|
|
234
|
+
const pushed = build(`::::page{template="push"}\n${story}\n::::`, [tpl('push')]);
|
|
235
|
+
const plain = build(`::::page{template="push"}\n${story}\n::::`, [tpl('none')]);
|
|
236
|
+
const photo = frame(pushed, 'photo');
|
|
237
|
+
const beside = lines(pushed).filter((l) => l.bbox.y < photo.rect.y + photo.rect.height);
|
|
238
|
+
expect(beside.length).toBeGreaterThan(0);
|
|
239
|
+
for (const l of beside)
|
|
240
|
+
expect(l.bbox.x + l.bbox.width).toBeLessThanOrEqual(photo.rect.x + 0.5);
|
|
241
|
+
expect(lines(pushed).length).toBeGreaterThan(lines(plain).length);
|
|
242
|
+
expect(photo.push).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
it('a styled frame draws its callout style, with the tab from the frame fence', () => {
|
|
245
|
+
const tpl = { id: 's', frames: [{ name: 'box', style: 'caja', placement: { anchor: { to: 'container', edge: 'top-left' } }, width: mm(100) }] };
|
|
246
|
+
const doc = build('::::page{template="s"}\n:::frame{slot="box" label="Money"}\nInside the box.\n:::\n::::', [tpl], {
|
|
247
|
+
calloutStyles: [{ id: 'caja', backgroundEnabled: true, background: { hex: '#d8ecf9' }, label: { position: 'top-left', offset: mm(5), height: mm(5) } }],
|
|
248
|
+
});
|
|
249
|
+
const box = blocksOf(doc, frame(doc, 'box'))[0];
|
|
250
|
+
expect(box.callout.styleId).toBe('caja');
|
|
251
|
+
const labelText = box.designOverlay.blocks.flatMap((b) => (b.kind === 'text' ? b.lines.map((l) => l.text) : []));
|
|
252
|
+
expect(labelText).toContain('Money');
|
|
253
|
+
expect(box.bbox.width).toBeCloseTo(px(100), 3);
|
|
254
|
+
expect(frame(doc, 'box').styleId).toBe('caja');
|
|
255
|
+
});
|
|
256
|
+
it('padding of the frame overrides its style’s, side by side', () => {
|
|
257
|
+
const tpl = (padding) => ({ id: 'p', frames: [{ name: 'box', style: 'caja', ...(padding ? { padding } : {}), width: mm(150), placement: { anchor: { to: 'container', edge: 'top-left' } } }] });
|
|
258
|
+
const md = '::::page{template="p"}\n:::frame{slot="box"}\nText.\n:::\n::::';
|
|
259
|
+
const cfg = { calloutStyles: [{ id: 'caja', padding: { top: mm(2), right: mm(2), bottom: mm(2), left: mm(2) } }] };
|
|
260
|
+
const plain = build(md, [tpl()], cfg);
|
|
261
|
+
const padded = build(md, [tpl({ left: mm(60) })], cfg);
|
|
262
|
+
const xOf = (doc) => blocksOf(doc, frame(doc, 'box')).find((b) => b.type === 'paragraph').bbox.x;
|
|
263
|
+
expect(xOf(padded) - xOf(plain)).toBeCloseTo(px(58), 3);
|
|
264
|
+
});
|
|
265
|
+
it('sets a frame in columns with the :::columns machinery', () => {
|
|
266
|
+
const doc = build(`::::page{template="c"}\n${words(40, 'a')}\n\n${words(40, 'b')}\n::::`, [{ id: 'c', frames: [{ name: 'f', flow: true, columns: 2, columnGap: mm(5), width: mm(150), placement: { anchor: { to: 'container', edge: 'top-left' } } }] }]);
|
|
267
|
+
const xs = new Set(blocksOf(doc, frame(doc, 'f')).filter((b) => b.type === 'paragraph').map((b) => Math.round(b.bbox.x)));
|
|
268
|
+
expect(xs.size).toBe(2);
|
|
269
|
+
});
|
|
270
|
+
it('records layer and turn; paints frames in layer order around the decoration', () => {
|
|
271
|
+
const tpl = { id: 'l', decoration: { elements: [{ kind: 'box', id: 'bg', placement: { anchor: { to: 'container', edge: 'top-left' }, size: { width: mm(10), height: mm(10) } }, style: { backgroundColor: { hex: '#ff0000' } } }] }, frames: [
|
|
272
|
+
{ name: 'over', layer: 3, rotate: 12, placement: { anchor: { to: 'container', edge: 'top-left' } } },
|
|
273
|
+
{ name: 'under', layer: -1, placement: { anchor: { to: 'container', edge: 'bottom-left' } } },
|
|
274
|
+
{ name: 'mid', flow: true, placement: { anchor: { to: 'container', edge: 'center' } }, width: mm(50) },
|
|
275
|
+
] };
|
|
276
|
+
const doc = build('::::page{template="l"}\n:::frame{slot="over"}\nOver.\n:::\n:::frame{slot="under"}\nUnder.\n:::\nMid.\n::::', [tpl]);
|
|
277
|
+
const page = composedPage(doc);
|
|
278
|
+
expect(frame(doc, 'over')).toMatchObject({ layer: 3, rotate: 12 });
|
|
279
|
+
const order = composedPaintOrder(page).map((i) => (i.kind === 'frame' ? i.frame.name : 'decoration'));
|
|
280
|
+
expect(order).toEqual(['under', 'decoration', 'mid', 'over']);
|
|
281
|
+
expect(page.composed.decoration).toBeDefined();
|
|
282
|
+
const b = frameBounds(frame(doc, 'over'));
|
|
283
|
+
expect(b.height).toBeGreaterThan(frame(doc, 'over').rect.height);
|
|
284
|
+
});
|
|
285
|
+
it('lays the decoration with the frames’ boxes and the page fence’s attributes', () => {
|
|
286
|
+
const tpl = { id: 'd', decoration: { elements: [
|
|
287
|
+
{ kind: 'text', id: 'tema', content: '{attr.tema}', placement: { anchor: { to: '#box', edge: 'below' } } },
|
|
288
|
+
{ kind: 'image', id: 'bg', resourceId: '{attr.fondo}', decorative: true, placement: { anchor: { to: 'bleed', edge: 'top-left' }, size: { width: 'fill', height: 'fill' } } },
|
|
289
|
+
] }, frames: [{ name: 'box', flow: true, width: mm(80), placement: { anchor: { to: 'container', edge: 'top-left' }, offset: { y: mm(10) } } }] };
|
|
290
|
+
const md = '---\ntitle: T\n---\n::::page{template="d" tema="Savings" fondo="green"}\nText.\n::::';
|
|
291
|
+
const doc = buildDocument({ markdown: md, resources: [{ id: 'green', typeId: 'figure', kind: 'bitmap', caption: '', createdAt: 0, updatedAt: 0, bitmap: { fileId: 'g', format: 'png', width: 10, height: 10 } }] }, { ...PAGE, pageTemplates: [tpl] }, createMeasurementCache());
|
|
292
|
+
const page = composedPage(doc);
|
|
293
|
+
const deco = page.composed.decoration.blocks;
|
|
294
|
+
const text = deco.find((b) => b.kind === 'text');
|
|
295
|
+
const box = frame(doc, 'box');
|
|
296
|
+
expect(text.kind === 'text' && text.lines[0].text).toBe('Savings');
|
|
297
|
+
expect(text.bbox.y).toBeCloseTo(box.rect.y + box.rect.height, 3);
|
|
298
|
+
expect(text.bbox.x).toBeCloseTo(box.rect.x, 3);
|
|
299
|
+
// The text maps back to the attribute's value in the chapter file.
|
|
300
|
+
expect(md.slice(text.kind === 'text' ? text.sourceStart : 0, text.kind === 'text' ? text.sourceEnd : 0)).toBe('Savings');
|
|
301
|
+
expect(page.composed.attrSources.tema && md.slice(page.composed.attrSources.tema.sourceStart, page.composed.attrSources.tema.sourceEnd)).toBe('Savings');
|
|
302
|
+
expect(deco.some((b) => b.kind === 'image' && b.fileId === 'g')).toBe(true);
|
|
303
|
+
});
|
|
304
|
+
it('hides, replaces or inherits the running heads per template', () => {
|
|
305
|
+
const slot = (content) => ({ elements: [{ kind: 'text', id: 'x', content, placement: { anchor: { to: 'container', edge: 'top-left' } } }] });
|
|
306
|
+
const texts = (s) => s?.blocks.flatMap((b) => (b.kind === 'text' ? b.lines.map((l) => l.text) : [])) ?? [];
|
|
307
|
+
const run = (header, footer) => {
|
|
308
|
+
const doc = build('Before.\n\n::::page{template="h"}\nText.\n::::', [{ id: 'h', ...(header !== undefined ? { header } : {}), ...(footer !== undefined ? { footer } : {}), frames: [{ name: 'f', flow: true }] }], { header: slot('HEAD {attr.tema}'), footer: slot('FOOT {pageNumber}') });
|
|
309
|
+
return composedPage(doc);
|
|
310
|
+
};
|
|
311
|
+
expect(run(false).header).toBeUndefined();
|
|
312
|
+
expect(texts(run(false).footer)).toEqual(['FOOT 2']);
|
|
313
|
+
expect(texts(run(slot('OWN'), false).header)).toEqual(['OWN']);
|
|
314
|
+
expect(run(slot('OWN'), false).footer).toBeUndefined();
|
|
315
|
+
expect(texts(run(undefined).header)).toEqual(['HEAD ']);
|
|
316
|
+
});
|
|
317
|
+
it('reads `{attr.x}` of the page fence in the running heads of its page', () => {
|
|
318
|
+
const doc = build('::::page{template="h" tema="Savings"}\nText.\n::::', [{ id: 'h', frames: [{ name: 'f', flow: true }] }], { header: { elements: [{ kind: 'text', id: 'x', content: '{attr.tema}', placement: { anchor: { to: 'container', edge: 'top-left' } } }] } });
|
|
319
|
+
const header = composedPage(doc).header;
|
|
320
|
+
expect(header.blocks.flatMap((b) => (b.kind === 'text' ? b.lines.map((l) => l.text) : []))).toEqual(['Savings']);
|
|
321
|
+
});
|
|
322
|
+
it('sets content no frame takes in the flow after the page, never losing it', () => {
|
|
323
|
+
const tpl = { id: 'n', frames: [{ name: 'box' }] };
|
|
324
|
+
const doc = build('::::page{template="n"}\n:::frame{slot="box"}\nIn the box.\n:::\n\nLoose text.\n::::\n\nAfter.', [tpl]);
|
|
325
|
+
expect(textOf(blocksOf(doc, frame(doc, 'box')))).toBe('In the box.');
|
|
326
|
+
const later = doc.pages.slice(1).flatMap((p) => p.columns.flatMap((c) => c.blocks));
|
|
327
|
+
expect(textOf(later)).toBe('Loose text. After.');
|
|
328
|
+
expect(later[0].pageIndex).toBe(1);
|
|
329
|
+
});
|
|
330
|
+
it('a picture sent to a frame is set in it, not floated', () => {
|
|
331
|
+
const resources = [{ id: 'pic', typeId: 'figure', kind: 'bitmap', caption: 'A picture', createdAt: 0, updatedAt: 0, bitmap: { fileId: 'p', format: 'png', width: 400, height: 300 } }];
|
|
332
|
+
const doc = buildDocument({ markdown: '::::page{template="r"}\nText.\n\n::resource{id="pic" slot="photo"}\n::::', resources }, { ...PAGE, pageTemplates: [{ id: 'r', frames: [{ name: 'text', flow: true, width: mm(80) }, { name: 'photo', width: mm(50), placement: { anchor: { to: 'container', edge: 'top-right' } } }] }] }, createMeasurementCache());
|
|
333
|
+
const photo = frame(doc, 'photo');
|
|
334
|
+
const pic = blocksOf(doc, photo).find((b) => b.type === 'resource');
|
|
335
|
+
expect(pic.resourceBlock).toBeDefined();
|
|
336
|
+
expect(pic.bbox.x).toBeGreaterThanOrEqual(photo.rect.x - 0.01);
|
|
337
|
+
expect(doc.pages.every((p) => p === composedPage(doc) || (p.floats ?? []).length === 0)).toBe(true);
|
|
338
|
+
});
|
|
339
|
+
it('a page of unknown template is set in the flow on pages of its own', () => {
|
|
340
|
+
const doc = build('A.\n\n::::page{template="nope"}\nInside.\n::::\n\nB.', [TWO]);
|
|
341
|
+
expect(doc.pages.some((p) => p.composed)).toBe(false);
|
|
342
|
+
expect(doc.pages.map((p) => textOf(p.columns.flatMap((c) => c.blocks)))).toEqual(['A.', 'Inside.', 'B.']);
|
|
343
|
+
});
|
|
344
|
+
it('the parity of the page picks the template’s variant', () => {
|
|
345
|
+
const tpl = { id: 'var', frames: [{ name: 'f', flow: true, width: mm(100) }], variants: { verso: { frames: [{ name: 'f', width: mm(60) }] } } };
|
|
346
|
+
const doc = build('::::page{template="var"}\nOne.\n::::\n\n::::page{template="var"}\nTwo.\n::::', [tpl]);
|
|
347
|
+
const pages = doc.pages.filter((p) => p.composed);
|
|
348
|
+
expect(pages.map((p) => p.composed.variant)).toEqual([undefined, 'verso']);
|
|
349
|
+
expect(pages[0].composed.frames[0].rect.width).toBeCloseTo(px(100), 3);
|
|
350
|
+
expect(pages[1].composed.frames[0].rect.width).toBeCloseTo(px(60), 3);
|
|
351
|
+
expect(pages[1].composed.frames[0].id).toBe('cp1:f');
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
describe('the page-8 stand-in (#855)', () => {
|
|
355
|
+
for (const locale of ['es', 'en']) {
|
|
356
|
+
it(`sets every word of the ${locale} page in its frames, on one page, none in the config`, () => {
|
|
357
|
+
const { markdown, config, resources } = page8Fixture(locale);
|
|
358
|
+
const doc = buildDocument({ markdown, resources }, config, createMeasurementCache());
|
|
359
|
+
expect(doc.pages).toHaveLength(1);
|
|
360
|
+
const page = doc.pages[0];
|
|
361
|
+
expect(page.role).toBe('composed');
|
|
362
|
+
const names = page.composed.frames.map((f) => f.name);
|
|
363
|
+
expect(names).toEqual(['titulo', 'texto', 'dinero', 'dinero-fotos', 'gasto', 'gasto-fotos', 'ahorro', 'ahorro-fotos']);
|
|
364
|
+
for (const name of ['dinero', 'gasto', 'ahorro']) {
|
|
365
|
+
const f = page.composed.frames.find((x) => x.name === name);
|
|
366
|
+
expect(f.overflowPx).toBe(0);
|
|
367
|
+
expect(blocksOf(doc, f)[0].designOverlay.blocks.some((b) => b.kind === 'text')).toBe(true);
|
|
368
|
+
}
|
|
369
|
+
for (const name of ['dinero-fotos', 'gasto-fotos', 'ahorro-fotos']) {
|
|
370
|
+
const f = page.composed.frames.find((x) => x.name === name);
|
|
371
|
+
expect(blocksOf(doc, f).some((b) => b.type === 'resource')).toBe(true);
|
|
372
|
+
}
|
|
373
|
+
// The boxes stack down the page, each below the one before.
|
|
374
|
+
const ys = ['titulo', 'texto', 'dinero', 'gasto', 'ahorro'].map((n) => page.composed.frames.find((f) => f.name === n).rect.y);
|
|
375
|
+
expect([...ys].sort((a, b) => a - b)).toEqual(ys);
|
|
376
|
+
// The configuration holds no sentence of the page.
|
|
377
|
+
const text = JSON.stringify(config);
|
|
378
|
+
for (const b of page.floats)
|
|
379
|
+
for (const l of b.lines)
|
|
380
|
+
if (l.text.length > 20)
|
|
381
|
+
expect(text).not.toContain(l.text);
|
|
382
|
+
expect((doc.contentWarnings ?? []).filter((w) => w.kind === 'frameOverflow')).toEqual([]);
|
|
383
|
+
expect(page.composed.decoration.blocks.some((b) => b.kind === 'image')).toBe(true);
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
//# sourceMappingURL=layout.test.js.map
|