340 lines
11 KiB
Typst
340 lines
11 KiB
Typst
// =========================================================
|
||
// style.typ – Szablon Pracy Dyplomowej
|
||
// Wydział Informatyki, ZUT w Szczecinie
|
||
// Typst 0.12+
|
||
// =========================================================
|
||
//
|
||
|
||
#let blue-wi = cmyk(60%, 20%, 0%, 0%) // blueWI – jasny niebieski WI
|
||
#let blue-zut = cmyk(100%, 75%, 0%, 20%) // blueZUT – granatowy ZUT
|
||
#let gray-zut = cmyk(0%, 0%, 0%, 40%) // grayZUT – szary ZUT
|
||
|
||
// Pochodne (odpowiedniki blueWI!20, blueZUT!60 itp.)
|
||
#let blue-wi-20 = blue-wi.lighten(80%)
|
||
#let blue-zut-60 = blue-zut.lighten(40%)
|
||
#let gray-zut-10 = gray-zut.lighten(90%)
|
||
|
||
// Kolory kodu źródłowego
|
||
#let code-green = rgb(0, 154, 0)
|
||
#let code-gray = rgb(128, 128, 128)
|
||
#let code-violet = rgb(148, 0, 211)
|
||
|
||
#let theorem(title: none, body) = block(
|
||
width: 100%,
|
||
fill: luma(97%),
|
||
stroke: (top: 1.2pt + blue-zut, bottom: 1.2pt + blue-zut, left: 1.2pt + blue-zut, right: 1.2pt + blue-zut),
|
||
inset: 8pt,
|
||
above: 8pt,
|
||
below: 8pt,
|
||
)[
|
||
#text(size: 10pt, weight: "bold", font: "Fira Sans", fill: blue-zut)[
|
||
Twierdzenie#if title != none [ — #title]
|
||
]
|
||
#parbreak()
|
||
#body
|
||
]
|
||
|
||
/// Definicja – lewa kreska 2pt blueZUT (dBox)
|
||
#let definition(title: none, body) = block(
|
||
width: 100%,
|
||
stroke: (left: 2pt + blue-zut),
|
||
inset: (left: 10pt, right: 8pt, top: 4pt, bottom: 4pt),
|
||
above: 8pt,
|
||
below: 8pt,
|
||
)[
|
||
#text(size: 10pt, weight: "bold", font: "Fira Sans")[
|
||
Definicja#if title != none [ — #title]
|
||
]
|
||
#parbreak()
|
||
#body
|
||
]
|
||
|
||
#let example(title: none, body) = block(
|
||
above: 6pt,
|
||
below: 6pt,
|
||
)[
|
||
#text(size: 10pt, weight: "bold", font: "Fira Sans", fill: blue-zut)[
|
||
Przykład#if title != none [ — #title]
|
||
]
|
||
#parbreak()
|
||
#body #h(1fr) $blacksquare$
|
||
]
|
||
|
||
// ╔═══════════════════════════════════════════════════════╗
|
||
// ║ 3. GŁÓWNA FUNKCJA SZABLONU ║
|
||
// ╚═══════════════════════════════════════════════════════╝
|
||
|
||
/// Główna funkcja opakowująca dokument.
|
||
///
|
||
/// Parametry:
|
||
/// shorttitle – krótki tytuł pracy wyświetlany w nagłówku parzystych stron
|
||
/// body – treść dokumentu
|
||
#let zut-template(
|
||
shorttitle: "",
|
||
body,
|
||
) = {
|
||
// ── 3.1 STRONA ──────────────────────────────────────────
|
||
set page(
|
||
paper: "a4",
|
||
margin: (
|
||
top: 3.2cm,
|
||
bottom: 3.5cm,
|
||
inside: 3.5cm, // 2.5cm tekst + 1cm bindingoffset (rozwiązuje problem marginesu z lewej z Twojego kodu)
|
||
outside: 2.5cm,
|
||
),
|
||
|
||
// ── Nagłówek ─────────────────────────────────────────
|
||
header: context {
|
||
let pg = counter(page).get().first()
|
||
let is-odd = calc.odd(pg)
|
||
|
||
// Czy ta strona otwiera rozdział? (→ plain style: brak nagłówka)
|
||
let chapter-locs = query(heading.where(level: 1))
|
||
let is-chapter-page = chapter-locs.any(h => counter(page).at(h.location()).first() == pg)
|
||
|
||
if not is-chapter-page {
|
||
// Tytuł bieżącego rozdziału do nagłówka
|
||
let prev = query(heading.where(level: 1).before(here()))
|
||
let ch-label = if prev.len() > 0 {
|
||
let h = prev.last()
|
||
let num = counter(heading).at(h.location())
|
||
[#num.first().#h.body]
|
||
} else { [] }
|
||
|
||
set text(font: "Fira Sans", size: 10pt)
|
||
|
||
if is-odd {
|
||
// ── Nieparzysta (prawa) ──────────────────────────
|
||
grid(
|
||
columns: (1fr, auto),
|
||
align: horizon,
|
||
box(
|
||
fill: blue-wi-20,
|
||
width: 100%,
|
||
inset: (x: 7pt, y: 4pt),
|
||
)[#ch-label],
|
||
box(
|
||
fill: blue-zut,
|
||
inset: (x: 7pt, y: 4pt),
|
||
)[#text(fill: white, weight: "bold")[#pg]],
|
||
)
|
||
} else {
|
||
// ── Parzysta (lewa) ──────────────────────────────
|
||
grid(
|
||
columns: (auto, 1fr),
|
||
align: horizon,
|
||
box(
|
||
fill: blue-zut,
|
||
inset: (x: 7pt, y: 4pt),
|
||
)[#text(fill: white, weight: "bold")[#pg]],
|
||
box(
|
||
fill: blue-wi-20,
|
||
width: 100%,
|
||
inset: (x: 7pt, y: 4pt),
|
||
)[#shorttitle],
|
||
)
|
||
}
|
||
}
|
||
}, // /header
|
||
|
||
// ── Stopka ───────────────────────────────────────────
|
||
footer: context {
|
||
let pg = counter(page).get().first()
|
||
let chapter-locs = query(heading.where(level: 1))
|
||
let is-chapter-page = chapter-locs.any(h => counter(page).at(h.location()).first() == pg)
|
||
if is-chapter-page {
|
||
set text(font: "Fira Sans", size: 10pt)
|
||
align(right, box(fill: blue-zut, inset: (x: 7pt, y: 4pt))[
|
||
#text(fill: white, weight: "bold")[#pg]
|
||
])
|
||
}
|
||
},
|
||
|
||
numbering: "1",
|
||
) // /set page
|
||
|
||
|
||
// ── 3.2 TEKST ───────────────────────────────────────────
|
||
set text(font: "New Computer Modern", size: 12pt, lang: "pl")
|
||
set par(justify: true, leading: 0.65em)
|
||
|
||
|
||
// ── 3.3 LISTY ───────────────────────────────────────────
|
||
set list(marker: [--], spacing: 0.4em, indent: 1em)
|
||
set enum(spacing: 0.4em, indent: 1em)
|
||
|
||
|
||
// ── 3.4 NAGŁÓWKI ─────────────
|
||
|
||
set heading(numbering: "1.1")
|
||
|
||
show heading: it => {
|
||
let num = if it.numbering != none {
|
||
counter(heading).display(it.numbering)
|
||
}
|
||
|
||
if it.level == 1 {
|
||
// Wymuszenie nowej strony dla głównego rozdziału
|
||
pagebreak(weak: true)
|
||
v(1.5em, weak: true)
|
||
|
||
block(width: 100%, inset: (bottom: 1em, top: 7em))[
|
||
#if num != none {
|
||
place(left + top, dx: -1.5cm, dy: 0.1em)[
|
||
#text(size: 26pt, fill: blue-zut, weight: "bold", font: "Fira Sans", num)
|
||
]
|
||
}
|
||
#align(left)[
|
||
#text(size: 26pt, fill: blue-zut, weight: "bold", font: "Fira Sans", it.body)
|
||
]
|
||
]
|
||
} else {
|
||
let txt-size = if it.level == 2 { 14pt } else if it.level == 3 { 12pt } else { 11pt }
|
||
let spacing-top = if it.level == 2 { 1.5em } else if it.level == 3 { 1.2em } else { 1em }
|
||
let spacing-bottom = if it.level == 2 { 0.5em } else { 0.4em }
|
||
|
||
v(spacing-top, weak: true)
|
||
|
||
block(width: 100%, inset: (bottom: spacing-bottom))[
|
||
#if num != none {
|
||
let dy-val = if it.level == 2 { 0.0em } else { 0.05em }
|
||
place(left + top, dx: -1.5cm, dy: dy-val)[
|
||
#text(size: txt-size, fill: blue-zut, weight: "bold", font: "Fira Sans", num)
|
||
]
|
||
}
|
||
// Tytuł na czarno, dopasowany do tekstu
|
||
#text(size: txt-size, fill: black, weight: "bold", font: "Fira Sans", it.body)
|
||
]
|
||
}
|
||
}
|
||
|
||
|
||
// ── 3.5 SPIS TREŚCI ─────────────────────────────────────
|
||
set outline(indent: auto)
|
||
|
||
show outline.entry: it => {
|
||
if it.level == 1 {
|
||
v(12pt, weak: true)
|
||
text(size: 11pt, weight: "bold", font: "Fira Sans", fill: blue-zut)[
|
||
#h(1.25cm)
|
||
#it
|
||
]
|
||
} else if it.level == 2 {
|
||
v(3pt, weak: true)
|
||
text(size: 10pt, font: "Fira Sans")[
|
||
#h(1.25cm)
|
||
#it
|
||
]
|
||
} else if it.level == 3 {
|
||
v(1pt, weak: true)
|
||
text(size: 9pt, font: "Fira Sans")[
|
||
#h(2.5cm)
|
||
#it
|
||
]
|
||
} else {
|
||
it
|
||
}
|
||
}
|
||
|
||
|
||
// ── 3.6 BLOKI KODU ──────────────────────────────────────
|
||
show raw.where(block: true): it => {
|
||
block(
|
||
width: 100%,
|
||
fill: gray-zut.lighten(93%),
|
||
stroke: (left: 2pt + blue-wi),
|
||
inset: (x: 12pt, y: 9pt),
|
||
above: 9pt,
|
||
below: 9pt,
|
||
radius: (right: 2pt),
|
||
)[
|
||
#set text(size: 9.5pt, font: ("Fira Mono", "Courier New"))
|
||
#it
|
||
]
|
||
}
|
||
|
||
set raw(theme: "zut.tmTheme")
|
||
show raw.where(block: true): it => {
|
||
let lines = it.lines
|
||
|
||
block(
|
||
fill: gray-zut.lighten(100%),
|
||
radius: 4pt,
|
||
clip: true,
|
||
width: 100%,
|
||
)[
|
||
#set text(size: 10pt, font: ("Fira Mono", "Courier New"))
|
||
#grid(
|
||
columns: (auto, 1fr),
|
||
inset: (left: 10pt, right: 10pt, top: 2pt, bottom: 2pt),
|
||
align: (x, y) => if x == 0 { right + top } else { left + top },
|
||
|
||
grid.vline(x: 1, stroke: (paint: luma(160), thickness: 1pt)),
|
||
|
||
..lines
|
||
.map(line => (
|
||
text(fill: luma(160), size: 9pt)[#line.number],
|
||
line.body,
|
||
))
|
||
.flatten(),
|
||
)
|
||
]
|
||
}
|
||
|
||
// ── 3.7 PODPISY RYSUNKÓW I TABEL ────────────────────────
|
||
set figure(gap: 0.8em)
|
||
let raw-fig-counter = counter("raw-fig")
|
||
let img-fig-counter = counter("img-fig")
|
||
|
||
show heading.where(level: 1): it => {
|
||
raw-fig-counter.update(1)
|
||
img-fig-counter.update(1)
|
||
it
|
||
}
|
||
|
||
show figure.where(kind: raw): it => {
|
||
// Step the custom counter
|
||
raw-fig-counter.step()
|
||
|
||
// Build the number: heading-index.figure-index
|
||
let heading-num = counter(heading.where(level: 1)).display()
|
||
let fig-num = raw-fig-counter.display("1")
|
||
let full-number = heading-num + "." + fig-num
|
||
|
||
it.body
|
||
v(10pt, weak: true)
|
||
align(center, block(width: auto)[
|
||
#text(weight: "bold", fill: blue-zut, font: "Fira Sans")[Algorytm #full-number:]
|
||
#text(font: "Fira Sans")[#it.caption.body]
|
||
])
|
||
}
|
||
|
||
show figure.where(kind: image): it => {
|
||
// Step the custom counter
|
||
img-fig-counter.step()
|
||
|
||
// Build the number: heading-index.figure-index
|
||
let heading-num = counter(heading.where(level: 1)).display()
|
||
let fig-num = img-fig-counter.display("1")
|
||
let full-number = heading-num + "." + fig-num
|
||
|
||
it.body
|
||
v(10pt, weak: true)
|
||
align(center, block(width: auto)[
|
||
#text(weight: "bold", fill: blue-zut, font: "Fira Sans")[Rysunek #full-number:]
|
||
#text(font: "Fira Sans")[#it.caption.body]
|
||
])
|
||
}
|
||
|
||
// ── 3.8 TABELE ──────────────────────────────────────────
|
||
set table(
|
||
stroke: none,
|
||
inset: (x: 8pt, y: 5pt),
|
||
)
|
||
show table.cell.where(y: 0): set text(weight: "bold")
|
||
show table: set block(above: 8pt, below: 8pt)
|
||
|
||
|
||
body
|
||
} // /zut-template
|