27 Commits
Author SHA1 Message Date
JustAnyone b803a906ea chore: bump version to 0.4.0 2026-10-15 00:24:39 +03:00
JustAnyone 71b83ab97f style: use consistent 2 space indentation 2026-10-15 00:21:18 +03:00
JustAnyone f8a747e6b3 refactor: add support for custom figure kinds
This also fixes some bugs with referencing figures in elements such as align,
this also generalizes the logic to handle nesting.

#__format-ref show rule now goes through #custom-ref most of the time,
so builtin #ref can be used instead.

Equations are now referenceable and respect numbering per sections.
2026-10-15 00:05:23 +03:00
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
JustAnyone b046a59a89 chore: bump typst.toml 2026-09-03 16:53:10 +03:00
JustAnyone ca96637e50 chore: bump version 2026-09-03 16:50:23 +03:00
JustAnyone ef3e1f6432 feat: add support for per-section counting 2026-09-03 16:46:04 +03:00
JustAnyone e37150fc87 chore: add a script to test for regressions 2026-09-03 16:30:12 +03:00
JustAnyone f5835937db refactor: prepare to support customizing figure numbering 2026-09-03 16:28:26 +03:00
JustAnyone 05da5ad1bf refactor: remove custom-figure, figureDefinitions as they've never been used anywhere 2026-09-03 13:31:51 +03:00
JustAnyone 22c9e7df8e feat: support specifying custom font 2026-09-03 13:27:08 +03:00
JustAnyone 2b0406163e chore: fix repository link in typst.toml 2026-09-03 13:25:26 +03:00
JustAnyone 39a5959bca chore: add a license 2026-09-03 13:03:26 +03:00
13 changed files with 970 additions and 635 deletions

No files matched your search

+2
View File
@@ -1 +1,3 @@
template/*.pdf template/*.pdf
tests/
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Dominykas Svetikas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+9
View File
@@ -1,3 +1,12 @@
# ktu-paper # 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. 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.4.0/`
And then initiate a new project:
```sh
typst init @local/ktu-paper:0.4.0
```
+15 -2
View File
@@ -1,9 +1,22 @@
/// Whether to number figures per section or not.
/// If true, figures will be numbered like "1.1", "1.2", "2.1", etc.
/// If false, figures will be numbered like "1", "2", "3", etc.
///
/// TODO: figure out how to deal with numbering on sections such as
/// 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 = state("$ktu-template-DEBUG_AUTHOR_TABLE", false)
#let debug-author-table() = { #let debug-author-table() = {
__DEBUG_AUTHOR_TABLE.update(true) __DEBUG_AUTHOR_TABLE.update(true)
} }
#let __HEADING_EXPERIMENT = state("$ktu-template-HEADING_EXPERIMENT", false) #let __HEADING_EXPERIMENT = state("$ktu-template-HEADING_EXPERIMENT", false)
#let enable-heading-experiment() = { #let enable-heading-experiment() = {
__HEADING_EXPERIMENT.update(true) __HEADING_EXPERIMENT.update(true)
} }
+302
View File
@@ -0,0 +1,302 @@
// 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
// ---------------------------------------------------------------------------
// Figure kinds
// ---------------------------------------------------------------------------
/// Localizations for the recognized referenceable figure kinds, keyed by `repr(func)`.
/// - supplement: used in captions (the caption adds the separator, e.g. "1 pav.").
/// - ref-name: used in references, e.g. "(žr. 1 pav.)".
#let __figure-kinds = (
table: (supplement: "lentelė", ref-name: "lentelė"),
image: (supplement: "pav", ref-name: "pav."),
raw: (supplement: "kodo frag", ref-name: "kodo frag."),
equation: (supplement: "lygtis", ref-name: "formulė"),
)
/// Figure kinds registered by the document author via `ktu-paper(figureKinds: ...)`.
#let __USER_FIGURE_KINDS = state("ktu-user-figure-kinds", (:))
/// Validates author-supplied kinds and fills in defaults (`ref-name` falls back
/// to `supplement`).
#let __normalize-figure-kinds(kinds) = {
let normalized = (:)
for (key, spec) in kinds {
assert(
"supplement" in spec,
message: "figure kind `" + key + "` needs a `supplement`",
)
normalized.insert(key, (
supplement: spec.supplement,
ref-name: spec.at("ref-name", default: spec.supplement),
))
}
normalized
}
/// Recognized kinds merged with the author's. Must be called in a context.
#let __all-figure-kinds() = __figure-kinds + __USER_FIGURE_KINDS.get()
/// Resets all figure and equation counters (used when numbering per section).
/// Must be called in a context.
#let __reset-figure-counters() = {
let custom = (..__USER_FIGURE_KINDS.get().keys(),)
for kind in (image, table, raw, ..custom) {
counter(figure.where(kind: kind)).update(0)
}
counter(math.equation).update(0)
}
/// Returns the caption supplement text for a figure kind (e.g. `table`, `math.equation`).
#let __fig-supplement(func) = __figure-kinds.at(repr(func)).supplement
/// Finds the first recognized kind ("table", "image", "raw" or "equation") inside
/// `node`, descending through wrappers (align, block, grid, stack, ...).
/// Returns the key into `__figure-kinds`, or `none` if nothing is recognized.
#let __find-figure-kind(node) = {
if type(node) != content { return none }
let key = repr(node.func())
if key in __figure-kinds { return key }
// Single-child wrappers
for field in ("body", "child") {
if node.has(field) {
let found = __find-figure-kind(node.at(field))
if found != none { return found }
}
}
// Multi-child containers
if node.has("children") {
for child in node.children {
let found = __find-figure-kind(child)
if found != none { return found }
}
}
none
}
/// Resolves the `__figure-kinds` key of a figure element. An explicit custom
/// (string) kind always wins; otherwise the kind is detected from the body.
#let __figure-key(fig) = {
if type(fig.kind) == str { return fig.kind }
let found = __find-figure-kind(fig.body)
if found != none { found } else { repr(fig.kind) }
}
/// Returns the caption supplement of a figure element.
#let __figure-supplement-of(fig) = {
let key = __figure-key(fig)
let kinds = __all-figure-kinds()
assert(
key in kinds,
message: "unregistered figure kind `" + key + "`: register it with ktu-paper(figureKinds: ...)",
)
kinds.at(key).supplement
}
// ---------------------------------------------------------------------------
// Numbering
// ---------------------------------------------------------------------------
/// Resolves a figure's number (global "1" or section-based "1.1") at `loc`,
/// returned as a string.
#let __get-figure-number(fig-item, loc) = {
assert(
type(fig-item) == content and fig-item.func() == figure,
message: "can only get figure number for figure elements",
)
let fig-counter = counter(figure.where(kind: fig-item.kind))
if __FIG_PER_SECTION.at(loc) {
let chapter = counter(heading).at(loc).at(0, default: 0)
let fig-num = fig-counter.at(loc).at(0, default: 1)
str(chapter) + "." + str(fig-num)
} else {
str(fig-counter.at(loc).at(0))
}
}
/// Resolves an equation's number (global "2" or section-based "1.2") at `loc` without parentheses.
/// `loc` is the location of the equation.
#let __get-equation-number(loc) = {
let number = counter(math.equation).at(loc).at(0)
if __FIG_PER_SECTION.at(loc) {
let chapter = counter(heading).at(loc).at(0, default: 0)
str(chapter) + "." + str(number)
} else {
str(number)
}
}
/// Numbering function for `math.equation`: "(2)" or, when numbering per
/// section, "(1.2)".
#let __equation-numbering(..nums) = context {
let number = nums.pos().first()
if __FIG_PER_SECTION.get() {
let chapter = counter(heading).get().at(0, default: 0)
// TODO: this is a hack to prevent the numbers from being completely
// part of a regex match against math.equation.
// It would be better to render math equation numbers as a separate element, so that regexes don't see them at all.
[(#box[#chapter].#box[#number])]
} else {
[(#number)]
}
}
/// Returns the number of a located figure element as a string, formatted
/// according to the current numbering scheme.
#let __get-locatable-element-number(element) = context {
assert(type(element) == content, message: "can only reference elements of type content")
assert(element.func() == figure, message: "referenced element does not have a figure function")
__get-figure-number(element, element.location())
}
/// Appendix headings carry an "appendix-heading" metadata marker as their
/// first child and are numbered differently from normal headings.
#let __is-appendix-heading(item) = {
let body = item.body
body.has("children") and body.children.at(0, default: none) == metadata("appendix-heading")
}
/// Parses a dotted number such as "12" or "1.12.5" into an array of ints.
/// Returns `none` if any part is not purely numeric.
#let __parse-number(value) = {
let parts = value.split(".")
if parts.all(p => p.match(regex("^\\d+$")) != none) {
parts.map(int)
} else {
none
}
}
/// Sorts `(location, number)` entries. If every number is numeric (optionally
/// dotted, e.g. "1.12.5"), sorts component-wise numerically; otherwise falls
/// back to plain string ordering.
#let __sort-entries(entries) = {
let keys = entries.map(e => __parse-number(e.at(1)))
if keys.all(k => k != none) {
entries.zip(keys).sorted(key: pair => pair.at(1)).map(pair => pair.at(0))
} else {
entries.sorted(key: e => e.at(1))
}
}
// ---------------------------------------------------------------------------
// References
// ---------------------------------------------------------------------------
/// Returns a number for the given reference target.
/// - target (label): Label to get the reference number for.
#let ref-no(target) = context {
if type(target) != label {
panic("ref-no expects a label as input")
}
let item = query(target).first()
let loc = item.location()
let kind = item.func()
let number = if kind == figure {
__get-figure-number(item, loc)
} else if kind == math.equation {
"(" + __get-equation-number(loc) + ")"
} else if kind == heading {
let numbers = counter(heading).at(loc)
if __is-appendix-heading(item) {
str(numbers.at(1))
} else {
numbers.map(str).join(".")
}
} else {
panic("referencing unknown type: " + repr(kind))
}
link(loc, number)
}
/// Create a custom reference for multiple targets of the same type.
/// - targets (label...): Labels to create a custom reference for.
#let custom-ref(..targets) = context {
let group-name = none
let entries = () // (location, number) pairs
for target in targets.pos() {
let item = query(target).first()
let loc = item.location()
let (name, number) = if item.func() == math.equation {
(__figure-kinds.equation.ref-name, __get-equation-number(loc))
} else if item.func() == figure {
let kind = __figure-key(item)
let kinds = __all-figure-kinds()
if kind not in kinds {
panic("unregistered figure kind `" + kind + "`: register it with ktu-paper(figureKinds: ...)")
}
(kinds.at(kind).ref-name, __get-figure-number(item, loc))
} else {
panic("referencing unknown type: " + repr(item.func()))
}
if group-name != none and group-name != name {
panic("custom-ref does not support grouping different elements together")
}
group-name = name
entries.push((loc, number))
}
if entries.len() > 0 {
let links = __sort-entries(entries).map(e => link(e.at(0), e.at(1)))
[(žr. #links.join(", ") #group-name)]
}
}
/// Show rule for `@label` references. Equations render as a bare "(1.2)";
/// figures delegate to `custom-ref`, so "(žr. 1 pav.)" is built in one place.
/// Anything else (headings, citations, unregistered figure kinds) keeps
/// Typst's default rendering.
#let __format-ref(it) = {
let el = it.element
if el == none { return it }
if el.func() == math.equation {
let loc = el.location()
link(loc, "(" + __get-equation-number(loc) + ")")
} else if el.func() == figure and __figure-key(el) in __all-figure-kinds() {
custom-ref(it.target)
} else {
it
}
}
// ---------------------------------------------------------------------------
// Figure layout
// ---------------------------------------------------------------------------
/// Formats a figure with a caption and number.
#let __format-figure(it) = {
let caption = it.caption
// If caption is not provided, just show the figure body
if caption == none { return it.body }
set text(size: 11pt)
let supplement = __figure-supplement-of(it)
let separator = [.]
let fig-num = context __get-figure-number(it, here())
let caption-line = [*#fig-num #supplement#separator* #caption.body]
// Tables have captions at the top
if it.kind == table {
align(left, caption-line)
it.body
} else {
it.body
align(center, caption-line)
}
}
+215 -163
View File
@@ -3,179 +3,231 @@
/// Wrapper around the table function that returns a KTU style conformant table. /// Wrapper around the table function that returns a KTU style conformant table.
/// This includes setting text size to 10 pt. /// This includes setting text size to 10 pt.
#let ktu-table(..args) = { #let ktu-table(..args) = {
show table: it => { show table: it => {
set text(size: 10pt) set text(size: 10pt)
// TODO: FIXME line spacing is defined differently // TODO: FIXME line spacing is defined differently
set par(leading: 1em - 0.75em) // intervalas tarp eilučių -- 1 set par(leading: 1em - 0.75em) // intervalas tarp eilučių -- 1
it it
} }
table( table(
..args ..args
) )
} }
#let ktu-author-table( #let ktu-author-table(
authors: (), authors: (),
alignment: right, alignment: right,
namesFirst: true, // if false, roles will be displayed first
) = context { ) = context {
// The layout should be a list of lists whose elements are (name, role) pairs. // 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") assert(authors.len() > 0, message: "author table must have at least one author")
assert(authors.all(it => it.len() == 2), message: "each author entry must be a pair of name and role") assert(authors.all(it => it.len() == 2), message: "each author entry must be a pair of name and role")
let frame(color) = (x, y) => ( let frame(color) = (x, y) => (
left: 0pt, left: 0pt,
right: 0pt, right: 0pt,
top: if y == 0 { 0.5pt + color } else { 0pt }, top: if y == 0 { 0.5pt + color } else { 0pt },
bottom: 0.5pt + color, bottom: 0.5pt + color,
) )
set table( set table(
inset: 5.4pt, inset: 5.4pt,
stroke: if __DEBUG_AUTHOR_TABLE.get() { stroke: if __DEBUG_AUTHOR_TABLE.get() {
0.5pt + rgb("D4AF37") 0.5pt + rgb("D4AF37")
} else {
frame(rgb("D4AF37"))
},
)
show table.cell: it => context {
[#it]
if it.body == [] {
v(10pt)
} else {
v(0pt)
}
}
if alignment == center {
align(center, table(
columns: (198.5pt + 56.7pt),
table.header([]),
..for author in authors {
let (name, role) = author
(align(center)[
#set block(below: 8pt)
#block[*#name*]
#block[#role]
], [])
}
))
} else { } else {
align(alignment, table( frame(rgb("D4AF37"))
columns: (198.5pt, 56.7pt), },
table.header( )
[], [], show table.cell: it => context {
), [#it]
..for author in authors { if it.body == [] {
let (name, role) = author v(10pt)
(align(left)[ } else {
#set block(below: 8pt) v(0pt)
#block[*#name*]
#block[#role]
], [], [], [])
}
))
} }
}
if alignment == center {
align(center, table(
columns: (198.5pt + 56.7pt),
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[#role]
],
[],
)
}
))
} else {
align(alignment, table(
columns: (198.5pt, 56.7pt),
table.header([], []),
..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[#role]
],
[],
[],
[],
)
},
))
}
} }
/// This function constructs the title page header block for KTU reports. It includes the university logo, name, subheaders, the main and sub titles of the report. /// This function constructs the title page header block for KTU reports. It includes the university logo, name, subheaders, the main and sub titles of the report.
/// ///
/// - bigTitle (content): The main title of the report, usually the name of the work. Displayed in 18 pt font. /// - bigTitle (content): The main title of the report, usually the name of the work. Displayed in 18 pt font.
/// - subTitles (): Additional titles or information to be displayed under the main title, such as the type of work. Displayed in 14 pt font. If not provided, defaults to "Rašto darbo tipas". /// - subTitles (): Additional titles or information to be displayed under the main title, such as the type of work. Displayed in 14 pt font. If not provided, defaults to "Rašto darbo tipas".
/// - subHeaders (): Additional header information to be displayed under the university name, such as the faculty. If not provided, defaults to "Fakulteto pavadinimas". /// - subHeaders (): Additional header information to be displayed under the university name, such as the faculty. If not provided, defaults to "Fakulteto pavadinimas".
/// -> /// ->
#let ktu-title-header-block( #let ktu-title-header-block(
bigTitle: highlight[Didelis pavadinimas], bigTitle: highlight[Didelis pavadinimas],
subTitles: (), subTitles: (),
subHeaders: (), subHeaders: (),
) = { ) = {
let parsedSubtext = if subHeaders.len() > 0 { let parsedSubtext = if subHeaders.len() > 0 {
subHeaders subHeaders
} else { } else {
( (
highlight[Fakulteto pavadinimas], highlight[Fakulteto pavadinimas],
) )
}
let subTitles = if subTitles.len() > 0 {
subTitles
} else {
(
highlight[Rašto darbo tipas],
)
}
// Top of the title page
v(20pt)
align(center)[
#image("assets/ktu-logo.png", width: 2.46cm, height: 2.69cm)
#v(25pt)
#block[*Kauno technologijos universitetas*]
#for value in parsedSubtext {
block[#value]
} }
]
let subTitles = if subTitles.len() > 0 { // The title page information
subTitles v(91pt)
} else { align(center)[
( #set par(justify: false)
highlight[Rašto darbo tipas], #set linebreak(justify: false)
) #block(text(size: 18pt, hyphenate: false)[*#bigTitle*])
#for value in subTitles {
block(text(size: 14pt)[#value])
} }
]
}
// Top of the title page #let ktu-heading-page(
v(20pt) faculty: highlight[Fakulteto pavadinimas],
align(center)[ department: none, // optional
#image("assets/ktu-logo.png", width: 2.46cm, height: 2.69cm) title: highlight[Rašto darbo pavadinimas],
#v(25pt) subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)],
#block[*Kauno technologijos universitetas*] authors: (
#for value in parsedSubtext { (highlight[Vardenis Pavardenis], highlight[Projekto autorius]),
block[#value] (highlight[Vardenis Pavardenis], highlight[Vadovas]),
} ),
] place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
// The title page information centerAuthors: false,
v(91pt) authorNamesFirst: true, // if false, roles will be displayed first
align(center)[ ) = {
#set par(justify: false) let subHeaders = (faculty,)
#set linebreak(justify: false) if department != none {
#block(text(size: 18pt, hyphenate: false)[*#bigTitle*]) subHeaders = (faculty, department)
#for value in subTitles { }
block(text(size: 14pt)[#value]) if type(subtitles) != array {
} subtitles = (subtitles,)
] }
ktu-title-header-block(
subHeaders: subHeaders,
bigTitle: title,
subTitles: subtitles,
)
v(94pt)
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( #let ktu-heading-page-centered(
faculty: highlight[Fakulteto pavadinimas], faculty: highlight[Fakulteto pavadinimas],
title: highlight[Rašto darbo pavadinimas], department: none, // optional
subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)], title: highlight[Rašto darbo pavadinimas],
authors: ( subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)],
(highlight[Vardenis Pavardenis], highlight[Projekto autorius]), authors: (
(highlight[Vardenis Pavardenis], highlight[Vadovas]), (highlight[Vardenis Pavardenis], highlight[Projekto autorius]),
), (highlight[Vardenis Pavardenis], highlight[Vadovas]),
place: highlight[Kaunas], ),
year: highlight[#datetime.today().year()], authorNamesFirst: true, // if false, roles will be displayed first
place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
) = { ) = {
let isArr = type(subtitles) == array ktu-heading-page(
if not isArr { faculty: faculty,
subtitles = (subtitles,) department: department,
} title: title,
ktu-title-header-block( subtitles: subtitles,
subHeaders: (faculty,), authors: authors,
bigTitle: title, place: place,
subTitles: subtitles, year: year,
) centerAuthors: true,
v(94pt) authorNamesFirst: authorNamesFirst,
ktu-author-table(authors: authors, alignment: center) )
align(center+bottom, block([*#place, #year*]))
} }
#let ktu-heading-page-normal( #let ktu-heading-page-normal(
faculty: highlight[Fakulteto pavadinimas], faculty: highlight[Fakulteto pavadinimas],
title: highlight[Rašto darbo pavadinimas], department: none, // optional
subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)], title: highlight[Rašto darbo pavadinimas],
authors: ( subtitles: highlight[Praktikos ataskaita / referatas ir pan. (įrašyti rašto darbo tipą)],
(highlight[Vardenis Pavardenis], highlight[Projekto autorius]), authors: (
(highlight[Vardenis Pavardenis], highlight[Vadovas]), (highlight[Vardenis Pavardenis], highlight[Projekto autorius]),
), (highlight[Vardenis Pavardenis], highlight[Vadovas]),
place: highlight[Kaunas], ),
year: highlight[#datetime.today().year()], authorNamesFirst: true, // if false, roles will be displayed first
place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
) = { ) = {
let isArr = type(subtitles) == array ktu-heading-page(
if not isArr { faculty: faculty,
subtitles = (subtitles,) department: department,
} title: title,
ktu-title-header-block( subtitles: subtitles,
subHeaders: (faculty,), authors: authors,
bigTitle: title, place: place,
subTitles: subtitles, year: year,
) centerAuthors: false,
v(94pt) authorNamesFirst: authorNamesFirst,
ktu-author-table(authors: authors) )
align(center+bottom, block([*#place, #year*]))
} }
#let ktu-academic-honestly-declaration-page( #let ktu-academic-honestly-declaration-page(
@@ -183,22 +235,22 @@
author: highlight[Vardenis Pavardenis], author: highlight[Vardenis Pavardenis],
projectTitle: highlight[Projekto pavadinimas], projectTitle: highlight[Projekto pavadinimas],
) = { ) = {
ktu-title-header-block( ktu-title-header-block(
subHeaders: (faculty, author), subHeaders: (faculty, author),
bigTitle: projectTitle, bigTitle: projectTitle,
subTitles: ("Akademinio sąžiningumo deklaracija",) subTitles: ("Akademinio sąžiningumo deklaracija",),
) )
v(20pt) v(20pt)
[ [
Patvirtinu, kad: Patvirtinu, kad:
+ baigiamąjį projektą parengiau savarankiškai ir sąžiningai, nepažeisdama(s) kitų asmenų autoriaus ar kitų teisių, laikydamasi(s) Lietuvos Respublikos autorių teisių ir gretutinių teisių įstatymo nuostatų, Kauno technologijos universiteto (toliau -- Universitetas) intelektinės nuosavybės valdymo ir perdavimo nuostatų bei Universiteto akademinės etikos kodekse nustatytų etikos reikalavimų; + baigiamąjį projektą parengiau savarankiškai ir sąžiningai, nepažeisdama(s) kitų asmenų autoriaus ar kitų teisių, laikydamasi(s) Lietuvos Respublikos autorių teisių ir gretutinių teisių įstatymo nuostatų, Kauno technologijos universiteto (toliau -- Universitetas) intelektinės nuosavybės valdymo ir perdavimo nuostatų bei Universiteto akademinės etikos kodekse nustatytų etikos reikalavimų;
+ baigiamajame projekte visi pateikti duomenys ir tyrimų rezultatai yra teisingi ir gauti teisėtai, nei viena šio projekto dalis nėra plagijuota nuo jokių spausdintinių ar elektroninių šaltinių, visos baigiamojo projekto tekste pateiktos citatos ir nuorodos yra nurodytos literatūros sąraše; + baigiamajame projekte visi pateikti duomenys ir tyrimų rezultatai yra teisingi ir gauti teisėtai, nei viena šio projekto dalis nėra plagijuota nuo jokių spausdintinių ar elektroninių šaltinių, visos baigiamojo projekto tekste pateiktos citatos ir nuorodos yra nurodytos literatūros sąraše;
+ įstatymų nenumatytų piniginių sumų už baigiamąjį projektą ar jo dalis niekam nesu mokėjęs (-usi); + įstatymų nenumatytų piniginių sumų už baigiamąjį projektą ar jo dalis niekam nesu mokėjęs (-usi);
+ suprantu, kad išaiškėjus nesąžiningumo ar kitų asmenų teisių pažeidimo faktui, man bus taikomos akademinės nuobaudos pagal Universitete galiojančią tvarką ir būsiu pašalinta(s) iš Universiteto, o baigiamasis projektas gali būti pateiktas Akademinės etikos ir procedūrų kontrolieriaus tarnybai nagrinėjant galimą akademinės etikos pažeidimą. + suprantu, kad išaiškėjus nesąžiningumo ar kitų asmenų teisių pažeidimo faktui, man bus taikomos akademinės nuobaudos pagal Universitete galiojančią tvarką ir būsiu pašalinta(s) iš Universiteto, o baigiamasis projektas gali būti pateiktas Akademinės etikos ir procedūrų kontrolieriaus tarnybai nagrinėjant galimą akademinės etikos pažeidimą.
#v(10pt) #v(10pt)
#h(58%) #author #h(58%) #author
#h(58%) _Patvirtinta elektroniniu būdu_ #h(58%) _Patvirtinta elektroniniu būdu_
] ]
} }
+340 -451
View File
@@ -1,75 +1,75 @@
#import "@preview/codly:1.3.0": codly, codly-init #import "@preview/codly:1.3.0": codly, codly-init
#import "config.typ": debug-author-table, enable-heading-experiment, __HEADING_EXPERIMENT #import "config.typ": debug-author-table, enable-heading-experiment, __HEADING_EXPERIMENT, __FIG_PER_SECTION, __UPPERCASE_SECTION_TITLES
#import "fragments.typ": ( #import "fragments.typ": (
ktu-table, 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": (
ref-no, custom-ref,
__fig-supplement,
__figure-supplement-of,
__get-locatable-element-number,
__reset-figure-counters,
__equation-numbering,
__normalize-figure-kinds,
__USER_FIGURE_KINDS,
__format-ref,
__format-figure,
) )
/** /**
* Paruošta pagal "Rašto ..." * Paruošta pagal "Rašto ..."
*/ */
/* /*
* Further reading required: * Further reading required:
* - Double line spacing | https://github.com/typst/typst/issues/106 * - Double line spacing | https://github.com/typst/typst/issues/106
* - Proposal: change `leading` option to `line-height` | https://github.com/typst/typst/issues/4224 * - 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. /// Insert a heading without a number.
/// ///
/// Usually used for introductory or summary sections at the start and end. /// 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) #let unnumbered-heading(headingElement, alignLeft: false) = {
[#text] let fields = headingElement.fields()
} let body = fields.body
let level = fields.at("level", default: 1)
#let figureDefinitions = ( if alignLeft {
table: ( body = body + __METADATA_HEADING_ALIGN_LEFT
outlineFormat: (number) => [*#number lentelė.*], }
captionFormat: (number) => [*#number lentelė.*],
),
)
#let custom-figure( heading(level: level, numbering: none, body)
kind,
caption: none,
body
) = {
figure(
body,
kind: kind,
caption: caption,
supplement: "CUSTOM-FIGURE"
)
} }
#let appendix-item(body) = { #let appendix-item(body) = {
heading( heading(
metadata("appendix-heading") + "priedas. " + body, metadata("appendix-heading") + "priedas. " + body,
level: 2, level: 2,
numbering: (..any) => {str(any.at(1))} numbering: (..any) => {str(any.at(1))}
) )
} }
// Stores localized names for various figure types
#let figureNames = (
table: ("lentelė"),
image: ("pav"),
code: ("kodo frag"),
equation: ("lygtis"),
)
// Utility function to round numbers with padded zeros // Utility function to round numbers with padded zeros
#let round-and-pad(number, decimals) = { #let round-and-pad(number, decimals) = {
let rounded = calc.round(number, digits: decimals) let rounded = calc.round(number, digits: decimals)
let s = str(rounded) let s = str(rounded)
let parts = s.split(".") let parts = s.split(".")
let integer-part = parts.at(0) let integer-part = parts.at(0)
let decimal-part = if parts.len() == 2 { parts.at(1) } else { ""} let decimal-part = if parts.len() == 2 { parts.at(1) } else { "" }
let padded-decimal = if decimals > 0 { let padded-decimal = if decimals > 0 {
let current-decimal-length = decimal-part.len() let current-decimal-length = decimal-part.len()
@@ -90,120 +90,6 @@
} }
/// Returns a number for the given reference target.
/// - target (label): Label to get the reference number for.
#let ref-no(target) = context {
let loc = locate(target)
let elem = query(target)
let item = elem.at(0)
// If it's a figure, return the figure number
if item.func() == figure {
link(loc, [#counter(figure.where(kind: item.kind)).at(loc).at(0)])
} else if item.func() == heading {
let fullValue = counter(heading).at(loc)
// If the heading has the "appendix-heading" metadata attached,
// it means that it's an appendix heading and should be numbered differently
if item.body.has("children") and item.body.children.at(0) == metadata("appendix-heading") {
link(loc, [#fullValue.at(1)])
// Otherwise, show the fully qualified heading numbering
} else {
link(loc, [#fullValue.map((x) => str(x)).join(".")])
}
} else {
panic("referencing unknown type: ", item.func())
}
}
#let __ref-no-element(element) = context {
// Ensure that we pass an element
if type(element) != content {
panic("can only reference elements of type content")
}
let loc = element.location()
let elem = query(loc)
let fig = elem.at(0)
if fig.func() != figure {
panic("referencing unknown type: " + fig.func())
}
fig.caption.counter.at(loc).at(0)
}
// Constructs a reference from multiple targets
#let custom-ref(..targets) = context {
let collected = (:)
for target in targets.pos() {
let loc = locate(target)
let elem = query(target)
let fig = elem.at(0)
if fig.func() != figure {
panic("referencing unknown type")
}
// determine what we...
let element = fig.body;
let elementName = "unknown";
let found = false;
while not found {
// If it's a grid
if (element.func() == grid) {
for cell in element.children {
if cell.body.func() == image {
elementName = "pav."
found = true
break
}
}
break
}
if (element.func() == table) {
elementName = "lentelė"
found = true
break
}
if (element.func() == raw) {
elementName = "kodo frag."
found = true
break
}
if (element.func() == image) {
elementName = "pav."
found = true
break
}
panic("unknown element", element.func())
}
if elementName not in collected {
collected.insert(elementName, ())
}
collected.at(elementName).push(
(loc, fig.caption.counter.at(loc).at(0))
)
}
if collected.len() > 1 {
panic("multiple types detected")
}
for type in collected {
let name = type.at(0)
let arr = type.at(1).sorted(key: k => k.at(1))
[(žr. #arr.map(x =>link(x.at(0), [#x.at(1)])).join(", ") #name)]
}
}
/// Returns a KTU template-conformant outline. /// Returns a KTU template-conformant outline.
/// - depth (int | none): Same as outline depth. /// - depth (int | none): Same as outline depth.
/// - indent (auto | function | relative): Same as outline indent. /// - indent (auto | function | relative): Same as outline indent.
@@ -211,105 +97,124 @@
/// - title (auto | content | none): Same as outline title. /// - title (auto | content | none): Same as outline title.
/// -> outline /// -> outline
#let __ktu_outline( #let __ktu_outline(
depth: none, depth: none,
indent: 0em, // Disable the default outline indenting indent: 0em, // Disable the default outline indenting
target: heading, target: heading,
title: auto title: auto,
) = { ) = {
// TODO: įtrauka // TODO: įtrauka
set block(spacing: 0em) set block(spacing: 0em)
// genius solution by @two_horus on Discord (364397430059302912) // genius solution by @two_horus on Discord (364397430059302912)
show outline: o => context { show outline: o => context {
// If it's a heading, show the default outline with bold text // If it's a heading, show the default outline with bold text
if (o.target == selector(heading)) { if (o.target == selector(heading)) {
// Define known target widths based on KTU template // Determine whether to uppercase section titles based on the global setting
let targets = (0.64cm, 0.96cm, 1.28cm, 1.6cm, 1.92cm) let isUpper = __UPPERCASE_SECTION_TITLES.get()
// Apply custom outline entry formatting
show outline.entry: it => {
// Recompute the gap based on the prefix content, so that the outline entry is indented
// from the left regardless of the length of the prefix (based on the requirement)
let prefixContent = it.prefix()
let measurement = measure(prefixContent)
let target = targets.at(it.level - 1, default: 0.64cm)
let newGap = target - measurement.width
if it.element.body.has("children") and it.element.body.children.at(0) == metadata("appendix-heading") {
target = 0.64cm
}
let newGap = target - measurement.width
link(
it.element.location(),
it.indented(prefixContent, it.inner(), gap: newGap),
)
}
show outline.entry.where(level: 1): e => { // Define known target widths based on KTU template
strong(e) let targets = (0.64cm, 0.96cm, 1.28cm, 1.6cm, 1.92cm)
}
o
// If it's an image // Apply custom outline entry formatting
} else if (o.target == figure.where(kind: image)) { show outline.entry: it => {
show outline.entry: it => link( // Recompute the gap based on the prefix content, so that the outline entry is indented
it.element.location(), // from the left regardless of the length of the prefix (based on the requirement)
it.indented([*#__ref-no-element(it.element) #figureNames.image.*], [ #it.inner()], gap: 0pt), let prefixContent = it.prefix()
) let measurement = measure(prefixContent)
o let target = targets.at(it.level - 1, default: 0.64cm)
let newGap = target - measurement.width
// If it is a table, show table formatting if it.element.body.has("children") and it.element.body.children.at(0) == metadata("appendix-heading") {
} else if (o.target == figure.where(kind: table)) { target = 0.64cm
show outline.entry: it => link(
it.element.location(),
it.indented([*#__ref-no-element(it.element) #figureNames.table.*], [ #it.inner()], gap: 0pt),
)
o
// If it is a table, show table formatting
} else if (o.target == figure.where(kind: raw)) {
show outline.entry: it => link(
it.element.location(),
it.indented([*#__ref-no-element(it.element) #figureNames.code.*], [ #it.inner()], gap: 0pt),
)
o
// Otherwise, try to determine what kind of outline it is
} else {
/*[
#o.target #type(o.target),
#(o.target == "heading")
#(o.target == heading)
#(o.target == figure.where(kind: heading))
#(o.target == heading.where())
#(o.target == selector(heading))
]*/
show outline.entry: it => link(
it.element.location(),
it.indented([*#__ref-no-element(it.element) unknown.*], it.inner(), gap: 0pt),
)
o
} }
} let newGap = target - measurement.width
outline(depth: depth, indent: indent, target: target, title: title)
}
#let func-to-name(func) = { // Uppercase body if need be
if func == table { let bodyContent = it.element.body
return "lentelė" 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, formattedInner, gap: newGap),
)
}
show outline.entry.where(level: 1): e => {
strong(e)
}
o
// If it's an image
} else if (o.target == figure.where(kind: image)) {
show outline.entry: it => link(
it.element.location(),
it.indented(
[*#__get-locatable-element-number(it.element) #__fig-supplement(image).*],
[ #it.inner()], gap: 0pt
),
)
o
// If it is a table, show table formatting
} else if (o.target == figure.where(kind: table)) {
show outline.entry: it => link(
it.element.location(),
it.indented(
[*#__get-locatable-element-number(it.element) #__fig-supplement(table).*],
[ #it.inner()], gap: 0pt
),
)
o
// If it is a table, show table formatting
} else if (o.target == figure.where(kind: raw)) {
show outline.entry: it => link(
it.element.location(),
it.indented(
[*#__get-locatable-element-number(it.element) #__fig-supplement(raw).*],
[ #it.inner()], gap: 0pt
),
)
o
// Otherwise, try to determine what kind of outline it is
} else {
/*[
#o.target #type(o.target),
#(o.target == "heading")
#(o.target == heading)
#(o.target == figure.where(kind: heading))
#(o.target == heading.where())
#(o.target == selector(heading))
]*/
// Any other figure kind (generally, user defined kinds)
show outline.entry: it => link(
it.element.location(),
it.indented(
[*#__get-locatable-element-number(it.element) #__figure-supplement-of(it.element).*],
[ #it.inner()], gap: 0pt
),
)
o
} }
if func == image { }
return "pav." outline(depth: depth, indent: indent, target: target, title: title)
}
if func == grid {
return "pav."
}
return func
} }
// Doesn't work in Typst 0.12.0 due to relative paths // Doesn't work in Typst 0.12.0 due to relative paths
#let bibliography-list(path) = { #let bibliography-list(path) = {
bibliography(path, title: "Literatūros sąrašas", full: true) bibliography(path, title: "Literatūros sąrašas", full: true)
} }
/// Returns a KTU style conformant outline. /// Returns a KTU style conformant outline.
@@ -319,24 +224,28 @@
/// - title (auto | content | none): Same as outline title. /// - title (auto | content | none): Same as outline title.
/// -> outline /// -> outline
#let ktu-outline( #let ktu-outline(
depth: none, depth: none,
indent: 0em, // Disable the default outline indenting indent: 0em, // Disable the default outline indenting
target: heading, target: heading,
title: auto title: auto,
) = { ) = {
__ktu_outline(depth: depth, indent: indent, target: target, title: title) __ktu_outline(depth: depth, indent: indent, target: target, title: title)
} }
#let ktu-table-of-contents(depth: 3) = { #let ktu-table-of-contents(depth: 3) = {
ktu-outline(target: heading, title: "Turinys", depth: depth) ktu-outline(target: heading, title: "Turinys", depth: depth)
} }
#let ktu-table-list() = { #let ktu-table-list() = {
ktu-outline(title: heading("Lentelių sąrašas", outlined: true), target: figure.where(kind: table)) ktu-outline(title: heading("Lentelių sąrašas", outlined: true), target: figure.where(kind: table))
} }
#let ktu-picture-list() = { #let ktu-picture-list() = {
ktu-outline(title: heading("Paveikslų sąrašas", outlined: true), target: figure.where(kind: image)) ktu-outline(title: heading("Paveikslų sąrašas", outlined: true), target: figure.where(kind: image))
}
#let ktu-code-list() = {
ktu-outline(title: heading("Kodo fragmentų sąrašas", outlined: true), target: figure.where(kind: raw))
} }
/// Define a custom outline for a specific figure type. /// Define a custom outline for a specific figure type.
@@ -345,236 +254,216 @@
/// - title (str): Title of the outline /// - title (str): Title of the outline
/// - kind (str): Type of the figure /// - kind (str): Type of the figure
#let custom-list(title, kind) = { #let custom-list(title, kind) = {
__ktu_outline(title: heading(title, outlined: true), target: figure.where(kind: kind)) __ktu_outline(title: heading(title, outlined: true), target: figure.where(kind: kind))
} }
#let setup-code(body) = { #let setup-code(body) = {
show: codly-init.with() show: codly-init.with()
codly( codly(
fill: white, fill: white,
zebra-fill: none, zebra-fill: none,
stroke: none, stroke: none,
display-name: false, display-name: false,
display-icon: false, display-icon: false,
) )
show raw: set text(font: "Hack", spacing: 80%) show raw: set text(font: "Hack", spacing: 80%)
body body
} }
#let __page_rules(body) = { #let __page_rules(font: "Times New Roman", body) = {
// Page size and margins // Page size and margins
set page( set page(
paper: "a4", paper: "a4",
width: 210mm, width: 210mm,
height: 297mm, height: 297mm,
margin: ( margin: (
left: 30mm, left: 30mm,
right: 10mm, right: 10mm,
bottom: 20mm, bottom: 20mm,
top: 20mm top: 20mm,
) ),
) )
set page(width: 595pt, height: 842pt) set page(width: 595pt, height: 842pt)
// Numbering rules // Numbering rules
set page(numbering: none, number-align: right) set page(numbering: none, number-align: right)
// Use Lithuanian quotes // Use Lithuanian quotes
set smartquote(quotes: "„“") set smartquote(quotes: "„“")
// Heading numbering // Heading numbering
set heading(numbering: "1.") set heading(numbering: "1.")
// Number equations // Number equations
set math.equation(numbering: "(1)") set math.equation(numbering: __equation-numbering)
// Set text font // Set text font
set text( set text(
font: "Times New Roman", font: font,
size: 12pt, size: 12pt,
lang: "lt", lang: "lt",
// Change where the bounding box is drawn for the text // Change where the bounding box is drawn for the text
top-edge: "ascender", top-edge: "ascender",
bottom-edge: "descender", bottom-edge: "descender",
kerning: false, // Disable kerning kerning: false, // Disable kerning
) )
// Text line interval, defined differently in Typst // Text line interval, defined differently in Typst
let leading = 1.15em // Your line spacing (1, 1.5, 2, etc.) let leading = 1.15em // Your line spacing (1, 1.5, 2, etc.)
let leading = leading - 0.75em // "Normalization" let leading = leading - 0.75em // "Normalization"
set par(leading: leading) set par(leading: leading)
// Indentation for lists and enumerations // Indentation for lists and enumerations
set enum(indent: 12pt) set enum(indent: 12pt)
set list(indent: 12pt) set list(indent: 12pt)
body body
} }
#let setup-page(body) = { #let setup-page(
show: __page_rules.with() font: "Times New Roman",
uppercaseSectionTitles: false,
unnumberedHeadingAlignment: center,
body,
) = {
show: __page_rules.with(font: font)
// Pagal formaliuosius rašto darbų reikalavimus // Pagal formaliuosius rašto darbų reikalavimus
// Tarpas tarp teksto pastraipų // Tarpas tarp teksto pastraipų
set par(spacing: 10pt) set par(spacing: 10pt)
set block(below: 10pt)
// Teksto lygiuotė, abipusė
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 => {
// Align everything left
set align(left)
// Visų antraščių teksto dydis vienodas
// Visų antraščių teksto šifras paryškintas
set text(size: 12pt)
set block(below: 10pt) set block(below: 10pt)
// Teksto lygiuotė, abipusė let useExperiment = __HEADING_EXPERIMENT.get()
set par(justify: true)
set linebreak(justify: true)
// Antraštės // TODO: intervalas tarp eilučių – 1,15 kiekvienai antraštei
// Force them into blocks so they don't count as paragraphs
show heading: it => {
// Align everything left
set align(left)
// Visų antraščių teksto dydis vienodas // Antraštė be nr.
// Visų antraščių teksto šifras paryškintas if it.numbering == none {
set text(size: 12pt) // Centruota lygiuotė by default, but can be overridden by the user
set block(below: 10pt) let explicitlyAlignLeft = (
it.body.has("children")
and it.body.children.len() > 1
and it.body.children.at(1) == __METADATA_HEADING_ALIGN_LEFT
)
let useExperiment = __HEADING_EXPERIMENT.get() // If the user explicitly set alignLeft to true, we should align left regardless of the global setting
let targetAlign = if explicitlyAlignLeft { left } else { unnumberedHeadingAlignment }
// TODO: intervalas tarp eilučių – 1,15 kiekvienai antraštei set align(targetAlign)
// Antraštė be nr. // atstumas prieš ir po antraštės - 10 pt
if it.numbering == none { block[#maybe-uppercase(it.body)]
// Centruota lygiuotė if useExperiment {
set align(center) v(10pt)
}
// atstumas prieš ir po antraštės - 10 pt
block[#it.body]
if useExperiment {
v(10pt)
}
}
// Skyrius
if it.numbering != none and it.level == 1 {
// abipusė lygiuotė
set par(justify: true)
//set align
// po antraštės - 10 pt
block[#counter(heading).display() #it.body]
if useExperiment {
v(10pt)
}
}
// Determine whether the heading has appending metadata attached and adjust its display accordingly
let isAppendixHeading = it.body.has("children") and it.body.children.at(0) == metadata("appendix-heading")
if isAppendixHeading {
block[#counter(heading).display(it.numbering) #it.body]
}
// Poskyris ir skyrelis
if not isAppendixHeading and it.numbering != none and it.level != 1 {
// abipusė lygiuotė
set par(justify: true)
// prieš ir po antraštės - 10 pt
if useExperiment {
v(10pt)
}
block[#counter(heading).display() #it.body]
if useExperiment {
v(10pt)
}
}
} }
// Override references for equations // Skyrius
show ref: it => { if it.numbering != none and it.level == 1 {
let el = it.element // abipusė lygiuotė
set par(justify: true)
if el == none { // Reset figure counters if numbering per section is enabled
return it if __FIG_PER_SECTION.get() {
} __reset-figure-counters()
}
// Override equation references // po antraštės - 10 pt
if el.func() == math.equation { block[#counter(heading).display() #maybe-uppercase(it.body)]
link( if useExperiment {
el.location(), v(10pt)
numbering(el.numbering, ..counter(math.equation).at(el.location())) }
)
// Override references to figures
} else if el.func() == figure {
let fig = el.body
let figType = fig.func()
link(el.location(), [
(žr. #numbering(el.numbering, ..counter(figure.where(kind: figType)).at(el.location())) #func-to-name(figType))
])
} else {
it
}
} }
// Change captions for figures // Determine whether the heading has appending metadata attached and adjust its display accordingly
show figure: it => { let isAppendixHeading = it.body.has("children") and it.body.children.at(0) == metadata("appendix-heading")
let separator = [.] if isAppendixHeading {
let caption = it.caption block[#counter(heading).display(it.numbering) #it.body]
// If caption is not provided, just show the figure body
if caption == none {
it.body
return
}
let counter = caption.counter
let supplement = caption.supplement
if it.body.func() == math.equation {
supplement = figureNames.equation
} else if it.kind == image {
supplement = figureNames.image
} else if it.kind == table {
supplement = figureNames.table
} else if it.kind == raw {
supplement = figureNames.code
}
// Tables have captions at the top
set text(size: 11pt)
if it.kind == table {
{set align(left)
[*#context counter.display(caption.numbering) #supplement#separator* #caption.body]}
it.body
} else {
it.body
[*#context counter.display(caption.numbering) #supplement#separator* #caption.body]
}
} }
// Lentelės pirma eilutė // Poskyris ir skyrelis
/* if not isAppendixHeading and it.numbering != none and it.level != 1 {
show table.cell.where(y: 0): it => context { // abipusė lygiuotė
set align(left) set par(justify: true)
set par(justify: false)
set text(size: TableHeadSize.get()) // Šrifto dydis // prieš ir po antraštės - 10 pt
v(3pt) if useExperiment {
// Paryškintas v(10pt)
[*#it*] }
v(3pt) block[#counter(heading).display() #it.body]
if useExperiment {
v(10pt)
}
} }
}
show table.cell: it => context { // Reference style and numbering
set align(left) show ref: it => __format-ref(it)
set par(justify: false)
set text(size: TableCellSize.get())
v(3pt)
[#it]
v(3pt)
}*/
// Set bibliography and citing style // Figure captions and numbering
set bibliography(style: "assets/iso690-numeric-lt.csl") show figure: it => __format-figure(it)
// Return the body // Bibliography citing style
body 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): 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.
/// - figureKinds (dictionary): Extra figure kinds, e.g.
/// `(command: (supplement: "komanda", ref-name: "komanda"))`. `ref-name`
/// defaults to `supplement`. Use them with `figure(kind: "command", ...)`.
#let ktu-paper(
font: "Times New Roman",
figureNumberingPerSection: false,
figureKinds: (:),
// 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,
uppercaseSectionTitles: uppercaseSectionTitles,
unnumberedHeadingAlignment: unnumberedHeadingAlignment,
)
__FIG_PER_SECTION.update(figureNumberingPerSection)
__USER_FIGURE_KINDS.update(__normalize-figure-kinds(figureKinds))
__UPPERCASE_SECTION_TITLES.update(uppercaseSectionTitles)
body
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

File renamed without changes.
+14 -12
View File
@@ -1,12 +1,14 @@
#import "@local/ktu-paper:0.1.0": ( #import "@local/ktu-paper:0.4.0": (
setup-page, setup-code, ktu-paper, setup-code,
ktu-table-of-contents, ktu-picture-list, ktu-table-list, ktu-table-of-contents, ktu-picture-list, ktu-table-list,
ktu-heading-page-normal, ktu-heading-page-normal,
ktu-table, ktu-table,
custom-ref, ref-no custom-ref, ref-no
) )
#show: setup-page.with() #show: ktu-paper.with(
font: "Times New Roman",
)
#show: setup-code.with() #show: setup-code.with()
#show math.equation: it => { #show math.equation: it => {
@@ -92,7 +94,7 @@ show table.cell: set text(size: 8pt)
Duomenų sklaidą yra patogu pavaizduoti ir stačiakampe diagrama: Duomenų sklaidą yra patogu pavaizduoti ir stačiakampe diagrama:
#figure( #figure(
image("image.jpeg", width: 70%), image("images/santaka.png", width: 70%),
caption: [ caption: [
Skaitinių atributų stačiakampė diagrama Skaitinių atributų stačiakampė diagrama
], ],
@@ -134,8 +136,8 @@ Paveikslėlius galima grupuoti į "grid" #custom-ref(<image-grid-2>, <image-grid
#figure(grid( #figure(grid(
columns: 2, columns: 2,
gutter: 2mm, gutter: 2mm,
image("image.jpeg", width: 100%), image("images/santaka.png", width: 100%),
image("image.jpeg", width: 100%), image("images/santaka.png", width: 100%),
), caption: [ ), caption: [
Paveikslėliai "grid'e" 1 Paveikslėliai "grid'e" 1
]) <image-grid-2> ]) <image-grid-2>
@@ -143,11 +145,11 @@ Paveikslėlius galima grupuoti į "grid" #custom-ref(<image-grid-2>, <image-grid
#figure(grid( #figure(grid(
columns: 3, columns: 3,
gutter: 2mm, gutter: 2mm,
image("image.jpeg", width: 100%), image("images/santaka.png", width: 100%),
image("image.jpeg", width: 100%), image("images/santaka.png", width: 100%),
image("image.jpeg", width: 100%), image("images/santaka.png", width: 100%),
image("image.jpeg", width: 100%), image("images/santaka.png", width: 100%),
image("image.jpeg", width: 100%), image("images/santaka.png", width: 100%),
), caption: [ ), caption: [
Paveikslėliai "grid'e" 2 Paveikslėliai "grid'e" 2
]) <image-grid-3> ]) <image-grid-3>
@@ -188,7 +190,7 @@ $
čia $X$ -- imtis, $mu$ -- imties vidurkis, $sigma$ -- imties standartinis nuokrypis. Fragmentas pateikiamas #ref-no(<image-1>) paveikslėlyje. čia $X$ -- imtis, $mu$ -- imties vidurkis, $sigma$ -- imties standartinis nuokrypis. Fragmentas pateikiamas #ref-no(<image-1>) paveikslėlyje.
#figure( #figure(
image("image.jpeg", width: 90%), image("images/santaka.png", width: 90%),
caption: [ caption: [
Paveikslėlio pavadinimas Paveikslėlio pavadinimas
], ],
+6 -4
View File
@@ -1,5 +1,5 @@
#import "@local/ktu-paper:0.1.0": ( #import "@local/ktu-paper:0.4.0": (
setup-page, setup-code, ktu-paper, setup-code,
ktu-table-of-contents, ktu-picture-list, ktu-table-list, ktu-table-of-contents, ktu-picture-list, ktu-table-list,
ktu-heading-page-normal, ktu-heading-page-normal,
ktu-table, ktu-table,
@@ -7,7 +7,9 @@
custom-ref, ref-no custom-ref, ref-no
) )
#show: setup-page.with() #show: ktu-paper.with(
font: "Times New Roman",
)
#show: setup-code.with() #show: setup-code.with()
#show figure: it => { #show figure: it => {
@@ -189,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. 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( #figure(
image("Picture.png", width: 11.2cm, height: 8cm), image("images/santaka.png", width: 11.2cm, height: 8cm),
caption: [ caption: [
Kauno technologijos universiteto „Santakos“ slėnio fasadas Kauno technologijos universiteto „Santakos“ slėnio fasadas
], ],
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
# Clean up previous test results
if [ -d "tests/original-res" ]; then
rm -r tests/original-res
fi
if [ -d "tests/modified-res" ]; then
rm -r tests/modified-res
fi
if [ -d "tests/modified" ]; then
rm -r tests/modified
fi
if [ -d "tests/original/src" ]; then
rm -r tests/original/src
fi
mkdir -p tests/original-res
mkdir -p tests/modified-res
# Copy current template
cp -r src/ tests/original/src
cp -r tests/original tests/modified
# replace the "#import "@local/ktu-paper:0.1.0""
# with "#import "./src/lib.typ"" in the modified files
find tests/modified -type f -name "*.typ" -exec \
sed -i 's/#import "@local\/ktu-paper:0.1.0"/#import ".\/src\/lib.typ"/g' {} \;
find tests/modified -type f -name "ataskaita.typ" -exec \
sed -i 's/setup-page/ktu-paper/g' {} \;
# Compile the original document
typst compile --root tests/original tests/original/ataskaita.typ "tests/original-res/{0p}.png"
# Compile the modified document
typst compile --root tests/modified tests/modified/ataskaita.typ "tests/modified-res/{0p}.png"
typst compile --root tests/modified tests/modified/ataskaita.typ "tests/modified.pdf"
# Compare the results
diff -r tests/original-res tests/modified-res
if [ $? -eq 0 ]; then
echo "No differences found between original and modified results."
else
echo "Differences found between original and modified results."
fi
+3 -3
View File
@@ -1,9 +1,9 @@
[package] [package]
name = "ktu-paper" name = "ktu-paper"
version = "0.1.0" version = "0.4.0"
compiler = "0.12.0" compiler = "0.15.0"
entrypoint = "src/lib.typ" entrypoint = "src/lib.typ"
repository = "https://git.svetikas.lt/JustAnyone/dotfiles" repository = "https://github.com/svetikas/ktu-paper"
authors = ["Dominykas Svetikas <https://svetikas.lt>"] authors = ["Dominykas Svetikas <https://svetikas.lt>"]
license = "MIT" license = "MIT"
description = "KTU report template to use for writing lab reports or final thesis." description = "KTU report template to use for writing lab reports or final thesis."