#import "@preview/alexandria:0.2.2": alexandria, get-bibliography, load-bibliography // Global state #let used-refs = state("used-refs", ()) // 1. Analizes and sets up the document #let bib-setup(body) = { show: alexandria(prefix: "cite:", read: path => read(path)) // Searching for references in document show ref: it => { let target = str(it.target) if target.starts-with("cite:") { let key = target.slice(5) used-refs.update(arr => if key not in arr { arr + (key,) } else { arr }) context { let cites = used-refs.final() let bib = get-bibliography("cite:") let get-type(k) = { let e = bib.references.find(x => x.key == k) if e != none { lower(e.details.at("type", default: "")) } else { "" } } let is-book(t) = t == "book" let is-article(t) = t == "article" or t == "paper-conference" or t == "inproceedings" let is-other(t) = not is-book(t) and not is-article(t) let books = cites.filter(k => is-book(get-type(k))) let articles = cites.filter(k => is-article(get-type(k))) let others = cites.filter(k => is-other(get-type(k))) let sorted = books + articles + others let idx = sorted.position(k => k == key) if idx != none { link(it.target, [\[#(idx + 1)\]]) } else { [\[?\]] } } } else { it } } body } // 2. Bibliography rendering #let render-bib() = { load-bibliography("Bibliography.bib", prefix: "cite:", full: true, style: "ieee") heading(level: 1, outlined: true, numbering: none)[Spis literatury] v(1em) context { let cites = used-refs.final() if cites.len() > 0 { let bib = get-bibliography("cite:") let get-entry(k) = bib.references.find(e => e.key == k) // custom implementation adapting the ZUT's formatting let format-custom(entry) = { let d = entry.details let entry-type = lower(d.at("type", default: "")) let format-author(a) = { if type(a) == dictionary { let parts = () if "given" in a { parts.push(str(a.given)) } if "family" in a { parts.push(str(a.family)) } if parts.len() > 0 { return parts.join(" ") } if "name" in a { return str(a.name) } return "" } else { let s = str(a) let parts = s.split(",") if parts.len() == 2 { return parts.at(1).trim() + " " + parts.at(0).trim() } return s } } let raw-auth = d.at("author", default: ()) let author-list = if type(raw-auth) == array { raw-auth.map(format-author) } else if raw-auth != none and raw-auth != "" { (format-author(raw-auth),) } else { () } let authors = if author-list.len() > 1 { author-list.slice(0, -1).join(", ") + " i " + author-list.last() } else if author-list.len() == 1 { author-list.first() } else { "" } let get-val(..keys) = { let extract(obj, k) = { if type(obj) != dictionary { return none } let v = obj.at(k, default: none) if v == none { return none } if type(v) == str { return v } if type(v) in (int, float) { return str(v) } if type(v) == array { return v.filter(x => type(x) in (str, int, float)).map(x => str(x)).join(", ") } if type(v) == dictionary { if "isbn" in v { return str(v.isbn) } if "value" in v { return str(v.value) } if "name" in v { return str(v.name) } if "text" in v { return str(v.text) } return "" } return "" } for k in keys.pos() { let res = extract(d, k) if res == none and "parent" in d { res = extract(d.parent, k) } if res != none and res != "" { return res } } return "" } let y = "" let m = "" let date-val = d.at("date", default: none) if type(date-val) == dictionary { y = str(date-val.at("year", default: "")) m = str(date-val.at("month", default: "")) } else if date-val != none { y = str(date-val) } if y == "" { y = get-val("year") } if m == "" { m = get-val("month") } if m != "" { if m.match(regex("^\d+$")) != none { let idx = int(m) let months = ( "", "sty.", "lut.", "mar.", "kwi.", "maj", "cze.", "lip.", "sie.", "wrz.", "paź.", "lis.", "gru.", ) if idx >= 1 and idx <= 12 { m = months.at(idx) } } } let year = if m != "" { m + y } else { y } let title-txt = get-val("title") let series = get-val("collection-title", "series") let pub-place = get-val("location", "address", "publisher-place") let pub = get-val("publisher") let edition = get-val("edition") let volume = get-val("volume") let pages = get-val("page-range", "pages").replace("--", "–").replace("-", "–") let isbn = get-val("isbn", "ISBN", "serial-number", "number") let issue = get-val("issue", "number") let url = get-val("url") if entry-type == "book" [ #authors. #emph(title-txt). #if edition != "" { if edition.match(regex("^\d+$")) != none [#edition wyd. ] else [#edition. ] } #if volume != "" [T. #volume. ] #if series != "" [#series. ] #if pub-place != "" [#pub-place: ] #if pub != "" [#pub, ] #year#if pages != "" [, s. #pages]. #if isbn != "" [ isbn: #isbn.] ] else if entry-type == "article" or entry-type == "paper-conference" [ #authors. „#title-txt”. W:#if volume != "" or issue != "" [ #volume#if issue != "" [.#issue]] (#year)#if pages != "" [, s. #pages]. ] else [ #authors. #emph(title-txt). #if url != "" [ URL: #link(url)[#raw(url)].] ] } let get-type(k) = { let e = get-entry(k) if e != none { lower(e.details.at("type", default: "")) } else { "" } } let is-book(t) = t == "book" let is-article(t) = t == "article" or t == "paper-conference" or t == "inproceedings" let is-other(t) = not is-book(t) and not is-article(t) let render-group(title, keys, start-idx) = { if keys.len() > 0 [ #heading(level: 2, title, outlined: true, numbering: none) #grid( columns: (auto, 1fr), column-gutter: 0.65em, row-gutter: 1em, ..keys .enumerate() .map(((i, k)) => { let entry = get-entry(k) if entry == none { return () } let num = start-idx + i + 1 ([\[#num\]], [#format-custom(entry)#label("cite:" + k)]) }) .flatten() ) #v(1em) ] } let books = cites.filter(k => is-book(get-type(k))) let articles = cites.filter(k => is-article(get-type(k))) let others = cites.filter(k => is-other(get-type(k))) // last parameter is start idx render-group("Książki", books, 0) render-group("Artykuły", articles, books.len()) render-group("Źródła internetowe i inne", others, books.len() + articles.len()) } } }