WIP!: break up lib.typ

This commit is contained in:
2026-01-17 15:45:36 +02:00
parent 549d7f060f
commit 3cde131ed6
6 changed files with 438 additions and 314 deletions
+98
View File
@@ -0,0 +1,98 @@
// Template formatting functions {{{1
/// bold text
#let bold(content) = text(weight: "bold")[#content]
/// numberless heading
#let nheading(title) = heading(depth: 1, numbering: none, title)
/// fill horizontal space with a box and not an empty space
#let hfill(width) = box(
width: width,
repeat(""),
) // NOTE: This is a HAIR SPACE (U+200A), not a regular space
/// make underlined cell with filled value
#let uline(align: center, content) = underline[
#if align != left { hfill(1fr) }
#content
#if align != right { hfill(1fr) }
]
/// month name from its number
#let month_gen(month) = (
"січня",
"лютого",
"березня",
"квітня",
"травня",
"червня",
"липня",
"серпня",
"вересня",
"жовтня",
"листопада",
"грудня",
).at(month - 1)
#let is-cyr(c) = regex("[\p{Cyrillic}]") in c
#let gender-form(verb, gender: "p") = {
(
"author": ("m": "Виконав", "f": "Виконала", "p": "Виконали"),
"mentor": ("m": "Перевірив", "f": "Перевірила", "p": "Перевірили"),
)
.at(verb)
.at(if gender == "m" or gender == "f" { gender } else { "p" }, default: "p")
}
#let gender-get(a) = if "gender" in a.keys() { a.gender } else { "p" }
#let pz-lb-title(type, number: none) = {
let type-title = (
"ЛБ": [Звіт \ з лабораторної роботи],
"ПЗ": [Звіт \ з практичної роботи],
"КР": [Контрольна робота],
"РФ": [Реферат], // зрада
).at(type, default: type)
if number != none { [#type-title #number] } else { [#type-title] }
}
// Helper functions {{{1
/// captioned image with label derived from path:
/// - "image.png" = @image
/// - "img/image.png" = @image
/// - "img/foo/image.png" = @foo_image
/// - "img/foo/foo_image.png" = @foo_image
/// the caption will be modified based on a conditional positional value:
/// - `none`: no change
/// - some value: "`caption` (за даними `value`)"
/// - no value: "`caption` (рисунок виконано самостійно)"
/// additional named arguments will be passed to original `image` function
#let img(path, caption, ..sink) = {
let parts = path.split(".").first().split("/")
let label_string = if (
parts.len() <= 2 or parts.at(-1).starts-with(parts.at(-2))
) {
// ("image",), (_, "image") and (.., "img", "img_image")
parts.last()
} else {
// (.., "img", "image") = "img_image"
parts.at(-2) + "_" + parts.at(-1)
}.replace(" ", "_")
let caption = if sink.pos().len() == 0 {
caption
} else if sink.pos().first() == none {
caption + " (рисунок виконано самостійно)"
} else {
[#caption (за даними #sink.pos().first())]
}
[#figure(
image(path, ..sink.named()),
caption: caption,
) #label(label_string)]
}
+61
View File
@@ -0,0 +1,61 @@
#import "/src/helpers.typ": gender-form, gender-get, pz-lb-title
#let minimal(
uni,
edu_program,
subject,
type,
number,
title,
authors,
mentors,
) = {
align(center)[
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ \
#upper(uni.name)
\ \
Кафедра #uni.edu_programs.at(edu_program).department_gen
\ \ \
#pz-lb-title(type, number: number)
з дисципліни: "#uni.subjects.at(subject, default: subject)"
#if title != none [\ з теми: "#eval(title, mode: "markup")"]
\ \ \ \
#columns(2)[
#set align(left)
#set par(first-line-indent: 0pt)
#if authors.len() == 1 {
let a = authors.first()
[#gender-form("author", gender: gender-get(a)):\ ]
[ст. гр. #a.edu_program\-#a.group\ #a.name\ ]
if a.variant != none [Варіант: #a.variant]
} else if authors.len() > 1 [
#gender-form("author"):\
#for a in authors [ст. гр. #a.edu_program\-#a.group\ #a.name\ ]
]
#colbreak()
#set align(right)
#if mentors.len() == 1 {
let m = mentors.first()
[#gender-form("mentor", gender: gender-get(m)):\ ]
if "degree" in m.keys() and m.degree != none [#m.degree\ ]
[#m.name\ ]
} else if mentors.len() > 1 [
#gender-form("mentor"):\
#for m in mentors {
if "degree" in m.keys() and m.degree != none [#m.degree\ ]
[#m.name\ ]
}]
]
#v(1fr)
Харків -- #datetime.today().display("[year]")
]
}
+66
View File
@@ -0,0 +1,66 @@
// Title page layouts for pz-lb {{{1
// This module provides different title page layouts for practice/lab work templates
#import "/src/helpers.typ": gender-form, gender-get, pz-lb-title
/// Default DSTU title page layout (ministry + university header, 2-column author/mentor info)
/// Returns: content for the title page
/// Simple title page layout (university header only, single column layout)
/// Returns: content for the title page
#let simple(
uni,
edu_program,
subject,
type,
number,
title,
authors,
mentors,
) = {
align(center)[
#upper(uni.name)
Кафедра #uni.edu_programs.at(edu_program).department_gen
\ \ \
#pz-lb-title(type, number: number)
з дисципліни: "#uni.subjects.at(subject, default: subject)"
#if title != none [\ з теми: "#eval(title, mode: "markup")"]
\ \ \
#if authors.len() == 1 {
let a = authors.first()
[#gender-form("author", gender: gender-get(a)):\ #a.name]
[ст. гр. #a.edu_program\-#a.group]
if a.variant != none [Варіант: #a.variant]
} else if authors.len() > 1 [
#gender-form("author"):\
#for a in authors [#a.name (ст. гр. #a.edu_program\-#a.group)\ ]
]
\
#if mentors.len() == 1 {
let m = mentors.first()
[#gender-form("mentor", gender: gender-get(m)):\ ]
if "degree" in m.keys() and m.degree != none [#m.degree ]
[#m.name]
} else if mentors.len() > 1 [
#gender-form("mentor"):\
#for m in mentors {
if "degree" in m.keys() and m.degree != none [#m.degree\ ]
[#m.name\ ]
}
]
#v(1fr)
#datetime.today().display("[year]")
]
}
// vim:sts=2:sw=2:fdm=marker:cms=/*%s*/
+157
View File
@@ -0,0 +1,157 @@
#let spacing = 0.95em // spacing between lines
/// symbols used for numbering according to DSTU 3008:2015
#let ukr-enum = "абвгдежиклмнпрстуфхцшщюя".split("") // 0 = "", 1 = "а"
/// DSTU 3008:2015 Style
/// -> content
/// - it (content): Content to apply the style to.
/// - skip (int): Do not show page number for this number of pages.
/// - offset (int): Adjust all page numbers by this amount.
#let dstu-style(
it,
skip: 0,
offset: 0,
) = {
// General Styling {{{1
set page(
paper: "a4",
number-align: top + right,
margin: (top: 20mm, right: 10mm, bottom: 20mm, left: 25mm),
numbering: (i, ..) => if i > skip { numbering("1", i + offset) },
)
set text(lang: "uk", size: 14pt, hyphenate: false, font: ("Times New Roman", "Liberation Serif"))
set par(justify: true, spacing: spacing, leading: spacing, first-line-indent: (
amount: 1.25cm,
all: true,
))
set block(spacing: spacing)
set underline(evade: false)
// Enums & Lists {{{1
// First level
set enum(indent: 1.25cm, body-indent: 0.5cm, numbering: i => { ukr-enum.at(i) + ")" })
// Second level and further nesting
show enum: it => {
set enum(indent: 0em, numbering: "1)")
it
}
// Lists are not intended for multiple levels, use `enum`
set list(indent: 1.35cm, body-indent: 0.5cm, marker: [--])
// Figures {{{1
show figure: it => {
v(spacing * 2, weak: true)
it
v(spacing * 2, weak: true)
}
set figure.caption(separator: [ -- ])
show figure.where(kind: table): set figure.caption(position: top)
show figure.caption.where(kind: table): set align(left)
show figure.where(kind: raw): set figure.caption(position: top)
show figure.where(kind: raw): set align(left)
// Numbering {{{1
show heading.where(level: 1): it => {
counter(math.equation).update(0)
counter(figure.where(kind: raw)).update(0)
counter(figure.where(kind: image)).update(0)
counter(figure.where(kind: table)).update(0)
it
}
set figure(numbering: i => numbering("1.1", counter(heading).get().at(0), i))
set math.equation(numbering: i => numbering("(1.1)", counter(heading).get().at(0), i))
// Headings {{{1
set heading(numbering: "1.1")
show heading: it => if it.level == 1 {
set align(center)
set text(size: 14pt, weight: "semibold")
pagebreak(weak: true)
upper(it)
v(spacing * 2, weak: true)
} else {
set text(size: 14pt, weight: "regular")
v(spacing * 2, weak: true)
block(width: 100%, spacing: 0em)[
#h(1.25cm)
#counter(heading).display(auto)
#it.body
]
v(spacing * 2, weak: true)
}
show heading.where(level: 3): it => {
set text(size: 14pt, weight: "regular")
v(spacing * 2, weak: true)
block(width: 100%, spacing: 0em)[
#h(1.25cm)
#counter(heading).display(it.numbering)
#it.body
]
v(spacing * 2, weak: true)
}
// listings {{{3
show raw.where(block: true): it => {
let new_spacing = 0.5em
set block(spacing: new_spacing)
set par(spacing: new_spacing, leading: new_spacing)
set text(size: 11pt, weight: "semibold", font: ("Courier New", "Liberation Mono"))
v(spacing * 2.5, weak: true)
pad(it, left: 1.25cm)
v(spacing * 2.5, weak: true)
}
it
// }}}
}
/// DSTU 3008:2015 Appendices Style
/// -> content
/// - it (content): Content to apply the style to.
#let appendices-style(it) = /* {{{ */ {
// Numbering
counter(heading).update(0)
set heading(numbering: (i, ..n) => upper(ukr-enum.at(i)) + numbering(".1.1", ..n))
set figure(numbering: i => upper(ukr-enum.at(counter(heading).get().at(0))).i)
set math.equation(numbering: i => upper(ukr-enum.at(counter(heading).get().at(0))).i)
// Heading supplement (Heading name shown when citing with @ref)
set heading(supplement: [Додаток])
// Headings
show heading: it => if it.level == 1 {
set align(center)
set text(size: 14pt, weight: "regular")
pagebreak(weak: true)
bold([ДОДАТОК #counter(heading).display(auto)])
linebreak()
it.body
v(spacing * 2, weak: true)
} else {
set text(size: 14pt, weight: "regular")
v(spacing * 2, weak: true)
block(width: 100%, spacing: 0em)[
#h(1.25cm)
#counter(heading).display(auto)
#it.body
]
v(spacing * 2, weak: true)
}
it
} // }}}