commit f1abfbfa5b9fdbf0d3389354f87a9c7c20d70845 Author: Jean-Marie Mineau Date: Tue Jun 10 13:35:53 2025 +0200 first draft diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a17a8a --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Template Thesis MATISSE + +Typst template for thesis from the MATISSE doctoral school. + + +## Usage + +Install locally (run `ln` from the git repo): + +``` +mkdir -p ~/.local/share/typst/packages/local/template-thesis-matisse/ +ln -s $(pwd) ~/.local/share/typst/packages/local/template-thesis-matisse/0.0.1 +``` + +To generate a new project, run `typst init @local/template-thesis-matisse ` + +## Reference + +- Inspired by [the thesis of Timothe Albouy](https://github.com/TimotheAlbouy/talb-thesis/tree/main), some code might be copied from it. diff --git a/assets/UR.png b/assets/UR.png new file mode 100644 index 0000000..fe016fe Binary files /dev/null and b/assets/UR.png differ diff --git a/assets/abstracts-bg.svg b/assets/abstracts-bg.svg new file mode 100644 index 0000000..b559060 --- /dev/null +++ b/assets/abstracts-bg.svg @@ -0,0 +1,527 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/cover-bg.svg b/assets/cover-bg.svg new file mode 100644 index 0000000..456343f --- /dev/null +++ b/assets/cover-bg.svg @@ -0,0 +1,1876 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..cd29a13 Binary files /dev/null and b/assets/logo.png differ diff --git a/lib/abstracts.typ b/lib/abstracts.typ new file mode 100644 index 0000000..684fef3 --- /dev/null +++ b/lib/abstracts.typ @@ -0,0 +1,85 @@ + +#let assets-folder = "../assets/" + + +// workaround for: https://github.com/typst/typst/issues/466 +#let balanced-cols(n-cols, gutter: 11pt, body) = layout(bounds => context { + // Measure the height of the container of the text if it was single + // column, full width + let text-height = measure(box( + width: (bounds.width - (n-cols - 1) * gutter) / n-cols, + body + )).height + + // Recompute the height of the new container. Add a few points to avoid the + // second column being longer than the first one + let balanced-height = text-height/n-cols + text.size/2 + + box( + height: balanced-height, + columns(n-cols, gutter: gutter, body) + ) +}) + +#let abstracts( + title-fr: "", + keywords-fr: (), + abstract-fr: [], + title-en: "", + keywords-en: (), + abstract-en: [], + heading-font: "TeX Gyre Heros", + school-color-verso: rgb("0054a0") +) = { + set page( + numbering: none, + header: none, + ) + + pagebreak() + pagebreak(to: "even") + + set page( + margin: ( + left: 20mm, right: 30mm, + top: 30mm, bottom: 30mm, + ), + background: image(assets-folder + "abstracts-bg.svg") + ) + set text(font: heading-font, fill: black) + + place(dx: 100mm, dy: -15mm, image(assets-folder + "UR.png", width: 6cm)) + place(dx: 0mm, dy: -15mm, image(assets-folder + "logo.png", width: 7.5cm)) + + v(2cm) + line(length: 100%, stroke: .2cm + school-color-verso) + v(.4cm) + + [ + #show linebreak: none + #text(school-color-verso)[Titre :] #title-fr + ] + + [ + + Mots clés : #keywords-fr.join(", ") + ] + + balanced-cols(2,gutter: 11pt)[Résumé : #abstract-fr] + + v(1cm) + line(length: 100%, stroke: .2cm + school-color-verso) + v(.4cm) + + [ + #show linebreak: none + #text(school-color-verso)[Title :] #title-en + ] + + [ + + Keywords: #keywords-en.join(", ") + ] + + balanced-cols(2, gutter: 11pt)[Abstract: #abstract-en] +} diff --git a/lib/cover.typ b/lib/cover.typ new file mode 100644 index 0000000..6f49024 --- /dev/null +++ b/lib/cover.typ @@ -0,0 +1,85 @@ + +#let assets-folder = "../assets/" + +#let cover( + title-en: "", + title-fr: "", + author: "", + affiliation: "", + defense-place: "", + defense-date: "", + jury-content: [], + university: [], + speciality: "Informatique", + heading-font: "TeX Gyre Heros", +) = { + set page( + margin: (left: 20mm, right: 20mm, top: 30mm, bottom: 30mm), + header: none, + numbering: none, + background: image(assets-folder + "cover-bg.svg") + ) + set text(font: heading-font, fill: black) + + place(dx: 110mm, dy: -15mm, image(assets-folder + "UR.png", width: 6cm)) + place(dx: 0mm, dy: -15mm, image(assets-folder + "logo.png", width: 7.5cm)) + + v(2.1cm) + text(size: 2em, smallcaps[Thèse de doctorat de]) + v(2.25cm) + + set text(fill: white) + text(size: 1.5em, smallcaps[l'Université de Rennes]) + v(.01cm) + text(size: 1.2em)[ + #smallcaps[École Doctorale N° 601] \ + _Mathématiques, Télécommunications, Informatique, \ + Signal, Systèmes, Électronique_ \ + Spécialité : _ #speciality _ \ + #v(.1cm) #h(.6cm) Par \ + ] + + // Add a blue background with the width of the page + context { + let y-start = locate().position().y - 1cm + let y-end = locate().position().y + measure(query().first()).height + .5cm + let height = 5em + + place( + top + left, float: false, + dy: y-start - page.margin.top, + dx: -page.margin.left, + block( + width: page.width, + height: y-end - y-start, + fill: blue + ) + ) + } + + // Author name + v(0em) + h(.6cm) + text(size: 1.9em)[*#author* \ ] + v(.1cm) + + // Title + defense info block + text(size: 1.6em)[*#title-en* ] + parbreak() + text(size: 1.4em, title-fr) + parbreak() + + text(size: 1.1em)[ + *Thèse présentée et soutenue à #defense-place, le #defense-date* \ + *Unité de recherche : #affiliation* + + ] + + v(1em) + + set text(fill: black) + jury-content + + pagebreak() + set page(background: none) +} diff --git a/lib/lib.typ b/lib/lib.typ new file mode 100644 index 0000000..b371c94 --- /dev/null +++ b/lib/lib.typ @@ -0,0 +1,2 @@ +#import "matisse-thesis.typ": * +#import "todos.typ": * diff --git a/lib/matisse-thesis.typ b/lib/matisse-thesis.typ new file mode 100644 index 0000000..0c5edf8 --- /dev/null +++ b/lib/matisse-thesis.typ @@ -0,0 +1,214 @@ +#import "@preview/hydra:0.6.1": hydra + +#import "cover.typ": cover +#import "abstracts.typ": abstracts + +#let matisse-thesis( + // English title, will be used for the document title metadata + title-en: "", + title-fr: "", + author: "", + affiliation: "", + defense-place: "", + defense-date: "", + jury-content: [], + university: [l'Université de Rennes], + speciality: "Informatique", + // List of keyword for the abstract + keywords-en: (), + // List of french keywords for the abstract + keywords-fr: (), + abstract-en: [], + abstract-fr: [], + // If the current document is a draft + draft: true, + bibliography-style: "association-for-computing-machinery", + // text font + font: "New Computer Modern", + // font for heading + heading-font: "TeX Gyre Heros", + // text lang + lang: "en", + school-color-verso: rgb("0054a0"), + body +) = { + + // ---------- GENERAL ---------- + + let draft-string = "" + if draft { draft-string = "DRAFT - " } + + set document( + title: draft-string + title-en, + author: author, + ) + set par(justify: true) + set text(font: font, fill: black, lang: lang) + + // ---------- PAGE FORMAT ---------- // + + set page("a4", + margin: (outside: 20mm, inside: 30mm, top: 50mm, bottom: 50mm), + numbering: "1", + number-align: center, + header: context { + // disable linebreaks in header + show linebreak: none + // get the current page number + let current-page = here().page() + + let all-lvl1 = query(heading.where(level: 1)) + if all-lvl1.any(it => it.location().page() == current-page) { + return + } + + // if the page is odd + if calc.odd(current-page) { + // display the last level-1 heading + let header-content = hydra(1, + display: (_, it) => { + if it.numbering != none { + let nb = counter(heading).at(it.location()) + let nb-fmt = numbering(it.numbering, ..nb) + [#it.supplement #nb-fmt -- _ #it.body _ ] + } else { emph(it.body) } + } + ) + text(0.35cm, header-content) + } + // if the page is even + else { + // display last level-2 heading (current page included) + let header-content = hydra(2, use-last: true, + display: (_, it) => { + if it.numbering == none [_ #it.body _ ] + else { + let nb = counter(heading).at(it.location()) + let nb-fmt = numbering( + it.numbering.replace(" ", "."), + ..nb + ) + [ _ #nb-fmt #it.body _ ] + } + } + ) + align(right, text(0.35cm, header-content)) + } + // horizontal rule + v(-.3cm) + line(length: 100%, stroke: .2mm) + } + ) + + // ---------- HEADINGS ---------- + + // for the thesis' body: + // 1. headings are normally numbered + // (there is a small space after the last number) + + set heading(numbering: "1.1 ") + // 2. level-1 headings are called chapters + show heading.where(level: 1): set heading(supplement: [Chapter]) + + // for level-1 headings + show heading.where(level: 1): it => context { + // always start on odd pages + pagebreak(to: "odd") + set align(right) + v(-.8cm) + // if numbering is enabled, display level-1 heading number + if it.numbering != none { + let sec-nb = counter(heading).get().first() + let fmt-nb = numbering(heading.numbering, sec-nb) + text( + smallcaps[#heading.supplement #fmt-nb \ ], + size: .45cm, weight: "regular", font: font, + ) + v(0cm) + } + // level-1 heading name + text(smallcaps(it.body), font: heading-font, size: .9cm) + set align(left) + // horizontal rule + v(.7cm) + line(length: 100%, stroke: .2mm) + v(.7cm) + } + + + // ---------- FIGURES ---------- + + show figure.caption: it => box( + inset: (left: 1em, right: 1em), + align(left, it) + ) + + // ---------- OUTLINES ---------- + + // justify outline entries + show outline.entry: set par(justify: true) + + show outline: out => { + show outline.entry.where(level: 1): ent => { + // for the table of contents + if out.target == selector(heading) { + block(above: 1.2em)[#ent] + } + // for other types of outlines + else { + link( + ent.element.location(), + [#ent.prefix(): #h(.5em) #ent.inner()] + ) + } + } + out + } + + // disable linebreaks in outlines + show outline.entry: it => { + show linebreak: none + it + } + + + // ---------- FOOTNOTES ---------- + + show footnote.entry: it => { + let loc = it.note.location() + numbering( + "1. ", + ..counter(footnote).at(loc), + ) + it.note.body + } + + // ---------- BIBLIOGRAPHY ---------- + + set bibliography(style: bibliography-style) + + // ---------- COVER PAGE ---------- + + cover( + title-en: title-en, + title-fr: title-fr, + author: author, + affiliation: affiliation, + defense-place: defense-place, + defense-date: defense-date, + jury-content: jury-content, + university: university, + speciality: speciality, + heading-font: heading-font, + ) + + // ---------- BODY ---------- + body + + // ---------- ABSTRACT ---------- + abstracts( + title-fr: title-fr, keywords-fr: keywords-fr, abstract-fr: abstract-fr, + title-en: title-en, keywords-en: keywords-en, abstract-en: abstract-en, + heading-font: heading-font, school-color-verso: school-color-verso, + ) +} diff --git a/lib/todos.typ b/lib/todos.typ new file mode 100644 index 0000000..0860878 --- /dev/null +++ b/lib/todos.typ @@ -0,0 +1,21 @@ +#let todo-list = state("todo-list",()) +#let show-todos = state("show-todos", true) + +#let todo(done: false, content) = context { + if (not done) and (show-todos.get()) { + let todonum = todo-list.get().len() + 1 + text(weight: "bold", fill: red, [TODO n°#todonum #label("todo-"+str(todonum)): #content]) + todo-list.update(x => x + (("todo-"+str(x.len()+1),x.len()+1, content),)) + } +} + +#let todos() = context { + if (todo-list.final().len() != 0) and (show-todos.get()) { + pagebreak(weak: true) + [= TO-DOs] + for t in todo-list.final() { + let l = label(t.first()) + list.item(link(l)[TODO n°#t.at(1) p.#locate(l).page() : #t.last()]) + } + } +} diff --git a/template/abstract.typ b/template/abstract.typ new file mode 100644 index 0000000..3bf11ed --- /dev/null +++ b/template/abstract.typ @@ -0,0 +1,8 @@ + +#let keywords-en = ("Lorem", "Ipsum", "Dolore", "Sit", "Amet") +#let keywords-fr = ("Lorem", "Ipsum", "Dolore", "Sit", "Amet") + + +#let abstract-en = lorem(200) + +#let abstract-fr = lorem(200) diff --git a/template/bibliography.bib b/template/bibliography.bib new file mode 100644 index 0000000..e69de29 diff --git a/template/jury.typ b/template/jury.typ new file mode 100644 index 0000000..0bf9656 --- /dev/null +++ b/template/jury.typ @@ -0,0 +1,18 @@ +#let jury-content = [ + #text(size: 1.3em)[Composition du jury :] + + #{ + set text(size: .92em) + table( + columns: 4, + column-gutter: 2em, + stroke: 0pt, + inset: (x: 0pt, y: .5em), + "Présidente :", "Alice", "Professeure des universités", "Université de Rennes", + "Rapporteurs :", "Bob", "", "", + "", "Eve", "", "", + "Examinatrice :", "Mallory", "", "", + "Dir. de thèse :", "Trent", "", "", + ) + } +] diff --git a/template/main.typ b/template/main.typ new file mode 100644 index 0000000..b54b1c7 --- /dev/null +++ b/template/main.typ @@ -0,0 +1,26 @@ +#import "@local/template-thesis-matisse:0.0.1": * + +#import "jury.typ": jury-content +#import "abstract.typ": keywords-en, keywords-fr, abstract-en, abstract-fr + +#show: matisse-thesis.with( + title-fr: "Lorem Ipsum Fr", + title-en: "Lorem Ipsum", + author: "Anne Onyme", + affiliation: "Inria", + defense-place: "Rennes", + defense-date: datetime.today().display(), + jury-content: jury-content, + university: [l'Université de Rennes], + keywords-en: keywords-en, + keywords-fr: keywords-fr, + abstract-en: abstract-en, + abstract-fr: abstract-fr, + draft: true, +) + +#todo[Write the Thesis] + +#bibliography("bibliography.bib") + +#todos() diff --git a/typst.toml b/typst.toml new file mode 100644 index 0000000..6b2ad7a --- /dev/null +++ b/typst.toml @@ -0,0 +1,12 @@ +[package] +name = "template-thesis-matisse" +version = "0.0.1" +entrypoint = "lib/lib.typ" +authors = ["Jean-Marie Mineau"] +license = "AGPL-3.0-or-later" +description = "A typst template for thesis from the MATISSE doctoral school." +#repository = "" + +[template] +path = "template" +entrypoint = "main.typ"