first draft

This commit is contained in:
Jean-Marie Mineau 2025-06-10 13:35:53 +02:00
commit f1abfbfa5b
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
15 changed files with 2893 additions and 0 deletions

85
lib/abstracts.typ Normal file
View file

@ -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]
}

85
lib/cover.typ Normal file
View file

@ -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 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(<cover:title-en>).position().y - 1cm
let y-end = locate(<cover:defense-info>).position().y + measure(query(<cover:defense-info>).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* <cover: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*
<cover:defense-info>
]
v(1em)
set text(fill: black)
jury-content
pagebreak()
set page(background: none)
}

2
lib/lib.typ Normal file
View file

@ -0,0 +1,2 @@
#import "matisse-thesis.typ": *
#import "todos.typ": *

214
lib/matisse-thesis.typ Normal file
View file

@ -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,
)
}

21
lib/todos.typ Normal file
View file

@ -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 #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 #t.at(1) p.#locate(l).page() : #t.last()])
}
}
}