#import "@preview/codly:1.3.0": codly, codly-init #import "config.typ": debug-author-table, enable-heading-experiment, __HEADING_EXPERIMENT, __FIG_PER_SECTION #import "fragments.typ": ( ktu-table, ktu-heading-page-centered, ktu-heading-page-normal, ktu-academic-honestly-declaration-page ) #import "figures.typ": ( __fig-supplement, __get-figure-number, __get-locatable-element-number, __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 */ /// /// Insert a heading without a number. /// /// Usually used for introductory or summary sections at the start and end. /// - text (str): The heading's title. /// -> #let unnumbered-heading(text) = { set heading(numbering: none) [#text] } #let appendix-item(body) = { heading( metadata("appendix-heading") + "priedas. " + body, level: 2, numbering: (..any) => {str(any.at(1))} ) } // Utility function to round numbers with padded zeros #let round-and-pad(number, decimals) = { let rounded = calc.round(number, digits: decimals) 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 padded-decimal = if decimals > 0 { let current-decimal-length = decimal-part.len() let padding-needed = decimals - current-decimal-length let trailing-zeros = "" for _ in range(padding-needed) { trailing-zeros = trailing-zeros + "0" } if decimal-part == "" { "." + trailing-zeros } else { "." + decimal-part + trailing-zeros }.slice(0, decimals + 1) } else { "" } 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. /// - indent (auto | function | relative): Same as outline indent. /// - target (function | label | location | selector): Same as outline target. /// - 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 ) = { // 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)) { // 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 link( it.element.location(), it.indented(prefixContent, it.inner(), 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 } } 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) } /// Returns a KTU style conformant outline. /// - depth (int | none): Same as outline depth. /// - indent (auto | function | relative): Same as outline indent. /// - target (function | label | location | selector): Same as outline target. /// - 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 ) = { __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) } #let ktu-table-list() = { 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)) } /// Define a custom outline for a specific figure type. /// This function creates an outline entry with a specified title and figure type. /// The outline entry will link to the figure of the specified type. /// - 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)) } #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 } #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) // Numbering rules set page(numbering: none, number-align: right) // Use Lithuanian quotes set smartquote(quotes: "„“") // Heading numbering set heading(numbering: "1.") // Number equations set math.equation(numbering: "(1)") // 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) // Indentation for lists and enumerations set enum(indent: 12pt) set list(indent: 12pt) body } #let setup-page( font: "Times New Roman", body ) = { show: __page_rules.with(font: font) // Pagal formaliuosius rašto darbų reikalavimus // Tarpas tarp teksto pastraipų set par(spacing: 10pt) set block(below: 10pt) // Teksto lygiuotė, abipusė set par(justify: true) set linebreak(justify: true) // 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) 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ė set align(center) // 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) // 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() #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) } } } // 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") body } /// Configure the template for a KTU paper. /// - font (str): Font to use for the paper. /// - figureNumberingPerSection (bool): Whether to number figures per section rather than globally. #let ktu-paper( font: "Times New Roman", figureNumberingPerSection: false, body ) = context { show: setup-page.with(font: font) __FIG_PER_SECTION.update(figureNumberingPerSection) body }