5 Commits
Author SHA1 Message Date
JustAnyone ada5bc3f63 chore: bump version to 0.4.1 2026-10-18 17:24:57 +03:00
JustAnyone e5c774f96c feat: add support for figure names without numbering if they're single 2026-10-18 17:23:52 +03:00
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
9 changed files with 1018 additions and 730 deletions

No files matched your search

+2 -2
View File
@@ -4,9 +4,9 @@ This repository contains a Typst template for academic papers at the Kaunas Univ
## Initiating the template
Install the downloaded template under the following directory:
- Linux: `~/.local/share/typst/packages/local/ktu-paper/0.3.2/`
- Linux: `~/.local/share/typst/packages/local/ktu-paper/0.4.1/`
And then initiate a new project:
```sh
typst init @local/ktu-paper:0.3.2
typst init @local/ktu-paper:0.4.1
```
+2 -2
View File
@@ -13,10 +13,10 @@
#let __DEBUG_AUTHOR_TABLE = state("$ktu-template-DEBUG_AUTHOR_TABLE", false)
#let debug-author-table() = {
__DEBUG_AUTHOR_TABLE.update(true)
__DEBUG_AUTHOR_TABLE.update(true)
}
#let __HEADING_EXPERIMENT = state("$ktu-template-HEADING_EXPERIMENT", false)
#let enable-heading-experiment() = {
__HEADING_EXPERIMENT.update(true)
__HEADING_EXPERIMENT.update(true)
}
+325 -108
View File
@@ -1,137 +1,354 @@
// 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 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 = (
table: (
supplement: "lentelė",
),
image: (
supplement: "pav",
),
raw: (
supplement: "kodo frag",
),
equation: (
supplement: "lygtis",
),
// ---------------------------------------------------------------------------
// 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.)".
/// - single-name: full word used when the figure is the only one of its kind and
/// therefore unnumbered, e.g. "Paveikslas. ..." and "(žr. paveikslas)".
#let __figure-kinds = (
table: (supplement: "lentelė", ref-name: "lentelė", single-name: "lentelė"),
image: (supplement: "pav", ref-name: "pav.", single-name: "paveikslas"),
raw: (supplement: "kodo frag", ref-name: "kodo frag.", single-name: "kodo fragmentas"),
equation: (supplement: "lygtis", ref-name: "formulė", single-name: "lygtis"),
)
/// Helper function to get the supplement text for a given figure type.
#let __fig-supplement(func) = {
return __figureNames.at(repr(func)).supplement
/// 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),
single-name: spec.at("single-name", default: spec.supplement),
))
}
normalized
}
#let __fig-func-to-name(func) = {
if func == table {
return "lentelė"
}
if func == image {
return "pav."
}
if func == grid {
return "pav."
}
return func
/// 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)
}
/// 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.
/// 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) = {
if type(fig-item) != content {
panic("can only get figure number for content elements")
}
assert(
type(fig-item) == content and fig-item.func() == figure,
message: "can only get figure number for figure elements",
)
if fig-item.func() != figure {
panic("can only get figure number for figure elements")
}
let fig-counter = counter(figure.where(kind: fig-item.kind))
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)
str(chapter) + "." + str(figNum)
} else {
let caption = fig-item.caption
if caption != none and caption.numbering != none {
str(caption.counter.at(loc).at(0))
} else {
str(counter(figure.where(kind: fig-item.kind)).at(loc).at(0))
}
}
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))
}
}
/// Helper function to get the number of a locatable element (figure, table, etc.)
/// based on its location in the document. It returns the number as a string, formatted according to the current numbering scheme.
/// 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 {
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("referenced element does not have a figure function")
}
__get-figure-number(fig, loc)
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())
}
/// Formats a reference to a figure, table, or other numbered element.
#let __format-ref(it) = {
let el = it.element
if el == none {
return it
}
/// 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")
}
// Override equation references
if el.func() == math.equation {
link(
el.location(),
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()
let numStr = __get-figure-number(el, el.location())
link(el.location(), [
(žr. #numStr #__fig-func-to-name(figType))
])
/// 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))
}
}
// ---------------------------------------------------------------------------
// Single-figure handling
// ---------------------------------------------------------------------------
/// Figure kinds that are left unnumbered when the document contains only one
/// captioned figure of that kind: "Lentelė. Aprašas" and "(žr. lentelė)"
/// instead of "1 lentelė. Aprašas" and "(žr. 1 lentelė)". The word used comes
/// from the kind's `single-name`.
/// Keys are `__figure-key` values.
#let __UNNUMBERED_WHEN_SINGLE = ("table", "image")
/// Whether `fig` is the only captioned figure of its kind in the document and
/// its kind is listed in `__UNNUMBERED_WHEN_SINGLE`. Must be called in a context.
#let __is-single-unnumbered(fig) = {
if __figure-key(fig) not in __UNNUMBERED_WHEN_SINGLE { return false }
query(figure.where(kind: fig.kind)).filter(f => f.caption != none).len() == 1
}
/// Capitalizes the first letter of `value` ("lentelė" -> "Lentelė").
#let __capitalize(value) = {
let first = value.first()
upper(first) + value.slice(first.len())
}
/// Caption label of a figure element: "1 lentelė." normally, or
/// "Lentelė." for a single unnumbered figure.
#let __caption-label(fig) = context {
let supplement = __figure-supplement-of(fig)
if __is-single-unnumbered(fig) {
let name = __all-figure-kinds().at(__figure-key(fig)).single-name
[#__capitalize(name).]
} else {
[#__get-figure-number(fig, fig.location()) #supplement.]
}
}
// ---------------------------------------------------------------------------
// References
// ---------------------------------------------------------------------------
/// Returns a number for the given reference target.
/// Panics for a figure that is the only one of its kind,
/// since such figures have no number.
/// - 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 {
if __is-single-unnumbered(item) {
panic(
"ref-no: " + str(target) + " is the only " + __figure-key(item)
+ " in the document and is therefore unnumbered; "
+ "refer to it with @" + str(target) + " (or custom-ref) instead",
)
}
__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 {
it
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 single-name = none // set when the target is a lone, unnumbered figure
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: ...)")
}
if __is-single-unnumbered(item) { single-name = kinds.at(kind).single-name }
(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 {
if single-name != none {
// The only figure of its kind: no number, e.g. "(žr. lentelė)"
[(žr. #link(entries.first().at(0), single-name))]
} else {
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 separator = [.]
let caption = it.caption
let caption = it.caption
// If caption is not provided, just show the figure body
if caption == none {
return it.body
}
// If caption is not provided, just show the figure body
if caption == none { return it.body }
let counter = caption.counter
let supplement = caption.supplement
set text(size: 11pt)
if it.body.func() == math.equation {
supplement = __fig-supplement("equation")
} else {
supplement = __fig-supplement(it.kind)
}
set text(size: 11pt)
let caption-line = [*#__caption-label(it)* #caption.body]
let fig-num = context __get-figure-number(it, here())
// Tables have captions at the top
if it.kind == table {
align(left)[*#fig-num #supplement#separator* #caption.body]
it.body
} else {
it.body
align(center)[*#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)
}
}
+209 -203
View File
@@ -3,88 +3,94 @@
/// Wrapper around the table function that returns a KTU style conformant table.
/// This includes setting text size to 10 pt.
#let ktu-table(..args) = {
show table: it => {
set text(size: 10pt)
// TODO: FIXME line spacing is defined differently
set par(leading: 1em - 0.75em) // intervalas tarp eilučių -- 1
it
}
table(
..args
)
show table: it => {
set text(size: 10pt)
// TODO: FIXME line spacing is defined differently
set par(leading: 1em - 0.75em) // intervalas tarp eilučių -- 1
it
}
table(
..args
)
}
#let ktu-author-table(
authors: (),
alignment: right,
namesFirst: true, // if false, roles will be displayed first
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")
assert(authors.all(it => it.len() == 2), message: "each author entry must be a pair of name and role")
// 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.all(it => it.len() == 2), message: "each author entry must be a pair of name and role")
let frame(color) = (x, y) => (
left: 0pt,
right: 0pt,
top: if y == 0 { 0.5pt + color } else { 0pt },
bottom: 0.5pt + color,
)
set table(
inset: 5.4pt,
stroke: if __DEBUG_AUTHOR_TABLE.get() {
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
name = [*#name*]
if not namesFirst {
(role, name) = (name, role)
}
(align(center)[
#set block(below: 8pt)
#block[#name]
#block[#role]
], [])
}
))
let frame(color) = (x, y) => (
left: 0pt,
right: 0pt,
top: if y == 0 { 0.5pt + color } else { 0pt },
bottom: 0.5pt + color,
)
set table(
inset: 5.4pt,
stroke: if __DEBUG_AUTHOR_TABLE.get() {
0.5pt + rgb("D4AF37")
} 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]
], [], [], [])
}
))
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
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.
@@ -94,134 +100,134 @@
/// - 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(
bigTitle: highlight[Didelis pavadinimas],
subTitles: (),
subHeaders: (),
bigTitle: highlight[Didelis pavadinimas],
subTitles: (),
subHeaders: (),
) = {
let parsedSubtext = if subHeaders.len() > 0 {
subHeaders
} else {
(
highlight[Fakulteto pavadinimas],
)
let parsedSubtext = if subHeaders.len() > 0 {
subHeaders
} else {
(
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 {
subTitles
} else {
(
highlight[Rašto darbo tipas],
)
// The title page information
v(91pt)
align(center)[
#set par(justify: false)
#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
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]
}
]
// The title page information
v(91pt)
align(center)[
#set par(justify: false)
#set linebreak(justify: false)
#block(text(size: 18pt, hyphenate: false)[*#bigTitle*])
#for value in subTitles {
block(text(size: 14pt)[#value])
}
]
]
}
#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: (
(highlight[Vardenis Pavardenis], highlight[Projekto autorius]),
(highlight[Vardenis Pavardenis], highlight[Vadovas]),
),
place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
centerAuthors: false,
authorNamesFirst: true, // if false, roles will be displayed first
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]),
),
place: highlight[Kaunas],
year: highlight[#datetime.today().year()],
centerAuthors: false,
authorNamesFirst: true, // if false, roles will be displayed first
) = {
let subHeaders = (faculty,)
if department != none {
subHeaders = (faculty, department)
}
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 subHeaders = (faculty,)
if department != none {
subHeaders = (faculty, department)
}
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(
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()],
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,
)
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()],
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: false,
authorNamesFirst: authorNamesFirst,
)
ktu-heading-page(
faculty: faculty,
department: department,
title: title,
subtitles: subtitles,
authors: authors,
place: place,
year: year,
centerAuthors: false,
authorNamesFirst: authorNamesFirst,
)
}
#let ktu-academic-honestly-declaration-page(
@@ -229,22 +235,22 @@
author: highlight[Vardenis Pavardenis],
projectTitle: highlight[Projekto pavadinimas],
) = {
ktu-title-header-block(
subHeaders: (faculty, author),
bigTitle: projectTitle,
subTitles: ("Akademinio sąžiningumo deklaracija",)
)
v(20pt)
[
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ų;
+ 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);
+ 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ą.
ktu-title-header-block(
subHeaders: (faculty, author),
bigTitle: projectTitle,
subTitles: ("Akademinio sąžiningumo deklaracija",),
)
v(20pt)
[
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ų;
+ 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);
+ 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)
#h(58%) #author
#v(10pt)
#h(58%) #author
#h(58%) _Patvirtinta elektroniniu būdu_
]
#h(58%) _Patvirtinta elektroniniu būdu_
]
}
+327 -412
View File
@@ -1,33 +1,36 @@
#import "@preview/codly:1.3.0": codly, codly-init
#import "config.typ": debug-author-table, enable-heading-experiment, __HEADING_EXPERIMENT, __FIG_PER_SECTION, __UPPERCASE_SECTION_TITLES
#import "fragments.typ": (
ktu-table,
ktu-table,
// building blocks
ktu-author-table, ktu-title-header-block,
// 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
ktu-heading-page, ktu-heading-page-centered, ktu-heading-page-normal,
ktu-academic-honestly-declaration-page
)
#import "figures.typ": (
__fig-supplement,
ref-no, custom-ref,
__get-figure-number,
__get-locatable-element-number,
__caption-label,
__reset-figure-counters,
__equation-numbering,
__normalize-figure-kinds,
__USER_FIGURE_KINDS,
__format-ref,
__format-figure,
__format-ref,
__format-figure,
)
/**
* Paruošta pagal "Rašto ..."
*/
*/
/*
* Further reading required:
* - Double line spacing | https://github.com/typst/typst/issues/106
* - Proposal: change `leading` option to `line-height` | https://github.com/typst/typst/issues/4224
*/
*/
#let __METADATA_HEADING_ALIGN_LEFT = metadata("heading-align-left");
@@ -39,23 +42,23 @@
/// - alignLeft (bool): Whether the heading should be left-aligned or centered.
/// ->
#let unnumbered-heading(headingElement, alignLeft: false) = {
let fields = headingElement.fields()
let body = fields.body
let level = fields.at("level", default: 1)
let fields = headingElement.fields()
let body = fields.body
let level = fields.at("level", default: 1)
if alignLeft {
body = body + __METADATA_HEADING_ALIGN_LEFT
}
if alignLeft {
body = body + __METADATA_HEADING_ALIGN_LEFT
}
heading(level: level, numbering: none, body)
heading(level: level, numbering: none, body)
}
#let appendix-item(body) = {
heading(
metadata("appendix-heading") + "priedas. " + body,
level: 2,
numbering: (..any) => {str(any.at(1))}
)
heading(
metadata("appendix-heading") + "priedas. " + body,
level: 2,
numbering: (..any) => {str(any.at(1))}
)
}
// Utility function to round numbers with padded zeros
@@ -64,8 +67,7 @@
let s = str(rounded)
let parts = s.split(".")
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 current-decimal-length = decimal-part.len()
@@ -85,109 +87,6 @@
integer-part + padded-decimal
}
/// 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 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, [#__get-figure-number(item, loc)])
} 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())
}
}
// 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, __get-figure-number(fig, loc))
)
}
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.
/// - depth (int | none): Same as outline depth.
@@ -196,123 +95,124 @@
/// - title (auto | content | none): Same as outline title.
/// -> outline
#let __ktu_outline(
depth: none,
indent: 0em, // Disable the default outline indenting
target: heading,
title: auto
depth: none,
indent: 0em, // Disable the default outline indenting
target: heading,
title: auto,
) = {
// TODO: įtrauka
set block(spacing: 0em)
// genius solution by @two_horus on Discord (364397430059302912)
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()
// TODO: įtrauka
set block(spacing: 0em)
// genius solution by @two_horus on Discord (364397430059302912)
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)
// Define known target widths based on KTU template
let targets = (0.64cm, 0.96cm, 1.28cm, 1.6cm, 1.92cm)
// 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
// 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, 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))
]*/
show outline.entry: it => link(
it.element.location(),
it.indented(
[*#__get-locatable-element-number(it.element) unknown.*],
it.inner(), gap: 0pt
),
)
o
// 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
// 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, 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(
[*#__caption-label(it.element)*],
[ #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(
[*#__caption-label(it.element)*],
[ #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(
[*#__caption-label(it.element)*],
[ #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(
[*#__caption-label(it.element)*],
[ #it.inner()], gap: 0pt
),
)
o
}
outline(depth: depth, indent: indent, target: target, title: title)
}
outline(depth: depth, indent: indent, target: target, title: title)
}
// Doesn't work in Typst 0.12.0 due to relative paths
#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.
@@ -322,24 +222,28 @@
/// - title (auto | content | none): Same as outline title.
/// -> outline
#let ktu-outline(
depth: none,
indent: 0em, // Disable the default outline indenting
target: heading,
title: auto
depth: none,
indent: 0em, // Disable the default outline indenting
target: heading,
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) = {
ktu-outline(target: heading, title: "Turinys", depth: depth)
ktu-outline(target: heading, title: "Turinys", depth: depth)
}
#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() = {
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.
@@ -348,187 +252,188 @@
/// - title (str): Title of the outline
/// - kind (str): Type of the figure
#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) = {
show: codly-init.with()
codly(
fill: white,
zebra-fill: none,
stroke: none,
display-name: false,
display-icon: false,
)
show raw: set text(font: "Hack", spacing: 80%)
body
show: codly-init.with()
codly(
fill: white,
zebra-fill: none,
stroke: none,
display-name: false,
display-icon: false,
)
show raw: set text(font: "Hack", spacing: 80%)
body
}
#let __page_rules(font: "Times New Roman", body) = {
// Page size and margins
set page(
paper: "a4",
width: 210mm,
height: 297mm,
margin: (
left: 30mm,
right: 10mm,
bottom: 20mm,
top: 20mm
)
)
set page(width: 595pt, height: 842pt)
// Page size and margins
set page(
paper: "a4",
width: 210mm,
height: 297mm,
margin: (
left: 30mm,
right: 10mm,
bottom: 20mm,
top: 20mm,
),
)
set page(width: 595pt, height: 842pt)
// Numbering rules
set page(numbering: none, number-align: right)
// Numbering rules
set page(numbering: none, number-align: right)
// Use Lithuanian quotes
set smartquote(quotes: "„“")
// Use Lithuanian quotes
set smartquote(quotes: "„“")
// Heading numbering
set heading(numbering: "1.")
// Heading numbering
set heading(numbering: "1.")
// Number equations
set math.equation(numbering: "(1)")
// Number equations
set math.equation(numbering: __equation-numbering)
// Set text font
set text(
font: font,
size: 12pt,
lang: "lt",
// Change where the bounding box is drawn for the text
top-edge: "ascender",
bottom-edge: "descender",
kerning: false, // Disable kerning
)
// Set text font
set text(
font: font,
size: 12pt,
lang: "lt",
// Change where the bounding box is drawn for the text
top-edge: "ascender",
bottom-edge: "descender",
kerning: false, // Disable kerning
)
// Text line interval, defined differently in Typst
let leading = 1.15em // Your line spacing (1, 1.5, 2, etc.)
let leading = leading - 0.75em // "Normalization"
set par(leading: leading)
// Text line interval, defined differently in Typst
let leading = 1.15em // Your line spacing (1, 1.5, 2, etc.)
let leading = leading - 0.75em // "Normalization"
set par(leading: leading)
// Indentation for lists and enumerations
set enum(indent: 12pt)
set list(indent: 12pt)
// Indentation for lists and enumerations
set enum(indent: 12pt)
set list(indent: 12pt)
body
body
}
#let setup-page(
font: "Times New Roman",
uppercaseSectionTitles: false,
unnumberedHeadingAlignment: center,
body
font: "Times New Roman",
uppercaseSectionTitles: false,
unnumberedHeadingAlignment: center,
body,
) = {
show: __page_rules.with(font: font)
show: __page_rules.with(font: font)
// Pagal formaliuosius rašto darbų reikalavimus
// Pagal formaliuosius rašto darbų reikalavimus
// Tarpas tarp teksto pastraipų
set par(spacing: 10pt)
// Tarpas tarp teksto pastraipų
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)
// Teksto lygiuotė, abipusė
set par(justify: true)
set linebreak(justify: true)
let useExperiment = __HEADING_EXPERIMENT.get()
let maybe-uppercase(body) = {
if uppercaseSectionTitles {
upper(body)
} else {
body
}
// TODO: intervalas tarp eilučių – 1,15 kiekvienai antraštei
// Antraštė be nr.
if it.numbering == none {
// 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[#maybe-uppercase(it.body)]
if useExperiment {
v(10pt)
}
}
// Antraštės
// Force them into blocks so they don't count as paragraphs
show heading: it => {
// Align everything left
set align(left)
// Skyrius
if it.numbering != none and it.level == 1 {
// abipusė lygiuotė
set par(justify: true)
// Visų antraščių teksto dydis vienodas
// Visų antraščių teksto šifras paryškintas
set text(size: 12pt)
set block(below: 10pt)
// Reset figure counters if numbering per section is enabled
if __FIG_PER_SECTION.get() {
__reset-figure-counters()
}
let useExperiment = __HEADING_EXPERIMENT.get()
// TODO: intervalas tarp eilučių – 1,15 kiekvienai antraštei
// Antraštė be nr.
if it.numbering == none {
// 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[#maybe-uppercase(it.body)]
if useExperiment {
v(10pt)
}
}
// Skyrius
if it.numbering != none and it.level == 1 {
// abipusė lygiuotė
set par(justify: true)
// Reset figure counters if numbering per section is enabled
if __FIG_PER_SECTION.get() {
counter(figure.where(kind: image)).update(0)
counter(figure.where(kind: table)).update(0)
counter(figure.where(kind: raw)).update(0)
}
// po antraštės - 10 pt
block[#counter(heading).display() #maybe-uppercase(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)
}
}
// po antraštės - 10 pt
block[#counter(heading).display() #maybe-uppercase(it.body)]
if useExperiment {
v(10pt)
}
}
// Reference style and numbering
show ref: it => __format-ref(it)
// Figure captions and numbering
show figure: it => __format-figure(it)
// 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)
// 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]
}
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)
}
}
}
// Reference style and numbering
show ref: it => __format-ref(it)
// Figure captions and numbering
show figure: it => __format-figure(it)
// 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.
@@ -536,22 +441,32 @@
/// - 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", single-name: "komanda"))`.
/// Use them with `figure(kind: "command", ...)`. Keys:
/// - `supplement` (required): text after the number in captions, e.g. "1 komanda.".
/// - `ref-name`: text used in references, e.g. "(žr. 1 komanda)". Defaults to `supplement`.
/// - `single-name`: full word used when the figure is the only one of its kind
/// and is left unnumbered, e.g. "Komanda." and "(žr. komanda)". Defaults to `supplement`.
/// Currently only tables and images are left unnumbered when single, so this only matters for kinds added there.
#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,
uppercaseSectionTitles: uppercaseSectionTitles,
unnumberedHeadingAlignment: unnumberedHeadingAlignment,
)
__FIG_PER_SECTION.update(figureNumberingPerSection)
__UPPERCASE_SECTION_TITLES.update(uppercaseSectionTitles)
body
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
}
+150
View File
@@ -0,0 +1,150 @@
#import "../src/lib.typ": (
ktu-paper, setup-code,
ktu-table-of-contents, ktu-picture-list, ktu-table-list, ktu-code-list,
ktu-heading-page-normal,
ktu-table,
unnumbered-heading,
custom-ref, ref-no
)
#show: ktu-paper.with(
font: "Times New Roman", // or Arial
figureNumberingPerSection: true,
figureKinds: (
veryCustomKind: (supplement: "komanda", ref-name: "komanda"),
),
)
#show: setup-code.with()
#show math.equation: it => {
show regex("\d+\.\d+"): it => {show ".": {","+h(0pt)}
it}
it
}
#ktu-heading-page-normal(
faculty: highlight[Informatikos fakultetas],
title: highlight[Laboratorinio darbo ataskaitos pavadinimas],
subtitles: (
"Laboratorinio darbo ataskaita",
highlight[Studijų modulio pavadinimas (kodas)]
),
authors: (
(highlight[Vardenis Pavardenis], "Ataskaitą parengė"),
(highlight[Pareigų ir mokslo laipsnio sutrumpinimas Vardenis Pavardenis], "Laboratoriniam darbui vadovavo"),
),
authorNamesFirst: false,
place: "Kaunas",
year: datetime.today().year(),
)
#pagebreak()
#set page(numbering: "1")
#ktu-table-of-contents()
#pagebreak()
#ktu-picture-list()
#pagebreak()
#ktu-table-list()
#pagebreak()
#ktu-code-list()
#pagebreak()
#unnumbered-heading[= Įvadas]
#lorem(50)
#pagebreak()
= Skyrius
#lorem(50)
== Poskyrius
#lorem(50)
=== Dalis
#lorem(50)
#pagebreak()
= Komponentai
== Paveikslai
Galima gauti paveikslo numerį #ref-no(<image-label>) arba visą paveikslo nuorodą #custom-ref(<image-label>).
#figure(
image("images/santaka.png", width: 70%),
caption: [
Paveikslo pavadinimas
],
) <image-label>
== Paveikslų "grid" išdėstymas
Paveikslus taip pat galima sudėti į "grid" išdėstymą, į jį kreiptis kaip numerį #ref-no(<image-grid-label>) arba visą nuorodą #custom-ref(<image-grid-label>).
#figure(grid(
columns: 2,
gutter: 2mm,
image("images/santaka.png", width: 100%),
image("images/santaka.png", width: 100%),
), caption: [
Paveikslėliai "grid'e"
]) <image-grid-label>
== Lentelės
Galima gauti lentelės numerį /*#ref-no(<table-label>)*/ arba visą lentelės nuorodą #custom-ref(<table-label>).
#figure(
ktu-table(
columns: (auto, 1fr, 1fr, 1fr),
align: (x, y) => { horizon + left },
table.header([*Stulpelio pavadinimas*], [*Stulpelio pavadinimas*], [*Stulpelio pavadinimas*], [*Stulpelio pavadinimas*]),
[Eilutė 1], [...], [...], [...],
[Eilutė 2], [...], [...], [...],
[Eilutė 3], [...], [...], [...],
[Eilutė 4], [...], [...], [...],
),
caption: [Lentelės pavadinimas],
) <table-label>
== Formulės
Formules galima išreikšti eilutėje, pavyzdžiui $E = m c^2$ arba atskiroje eilutėje, pavyzdžiui:
$
E = m c^2
$ <formula-label>
// TODO: fix
Formulės atskiroje eilutėje numeruojamos besąlygiškai, galima gauti formulės numerį #ref-no(<formula-label>) arba visą formulės nuorodą #custom-ref(<formula-label>).
== Kodo fragmentai
Kodo fragmentus galima išreikšti eilutėje, pavyzdžiui `print("Hello, world!")` arba atskiroje eilutėje, pavyzdžiui:
#figure(```python
def main():
print("Hello, world!")
if __name__ == "__main__":
main()
```, caption: [
Python kodas
]) <code-label>
Galima gauti kodo fragmento numerį #ref-no(<code-label>) arba visą kodo fragmento nuorodą #custom-ref(<code-label>).
== Naudotojo apibrėžtos figūros
Naudotojai tai pat gali apibrėžti savo figūras, pavyzdžiui, komandų fragmentus. Pavyzdžiui, galima apibrėžti komandų fragmentą su papildoma informacija ir naudoti jį dokumente. Galima gauti kodo fragmento numerį #ref-no(<custom-figure-label>) arba visą kodo fragmento nuorodą #custom-ref(<custom-figure-label>).
#figure(
```sh
npm install foo
```,
kind: "veryCustomKind",
supplement: "...",
caption: [Diegimo komanda],
) <custom-figure-label>
#pagebreak()
= Išvados
#lorem(50)
+1 -1
View File
@@ -1,4 +1,4 @@
#import "@local/ktu-paper:0.3.2": (
#import "@local/ktu-paper:0.4.1": (
ktu-paper, setup-code,
ktu-table-of-contents, ktu-picture-list, ktu-table-list,
ktu-heading-page-normal,
+1 -1
View File
@@ -1,4 +1,4 @@
#import "@local/ktu-paper:0.3.2": (
#import "@local/ktu-paper:0.4.0": (
ktu-paper, setup-code,
ktu-table-of-contents, ktu-picture-list, ktu-table-list,
ktu-heading-page-normal,
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "ktu-paper"
version = "0.3.2"
version = "0.4.1"
compiler = "0.15.0"
entrypoint = "src/lib.typ"
repository = "https://github.com/svetikas/ktu-paper"