Files
Report-showcase/.template/lib/presentation/foundation.typ
T
2026-10-09 01:45:17 +00:00

120 lines
3.4 KiB
Typst

// ==========================================
// PRESENTATION: ОБЩАЯ ОСНОВА ВЁРСТКИ
// ==========================================
#let design-tokens(company, overrides: (:)) = {
let defaults = (
brand-color: company.brand.at("color", default: rgb("e39f49")),
text-font: "Times New Roman",
sans-font: "Arial",
text-size: 12pt,
small-size: 8pt,
line-height: 1.5em,
compact-line-height: 0.65em,
paper: "a4",
page-margin: (left: 3cm, right: 1.5cm, top: 2cm, bottom: 2cm),
)
let result = defaults
for (key, value) in overrides {
if key not in defaults {
panic("Scientia / design-tokens: неизвестный token " + repr(key))
}
result.insert(key, value)
}
result
}
#let watermark-layer(value, font: "Arial") = {
if value == none {
none
} else {
[
#place(center + horizon, dx: -4.8cm, dy: -8.5cm)[
#rotate(35deg)[#text(font: font, size: 40pt, weight: "bold", fill: rgb("e8c9c9").transparentize(65%))[#value]]
]
#place(center + horizon)[
#rotate(35deg)[#text(font: font, size: 40pt, weight: "bold", fill: rgb("e8c9c9").transparentize(65%))[#value]]
]
#place(center + horizon, dx: 4.8cm, dy: 8.5cm)[
#rotate(35deg)[#text(font: font, size: 40pt, weight: "bold", fill: rgb("e8c9c9").transparentize(65%))[#value]]
]
]
}
}
#let render-media-slot(
resource,
policy: "placeholder",
width: 4cm,
height: 2cm,
label: "РЕСУРС НЕ ЗАДАН",
fit: "contain",
) = {
if policy not in ("hide", "placeholder", "reserve-space") {
panic("Scientia / media-slot: неизвестная политика " + repr(policy))
}
if resource == none {
if policy == "hide" {
none
} else if policy == "reserve-space" {
box(width: width, height: height)
} else {
box(
width: width,
height: height,
stroke: 0.6pt + luma(150),
fill: luma(245),
inset: 4pt,
align(center + horizon)[
#text(font: "Arial", size: 7pt, fill: luma(110), weight: "bold")[#label]
],
)
}
} else if type(resource) == path {
image(resource, width: width, height: height, fit: fit)
} else if type(resource) == content {
box(width: width, height: height, align(center + horizon, resource))
} else {
panic("Scientia / media-slot: ожидались none, path или content")
}
}
#let apply-foundation(
body,
ctx: none,
tokens: (:),
page-options: (:),
text-options: (:),
par-options: (:),
) = {
if ctx == none {
panic("Scientia / foundation: document context обязателен")
}
let tokens = design-tokens(ctx.company, overrides: tokens)
let page-defaults = (
paper: tokens.paper,
margin: tokens.at("page-margin"),
foreground: watermark-layer(ctx.options.watermark, font: tokens.at("sans-font")),
)
let resolved-page = page-defaults
for (key, value) in page-options { resolved-page.insert(key, value) }
set page(..resolved-page)
set text(
font: tokens.at("text-font"),
size: tokens.at("text-size"),
lang: "ru",
..text-options,
)
set par(
justify: true,
first-line-indent: (amount: 1.25cm, all: true),
leading: tokens.at("line-height"),
..par-options,
)
show table.cell: set par(first-line-indent: 0pt)
show figure.caption.where(kind: table): set par(first-line-indent: 0pt)
body
}