Compare commits

11 Commits

Author SHA1 Message Date
2e65baae70 refactor: backport changes from noatu's fork
feat(style)!: remove reference styling & fix figure numbering for appendices
feat!: swap appending on 'none' being the first position in ..sink
feat: add chapters to pz-lb()
feat: add asserts
fix: better control flow

img() function now requires `none` to be the first position in ..sink,
e.g. `#img("/fig/1.png", "Caption", none)` to get "Caption (рисунок виконано самостійно)" caption.

Use `@some_label[див. рис.]` to get a "див. рис. 42" link.

Signed-off-by: unexplrd <unexplrd@linerds.us>
2025-07-09 17:17:53 +03:00
d338c21965 Merge pull request 'add name of educational program && names of subjects for this semester' (#14) from add-program-and-subject-names into 0.1.0
Reviewed-on: #14
LGTM
2025-05-25 04:11:28 +03:00
7e15ea09d4 add name of educational program && names of subjects for this semester 2025-05-25 04:08:53 +03:00
3b3d62fd9b lib.typ: too lazy to name it
Signed-off-by: unexplrd <unexplrd@linerds.us>
2025-05-20 20:46:32 +03:00
04c5b283e7 lib.typ: better code branching
Signed-off-by: unexplrd <unexplrd@linerds.us>
2025-05-20 20:31:41 +03:00
3ca8409e58 fix: heading numbering when no title 2025-04-26 18:38:36 +03:00
e0811afaae fix: move appendix styling out of the coursework template
It is now possible to style headings as appendices in the pz-lb template via `#show: appendices_style`.
2025-04-20 21:21:16 +03:00
7ec79ee898 fix: make variant in authors truly optional
In `pz-lb` template the `authors.variant` may be omitted now.
2025-04-16 22:05:22 +03:00
47117749cf fix: set fallback monospace font
Signed-off-by: unexplrd <unexplrd@linerds.us>
2025-04-08 21:22:16 +03:00
e214656166 docs(readme): update readme, add a note
Signed-off-by: unexplrd <unexplrd@linerds.us>
2025-03-24 00:27:50 +02:00
99234a73a9 README.md: add an image
assets/pz-lb_title_page.png: the image added

Signed-off-by: unexplrd <unexplrd@linerds.us>
2025-03-24 00:10:21 +02:00
6 changed files with 208 additions and 189 deletions

View File

@ -1,4 +1,5 @@
# Typst Template for NURE Works # Typst Template for NURE Works
![pz-lb title page](assets/pz-lb_title_page.png)
## General Info ## General Info
@ -6,12 +7,12 @@ This project contains two template functions and some utilities for writing NURE
### Templates ### Templates
#### `pz-lb-template` - For Laboratory and Practical Works #### `pz-lb` - For Practice and Laboratory Works
This template: This template:
- Sets up document styles; - Sets up document styles;
- Formats the title page according to NURE/DSTU guidelines. - Formats the title page according to NURE/DSTU guidelines.
#### `cw-template` - For Course Works #### `coursework` - For Course Works
This template: This template:
- Sets up document styles; - Sets up document styles;
- Formats the title, task, calendar plan, and abstract pages; - Formats the title, task, calendar plan, and abstract pages;
@ -46,15 +47,19 @@ Copy `lib.typ` to your project's root directory.
// ...or by importing a lib.typ directly // ...or by importing a lib.typ directly
// #import "/lib.typ": * // #import "/lib.typ": *
// Setup the document // 1. Setup the document
#show: pz-lb-template.with( // by setting values directly...
#show: pz-lb.with(
title: "Some title", title: "Some title",
// etc: "and so on", // etc: "and so on",
// ... // ...
) )
// ...or using a config/doc.yaml file
#show: pz-lb.with(..yaml("config/doc.yaml"))
// this template automatically inserts a `=title` // this template automatically inserts a `=title`
// Write your content... // Write your content
#v(-spacing) // remove spacing between headings #v(-spacing) // remove spacing between headings
== Purpose == Purpose
Some text Some text
@ -65,10 +70,23 @@ Some text
#include "src/chapter2.typ" #include "src/chapter2.typ"
// NOTE: if you want to use variables or utils provided by the package, // NOTE: if you want to use variables or utils provided by the package,
// you have to import the package or a lib.typ inside a module. // you have to import the package or a lib.typ inside a module.
// If you ever need appendices in pz-lb template use the show rule
// WARNING: when using coursework template use its own argument,
// so it can put bibliography before appendices
#show: appendices-style
= Quote
#link("https://youtu.be/bJQj1uKtnus")[
The art isn't the art, the art is never the art,
the art is the thing that happens inside you when you make it and the feeling in the heart of the beholder.
]
``` ```
### Notes: ### Notes:
1. Use `#v(-spacing)` to remove vertical spacing between titles (this cannot be automatically handled by the template). Variable `spacing` used here is imported from the template. 1. Use `#v(-spacing)` to remove vertical spacing between titles (this cannot be automatically handled by the template). Variable `spacing` used here is imported from the template.
2. When importing `@local/nure:0.1.0` and specifying file paths in functions handled by the package, the path will relative to package's root directory, e.g. setting `#show: coursework.with(bib_path: "bibl.yml")` will evaluate to `~/.local/share/typst/packages/local/nure/0.1.0/bibl.yml`, the same is for `#img` function, which makes it quite annoying and forces one to import `lib.typ` file. Please open an issue or contact us in any other way if you have any advice.
### Example Project Structure ### Example Project Structure
``` ```

BIN
assets/pz-lb_title_page.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@ -9,6 +9,10 @@
name_long: Управління інформаційною безпекою name_long: Управління інформаційною безпекою
department_gen: Інфокомунікаційної інженерії ім. В. В. Поповського department_gen: Інфокомунікаційної інженерії ім. В. В. Поповського
code: 125 code: 125
КНТ:
name_long: Комп'ютерні науки та технології
department_gen: Системотехніки
code: 122
subjects: subjects:
ODS: Основи Dаtа Sсіеnсе # NOTE: Eng O here ODS: Основи Dаtа Sсіеnсе # NOTE: Eng O here
ІМ: Іноземна мова ІМ: Іноземна мова
@ -44,3 +48,11 @@
ФІЗ: Фізика ФІЗ: Фізика
ФІЛ: Філософія ФІЛ: Філософія
ФВС: Фізичне виховання та спорт ФВС: Фізичне виховання та спорт
# Oleksii 6th semester
ХТ: Хмарні технології
ООАПС: Об'єктно-орієнтований аналіз в проектуванні систем
СА: Системний аналіз
МППС: Методології проектування програмних систем
ТЗІ: Технології захисту інформації
АДан: Аналітика даних
DMT: Decision making theory

305
lib.typ
View File

@ -66,9 +66,9 @@
}.replace(" ", "_") }.replace(" ", "_")
let caption = if sink.pos().len() == 0 { let caption = if sink.pos().len() == 0 {
caption + " (рисунок виконано самостійно)"
} else if sink.pos().first() == none {
caption caption
} else if sink.pos().first() == none {
caption + " (рисунок виконано самостійно)"
} else { } else {
[#caption (за даними #sink.pos().first())] [#caption (за даними #sink.pos().first())]
} }
@ -79,54 +79,62 @@
) #label(label_string)] ) #label(label_string)]
} }
// Styling {{{1 #let spacing = 0.95em // spacing between lines
/// NOTE: may be wrong #let num-to-alpha = "абвгдежиклмнпрстуфхцшщюя".split("") // 0 = "", 1 = "а"
#let ua_alpha_numbering = "абвгдежиклмнпрстуфхцшщюя".split("") // 0 = "", 1 = "а"
// general outlook {{{2 /// DSTU 3008:2015 Style
// spacing between lines /// -> content
#let spacing = 0.95em /// - it (content): Content to apply the style to.
/// - skip (int): Do not show page number for this number of pages.
#let style(it) = { /// - offset (int): Adjust all page numbers by this amount.
#let dstu-style(
it,
skip: 0,
offset: 0,
) = {
// General Styling {{{1
set page( set page(
paper: "a4", paper: "a4",
margin: (top: 20mm, right: 10mm, bottom: 20mm, left: 25mm),
number-align: top + right, number-align: top + right,
numbering: (..numbers) => { margin: (top: 20mm, right: 10mm, bottom: 20mm, left: 25mm),
if numbers.pos().at(0) != 1 { numbering: (i, ..) => if i > skip { numbering("1", i + offset) },
numbering("1", numbers.pos().at(0))
}
},
) )
set text( set text(
font: ("Times New Roman", "Liberation Serif"), lang: "uk",
size: 14pt, size: 14pt,
hyphenate: false, hyphenate: false,
lang: "uk", font: ("Times New Roman", "Liberation Serif"),
) )
set par(justify: true, first-line-indent: (amount: 1.25cm, all: true))
set par(
justify: true,
spacing: spacing,
leading: spacing,
first-line-indent: (amount: 1.25cm, all: true),
)
set block(spacing: spacing)
set underline(evade: false) set underline(evade: false)
// set 1.5 line spacing // Enums & Lists {{{1
set block(spacing: spacing) // First level
set par(spacing: spacing)
set par(leading: spacing)
// enums and lists {{{2
set enum( set enum(
numbering: i => { ua_alpha_numbering.at(i) + ")" },
indent: 1.25cm, indent: 1.25cm,
body-indent: 0.5cm, body-indent: 0.5cm,
numbering: i => { num-to-alpha.at(i) + ")" },
) )
// Second level and further nesting
show enum: it => { show enum: it => {
set enum(indent: 0em, numbering: "1)") set enum(indent: 0em, numbering: "1)")
it it
} }
// Lists are not intended for multiple levels, use `enum`
set list(indent: 1.35cm, body-indent: 0.5cm, marker: [--]) set list(indent: 1.35cm, body-indent: 0.5cm, marker: [--])
// figures {{{2 // Figures {{{1
show figure: it => { show figure: it => {
v(spacing * 2, weak: true) v(spacing * 2, weak: true)
it it
@ -137,103 +145,52 @@
show figure.where(kind: table): set figure.caption(position: top) show figure.where(kind: table): set figure.caption(position: top)
show figure.caption.where(kind: table): set align(left) show figure.caption.where(kind: table): set align(left)
// figure numbering // Numbering {{{1
show heading.where(level: 1): it => { show heading.where(level: 1): it => {
counter(math.equation).update(0) counter(math.equation).update(0)
counter(figure.where(kind: raw)).update(0)
counter(figure.where(kind: image)).update(0) counter(figure.where(kind: image)).update(0)
counter(figure.where(kind: table)).update(0) counter(figure.where(kind: table)).update(0)
counter(figure.where(kind: raw)).update(0)
it it
} }
set math.equation( set figure(numbering: i => numbering("1.1", counter(heading).get().at(0), i))
numbering: (..num) => numbering( set math.equation(numbering: i => numbering(
"(1.1)", "(1.1)",
counter(heading).get().at(0), counter(heading).get().at(0),
num.pos().first(), i,
), ))
)
set figure(
numbering: (..num) => numbering(
"1.1",
counter(heading).get().at(0),
num.pos().first(),
),
)
// appearance of references to images and tables {{{2 // Headings {{{1
set ref(
supplement: it => {
if it == none or not it.has("kind") {
it
} else if it.kind == image {
"див. рис."
} else if it.kind == table {
"див. таблицю"
} else {
it
}
},
)
show ref: it => {
let el = it.element
if el == none or not el.has("kind") {
return it
}
if el.kind != image and el.kind != table {
return it
}
[(#it)]
}
// headings {{{2
set heading(numbering: "1.1") set heading(numbering: "1.1")
show heading.where(level: 1): it => { show heading: it => if it.level == 1 {
set align(center) set align(center)
set text(size: 14pt, weight: "semibold") set text(size: 14pt, weight: "semibold")
pagebreak(weak: true) pagebreak(weak: true)
upper(it) upper(it)
v(spacing * 2, weak: true) v(spacing * 2, weak: true)
} } else {
show heading.where(level: 2): it => {
set text(size: 14pt, weight: "regular") set text(size: 14pt, weight: "regular")
v(spacing * 2, weak: true) v(spacing * 2, weak: true)
block(width: 100%, spacing: 0em)[ block(width: 100%, spacing: 0em)[
#h(1.25cm) #h(1.25cm)
#counter(heading).display(it.numbering) #counter(heading).display(auto)
#it.body #it.body
] ]
v(spacing * 2, weak: true) v(spacing * 2, weak: true)
} }
show heading.where(level: 3): it => { // Listings {{{1
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 {{{2
show raw: it => { show raw: it => {
let new_spacing = 0.5em let raw-spacing = 0.5em
set block(spacing: new_spacing) set block(spacing: raw-spacing)
set par( set par(spacing: raw-spacing, leading: raw-spacing)
spacing: new_spacing,
leading: new_spacing,
)
set text( set text(
size: 11pt, size: 11pt,
font: ("Courier New", "Iosevka NFM"),
weight: "semibold", weight: "semibold",
font: ("Courier New", "Liberation Mono"),
) )
v(spacing * 2.5, weak: true) v(spacing * 2.5, weak: true)
@ -242,8 +199,47 @@
} }
it 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(num-to-alpha.at(i)) + numbering(".1.1", ..n)
))
set figure(numbering: i => [#upper(num-to-alpha.at(counter(heading).get().at(0))).#i])
set math.equation(numbering: i => [(#upper(num-to-alpha.at(counter(heading).get().at(0))).#i)])
// Headings
show heading: it => if it.level == 1 {
set align(center)
set text(size: 14pt, weight: "regular")
pagebreak(weak: true)
text(weight: "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
} // }}}
// Coursework template {{{1 // Coursework template {{{1
/// DSTU 3008:2015 Template for NURE /// DSTU 3008:2015 Template for NURE
@ -275,7 +271,7 @@
) = { ) = {
set document(title: title, author: author.name) set document(title: title, author: author.name)
show: style show: dstu-style.with(skip: 1)
let bib-count = state("citation-counter", ()) let bib-count = state("citation-counter", ())
show cite: it => { show cite: it => {
@ -381,9 +377,7 @@
grid( grid(
columns: (1fr, 1fr, 1fr), columns: (1fr, 1fr, 1fr),
gutter: 0.3fr, gutter: 0.3fr,
[#bold[Курс] #uline(author.course)], [#bold[Курс] #uline(author.course)], [#bold[Група] #uline([#edu_program\-#author.group])], [#bold[Семестр] #uline(author.semester)],
[#bold[Група] #uline([#edu_program\-#author.group])],
[#bold[Семестр] #uline(author.semester)],
) )
linebreak() linebreak()
@ -504,21 +498,17 @@
#{ #{
let keywords = abstract.keywords.map(upper) let keywords = abstract.keywords.map(upper)
let is_cyrillic = word => word let is_cyrillic = word => word.split("").any(char => ("А" <= char and char <= "я"))
.split("")
.any(char => ("А" <= char and char <= "я"))
let n = keywords.len() let n = keywords.len()
for i in range(n) { for i in range(n) {
for j in range(0, n - i - 1) { for j in range(0, n - i - 1) {
if ( if (
( (
not is_cyrillic(keywords.at(j)) not is_cyrillic(keywords.at(j)) and is_cyrillic(keywords.at(j + 1))
and is_cyrillic(keywords.at(j + 1))
) )
or ( or (
is_cyrillic(keywords.at(j)) == is_cyrillic(keywords.at(j + 1)) is_cyrillic(keywords.at(j)) == is_cyrillic(keywords.at(j + 1)) and keywords.at(j) > keywords.at(j + 1)
and keywords.at(j) > keywords.at(j + 1)
) )
) { ) {
(keywords.at(j), keywords.at(j + 1)) = ( (keywords.at(j), keywords.at(j + 1)) = (
@ -595,10 +585,7 @@
} }
context { context {
for (i, citation) in query(ref.where(element: none)) for (i, citation) in query(ref.where(element: none)).map(r => str(r.target)).dedup().enumerate() {
.map(r => str(r.target))
.dedup()
.enumerate() {
enum.item( enum.item(
i + 1, i + 1,
format-entry(bib_data.at(citation)), format-entry(bib_data.at(citation)),
@ -607,72 +594,41 @@
} }
} }
// appendices {{{2 appendices-style(appendices)
{
counter(heading).update(0)
set heading(
numbering: (i, ..nums) => {
let char = upper(ua_alpha_numbering.at(i))
if nums.pos().len() == 0 { char } else {
char + "." + nums.pos().map(str).join(".")
}
},
)
show heading.where(level: 1): it => {
set align(center)
set text(size: 14pt, weight: "regular")
pagebreak(weak: true)
bold[ДОДАТОК #counter(heading).display(it.numbering)]
linebreak()
it.body
v(spacing * 2, weak: true)
}
show heading.where(level: 2): 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)
}
appendices
}
} }
// Laboratory work template {{{1 // Practice and Laboratory works template {{{1
/// DSTU 3008:2015 Template for NURE /// DSTU 3008:2015 Template for NURE
/// -> content /// -> content
/// - doc (content): Content to apply the template to. /// - doc (content): Content to apply the template to.
/// - doctype ("ЛБ" | "ПЗ"): Document type.
/// - edu_program (str): Education program shorthand. /// - edu_program (str): Education program shorthand.
/// - title (str): Title of the document. /// - doctype ("ЛБ" | "ПЗ" | str): Document type.
/// - title (str or none): Title of the document. Optional.
/// - subject (str): Subject shorthand. /// - subject (str): Subject shorthand.
/// - authors ((name: str, full_name_gen: str, group: str, gender: str, variant: int or none),): List of authors.
/// - mentors ((name: str, degree: str, gender: str or none),): List of mentors.
/// - worknumber (int or none): Number of the work. Optional. /// - worknumber (int or none): Number of the work. Optional.
/// - authors ((name: str, full_name_gen: str, group: str, gender: str, variant: int or none),): List of authors.
/// - mentors ((name: str, degree: str, gender: str or none),): List of mentors. Optional.
/// - chapters (): List of file names in chapters/ subdirectory. Optional.
#let pz-lb( #let pz-lb(
doc, doc,
doctype: none,
university: "ХНУРЕ", university: "ХНУРЕ",
edu_program: none, edu_program: none,
doctype: none,
title: none, title: none,
subject: none, subject: none,
worknumber: none, worknumber: none,
authors: (), authors: (),
mentors: (), mentors: (),
chapters: (),
) = { ) = {
assert.ne(edu_program, none, message: "Missing argument: \"edu_program\"")
assert.ne(doctype, none, message: "Missing argument: \"doctype\"")
assert.ne(subject, none, message: "Missing argument: \"subject\"")
set document(title: title, author: authors.at(0).name) set document(title: title, author: authors.at(0).name)
show: style show: dstu-style.with(skip: 1)
let uni = universities.at(university) let uni = universities.at(university)
let edu_prog = uni.edu_programs.at(edu_program) let edu_prog = uni.edu_programs.at(edu_program)
@ -685,15 +641,19 @@
Кафедра #edu_prog.department_gen Кафедра #edu_prog.department_gen
\ \ \ \ \ \
Звіт \ #if doctype == "ЛБ" [Звіт \ з лабораторної роботи] else if (
з doctype == "ПЗ"
#if doctype == "ЛБ" [лабораторної роботи] else [практичної роботи] ) [Звіт \ з практичної роботи] else [#doctype]
#if worknumber != none { #if worknumber != none {
context counter(heading).update(worknumber - 1) context counter(heading).update(
worknumber - if title == none { 0 } else { 1 },
)
[№#worknumber] [№#worknumber]
} else if title != none and worknumber != none {
context counter(heading).update(1)
} }
з дисципліни: "#uni.subjects.at(subject, default: "UNKNOWN SUBJECT, PLEASE OPEN AN ISSUE")" з дисципліни: "#uni.subjects.at(subject)"
#if title != none [з теми: "#title"] #if title != none [з теми: "#title"]
@ -709,8 +669,10 @@
ст. гр. #edu_program\-#author.group\ ст. гр. #edu_program\-#author.group\
#author.name\ #author.name\
] ]
if author.variant != none [Варіант: #author.variant] if (
} else [ "variant" in author.keys() and author.variant != none
) [Варіант: #author.variant]
} else if authors.len() > 1 [
Виконали:\ Виконали:\
ст. гр. #edu_program\-#authors.at(0).group\ ст. гр. #edu_program\-#authors.at(0).group\
#for author in authors [#author.name\ ] #for author in authors [#author.name\ ]
@ -719,16 +681,19 @@
#colbreak() #colbreak()
#set align(right) #set align(right)
#if mentors.len() == 1 { #if type(mentors) == array {
if mentors.len() == 1 {
let mentor = mentors.at(0) let mentor = mentors.at(0)
if mentor.gender == none [Перевірили:\ ] else if ( if "gender" in mentor.keys() {
mentor.gender == "m" if mentor.gender == "m" [Перевірив:\ ] else if (
) [Перевірив:\ ] else [Перевірилa:\ ] mentor.gender == "f"
[ ) [Перевірила:\ ]
#if mentor.degree != none [#mentor.degree\ ] } else [Перевірили:\ ]
#mentor.name\ if (
] "degree" in mentor.keys() and mentor.degree != none
} else [ ) [#mentor.degree\ ]
[#mentor.name\ ]
} else if mentors.len() > 1 [
Перевірили:\ Перевірили:\
#for mentor in mentors { #for mentor in mentors {
[ [
@ -737,6 +702,7 @@
] ]
} }
] ]
}
] ]
#v(1fr) #v(1fr)
@ -749,6 +715,7 @@
if title != none [#heading(title)] if title != none [#heading(title)]
doc doc
chapters.map(chapter => include "/chapters/" + str(chapter) + ".typ").join()
} }
// vim:sts=2:sw=2:fdl=0:fdm=marker:cms=/*%s*/ // vim:sts=2:sw=2:fdl=0:fdm=marker:cms=/*%s*/

View File

@ -106,7 +106,7 @@
task_list: task_list, task_list: task_list,
calendar_plan: calendar_plan, calendar_plan: calendar_plan,
abstract: abstract, abstract: abstract,
bib_path: "bibl.yml", bib_path: bytes("bibl.yml"), // NOTE: use `bytes` as typst looks in template dir when using just filename
appendices: appendices, appendices: appendices,
) )

View File

@ -29,3 +29,25 @@
- #lorem(25); - #lorem(25);
- #lorem(42); - #lorem(42);
- #lorem(27). - #lorem(27).
#show: appendices-style
= Quote
#link("https://youtu.be/bJQj1uKtnus")[
The art isn't the art, the art is never the art,
the art is the thing that happens inside you when you make it and the feeling in the heart of the beholder.
]
= Приклад звіту 1
#v(-spacing)
== Частина 1
#lorem(100)
== Частина2
#lorem(200)
= Приклад звіту 2
#lorem(200)
= Приклад звіту 3
#lorem(200)