@datatechsolutions/tympan-tokens 0.4.2 → 0.5.0
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/THIRD_PARTY_NOTICES.md +5 -5
- package/dist/fonts.css +80 -80
- package/dist/index.d.ts +229 -55
- package/dist/index.js +359 -16
- package/dist/index.js.map +1 -1
- package/dist/print-themes/print-aquarela.css +7 -7
- package/dist/print-themes/print-art-nouveau.css +2 -2
- package/dist/print-themes/print-atlas-oficial.css +7 -7
- package/dist/print-themes/print-azulejo-modernista.css +2 -2
- package/dist/print-themes/print-bauhaus.css +2 -2
- package/dist/print-themes/print-blocos-coloridos.css +5 -5
- package/dist/print-themes/print-brutalista.css +8 -8
- package/dist/print-themes/print-caderno.css +14 -14
- package/dist/print-themes/print-cartao-postal.css +5 -5
- package/dist/print-themes/print-cientifico.css +3 -3
- package/dist/print-themes/print-concretismo.css +8 -8
- package/dist/print-themes/print-construtivismo.css +6 -6
- package/dist/print-themes/print-cordel.css +6 -6
- package/dist/print-themes/print-dados-br.css +12 -12
- package/dist/print-themes/print-dashboard.css +18 -18
- package/dist/print-themes/print-diagrama-modernista.css +3 -3
- package/dist/print-themes/print-divulgacao.css +3 -3
- package/dist/print-themes/print-fluxo-historico.css +3 -3
- package/dist/print-themes/print-grade-holandesa.css +2 -2
- package/dist/print-themes/print-graficos-1900.css +6 -6
- package/dist/print-themes/print-infografico-ilustrado.css +6 -6
- package/dist/print-themes/print-isotype.css +2 -2
- package/dist/print-themes/print-jornal-1959.css +5 -5
- package/dist/print-themes/print-jornal.css +6 -6
- package/dist/print-themes/print-mapa-de-metro.css +1 -1
- package/dist/print-themes/print-memphis.css +5 -5
- package/dist/print-themes/print-minimo-de-tinta.css +4 -4
- package/dist/print-themes/print-papel-recortado.css +2 -2
- package/dist/print-themes/print-papel-salmao.css +6 -6
- package/dist/print-themes/print-pictogramas.css +2 -2
- package/dist/print-themes/print-pop-art.css +2 -2
- package/dist/print-themes/print-prancheta-clara.css +5 -5
- package/dist/print-themes/print-prancheta.css +5 -5
- package/dist/print-themes/print-proporcao-modular.css +2 -2
- package/dist/print-themes/print-riso.css +4 -4
- package/dist/print-themes/print-semanario.css +13 -13
- package/dist/print-themes/print-sinalizacao.css +8 -8
- package/dist/print-themes/print-suico.css +4 -4
- package/dist/print-themes/print-tropicalia.css +3 -3
- package/dist/print-themes.css +133 -133
- package/fonts/ibmplexsans/ibmplexsans-italic-cyrillic-400_500_600.woff2 +0 -0
- package/fonts/ibmplexsans/ibmplexsans-italic-cyrillic-ext-400_500_600.woff2 +0 -0
- package/fonts/ibmplexsans/ibmplexsans-italic-greek-400_500_600.woff2 +0 -0
- package/fonts/ibmplexsans/ibmplexsans-italic-latin-400_500_600.woff2 +0 -0
- package/fonts/ibmplexsans/ibmplexsans-italic-latin-ext-400_500_600.woff2 +0 -0
- package/fonts/ibmplexsans/ibmplexsans-italic-vietnamese-400_500_600.woff2 +0 -0
- package/fonts/manifest.json +998 -820
- package/fonts/nunitosans/nunitosans-normal-cyrillic-400_600_700_800.woff2 +0 -0
- package/fonts/nunitosans/nunitosans-normal-cyrillic-ext-400_600_700_800.woff2 +0 -0
- package/fonts/nunitosans/nunitosans-normal-latin-400_600_700_800.woff2 +0 -0
- package/fonts/nunitosans/nunitosans-normal-latin-ext-400_600_700_800.woff2 +0 -0
- package/fonts/nunitosans/nunitosans-normal-vietnamese-400_600_700_800.woff2 +0 -0
- package/fonts/sourceserif4/sourceserif4-italic-cyrillic-400_600.woff2 +0 -0
- package/fonts/sourceserif4/sourceserif4-italic-cyrillic-ext-400_600.woff2 +0 -0
- package/fonts/sourceserif4/sourceserif4-italic-greek-400_600.woff2 +0 -0
- package/fonts/sourceserif4/sourceserif4-italic-latin-400_600.woff2 +0 -0
- package/fonts/sourceserif4/sourceserif4-italic-latin-ext-400_600.woff2 +0 -0
- package/fonts/sourceserif4/sourceserif4-italic-vietnamese-400_600.woff2 +0 -0
- package/package.json +1 -1
- package/src/fonts.ts +2 -2
- package/src/index.ts +1 -0
- package/src/print-book-themes.ts +239 -0
- package/src/print-presets.ts +220 -10
- /package/fonts/ibmplexsans/{ibmplexsans-italic-cyrillic.woff2 → ibmplexsans-italic-cyrillic-400.woff2} +0 -0
- /package/fonts/ibmplexsans/{ibmplexsans-italic-cyrillic-ext.woff2 → ibmplexsans-italic-cyrillic-ext-400.woff2} +0 -0
- /package/fonts/ibmplexsans/{ibmplexsans-italic-greek.woff2 → ibmplexsans-italic-greek-400.woff2} +0 -0
- /package/fonts/ibmplexsans/{ibmplexsans-italic-latin.woff2 → ibmplexsans-italic-latin-400.woff2} +0 -0
- /package/fonts/ibmplexsans/{ibmplexsans-italic-latin-ext.woff2 → ibmplexsans-italic-latin-ext-400.woff2} +0 -0
- /package/fonts/ibmplexsans/{ibmplexsans-italic-vietnamese.woff2 → ibmplexsans-italic-vietnamese-400.woff2} +0 -0
- /package/fonts/sourceserif4/{sourceserif4-italic-cyrillic.woff2 → sourceserif4-italic-cyrillic-400.woff2} +0 -0
- /package/fonts/sourceserif4/{sourceserif4-italic-cyrillic-ext.woff2 → sourceserif4-italic-cyrillic-ext-400.woff2} +0 -0
- /package/fonts/sourceserif4/{sourceserif4-italic-greek.woff2 → sourceserif4-italic-greek-400.woff2} +0 -0
- /package/fonts/sourceserif4/{sourceserif4-italic-latin.woff2 → sourceserif4-italic-latin-400.woff2} +0 -0
- /package/fonts/sourceserif4/{sourceserif4-italic-latin-ext.woff2 → sourceserif4-italic-latin-ext-400.woff2} +0 -0
- /package/fonts/sourceserif4/{sourceserif4-italic-vietnamese.woff2 → sourceserif4-italic-vietnamese-400.woff2} +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatechsolutions/tympan-tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Tympan design tokens: W3C DTCG source (colour, space, radius, type, elevation and glass, motion, z) and an OKLCH theme generator, with the tympan, fakhir, astrlabe, neutral and high-contrast presets in light and dark (plus opt-in UI themes derived from the print book styles), built to --ty-* CSS custom properties, JSON and a TypeScript export.",
|
|
5
5
|
"license": "FSL-1.1-ALv2",
|
|
6
6
|
"author": "Natalia Mesquita",
|
package/src/fonts.ts
CHANGED
|
@@ -16,8 +16,8 @@ import { SCRIPT_RULES } from './scripts.ts'
|
|
|
16
16
|
|
|
17
17
|
/** Base stacks of every preset: Source Serif 4, IBM Plex Sans, IBM Plex Mono. */
|
|
18
18
|
export const BASE_FONT_SPECS: readonly string[] = [
|
|
19
|
-
'Source Serif 4:ital,opsz,wght@0,8..60,400;0,8..60,600;0,8..60,700;1,8..60,400',
|
|
20
|
-
'IBM Plex Sans:ital,wght@0,400;0,500;0,600;0,700;1,400',
|
|
19
|
+
'Source Serif 4:ital,opsz,wght@0,8..60,400;0,8..60,600;0,8..60,700;1,8..60,400;1,8..60,600',
|
|
20
|
+
'IBM Plex Sans:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600',
|
|
21
21
|
'IBM Plex Mono:ital,wght@0,400;0,500;0,600;0,700;1,400',
|
|
22
22
|
]
|
|
23
23
|
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './dtcg.ts'
|
|
|
6
6
|
export * from './stylesheet.ts'
|
|
7
7
|
export * from './flow.ts'
|
|
8
8
|
export * from './print-presets.ts'
|
|
9
|
+
export * from './print-book-themes.ts'
|
|
9
10
|
export * from './print-themes.ts'
|
|
10
11
|
export * from './print-aliases.ts'
|
|
11
12
|
export * from './fonts.ts'
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// Book themes: how a publisher or a collection defines its own look for
|
|
2
|
+
// @datatechsolutions/tympan-print without touching tympan's code.
|
|
3
|
+
//
|
|
4
|
+
// A theme inherits a base (one of the 39 presets or another registered theme)
|
|
5
|
+
// and overrides any token of PrintStyleOverrides, plus what belongs to a
|
|
6
|
+
// publisher rather than to a style: its marks (publisher logo, collection
|
|
7
|
+
// seal), the page format and bleed, the running head and folio, and local
|
|
8
|
+
// font files. Register it by id and pass the id to PrintBook as `estilo`.
|
|
9
|
+
//
|
|
10
|
+
// A theme may come from JSON, either in the theme's own shape
|
|
11
|
+
// (`PrintBookThemeJson`) or as a DTCG tree (`printThemeFromDtcg`), so a brand's
|
|
12
|
+
// identity can live in the brand's repository.
|
|
13
|
+
|
|
14
|
+
import { fromDtcgColor, type DtcgColor } from './dtcg.ts'
|
|
15
|
+
import { toHex } from './color.ts'
|
|
16
|
+
import { SYSTEM_FONT_FAMILIES, fontFaceCss, type FontManifest } from './fonts.ts'
|
|
17
|
+
import {
|
|
18
|
+
mergePrintStyle,
|
|
19
|
+
printPresetById,
|
|
20
|
+
resolvePrintStyleName,
|
|
21
|
+
type PrintCoresParciais,
|
|
22
|
+
type PrintPagina,
|
|
23
|
+
type PrintStyle,
|
|
24
|
+
type PrintStyleOverrides,
|
|
25
|
+
} from './print-presets.ts'
|
|
26
|
+
|
|
27
|
+
/** A book theme: an id, a base and overrides. */
|
|
28
|
+
export interface PrintBookTheme extends PrintStyleOverrides {
|
|
29
|
+
/** Registry id, e.g. 'lakebrasil-editorial'. Use it as PrintBook's `estilo`. */
|
|
30
|
+
id: string
|
|
31
|
+
/** A preset name (or deprecated alias) or the id of another registered theme. */
|
|
32
|
+
base: string
|
|
33
|
+
/** What the theme interprets (shown in galleries). */
|
|
34
|
+
referencia?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The JSON form of a theme: the same fields, colours as hex strings or DTCG colour objects. */
|
|
38
|
+
export type PrintBookThemeJson = Omit<PrintBookTheme, 'cor' | 'pb'> & {
|
|
39
|
+
cor?: Record<string, unknown>
|
|
40
|
+
pb?: Record<string, unknown>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const REGISTRO = new Map<string, PrintBookTheme>()
|
|
44
|
+
|
|
45
|
+
/** A style with overrides and theme extras applied (same as mergePrintStyle, kept for readability). */
|
|
46
|
+
export const mergePrintTheme = mergePrintStyle
|
|
47
|
+
|
|
48
|
+
/** Resolves a theme id or a preset id to a complete PrintStyle (`name` = the theme id). */
|
|
49
|
+
export function printThemeById(id: string, visitados: string[] = []): PrintStyle {
|
|
50
|
+
const tema = REGISTRO.get(id)
|
|
51
|
+
if (!tema) return printPresetById(id)
|
|
52
|
+
if (visitados.includes(id)) throw new Error(`tympan: book theme cycle ${[...visitados, id].join(' → ')}`)
|
|
53
|
+
const base = printThemeById(tema.base, [...visitados, id])
|
|
54
|
+
const { id: _id, base: _base, referencia, ...over } = tema
|
|
55
|
+
return { ...mergePrintTheme(base, over), name: tema.id, label: tema.label ?? base.label, referencia: referencia ?? `Tema sobre ${base.name}: ${base.referencia}` }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** True when `id` names a registered theme or a preset. */
|
|
59
|
+
export function isPrintThemeId(id: string): boolean {
|
|
60
|
+
return REGISTRO.has(id) || resolvePrintStyleName(id) !== undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function normalizarCores(c: Record<string, unknown> | undefined): PrintCoresParciais | undefined {
|
|
64
|
+
if (!c) return undefined
|
|
65
|
+
const cor = (v: unknown): string => (typeof v === 'string' ? v : toHex(fromDtcgColor(v as DtcgColor)))
|
|
66
|
+
const out: Record<string, unknown> = {}
|
|
67
|
+
for (const [k, v] of Object.entries(c)) {
|
|
68
|
+
if ((k === 'prova' || k === 'temas' || k === 'categorias') && v && typeof v === 'object') out[k] = Object.fromEntries(Object.entries(v).map(([e, x]) => [e, cor(x)]))
|
|
69
|
+
else if ((k === 'ornamento' || k === 'categorica' || k === 'divergente') && Array.isArray(v)) out[k] = v.map(cor)
|
|
70
|
+
else out[k] = cor(v)
|
|
71
|
+
}
|
|
72
|
+
return out as PrintCoresParciais
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Validates and normalises a theme read from JSON (colours may be hex or DTCG colour objects). */
|
|
76
|
+
export function printThemeFromJson(json: PrintBookThemeJson | string): PrintBookTheme {
|
|
77
|
+
const j = (typeof json === 'string' ? JSON.parse(json) : json) as PrintBookThemeJson
|
|
78
|
+
if (!j || typeof j.id !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(j.id)) throw new Error('tympan: a book theme needs an `id` in kebab case')
|
|
79
|
+
if (typeof j.base !== 'string') throw new Error(`tympan: book theme "${j.id}" needs a \`base\` (a preset or a registered theme)`)
|
|
80
|
+
const { cor, pb, ...rest } = j
|
|
81
|
+
const out: PrintBookTheme = { ...rest }
|
|
82
|
+
const c = normalizarCores(cor)
|
|
83
|
+
if (c) out.cor = c
|
|
84
|
+
const p = normalizarCores(pb)
|
|
85
|
+
if (p) out.pb = p
|
|
86
|
+
return out
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type No = Record<string, unknown>
|
|
90
|
+
|
|
91
|
+
/** Namespaces read for the theme's meta (id/name, base/extends): tympan's own and the short one brand repos use. */
|
|
92
|
+
const EXTS = ['br.com.datatechsolutions.tympan', 'dev.tympan.print'] as const
|
|
93
|
+
|
|
94
|
+
/** Unwraps a DTCG group into plain values: `{ $value }` leaves become their value; `$`-keys are dropped. */
|
|
95
|
+
function desembrulhar(n: unknown): unknown {
|
|
96
|
+
if (!n || typeof n !== 'object' || Array.isArray(n)) return n
|
|
97
|
+
const o = n as No
|
|
98
|
+
if ('$value' in o) {
|
|
99
|
+
const v = o.$value
|
|
100
|
+
if (o.$type === 'fontFamily' && Array.isArray(v)) return pilhaDeFontes(v as string[])
|
|
101
|
+
if (o.$type === 'color' && v && typeof v === 'object') return toHex(fromDtcgColor(v as DtcgColor))
|
|
102
|
+
if (o.$type === 'dimension' && v && typeof v === 'object' && 'unit' in (v as No)) return emMm(v as { value: number; unit: string })
|
|
103
|
+
return v
|
|
104
|
+
}
|
|
105
|
+
const out: No = {}
|
|
106
|
+
for (const [k, v] of Object.entries(o)) if (!k.startsWith('$')) out[k] = desembrulhar(v)
|
|
107
|
+
return out
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const GENERICAS = /^(serif|sans-serif|monospace|cursive|fantasy|system-ui|ui-[a-z-]+)$/
|
|
111
|
+
const pilhaDeFontes = (v: string[]) => v.map((x) => (GENERICAS.test(x) || /^["']/.test(x) ? x : `"${x}"`)).join(', ')
|
|
112
|
+
const emMm = (v: { value: number; unit: string }) => (v.unit === 'px' ? (v.value * 25.4) / 96 : v.unit === 'pt' ? (v.value * 25.4) / 72 : v.unit === 'cm' ? v.value * 10 : v.value)
|
|
113
|
+
|
|
114
|
+
/** Figures from the meta: an object by role, or a phrase such as "tabular-nums em numero e rotulo". */
|
|
115
|
+
function lerAlgarismos(v: unknown): Record<string, 'tabular' | 'proporcional'> | undefined {
|
|
116
|
+
if (v && typeof v === 'object') return v as Record<string, 'tabular' | 'proporcional'>
|
|
117
|
+
if (typeof v !== 'string') return undefined
|
|
118
|
+
const tipo = /proporc|proportional/.test(v) ? 'proporcional' : /tabular/.test(v) ? 'tabular' : undefined
|
|
119
|
+
if (!tipo) return undefined
|
|
120
|
+
const papeis = ['numero', 'rotulo', 'corpo'].filter((p) => v.includes(p))
|
|
121
|
+
return Object.fromEntries((papeis.length ? papeis : ['numero']).map((p) => [p, tipo]))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A theme from a DTCG (2025.10) tree. The tree mirrors PrintStyleOverrides: each group is a field
|
|
126
|
+
* (`label`, `fontes.<role>`, `googleFonts`, `cor.<role>`, `cor.prova.<state>`, `cor.categorica.<n>`,
|
|
127
|
+
* `cor.divergente.<n>`, `traco.largura`, `pb.*`, `estrutura.*`, `texto.*`, `marcas.*`, `pagina.*`, `folio.*`,
|
|
128
|
+
* `fontesLocais`…) and each `{ "$type", "$value" }` leaf becomes its value (colours as hex or DTCG colour
|
|
129
|
+
* objects, `fontFamily` arrays become CSS stacks, dimensions become mm). The theme's id and base come from
|
|
130
|
+
* the root `$extensions["dev.tympan.print"]` (or `["br.com.datatechsolutions.tympan"]`) as `name`/`id` and
|
|
131
|
+
* `extends`/`base`.
|
|
132
|
+
*/
|
|
133
|
+
export function printThemeFromDtcg(tree: No | string): PrintBookTheme {
|
|
134
|
+
const t = (typeof tree === 'string' ? JSON.parse(tree) : tree) as No
|
|
135
|
+
const exts = (t.$extensions ?? {}) as No
|
|
136
|
+
const meta = Object.assign({}, ...EXTS.map((e) => exts[e] as No | undefined).filter(Boolean).map((m) => ({ ...(m!.tema as No | undefined), ...m }))) as No
|
|
137
|
+
const id = (meta.id ?? meta.name) as string | undefined
|
|
138
|
+
const base = (meta.base ?? meta.extends) as string | undefined
|
|
139
|
+
if (!id || !base) throw new Error(`tympan: a DTCG book theme needs $extensions["dev.tympan.print"] with name and extends`)
|
|
140
|
+
const corpo = desembrulhar(t) as No
|
|
141
|
+
// Ordered lists written as groups ("1", "2"… or "a", "b"…) become arrays.
|
|
142
|
+
const cor = corpo.cor as No | undefined
|
|
143
|
+
for (const g of [cor, corpo.pb as No | undefined])
|
|
144
|
+
if (g) for (const k of ['categorica', 'divergente', 'ornamento']) if (g[k] && typeof g[k] === 'object' && !Array.isArray(g[k])) g[k] = Object.entries(g[k] as No).sort(([a], [b]) => a.localeCompare(b, 'en', { numeric: true })).map(([, v]) => v)
|
|
145
|
+
// Brand-repo extras in the meta: per-role weights, figures and the data palette.
|
|
146
|
+
const texto = { ...(corpo.texto as No | undefined) }
|
|
147
|
+
if (meta.pesos && typeof meta.pesos === 'object') texto.pesos = { ...(meta.pesos as No), ...(texto.pesos as No | undefined) }
|
|
148
|
+
const alg = lerAlgarismos(meta.algarismos)
|
|
149
|
+
if (alg) texto.algarismos = { ...alg, ...(texto.algarismos as No | undefined) }
|
|
150
|
+
if (Object.keys(texto).length) corpo.texto = texto
|
|
151
|
+
const dado = meta.dado as Record<string, string> | undefined
|
|
152
|
+
if (dado) {
|
|
153
|
+
const c = { ...(corpo.cor as No | undefined) }
|
|
154
|
+
if (dado.interesse && c.interesse === undefined) c.interesse = dado.interesse
|
|
155
|
+
if (dado.contexto) c.contexto = dado.contexto
|
|
156
|
+
if (dado.perdeu && dado.ganhou && c.divergente === undefined) c.divergente = [dado.perdeu, dado.ganhou]
|
|
157
|
+
corpo.cor = c
|
|
158
|
+
}
|
|
159
|
+
return printThemeFromJson({ ...corpo, id, base, ...(meta.referencia ? { referencia: meta.referencia as string } : {}) } as PrintBookThemeJson)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Registers (or replaces) a theme; accepts the theme object, its JSON or a DTCG tree. Returns the theme. */
|
|
163
|
+
export function registerPrintTheme(tema: PrintBookTheme | PrintBookThemeJson | No | string): PrintBookTheme {
|
|
164
|
+
const obj = typeof tema === 'string' ? (JSON.parse(tema) as No) : (tema as No)
|
|
165
|
+
const t = obj.$extensions ? printThemeFromDtcg(obj) : printThemeFromJson(obj as PrintBookThemeJson)
|
|
166
|
+
if (resolvePrintStyleName(t.id)) throw new Error(`tympan: book theme id "${t.id}" collides with a preset`)
|
|
167
|
+
REGISTRO.set(t.id, t)
|
|
168
|
+
printThemeById(t.id) // fails now on an unknown base or a cycle
|
|
169
|
+
return t
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Removes a registered theme (tests, hot reload). */
|
|
173
|
+
export function unregisterPrintTheme(id: string): boolean {
|
|
174
|
+
return REGISTRO.delete(id)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Ids of the registered themes, in registration order. */
|
|
178
|
+
export function registeredPrintThemes(): string[] {
|
|
179
|
+
return [...REGISTRO.keys()]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** `@font-face` rules for a style's local font files ('' when it has none). */
|
|
183
|
+
export function fontesLocaisCss(style: Pick<PrintStyle, 'fontesLocais'>): string {
|
|
184
|
+
return (style.fontesLocais ?? [])
|
|
185
|
+
.map(
|
|
186
|
+
(f) =>
|
|
187
|
+
`@font-face { font-family: "${f.familia.replace(/"/g, '')}"; src: url("${f.src.replace(/"/g, '%22')}") format("woff2"); font-weight: ${f.peso ?? 400}; font-style: ${f.estilo ?? 'normal'}; font-display: block; }`,
|
|
188
|
+
)
|
|
189
|
+
.join('\n')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Trim and bleed of a style (defaults 170 × 240 mm, 3 mm bleed). */
|
|
193
|
+
export function paginaDoEstilo(style: Pick<PrintStyle, 'pagina'>): Required<PrintPagina> {
|
|
194
|
+
const p = style.pagina ?? {}
|
|
195
|
+
return { largura: p.largura ?? 170, altura: p.altura ?? 240, sangria: p.sangria ?? 3, marcasCorte: p.marcasCorte ?? false }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** A neutral example theme (registered nowhere by default): a small press over `semanario`, in two inks. */
|
|
199
|
+
export const TEMA_EXEMPLO_EDITORA: PrintBookTheme = {
|
|
200
|
+
id: 'editora-exemplo',
|
|
201
|
+
base: 'semanario',
|
|
202
|
+
label: 'Editora Exemplo · coleção ensaios',
|
|
203
|
+
fontes: { titulo: '"Source Serif 4", Georgia, serif' },
|
|
204
|
+
cor: { destaque: '#1f4e79', destaque2: '#b5562b', marcaTexto: '#e8eef5' },
|
|
205
|
+
pb: { destaque: '#333333', destaque2: '#8a8a8a' },
|
|
206
|
+
texto: { nomesProprios: 'preservar' },
|
|
207
|
+
folio: { folio: 'centro', cabeco: 'externo' },
|
|
208
|
+
pagina: { largura: 160, altura: 230, sangria: 3 },
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
/** Families named in a style's font stacks (quotes removed), in role order, without generic and system faces. */
|
|
213
|
+
export function familiasDoEstilo(style: Pick<PrintStyle, 'fontes'>): string[] {
|
|
214
|
+
const out: string[] = []
|
|
215
|
+
for (const pilha of Object.values(style.fontes) as string[])
|
|
216
|
+
for (const f of pilha.split(',').map((x) => x.trim().replace(/^["']|["']$/g, '')))
|
|
217
|
+
if (f && !SYSTEM_FONT_FAMILIES.includes(f) && !out.includes(f)) out.push(f)
|
|
218
|
+
return out
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Families the style names (first of each stack) that have neither a bundled file nor a theme `fontesLocais`. */
|
|
222
|
+
export function familiasSemArquivo(style: Pick<PrintStyle, 'fontes' | 'fontesLocais'>, manifest: FontManifest): string[] {
|
|
223
|
+
const proprias = new Set((style.fontesLocais ?? []).map((f) => f.familia))
|
|
224
|
+
const primeiras = (Object.values(style.fontes) as string[]).map((p) => p.split(',')[0]!.trim().replace(/^["']|["']$/g, ''))
|
|
225
|
+
return [...new Set(primeiras)].filter((f) => !SYSTEM_FONT_FAMILIES.includes(f) && !proprias.has(f) && !manifest.families[f])
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @font-face rules for a style from the fonts bundled in this package (`fonts/manifest.json`): every face of
|
|
230
|
+
* every family its font stacks name (so a mono or label family outside `googleFonts` is not forgotten), with
|
|
231
|
+
* URLs `<base><file>`, then the theme's own `fontesLocais`, which win for their family.
|
|
232
|
+
*/
|
|
233
|
+
export function fontesEmpacotadasCss(style: Pick<PrintStyle, 'fontes' | 'googleFonts' | 'fontesLocais'>, manifest: FontManifest, base: string): string {
|
|
234
|
+
const proprias = new Set((style.fontesLocais ?? []).map((f) => f.familia))
|
|
235
|
+
const familias = new Set(familiasDoEstilo(style).filter((f) => manifest.families[f] && !proprias.has(f)))
|
|
236
|
+
// Every bundled spec of those families (faces are de-duplicated by fontFaceCss).
|
|
237
|
+
const specs = Object.keys(manifest.specs).filter((s) => familias.has(s.split(':')[0]!))
|
|
238
|
+
return [specs.length ? fontFaceCss(manifest, specs, base) : '', fontesLocaisCss(style)].filter(Boolean).join('\n')
|
|
239
|
+
}
|
package/src/print-presets.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// white variant) into `--ty-print-*` custom properties on
|
|
12
12
|
// `[data-ty-print-style="<name>"]`.
|
|
13
13
|
|
|
14
|
-
import { luminance, parseColor, toHex, type Rgba } from './color.ts'
|
|
14
|
+
import { contrastRatio, luminance, oklchToRgb, parseColor, rgbToOklch, toHex, type Rgba } from './color.ts'
|
|
15
15
|
import { PRINT_STYLE_ALIASES, warnDeprecatedPrintId, type PrintStyleAlias } from './print-aliases.ts'
|
|
16
16
|
|
|
17
17
|
export type RenderizadorGrafico = 'limpo' | 'mao' | 'isotype' | 'gravura' | 'prancheta' | 'aquarela' | 'riso' | 'pontos'
|
|
@@ -20,7 +20,17 @@ export type EstadoProva = 'sustentada' | 'refutada' | 'nao-da-para-afirmar' | 'p
|
|
|
20
20
|
|
|
21
21
|
export const ESTADOS_PROVA: readonly EstadoProva[] = ['sustentada', 'refutada', 'nao-da-para-afirmar', 'pendente', 'sem-dado', 'nao-testada']
|
|
22
22
|
|
|
23
|
-
/** The word printed for each proof state (
|
|
23
|
+
/** The word printed for each proof state in English (tympan-print picks by the book's `lang`). */
|
|
24
|
+
export const ROTULOS_PROVA_EN: Record<EstadoProva, string> = {
|
|
25
|
+
sustentada: 'Supported',
|
|
26
|
+
refutada: 'Refuted',
|
|
27
|
+
'nao-da-para-afirmar': 'Cannot claim',
|
|
28
|
+
pendente: 'Pending',
|
|
29
|
+
'sem-dado': 'No data',
|
|
30
|
+
'nao-testada': 'Not tested',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The word printed for each proof state (always visible, whatever the mark's shape); pt-BR. */
|
|
24
34
|
export const ROTULOS_PROVA: Record<EstadoProva, string> = {
|
|
25
35
|
sustentada: 'Sustentada',
|
|
26
36
|
refutada: 'Refutada',
|
|
@@ -52,6 +62,16 @@ export interface PrintCores {
|
|
|
52
62
|
prova: Record<EstadoProva, string>
|
|
53
63
|
/** Colours of page ornaments only (frames, tiles, waves); never data. Up to four. */
|
|
54
64
|
ornamento?: string[]
|
|
65
|
+
/** Colours by category key (official party colours…): used by categorical maps, parliament and bars. */
|
|
66
|
+
categorias?: Record<string, string>
|
|
67
|
+
/** Colours of data themes (blocks of a panel page: economia, saude…); --ty-print-tema-<id>. */
|
|
68
|
+
temas?: Record<string, string>
|
|
69
|
+
/** The series of interest in maps and charts (default: `destaque`), when the accent of the page differs from the data ink. */
|
|
70
|
+
interesse?: string
|
|
71
|
+
/** Data palette, diverging: the low and the high side (default destaque2 × destaque). */
|
|
72
|
+
divergente?: [string, string]
|
|
73
|
+
/** Data palette, qualitative (party, category: no implied order), up to eight (default: a colour-blind-safe set). */
|
|
74
|
+
categorica?: string[]
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
/** Partial colours: any role, and any subset of the proof states. */
|
|
@@ -137,6 +157,16 @@ export interface PrintEstrutura {
|
|
|
137
157
|
*/
|
|
138
158
|
realce?: 'serie' | 'linha'
|
|
139
159
|
/** Head of each small multiple: plain title (default), a band in the highlighter colour with an arrow (Schiphol), a dark band (Vignelli), a band in the ornament colour (Aicher), each multiple on its own tinted card (Le Corbusier, Bayer). */
|
|
160
|
+
/**
|
|
161
|
+
* Long runs (one page per unit, thousands of pages): no page ornaments, no tinted panel fills, the 'bolso'
|
|
162
|
+
* panel as a rule instead of a filled box. The style's type, colours and frames stay.
|
|
163
|
+
*/
|
|
164
|
+
economiaTinta?: boolean
|
|
165
|
+
/**
|
|
166
|
+
* Pages without a molde fill the type area: panels widen to close their row of the six columns and rows of
|
|
167
|
+
* panels share the free height (dashboard). A page can turn it on or off with `preencher`.
|
|
168
|
+
*/
|
|
169
|
+
preencher?: boolean
|
|
140
170
|
cabecaMultiplo?: 'titulo' | 'faixa' | 'faixa-escura' | 'faixa-cor' | 'caixa-cor'
|
|
141
171
|
}
|
|
142
172
|
|
|
@@ -176,7 +206,7 @@ export type Moldura =
|
|
|
176
206
|
| 'formas' // triangle, square and circle (Bauhaus)
|
|
177
207
|
| 'quartos' // quarter circles in the page corners (diagrama-modernista)
|
|
178
208
|
|
|
179
|
-
export interface PrintStyle {
|
|
209
|
+
export interface PrintStyle extends PrintTemaExtras {
|
|
180
210
|
/** Stable id, e.g. 'jornal'. */
|
|
181
211
|
name: string
|
|
182
212
|
/** Human label, e.g. "Editorial de jornal". */
|
|
@@ -207,10 +237,94 @@ export interface PrintStyle {
|
|
|
207
237
|
* has approved variations (ink, duotone, per style), see LogoDatatech.
|
|
208
238
|
*/
|
|
209
239
|
logo?: 'cor' | 'mono'
|
|
240
|
+
/** Typesetting options a book may need against the style's idiom (see PrintTexto). */
|
|
241
|
+
texto?: PrintTexto
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Typesetting switches for books whose content fights the style: a style in lower case (bauhaus,
|
|
246
|
+
* diagrama-modernista) would print "boa vista" for a proper name; a style with old-style figures would
|
|
247
|
+
* misalign table columns.
|
|
248
|
+
*/
|
|
249
|
+
export interface PrintTexto {
|
|
250
|
+
/**
|
|
251
|
+
* 'estilo' (default): page titles and eyebrows follow the style's casing. 'preservar': the level-1 title,
|
|
252
|
+
* eyebrows and table cells keep the casing written in the content (proper names, codes), while panel labels
|
|
253
|
+
* and running heads keep the style's.
|
|
254
|
+
*/
|
|
255
|
+
nomesProprios?: 'estilo' | 'preservar'
|
|
256
|
+
/** Figures in table cells: 'tabular' (default, lining and equal width, columns align) or 'proporcional'. */
|
|
257
|
+
numerosTabela?: 'tabular' | 'proporcional'
|
|
258
|
+
/** Font weight per role (titles, key numbers, labels, body, notes, mono), e.g. { titulo: 600, numero: 600 }. */
|
|
259
|
+
pesos?: Partial<Record<keyof PrintFontes, number>>
|
|
260
|
+
/** Figures per role: 'tabular' (equal width, lining) or 'proporcional'. Tables follow `numerosTabela`. */
|
|
261
|
+
algarismos?: Partial<Record<'numero' | 'rotulo' | 'corpo', 'tabular' | 'proporcional'>>
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// Book-theme extras (see print-book-themes.ts)
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
/** A mark the theme prints (publisher logo, collection seal): an image URL (data: URIs work offline) and its text alternative. */
|
|
269
|
+
export interface PrintMarca {
|
|
270
|
+
src: string
|
|
271
|
+
alt: string
|
|
272
|
+
/** Width in mm where the mark is placed (default 18 for the logo, 12 for the seal). */
|
|
273
|
+
largura?: number
|
|
274
|
+
/** Image used in black and white print (default: the same `src`, greyed by CSS). */
|
|
275
|
+
srcPb?: string
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Page format and print finishing. */
|
|
279
|
+
export interface PrintPagina {
|
|
280
|
+
/** Trim width in mm (default 170). */
|
|
281
|
+
largura?: number
|
|
282
|
+
/** Trim height in mm (default 240). */
|
|
283
|
+
altura?: number
|
|
284
|
+
/** Bleed in mm added around every page when the book prints for the press (default 3). */
|
|
285
|
+
sangria?: number
|
|
286
|
+
/** Crop marks in the slug, outside the bleed (default false). */
|
|
287
|
+
marcasCorte?: boolean
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Running head and folio. */
|
|
291
|
+
export interface PrintFolio {
|
|
292
|
+
/** Where the folio sits: outer corner (default) or centred. */
|
|
293
|
+
folio?: 'externo' | 'centro'
|
|
294
|
+
/** Where the running head sits: outer corner (default), centred, or not printed. */
|
|
295
|
+
cabeco?: 'externo' | 'centro' | 'nenhum'
|
|
296
|
+
/** Text before the folio number, e.g. "p. ". */
|
|
297
|
+
prefixoFolio?: string
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** A local font file (offline, deterministic builds and EPUB): one @font-face. */
|
|
301
|
+
export interface PrintFonteLocal {
|
|
302
|
+
/** Family name as used in the theme's font stacks. */
|
|
303
|
+
familia: string
|
|
304
|
+
/** URL or path of the .woff2 (relative to the HTML, absolute, or a data: URI). */
|
|
305
|
+
src: string
|
|
306
|
+
peso?: number | string
|
|
307
|
+
estilo?: 'normal' | 'italic'
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** What a theme adds to a preset beyond the style tokens. */
|
|
311
|
+
export interface PrintTemaExtras {
|
|
312
|
+
marcas?: { editora?: PrintMarca; selo?: PrintMarca }
|
|
313
|
+
pagina?: PrintPagina
|
|
314
|
+
folio?: PrintFolio
|
|
315
|
+
/** Local font files; with them PrintBook can run without network (`fontes="locais"`). */
|
|
316
|
+
fontesLocais?: PrintFonteLocal[]
|
|
317
|
+
/**
|
|
318
|
+
* Trace of a number (Rastro): sign it with the theme's seal (`marcas.selo`) or the lakebrasil mark, and add
|
|
319
|
+
* the lake version seal when the version carries a date. Default: neutral (neither).
|
|
320
|
+
*/
|
|
321
|
+
rastro?: { assinatura?: 'selo' | 'lakebrasil' | 'nenhuma'; seloLake?: boolean }
|
|
322
|
+
/** Texts the print components write by themselves, by key (see tympan-print i18n: TEXTOS_PT / TEXTOS_EN). */
|
|
323
|
+
textos?: Record<string, string>
|
|
210
324
|
}
|
|
211
325
|
|
|
212
326
|
/** Any token of a style may be overridden by the editor (partial, nested). */
|
|
213
|
-
export interface PrintStyleOverrides {
|
|
327
|
+
export interface PrintStyleOverrides extends PrintTemaExtras {
|
|
214
328
|
label?: string
|
|
215
329
|
fontes?: Partial<PrintFontes>
|
|
216
330
|
googleFonts?: string[]
|
|
@@ -224,6 +338,7 @@ export interface PrintStyleOverrides {
|
|
|
224
338
|
pb?: PrintCoresParciais
|
|
225
339
|
estrutura?: Partial<PrintEstrutura>
|
|
226
340
|
logo?: 'cor' | 'mono'
|
|
341
|
+
texto?: PrintTexto
|
|
227
342
|
}
|
|
228
343
|
|
|
229
344
|
// ---------------------------------------------------------------------------
|
|
@@ -265,8 +380,9 @@ const dashboard: PrintStyle = {
|
|
|
265
380
|
tinta2: '#3d3933',
|
|
266
381
|
tinta3: '#5f594f',
|
|
267
382
|
linha: '#d6cfc2',
|
|
268
|
-
|
|
269
|
-
|
|
383
|
+
// The highlight is not the ink (a highlighted bar must stand out): rust, AA on the paper, not a party colour.
|
|
384
|
+
destaque: '#9a4f2a',
|
|
385
|
+
destaque2: '#c4b8a8',
|
|
270
386
|
marcaTexto: '#f3e6cf',
|
|
271
387
|
contexto: '#8f877a',
|
|
272
388
|
prova: {
|
|
@@ -285,7 +401,7 @@ const dashboard: PrintStyle = {
|
|
|
285
401
|
raio: 1.6,
|
|
286
402
|
caixaAlta: false,
|
|
287
403
|
pb: {},
|
|
288
|
-
estrutura: { painel: 'nenhum', rotulo: 'letra', rotuloCaixa: 'alta', notas: 'dentro', figura: 'simples', minusculas: false },
|
|
404
|
+
estrutura: { painel: 'nenhum', rotulo: 'letra', rotuloCaixa: 'alta', notas: 'dentro', figura: 'simples', minusculas: false, preencher: true },
|
|
289
405
|
}
|
|
290
406
|
|
|
291
407
|
const graficos1900: PrintStyle = {
|
|
@@ -1476,7 +1592,26 @@ function mergeCores(base: PrintCores, over?: PrintCoresParciais): PrintCores {
|
|
|
1476
1592
|
if (!over) return base
|
|
1477
1593
|
const { prova, ...rest } = over
|
|
1478
1594
|
const clean = Object.fromEntries(Object.entries(rest).filter(([, v]) => v !== undefined)) as Partial<PrintCores>
|
|
1479
|
-
|
|
1595
|
+
const temas = base.temas || clean.temas ? { temas: { ...base.temas, ...clean.temas } } : {}
|
|
1596
|
+
const categorias = base.categorias || clean.categorias ? { categorias: { ...base.categorias, ...clean.categorias } } : {}
|
|
1597
|
+
return { ...base, ...clean, ...temas, ...categorias, prova: { ...base.prova, ...(prova ?? {}) } }
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
function mergeExtras(base: PrintTemaExtras, over: PrintTemaExtras): PrintTemaExtras {
|
|
1601
|
+
const out: PrintTemaExtras = {}
|
|
1602
|
+
const marcas = { ...base.marcas, ...over.marcas }
|
|
1603
|
+
if (Object.keys(marcas).length) out.marcas = marcas
|
|
1604
|
+
const pagina = { ...base.pagina, ...over.pagina }
|
|
1605
|
+
if (Object.keys(pagina).length) out.pagina = pagina
|
|
1606
|
+
const folio = { ...base.folio, ...over.folio }
|
|
1607
|
+
if (Object.keys(folio).length) out.folio = folio
|
|
1608
|
+
const fl = over.fontesLocais ?? base.fontesLocais
|
|
1609
|
+
if (fl) out.fontesLocais = fl
|
|
1610
|
+
const rs = { ...base.rastro, ...over.rastro }
|
|
1611
|
+
if (Object.keys(rs).length) out.rastro = rs
|
|
1612
|
+
const tx = { ...base.textos, ...over.textos }
|
|
1613
|
+
if (Object.keys(tx).length) out.textos = tx
|
|
1614
|
+
return out
|
|
1480
1615
|
}
|
|
1481
1616
|
|
|
1482
1617
|
/** A style with the editor's overrides applied (nested, partial). */
|
|
@@ -1494,9 +1629,12 @@ export function mergePrintStyle(style: PrintStyle, overrides?: PrintStyleOverrid
|
|
|
1494
1629
|
marcaProva: overrides.marcaProva ?? style.marcaProva,
|
|
1495
1630
|
raio: overrides.raio ?? style.raio,
|
|
1496
1631
|
caixaAlta: overrides.caixaAlta ?? style.caixaAlta,
|
|
1497
|
-
|
|
1632
|
+
// P&B adjustments stay partial: merging them over the full colour set would put colours back over the greys.
|
|
1633
|
+
pb: overrides.pb ? { ...style.pb, ...overrides.pb, ...(style.pb.prova || overrides.pb.prova ? { prova: { ...style.pb.prova, ...overrides.pb.prova } } : {}) } : style.pb,
|
|
1498
1634
|
estrutura: { ...style.estrutura, ...overrides.estrutura },
|
|
1499
1635
|
logo: overrides.logo ?? style.logo,
|
|
1636
|
+
...(overrides.texto || style.texto ? { texto: { ...style.texto, ...overrides.texto } } : {}),
|
|
1637
|
+
...mergeExtras(style, overrides),
|
|
1500
1638
|
}
|
|
1501
1639
|
}
|
|
1502
1640
|
|
|
@@ -1525,6 +1663,10 @@ export function printStyleCoresPb(style: PrintStyle): PrintCores {
|
|
|
1525
1663
|
contexto: toGrey(g.contexto),
|
|
1526
1664
|
prova: Object.fromEntries(ESTADOS_PROVA.map((e) => [e, toGrey(g.prova[e])])) as Record<EstadoProva, string>,
|
|
1527
1665
|
...(g.ornamento ? { ornamento: g.ornamento.map(toGrey) } : {}),
|
|
1666
|
+
...(g.interesse ? { interesse: toGrey(g.interesse) } : {}),
|
|
1667
|
+
...(g.temas ? { temas: Object.fromEntries(Object.entries(g.temas).map(([k, v]) => [k, toGrey(v)])) } : {}),
|
|
1668
|
+
...(g.divergente ? { divergente: g.divergente.map(toGrey) as [string, string] } : {}),
|
|
1669
|
+
categorica: categoricaDoEstilo(style).map(toGrey),
|
|
1528
1670
|
}
|
|
1529
1671
|
return mergeCores(grey, style.pb)
|
|
1530
1672
|
}
|
|
@@ -1550,7 +1692,7 @@ export function resolvePrintStyle(style: PrintStyle | PrintStyleId, options: Omi
|
|
|
1550
1692
|
return { ...merged, cor: printStyleCoresPb(merged) }
|
|
1551
1693
|
}
|
|
1552
1694
|
|
|
1553
|
-
const kebab: Record<Exclude<keyof PrintCores, 'prova' | 'ornamento'>, string> = {
|
|
1695
|
+
const kebab: Record<Exclude<keyof PrintCores, 'prova' | 'ornamento' | 'divergente' | 'categorica' | 'interesse' | 'temas' | 'categorias'>, string> = {
|
|
1554
1696
|
papel: 'papel',
|
|
1555
1697
|
tinta: 'tinta',
|
|
1556
1698
|
tinta2: 'tinta-2',
|
|
@@ -1564,6 +1706,58 @@ const kebab: Record<Exclude<keyof PrintCores, 'prova' | 'ornamento'>, string> =
|
|
|
1564
1706
|
|
|
1565
1707
|
const num = (n: number) => String(Math.round(n * 1000) / 1000)
|
|
1566
1708
|
|
|
1709
|
+
/**
|
|
1710
|
+
* `cor` moved along OKLCH lightness (hue and chroma kept) until it reaches `min`:1 against `papel`: the
|
|
1711
|
+
* context grey of a theme stays visible on cream paper (areas ≥ 1.5:1, strokes ≥ 3:1).
|
|
1712
|
+
*/
|
|
1713
|
+
export function contrasteMinimo(cor: string, papel: string, min: number): string {
|
|
1714
|
+
const p = parseColor(papel)
|
|
1715
|
+
if (contrastRatio(parseColor(cor), p) >= min) return toHex(parseColor(cor))
|
|
1716
|
+
const v = rgbToOklch(parseColor(cor))
|
|
1717
|
+
const passo = luminance(p) > 0.2 ? -0.005 : 0.005
|
|
1718
|
+
let out = parseColor(cor)
|
|
1719
|
+
for (let i = 0; i < 200 && contrastRatio(out, p) < min; i++) {
|
|
1720
|
+
v.l = Math.min(0.995, Math.max(0.02, v.l + passo))
|
|
1721
|
+
out = oklchToRgb(v)
|
|
1722
|
+
}
|
|
1723
|
+
return toHex(out)
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
const SELETORES_PAPEL: Record<keyof PrintFontes, string> = {
|
|
1727
|
+
titulo: ':is(h1, h2, h3, h4, .ty-print-title, .ty-print-figure-title, [class*="-title"]:not(.ty-print-panel-title, .ty-print-block-title, .ty-print-margin-title))',
|
|
1728
|
+
numero: ':is(.ty-print-number-value, .ty-print-table td[data-numerica], .ty-print-parliament-total, [class*="-numero"], [class*="-number"])',
|
|
1729
|
+
rotulo: ':is(.ty-print-panel-title, .ty-print-block-title, .ty-print-margin-title, .ty-print-sobretitle, .ty-print-table th, .ty-print-map-legend, .ty-print-running-head, .ty-print-folio, .ty-print-letter)',
|
|
1730
|
+
corpo: '',
|
|
1731
|
+
anotacao: ':is(.ty-print-annotation, .ty-print-margin, .ty-print-callout-text)',
|
|
1732
|
+
mono: ':is(code, pre, .ty-print-mono, .ty-print-table td[data-mono])',
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* Unlayered rules (they win over `@layer tympan-print`) for a theme's per-role font weights and figures,
|
|
1737
|
+
* scoped to `seletor`; '' when the theme sets neither.
|
|
1738
|
+
*/
|
|
1739
|
+
export function printTextoRolesCss(style: Pick<PrintStyle, 'texto'>, seletor: string): string {
|
|
1740
|
+
const out: string[] = []
|
|
1741
|
+
for (const [papel, peso] of Object.entries(style.texto?.pesos ?? {}) as Array<[keyof PrintFontes, number]>) {
|
|
1742
|
+
const s = SELETORES_PAPEL[papel]
|
|
1743
|
+
out.push(`${seletor}${s ? ` ${s}` : ''} { font-weight: ${peso}; }`)
|
|
1744
|
+
}
|
|
1745
|
+
for (const [papel, a] of Object.entries(style.texto?.algarismos ?? {}) as Array<[keyof PrintFontes, string]>) {
|
|
1746
|
+
const s = SELETORES_PAPEL[papel]
|
|
1747
|
+
out.push(`${seletor}${s ? ` ${s}` : ''} { font-variant-numeric: ${a === 'tabular' ? 'tabular-nums lining-nums' : 'proportional-nums'}; }`)
|
|
1748
|
+
}
|
|
1749
|
+
return out.join('\n')
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/** Qualitative palette (Okabe & Ito 2008, colour-blind safe) used when a style defines none. */
|
|
1753
|
+
export const CATEGORICA_PADRAO = ['#0072b2', '#e69f00', '#009e73', '#cc79a7', '#56b4e9', '#d55e00', '#f0e442', '#555555'] as const
|
|
1754
|
+
|
|
1755
|
+
/** The eight qualitative data colours of a style: its own `categorica`, completed by CATEGORICA_PADRAO. */
|
|
1756
|
+
export function categoricaDoEstilo(style: Pick<PrintStyle, 'cor'>): string[] {
|
|
1757
|
+
const own = style.cor.categorica ?? []
|
|
1758
|
+
return Array.from({ length: 8 }, (_, i) => own[i] ?? CATEGORICA_PADRAO[i]!)
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1567
1761
|
/** Custom properties of a resolved style, in a stable order. */
|
|
1568
1762
|
export function printStyleVariables(style: PrintStyle): Array<[string, string]> {
|
|
1569
1763
|
const vars: Array<[string, string]> = []
|
|
@@ -1581,6 +1775,22 @@ export function printStyleVariables(style: PrintStyle): Array<[string, string]>
|
|
|
1581
1775
|
vars.push(['--ty-print-rotulo-caixa', minusculas ? 'lowercase' : rc === 'alta' ? 'uppercase' : 'none'])
|
|
1582
1776
|
vars.push(['--ty-print-rotulo-variante', rc === 'versalete' ? 'small-caps' : 'normal'])
|
|
1583
1777
|
vars.push(['--ty-print-rotulo-espaco', rc === 'normal' ? 'normal' : '0.07em'])
|
|
1778
|
+
vars.push(['--ty-print-pagina-largura', `${num(style.pagina?.largura ?? 170)}mm`])
|
|
1779
|
+
vars.push(['--ty-print-pagina-altura', `${num(style.pagina?.altura ?? 240)}mm`])
|
|
1780
|
+
const preservar = style.texto?.nomesProprios === 'preservar'
|
|
1781
|
+
vars.push(['--ty-print-titulo1-caixa', preservar ? 'none' : style.caixaAlta ? 'uppercase' : minusculas ? 'lowercase' : 'none'])
|
|
1782
|
+
vars.push(['--ty-print-sobretitulo-caixa', preservar ? 'none' : minusculas ? 'lowercase' : rc === 'alta' ? 'uppercase' : 'none'])
|
|
1783
|
+
vars.push(['--ty-print-celula-caixa', preservar ? 'none' : minusculas ? 'lowercase' : 'none'])
|
|
1784
|
+
vars.push(['--ty-print-tabela-numeros', style.texto?.numerosTabela === 'proporcional' ? 'proportional-nums lining-nums' : 'tabular-nums lining-nums'])
|
|
1785
|
+
for (const [k, v] of Object.entries(style.cor.temas ?? {})) vars.push([`--ty-print-tema-${k.replace(/[^a-z0-9-]/gi, '')}`, v])
|
|
1786
|
+
vars.push(['--ty-print-interesse', style.cor.interesse ?? style.cor.destaque])
|
|
1787
|
+
vars.push(['--ty-print-contexto-area', contrasteMinimo(style.cor.contexto, style.cor.papel, 1.5)])
|
|
1788
|
+
vars.push(['--ty-print-contexto-traco', contrasteMinimo(style.cor.contexto, style.cor.papel, 3)])
|
|
1789
|
+
const dv = style.cor.divergente ?? [style.cor.destaque2, style.cor.destaque]
|
|
1790
|
+
vars.push(['--ty-print-divergente-1', dv[0]])
|
|
1791
|
+
vars.push(['--ty-print-divergente-2', dv[1]])
|
|
1792
|
+
const cat = categoricaDoEstilo(style)
|
|
1793
|
+
cat.forEach((c, i) => vars.push([`--ty-print-categorica-${i + 1}`, c]))
|
|
1584
1794
|
const orn = style.cor.ornamento ?? []
|
|
1585
1795
|
const reserva = [style.cor.destaque, style.cor.destaque2, style.cor.tinta, style.cor.marcaTexto]
|
|
1586
1796
|
for (let i = 0; i < 4; i++) vars.push([`--ty-print-ornamento-${i + 1}`, orn[i] ?? reserva[i]!])
|
|
File without changes
|
|
File without changes
|
/package/fonts/ibmplexsans/{ibmplexsans-italic-greek.woff2 → ibmplexsans-italic-greek-400.woff2}
RENAMED
|
File without changes
|
/package/fonts/ibmplexsans/{ibmplexsans-italic-latin.woff2 → ibmplexsans-italic-latin-400.woff2}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|