15 Commits
Author SHA1 Message Date
JustAnyone dfac289590 chore: bump version to 0.3.2 2026-09-28 10:44:44 +03:00
JustAnyone 1740adfc5a feat: expose ktu-author-table and ktu-title-header-block 2026-09-28 10:43:21 +03:00
JustAnyone 55a236fe8b feat: add support for flipping the order of role and name in author table 2026-09-28 10:42:49 +03:00
JustAnyone d0d7d2a197 chore: bump version to 0.3.1 2026-09-21 18:27:15 +03:00
JustAnyone 9736619563 chore: move template images to a separate directory 2026-09-21 18:05:37 +03:00
JustAnyone 340c5ab92c feat: support custom alignment for unnumbered headings 2026-09-21 18:00:36 +03:00
JustAnyone 59d197f291 feat: support specifying department in heading page fragment 2026-09-21 17:37:29 +03:00
JustAnyone f8001593dd style: spelling mistake 2026-09-21 17:20:26 +03:00
JustAnyone 1ff507941d fix: math equation blocks were not left aligned 2026-09-21 17:18:29 +03:00
JustAnyone 3e91fbfefd chore: bump version to 0.3.0 2026-09-05 14:32:26 +03:00
JustAnyone 2e3f4a890e feat: add support for section title uppercasing and unnumbered heading alignment 2026-09-05 14:31:22 +03:00
JustAnyone 3040693015 chore: minor documentation on how to init a new template project 2026-09-04 11:53:08 +03:00
JustAnyone 67947b07a7 chore: bump version to 0.2.1 2026-09-04 11:46:52 +03:00
JustAnyone cd64907a36 fix: custom-ref could no longer handle multiple references 2026-09-04 11:42:12 +03:00
JustAnyone 920866695c fix: drop compiler version to 0.15.0 as there's no reason for 0.15.1 2026-09-04 10:58:29 +03:00
10 changed files with 181 additions and 53 deletions

No files matched your search

+9
View File
@@ -1,3 +1,12 @@
# ktu-paper
This repository contains a Typst template for academic papers at the Kaunas University of Technology, Faculty of Informatics. A final Bachelor's thesis written with this template has successfully passed the formal review.
## Initiating the template
Install the downloaded template under the following directory:
- Linux: `~/.local/share/typst/packages/local/ktu-paper/0.3.2/`
And then initiate a new project:
```sh
typst init @local/ktu-paper:0.3.2
```
+5
View File
@@ -6,6 +6,11 @@
/// Appendix and other non-standard sections.
#let __FIG_PER_SECTION = state("svetikas.lt/ktu-paper/figure-numbering-per-section", false)
/// Whether to uppercase section titles or not.
/// If true, section titles will be uppercased.
/// If false, section titles will be displayed as-is.
#let __UPPERCASE_SECTION_TITLES = state("svetikas.lt/ktu-paper/uppercase-section-titles", false)
#let __DEBUG_AUTHOR_TABLE = state("$ktu-template-DEBUG_AUTHOR_TABLE", false)
#let debug-author-table() = {
__DEBUG_AUTHOR_TABLE.update(true)
+10 -8
View File
@@ -1,7 +1,7 @@
// This file includes logic for displaying figures in the document, including numbering, captions, and references. It also includes logic for handling figure numbering per section or globally, based on the configuration set in `config.typ`.
#import "config.typ": __FIG_PER_SECTION
/// Localizations for various recognized referencable figure types.
/// Localizations for various recognized referenceable figure types.
/// This is used to provide the correct supplement text for each type of figure
/// when generating references, captions and outlines.
#let __figureNames = (
@@ -39,7 +39,7 @@
/// Helper function to resolve figure numbers (either global "1" or section-based "1.1").
/// It takes a figure item and referenced location and returns the appropriate number as a string.
#let __get-figure-number(fig-item, loc) = context {
#let __get-figure-number(fig-item, loc) = {
if type(fig-item) != content {
panic("can only get figure number for content elements")
}
@@ -48,17 +48,17 @@
panic("can only get figure number for figure elements")
}
let perSection = __FIG_PER_SECTION.get()
let perSection = __FIG_PER_SECTION.at(loc)
if perSection {
let chapter = counter(heading).at(loc).at(0, default: 0)
let figNum = counter(figure.where(kind: fig-item.kind)).at(loc).at(0, default: 1)
[#chapter.#figNum]
str(chapter) + "." + str(figNum)
} else {
let caption = fig-item.caption
if caption != none and caption.numbering != none {
caption.counter.at(loc).at(0)
str(caption.counter.at(loc).at(0))
} else {
[#counter(figure.where(kind: fig-item.kind)).at(loc).at(0)]
str(counter(figure.where(kind: fig-item.kind)).at(loc).at(0))
}
}
}
@@ -124,12 +124,14 @@
}
set text(size: 11pt)
let fig-num = context __get-figure-number(it, here())
// Tables have captions at the top
if it.kind == table {
align(left)[*#__get-figure-number(it, here()) #supplement#separator* #caption.body]
align(left)[*#fig-num #supplement#separator* #caption.body]
it.body
} else {
it.body
[*#__get-figure-number(it, here()) #supplement#separator* #caption.body]
align(center)[*#fig-num #supplement#separator* #caption.body]
}
}
+64 -18
View File
@@ -17,6 +17,7 @@
#let ktu-author-table(
authors: (),
alignment: right,
namesFirst: true, // if false, roles will be displayed first
) = context {
// The layout should be a list of lists whose elements are (name, role) pairs.
assert(authors.len() > 0, message: "author table must have at least one author")
@@ -51,9 +52,13 @@
table.header([]),
..for author in authors {
let (name, role) = author
name = [*#name*]
if not namesFirst {
(role, name) = (name, role)
}
(align(center)[
#set block(below: 8pt)
#block[*#name*]
#block[#name]
#block[#role]
], [])
@@ -67,9 +72,13 @@
),
..for author in authors {
let (name, role) = author
name = [*#name*]
if not namesFirst {
(role, name) = (name, role)
}
(align(left)[
#set block(below: 8pt)
#block[*#name*]
#block[#name]
#block[#role]
], [], [], [])
@@ -128,8 +137,9 @@
]
}
#let ktu-heading-page-centered(
#let ktu-heading-page(
faculty: highlight[Fakulteto pavadinimas],
department: none, // optional
title: highlight[Rašto darbo pavadinimas],
subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)],
authors: (
@@ -138,44 +148,80 @@
),
place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
centerAuthors: false,
authorNamesFirst: true, // if false, roles will be displayed first
) = {
let isArr = type(subtitles) == array
if not isArr {
let subHeaders = (faculty,)
if department != none {
subHeaders = (faculty, department)
}
if type(subtitles) != array {
subtitles = (subtitles,)
}
ktu-title-header-block(
subHeaders: (faculty,),
subHeaders: subHeaders,
bigTitle: title,
subTitles: subtitles,
)
v(94pt)
ktu-author-table(authors: authors, alignment: center)
if centerAuthors {
ktu-author-table(authors: authors, namesFirst: authorNamesFirst, alignment: center)
} else {
ktu-author-table(authors: authors, namesFirst: authorNamesFirst)
}
align(center+bottom, block([*#place, #year*]))
}
#let ktu-heading-page-centered(
faculty: highlight[Fakulteto pavadinimas],
department: none, // optional
title: highlight[Rašto darbo pavadinimas],
subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)],
authors: (
(highlight[Vardenis Pavardenis], highlight[Projekto autorius]),
(highlight[Vardenis Pavardenis], highlight[Vadovas]),
),
authorNamesFirst: true, // if false, roles will be displayed first
place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
) = {
ktu-heading-page(
faculty: faculty,
department: department,
title: title,
subtitles: subtitles,
authors: authors,
place: place,
year: year,
centerAuthors: true,
authorNamesFirst: authorNamesFirst,
)
}
#let ktu-heading-page-normal(
faculty: highlight[Fakulteto pavadinimas],
department: none, // optional
title: highlight[Rašto darbo pavadinimas],
subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)],
authors: (
(highlight[Vardenis Pavardenis], highlight[Projekto autorius]),
(highlight[Vardenis Pavardenis], highlight[Vadovas]),
),
authorNamesFirst: true, // if false, roles will be displayed first
place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
) = {
let isArr = type(subtitles) == array
if not isArr {
subtitles = (subtitles,)
}
ktu-title-header-block(
subHeaders: (faculty,),
bigTitle: title,
subTitles: subtitles,
ktu-heading-page(
faculty: faculty,
department: department,
title: title,
subtitles: subtitles,
authors: authors,
place: place,
year: year,
centerAuthors: false,
authorNamesFirst: authorNamesFirst,
)
v(94pt)
ktu-author-table(authors: authors)
align(center+bottom, block([*#place, #year*]))
}
#let ktu-academic-honestly-declaration-page(
+79 -13
View File
@@ -1,8 +1,13 @@
#import "@preview/codly:1.3.0": codly, codly-init
#import "config.typ": debug-author-table, enable-heading-experiment, __HEADING_EXPERIMENT, __FIG_PER_SECTION
#import "config.typ": debug-author-table, enable-heading-experiment, __HEADING_EXPERIMENT, __FIG_PER_SECTION, __UPPERCASE_SECTION_TITLES
#import "fragments.typ": (
ktu-table,
ktu-heading-page-centered, ktu-heading-page-normal, ktu-academic-honestly-declaration-page
// building blocks
ktu-author-table, ktu-title-header-block,
ktu-heading-page, ktu-heading-page-centered, ktu-heading-page-normal,
ktu-academic-honestly-declaration-page
)
#import "figures.typ": (
__fig-supplement,
@@ -24,15 +29,25 @@
* - Proposal: change `leading` option to `line-height` | https://github.com/typst/typst/issues/4224
*/
#let __METADATA_HEADING_ALIGN_LEFT = metadata("heading-align-left");
///
/// Insert a heading without a number.
///
/// Usually used for introductory or summary sections at the start and end.
/// - text (str): The heading's title.
/// - headingElement (heading): The heading's title.
/// - alignLeft (bool): Whether the heading should be left-aligned or centered.
/// ->
#let unnumbered-heading(text) = {
set heading(numbering: none)
[#text]
#let unnumbered-heading(headingElement, alignLeft: false) = {
let fields = headingElement.fields()
let body = fields.body
let level = fields.at("level", default: 1)
if alignLeft {
body = body + __METADATA_HEADING_ALIGN_LEFT
}
heading(level: level, numbering: none, body)
}
#let appendix-item(body) = {
@@ -192,6 +207,9 @@
show outline: o => context {
// If it's a heading, show the default outline with bold text
if (o.target == selector(heading)) {
// Determine whether to uppercase section titles based on the global setting
let isUpper = __UPPERCASE_SECTION_TITLES.get()
// Define known target widths based on KTU template
let targets = (0.64cm, 0.96cm, 1.28cm, 1.6cm, 1.92cm)
@@ -207,9 +225,25 @@
target = 0.64cm
}
let newGap = target - measurement.width
// Uppercase body if need be
let bodyContent = it.element.body
if isUpper and (it.level == 1 or it.element.numbering == none) {
bodyContent = upper(bodyContent)
}
// Reconstruct the it.inner content with the new body
let formattedInner = {
bodyContent
[ ] // whitespace is required for backwards compat
box(width: 1fr, it.fill)
[ ] // whitespace is required for backwards compat
it.page()
}
link(
it.element.location(),
it.indented(prefixContent, it.inner(), gap: newGap),
it.indented(prefixContent, formattedInner, gap: newGap),
)
}
@@ -382,6 +416,8 @@
#let setup-page(
font: "Times New Roman",
uppercaseSectionTitles: false,
unnumberedHeadingAlignment: center,
body
) = {
show: __page_rules.with(font: font)
@@ -396,6 +432,14 @@
set par(justify: true)
set linebreak(justify: true)
let maybe-uppercase(body) = {
if uppercaseSectionTitles {
upper(body)
} else {
body
}
}
// Antraštės
// Force them into blocks so they don't count as paragraphs
show heading: it => {
@@ -413,11 +457,16 @@
// Antraštė be nr.
if it.numbering == none {
// Centruota lygiuotė
set align(center)
// Centruota lygiuotė by default, but can be overridden by the user
let explicitlyAlignLeft = it.body.has("children") and it.body.children.len() > 1 and it.body.children.at(1) == __METADATA_HEADING_ALIGN_LEFT
// If the user explicitly set alignLeft to true, we should align left regardless of the global setting
let targetAlign = if explicitlyAlignLeft { left } else { unnumberedHeadingAlignment }
set align(targetAlign)
// atstumas prieš ir po antraštės - 10 pt
block[#it.body]
block[#maybe-uppercase(it.body)]
if useExperiment {
v(10pt)
}
@@ -437,7 +486,7 @@
}
// po antraštės - 10 pt
block[#counter(heading).display() #it.body]
block[#counter(heading).display() #maybe-uppercase(it.body)]
if useExperiment {
v(10pt)
}
@@ -473,19 +522,36 @@
// Bibliography citing style
set bibliography(style: "assets/iso690-numeric-lt.csl")
// Block equations are left-aligned
show math.equation.where(block: true): it => {
align(left, it)
}
body
}
/// Configure the template for a KTU paper.
/// - font (str): Font to use for the paper.
/// - font (str): Text font to use for the paper.
/// - figureNumberingPerSection (bool): Whether to number figures per section rather than globally.
/// - uppercaseSectionTitles (bool): Whether to uppercase section titles.
/// - unnumberedHeadingAlignment (alignment): Alignment for unnumbered headings.
#let ktu-paper(
font: "Times New Roman",
figureNumberingPerSection: false,
// Not sure about formal conformity of deviating
// from the defaults of the next options,
// but some modules may require them
uppercaseSectionTitles: false,
unnumberedHeadingAlignment: center,
body
) = context {
show: setup-page.with(font: font)
show: setup-page.with(
font: font,
uppercaseSectionTitles: uppercaseSectionTitles,
unnumberedHeadingAlignment: unnumberedHeadingAlignment,
)
__FIG_PER_SECTION.update(figureNumberingPerSection)
__UPPERCASE_SECTION_TITLES.update(uppercaseSectionTitles)
body
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

File renamed without changes.
+10 -10
View File
@@ -1,4 +1,4 @@
#import "@local/ktu-paper:0.2.0": (
#import "@local/ktu-paper:0.3.2": (
ktu-paper, setup-code,
ktu-table-of-contents, ktu-picture-list, ktu-table-list,
ktu-heading-page-normal,
@@ -94,7 +94,7 @@ show table.cell: set text(size: 8pt)
Duomenų sklaidą yra patogu pavaizduoti ir stačiakampe diagrama:
#figure(
image("image.jpeg", width: 70%),
image("images/santaka.png", width: 70%),
caption: [
Skaitinių atributų stačiakampė diagrama
],
@@ -136,8 +136,8 @@ Paveikslėlius galima grupuoti į "grid" #custom-ref(<image-grid-2>, <image-grid
#figure(grid(
columns: 2,
gutter: 2mm,
image("image.jpeg", width: 100%),
image("image.jpeg", width: 100%),
image("images/santaka.png", width: 100%),
image("images/santaka.png", width: 100%),
), caption: [
Paveikslėliai "grid'e" 1
]) <image-grid-2>
@@ -145,11 +145,11 @@ Paveikslėlius galima grupuoti į "grid" #custom-ref(<image-grid-2>, <image-grid
#figure(grid(
columns: 3,
gutter: 2mm,
image("image.jpeg", width: 100%),
image("image.jpeg", width: 100%),
image("image.jpeg", width: 100%),
image("image.jpeg", width: 100%),
image("image.jpeg", width: 100%),
image("images/santaka.png", width: 100%),
image("images/santaka.png", width: 100%),
image("images/santaka.png", width: 100%),
image("images/santaka.png", width: 100%),
image("images/santaka.png", width: 100%),
), caption: [
Paveikslėliai "grid'e" 2
]) <image-grid-3>
@@ -190,7 +190,7 @@ $
čia $X$ -- imtis, $mu$ -- imties vidurkis, $sigma$ -- imties standartinis nuokrypis. Fragmentas pateikiamas #ref-no(<image-1>) paveikslėlyje.
#figure(
image("image.jpeg", width: 90%),
image("images/santaka.png", width: 90%),
caption: [
Paveikslėlio pavadinimas
],
+2 -2
View File
@@ -1,4 +1,4 @@
#import "@local/ktu-paper:0.2.0": (
#import "@local/ktu-paper:0.3.2": (
ktu-paper, setup-code,
ktu-table-of-contents, ktu-picture-list, ktu-table-list,
ktu-heading-page-normal,
@@ -191,7 +191,7 @@ Eu eum decore inimicus consetetur, cu usu habeo corpora intellegam. Ut antiopam
Lorem ipsum dolor sit amet, eam ex decore persequeris, sit at illud lobortis atomorum. Sed dolorem quaerendum ne, prompta instructior ne pri. Et mel partiendo suscipiantur, docendi abhorreant ea sit. Recteque imperdiet eum te.
#figure(
image("Picture.png", width: 11.2cm, height: 8cm),
image("images/santaka.png", width: 11.2cm, height: 8cm),
caption: [
Kauno technologijos universiteto „Santakos“ slėnio fasadas
],
+2 -2
View File
@@ -1,7 +1,7 @@
[package]
name = "ktu-paper"
version = "0.2.0"
compiler = "0.15.1"
version = "0.3.2"
compiler = "0.15.0"
entrypoint = "src/lib.typ"
repository = "https://github.com/svetikas/ktu-paper"
authors = ["Dominykas Svetikas <https://svetikas.lt>"]