first commit

This commit is contained in:
liuyihui 2024-01-05 00:55:01 +08:00
commit c7730985d2
8 changed files with 804 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pdf

92
in-dexter.typ Normal file
View File

@ -0,0 +1,92 @@
// Copyright 2023 Rolf Bremer, Jutta Klebe
// Use of this code is governed by the License in the LICENSE.txt file.
// For a 'how to use this package', see the accompanying .md, .pdf + .typ documents.
// Index Entry; used to mark an entry in the document to be included in the Index.
// An optional initial may be provided.
#let index(
content,
initial: none
) = locate(loc => [#metadata((initial: initial, content: content, location: loc.position()))<jkrb_index>])
// Create the index page.
#let make-index(title: none, outlined: false) = {
// This function combines the text(s) of a content.
let content-text(content) = {
let ct = ""
if content.has("text") {
ct = content.text
}
else {
for cc in content.children {
if cc.has("text") {
ct += cc.text
}
}
}
return ct
}
locate(loc => {
let elements = query(<jkrb_index>, loc)
let pages = (:)
let words = (:)
for el in elements {
let ct = content-text(el.value.content)
// Have we already know that entry text? If not,
// add it to our list of entry words
if pages.keys().contains(ct) != true {
pages.insert(ct, ())
}
// Add the new page entry to the list.
let ent = (page: el.value.location.page)
if not pages.at(ct).contains(ent){
pages.at(ct).push(ent)
}
}
for el in elements {
let ct = content-text(el.value.content)
if words.keys().contains(ct) != true {
words.insert(ct, ())
words.at(ct) = (content: el.value.content, initial: el.value.initial, pages: pages.at(ct))
}
}
// Sort the entries.
let initial-sorted(ct) = {
let char = words.at(ct).initial
if char == none {
char = content-text(words.at(ct).content)
}
return char
}
let sortedkeys = words.keys().sorted(key: initial-sorted)
// Output.
let register = ""
if title != none { heading(outlined: outlined, numbering: none, title) }
for sk in sortedkeys [
// Use class specific formatting for the page numbers.
#let formattedPageNumbers = words.at(sk).pages.map(en => {
link((page: en.page, x:0pt, y:0pt))[#strong[#en.page]]
})
#let firstCharacter = content-text(words.at(sk).content).first()
#if (words.at(sk).initial != none) {
firstCharacter = words.at(sk).initial
}
#if firstCharacter != register {
heading(level: 2, numbering: none, outlined: false, firstCharacter)
register = firstCharacter
}
#words.at(sk).content
#box(width: 1fr)
#formattedPageNumbers.join(", ")
]
})
}

398
lapreprint.typ Normal file
View File

@ -0,0 +1,398 @@
#import "@preview/i-figured:0.2.3"
#let count-words(it) = {
let fn = repr(it.func())
if fn == "sequence" { it.children.map(count-words).sum() }
else if fn == "heading" { count-words(it.body) }
else if fn == "figure" { count-words(it.caption.body) }
else if fn == "text" { it.text.split().len() }
else if fn in ("styled") { count-words(it.child) }
else if fn in ("highlight", "item", "strong", "link") { count-words(it.body) }
else if fn == "equation" { 1 }
else { 0 }
}
#let lapreprint(
// The paper's title.
title: "Paper Title",
subtitle: none,
// An array of authors. For each author you can specify a name, orcid, and affiliations.
// affiliations should be content, e.g. "1", which is shown in superscript and should match the affiliations list.
// Everything but but the name is optional.
authors: (),
// This is the affiliations list. Include an id and `name` in each affiliation. These are shown below the authors.
affiliations: (),
// The paper's abstract. Can be omitted if you don't have one.
abstract: none,
// The short-title is shown in the running header
short-title: none,
// The short-citation is shown in the running header, if set to auto it will show the author(s) and the year in APA format.
short-citation: auto,
// The venue is show in the footer
venue: none,
// An image path that is shown in the top right of the page. Can also be content.
logo: none,
// A DOI link, shown in the header on the first page. Should be just the DOI, e.g. `10.10123/123456` ,not a URL
doi: none,
heading-numbering: "1.a.i",
// Show an Open Access badge on the first page, and support open science, default is true, because that is what the default should be.
open-access: true,
// A list of keywords to display after the abstract
keywords: (),
// The "kind" of the content, e.g. "Original Research", this is shown as the title of the margin content on the first page.
kind: none,
// Content to put on the margin of the first page
// Should be a list of dicts with `title` and `content`
margin: (),
paper-size: "us-letter",
// A color for the theme of the document
theme: blue.darken(30%),
// Date published, for example, when you publish your preprint to an archive server.
// To hide the date, set this to `none`. You can also supply a list of dicts with `title` and `date`.
date: datetime.today(),
// Feel free to change this, the font applies to the whole document
font-face: "Noto Sans",
// The path to a bibliography file if you want to cite some external works.
bibliography-file: none,
bibliography-style: "apa",
// The outline
outline-show: true,
outline-depth: 2,
// Language of the document
lang: "en",
// The paper's content.
body
) = {
/* Logos */
let orcidSvg = ```<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24"> <path fill="#AECD54" d="M21.8,12c0,5.4-4.4,9.8-9.8,9.8S2.2,17.4,2.2,12S6.6,2.2,12,2.2S21.8,6.6,21.8,12z M8.2,5.8c-0.4,0-0.8,0.3-0.8,0.8s0.3,0.8,0.8,0.8S9,7,9,6.6S8.7,5.8,8.2,5.8z M10.5,15.4h1.2v-6c0,0-0.5,0,1.8,0s3.3,1.4,3.3,3s-1.5,3-3.3,3s-1.9,0-1.9,0H10.5v1.1H9V8.3H7.7v8.2h2.9c0,0-0.3,0,3,0s4.5-2.2,4.5-4.1s-1.2-4.1-4.3-4.1s-3.2,0-3.2,0L10.5,15.4z"/></svg>```.text
let spacer = text(fill: gray)[#h(8pt) | #h(8pt)]
let dates;
if (type(date) == "datetime") {
dates = ((title: "Published", date: date),)
}else if (type(date) == "dictionary") {
dates = (date,)
} else {
dates = date
}
date = dates.at(0).date
// Create a short-citation, e.g. Cockett et al., 2023
let year = if (date != none) { ", " + date.display("[year]") }
if (short-citation == auto and authors.len() == 1) {
short-citation = authors.at(0).name.split(" ").last() + year
} else if (short-citation == auto and authors.len() == 2) {
short-citation = authors.at(0).name.split(" ").last() + " & " + authors.at(1).name.split(" ").last() + year
} else if (short-citation == auto and authors.len() > 2) {
short-citation = authors.at(0).name.split(" ").last() + " " + emph("et al.") + year
}
// Set document metadata.
set document(title: title, author: authors.map(author => author.name))
if short-title == none { short-title = title }
set page(
paper-size,
margin: (left: 20%, bottom: 1.5cm),
header: locate(loc => {
if(loc.page() == 1) {
let headers = (
if (open-access) {smallcaps[Open Access]},
if (doi != none) { link("https://doi.org/" + doi, "https://doi.org/" + doi)}
)
return align(left, text(size: 8pt, fill: gray, headers.filter(header => header != none).join(spacer)))
} else {
return align(right, text(size: 8pt, fill: gray.darken(50%),
(short-title, short-citation).join(spacer)
))
}
}),
footer: block(
width: 100%,
stroke: (top: 1pt + gray),
inset: (top: 8pt, right: 2pt),
[
#grid(columns: (75%, 25%),
align(left, text(size: 9pt, fill: gray.darken(50%),
(
if(venue != none) {emph(venue)},
if(date != none) {
if (lang == "en") {
date.display("[month repr:short] [day], [year]")
} else {
[#date.year() 年 #date.month() 月 #date.day() 日]
}
}
).filter(t => t != none).join(spacer)
)),
align(right)[
#text(
size: 9pt, fill: gray.darken(50%)
)[
#counter(page).display() of #locate((loc) => {counter(page).final(loc).first()})
]
]
)
]
)
)
// Set the body font.
set text(font: font-face, size: 10pt)
// Configure lists.
set enum(indent: 10pt, body-indent: 9pt)
set list(indent: 10pt, body-indent: 9pt)
// Configure i-figured
show figure: i-figured.show-figure
show figure.where(
kind: table
): set figure.caption(position: top)
show math.equation: i-figured.show-equation
// Configure Reference Link
show link: it => [#text(fill: theme)[#it]]
show ref: it => [#text(fill: theme)[#it]]
show ref: it => {
let el = it.element
if (el != none) {
let fn = repr(el.func())
if (fn == "equation") {link(
it.target,
if (lang == "en") {"Eq."} else {"式 "} +
numbering(
el.numbering,
..counter(math.equation).at(el.location())
)
)} else if (fn == "heading") {link(
it.target,
if (lang == "en") {"Section "} else {"第 "} +
numbering(
el.numbering,
..counter(heading).at(el.location())
) + if (lang == "zh") {" 节"}
)}
else { it }
} else {it}
}
// Configure headings.
set heading(numbering: heading-numbering)
show heading: it => locate(loc => {
// Find out the final number of the heading counter.
let levels = counter(heading).at(loc)
set text(10pt, weight: 400)
if it.level == 1 [
// First-level headings are centered smallcaps.
// We don't want to number of the acknowledgment section.
#let is-ack = it.body in ([Acknowledgment], [Acknowledgement])
// #set align(center)
#set text(if is-ack { 10pt } else { 12pt })
#show: smallcaps
#v(20pt, weak: true)
#if it.numbering != none and not is-ack {
numbering(heading-numbering, ..levels)
h(7pt, weak: true)
}
#it.body
#v(13.75pt, weak: true)
#for kind in (image, table, raw) {
counter(figure.where(kind: "i-figured-" + repr(kind))).update(0)
}
#counter(math.equation).update(0)
] else if it.level == 2 [
// Second-level headings are run-ins.
#set par(first-line-indent: 0pt)
#v(10pt, weak: true)
#if it.numbering != none {
numbering(heading-numbering, ..levels)
h(7pt, weak: true)
}
#it.body
#v(10pt, weak: true)
] else if it.level == 3 [
#set par(first-line-indent: 0pt)
#v(10pt, weak: true)
#if it.numbering != none {
numbering(heading-numbering, ..levels)
}
#it.body
#v(10pt, weak: true)
] else [
// Fourth level headings are run-ins too, but different.
#set par(first-line-indent: 0pt)
#if it.level >= 4 {
numbering(heading-numbering, ..levels)
}
#it.body
]
})
if (logo != none) {
place(
top,
dx: -33%,
float: false,
box(
width: 27%,
{
if (type(logo) == "content") {
logo
} else {
image(logo, width: 100%)
}
},
),
)
}
// Title and subtitle
box(inset: (bottom: 2pt), text(17pt, weight: "bold", fill: theme, title))
if subtitle != none {
parbreak()
box(text(14pt, fill: gray.darken(30%), subtitle))
}
// Authors and affiliations
linebreak()
if authors.len() > 0 {
box(inset: (y: 10pt), {
authors.map(author => {
text(11pt, weight: "semibold", author.name)
h(1pt)
if "affiliations" in author {
super(author.affiliations)
}
if "orcid" in author {
link("https://orcid.org/" + author.orcid)[#box(height: 1.1em, baseline: 13.5%)[#image.decode(orcidSvg)]]
}
}).join(", ", last: ", and ")
})
}
if affiliations.len() > 0 {
linebreak()
box(inset: (bottom: 10pt), {
affiliations.map(affiliation => {
super(affiliation.id)
h(1pt)
affiliation.name
}).join(", ")
})
}
place(
left + bottom,
dx: -25%,
dy: -10pt,
box(width: 22%, {
if (kind != none) {
show par: set block(spacing: 0em)
text(11pt, fill: theme, weight: "semibold", smallcaps(kind))
parbreak()
}
if (dates != none) {
let formatted-dates
grid(columns: (40%, 60%), gutter: 7pt,
..dates.zip(range(dates.len())).map((formatted-dates) => {
let d = formatted-dates.at(0);
let i = formatted-dates.at(1);
return (
if (i == 0){
strong[#text(size: 7pt, fill: theme, d.title)]
} else {
text(size: 7pt, fill: theme, d.title)
},
if (lang == "en") {
text(size: 7pt, d.date.display("[month repr:short] [day], [year]"))
} else {[
#set text(7pt)
#d.date.year()-#d.date.month()-#d.date.day()
]}
)
}).flatten()
)
}
v(2em)
grid(columns: 1, gutter: 2em, ..margin.map(side => {
text(size: 7pt, {
if ("title" in side) {
text(fill: theme, weight: "bold", side.title)
[\ ]
}
set enum(indent: 0.1em, body-indent: 0.25em)
set list(indent: 0.1em, body-indent: 0.25em)
side.content
})
}))
}),
)
let abstracts
if (type(abstract) == "content") {
abstracts = (title: "Abstract", content: abstract)
} else {
abstracts = abstract
}
if abstracts != none {
box(inset: (top: 16pt, bottom: 16pt), stroke: (top: 1pt + gray, bottom: 1pt + gray), {
abstracts.map(abs => {
set par(justify: true)
text(fill: theme, weight: "semibold", size: 9pt, abs.title)
parbreak()
abs.content
}).join(parbreak())
})
if (keywords.len() > 0) {
text(size: 9pt, {
if (lang == "en") {
text(fill: theme, weight: "semibold", "Keywords")
} else {
text(fill: theme, weight: "semibold", "关键词")
}
h(8pt)
keywords.join(", ")
})
}
v(10pt)
}
if (outline-show) {outline(
title: if (lang == "en") {"Content"} else {"目录"},
indent: 2em,
depth: outline-depth,
)}
pagebreak()
// Display the paper's contents.
show par: set block(spacing: 1.5em)
set page(
margin: (left: auto, bottom: 1.5cm),
)
// show: rest => {
// let n = count-words(rest)
// if (lang == "en") {
// rest + align(right, [(#n words)])
// } else {
// rest + align(right, [(#n 字)])
// }
// }
body
if (bibliography-file != none) {
show bibliography: set text(8pt)
bibliography(bibliography-file, title: text(10pt, "References"), style: bibliography-style)
}
}

5
lib.typ Normal file
View File

@ -0,0 +1,5 @@
#import "utils.typ": *
#import "table.typ": *
#import "in-dexter.typ": index, make-index
#import "lapreprint.typ": lapreprint
#import "slides.typ": slides-theme, slide, matrix-slide, title-slide, slide-outline, slide-outline-length

200
slides.typ Normal file
View File

@ -0,0 +1,200 @@
#import "@preview/polylux:0.3.1": *
#import themes.university as uni
#let count-length(it) = {
let fn = repr(it.func())
if fn == "sequence" { it.children.map(count-length).sum() }
else if fn == "heading" { count-length(it.body) }
else if fn == "figure" { count-length(it.caption.body) }
else if fn == "text" { it.text.len() }
else if fn in ("styled") { count-length(it.child) }
else if fn in ("highlight", "item", "strong", "link") { count-length(it.body) }
else if fn == "equation" { count-length(it.body) }
else { 0 }
}
#let progress-barline = locate( loc => {
if uni.uni-progress-bar.at(loc) {
let cell = block.with( width: 100%, height: 100%, above: 0pt, below: 0pt, breakable: false )
let colors = uni.uni-colors.at(loc)
utils.polylux-progress( ratio => {
grid(
rows: 2pt, columns: (ratio * 100%, 1fr),
cell(fill: colors.a),
cell(fill: colors.b)
)
})
} else { [] }
})
#let header-text(
title: none,
new-section: none
) = {
if title != none {
if new-section != none {
utils.register-section(new-section)
}
locate( loc => {
let colors = uni.uni-colors.at(loc)
block(fill: colors.c, inset: (x: .5em), grid(
columns: (1fr, auto),
align(top + left, heading(level: 2, text(fill: colors.a, title))),
align(top + right, text(fill: colors.a.lighten(65%), utils.current-section))
))
})
} else { [] }
}
#let slides-theme(
aspect-ratio: "16-9",
short-title: none,
short-author: none,
short-date: none,
color-a: rgb("#0C6291"),
color-b: rgb("#A63446"),
color-c: rgb("#FBFEF9"),
progress-bar: true,
body
) = {
uni.university-theme(
aspect-ratio: aspect-ratio,
short-title: short-title,
short-author: short-author,
short-date: short-date,
color-a: color-a,
color-b: color-b,
color-c: color-c,
progress-bar: progress-bar,
body
)
}
#let title-slide(
title: [],
subtitle: none,
authors: (),
institution-name: "University",
date: none,
) = {
let authors = if type(authors) == "array" { authors }else { (authors,) }
let content = locate( loc => {
let colors = uni.uni-colors.at(loc)
align(center + horizon, {
block(
inset: 0em,
breakable: false,
{
text(size: 1.8em, fill: colors.a, strong(title))
if subtitle != none {
parbreak()
text(size: 1.2em, fill: colors.a, subtitle)
}
}
)
set text(size: .8em)
grid(
columns: (1fr,) * calc.min(authors.len(), 3),
column-gutter: 1em,
row-gutter: 1em,
..authors.map(author => text(fill: black, author))
)
v(1em)
if institution-name != none {
parbreak()
text(size: .9em, institution-name)
}
if date != none {
parbreak()
text(size: .8em, date)
}
})
})
logic.polylux-slide(content)
}
#let slide(
title: none,
new-section: none,
body
) = {
uni.slide(title: title, header: header-text(title: title, new-section: new-section), footer: none, new-section: new-section, body)
}
#let matrix-slide(
title: none,
new-section: none,
columns: none,
rows: none,
..bodies
) = {
let header = {
set align(top)
grid(rows: (auto, auto), row-gutter: 3mm, progress-barline, header-text(title: title, new-section: new-section))
}
let footer = {
set text(size: 10pt)
set align(center + bottom)
let cell(fill: none, it) = rect(
width: 100%, height: 100%, inset: 1mm, outset: 0mm, fill: fill, stroke: none,
align(horizon, text(fill: white, it))
)
locate( loc => {
let colors = uni.uni-colors.at(loc)
show: block.with(width: 100%, height: auto, fill: colors.b)
grid(
columns: (25%, 1fr, 15%, 10%),
rows: (1.5em, auto),
cell(fill: colors.a, uni.uni-short-author.display()),
cell(uni.uni-short-title.display()),
cell(uni.uni-short-date.display()),
cell(logic.logical-slide.display() + [~/~] + utils.last-slide-number)
)
})
}
set page(
margin: ( top: 2em, bottom: 1em, x: 0em ),
header: header,
footer: footer,
footer-descent: 0em,
header-ascent: .6em,
)
uni.matrix-slide(..bodies)
}
#let slide-outline-length(loc) = {
let section-state = state("polylux-sections", ())
let sections = section-state.final(loc)
sections.len() - 1
}
#let slide-outline(num) = {
set text(font: "Cascadia Mono")
locate(loc => {
let section-state = state("polylux-sections", ())
let sections = section-state.final(loc)
for p in range(0, num) {
let section = sections.at(p)
let content = {
let url = link(section.loc, section.body)
let page = logic.logical-slide.at(section.loc).at(0)
let length = 46 - str(page).len() - count-length(section.body)
let dots = " " + "." * length
[#grid(
columns: (auto, 1fr, 1fr),
url, align[#dots], align(right)[#page]
)]
}
[+ #content ]
}
})
}

19
table.typ Normal file
View File

@ -0,0 +1,19 @@
#import "@preview/tablex:0.0.6": tablex, rowspanx, colspanx, hlinex, vlinex
#let three-line-table(columns, headers, contents, caption, lang: "en",rows: auto) = {figure(
tablex(
columns: columns,
rows: rows,
align: center + horizon,
auto-lines: false,
repeat-header: true,
hlinex(stroke: 1.5pt),
..headers,
hlinex(stroke: 0.75pt),
..contents,
hlinex(stroke: 1.5pt),
),
kind: table,
supplement: [#if (lang == "en") {"Table"} else {"表"}],
caption: caption,
)}

4
typst.toml Normal file
View File

@ -0,0 +1,4 @@
[package]
name = "custom"
version = "0.1.0"
entrypoint = "lib.typ"

85
utils.typ Normal file
View File

@ -0,0 +1,85 @@
#import "@preview/showybox:2.0.1": showybox
#import "@preview/physica:0.9.0" as ph
#import "@preview/pinit:0.1.2": *
#let sp = [#h(0.5em)]
#let pm = [#sym.plus.minus]
#let le = [#sym.lt.eq.slant]
#let ge = [#sym.gt.eq.slant]
#let sim = [#sym.tilde.op]
#let inf = [#sym.infinity]
#let rm(body) = {math.upright(body)}
#let iso(b, A) = [#ph.isotope(b, a: A)]
#let isoz(b, A, Z) = [#ph.isotope(b, a: A, z: Z)]
#let isobox(body, width: 25pt, fill: white) = {block(
fill: fill,
stroke: black,
width: width,
height: width,
align(center + horizon)[#body]
)}
#let box-quote(body, width: 100%, inset: 5pt, outset: 0pt) = {block(
fill: green.lighten(80%),
width: width,
inset: inset,
outset: outset,
radius: 4pt,
stroke: green.darken(60%),
breakable: true,
body
)}
#let box-confusion(body, width: 100%, inset: 8pt, outset: 0pt) = {block(
fill: red.lighten(80%),
width: width,
inset: inset,
outset: outset,
radius: 4pt,
stroke: red.darken(60%),
breakable: false,
body
)}
#let box-title(content, title: none) = {showybox(
title: title,
frame: (
title-color: red.darken(30%),
border-color: red.darken(30%),
body-color: red.lighten(90%),
radius: 0pt,
thickness: 2pt,
),
above: 0.5em,
below: 0.5em,
breakable: false,
content
)}
#let mark-quote(body) = {
rect(fill: luma(240), stroke: (left: 0.25em), inset: (left: 0.5em), body)
}
#let pinit-highlight-equation-from(height: 2em, extended-height: 1.4em, pos: bottom, fill: rgb(0, 180, 255), highlight-pins, point-pin, body) = {
pinit-highlight(..highlight-pins, dy: -extended-height / 2, extended-height: extended-height, fill: rgb(..fill.components().slice(0, -1), 40))
pinit-point-from(
fill: fill,
pin-dx: -0.6em,
pin-dy: if pos == bottom { 0.8em } else { -0.6em },
body-dx: 0pt,
body-dy: if pos == bottom { -0.05em } else { -1.4em },
offset-dx: -0.6em,
offset-dy: if pos == bottom { 0.8em + height } else { -0.6em - height },
point-pin,
rect(
inset: if pos == bottom { 0.4em } else { 0.4em },
stroke: (
bottom: if pos == bottom { 0em } else { 0.12em } + fill,
top: if pos == bottom { 0.12em } else { 0em } + fill
), {
set text(fill: fill)
body
}
)
)
}