start taking in account feedbacks
All checks were successful
/ test_checkout (push) Successful in 1m50s

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2025-10-21 01:52:00 +02:00
parent 0d50644ede
commit de6359db3f
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
3 changed files with 354 additions and 192 deletions

View file

@ -27,8 +27,7 @@
aspect-ratio : "16-9", aspect-ratio : "16-9",
title : [From Large Scale Analysis to Dynamic Deobfuscation], title : [From Large Scale Analysis to Dynamic Deobfuscation],
subtitle : [The Woes of Android Reverse Engineering], subtitle : [The Woes of Android Reverse Engineering],
event : [], //[PhD Defense], footer-text : [Jean-Marie Mineau PhD Defense],
short-title : [], //[PhD Defense],
//short-event : [Rennes, 2025/12/9], //short-event : [Rennes, 2025/12/9],
title-size : 32pt, title-size : 32pt,
section-size : 18pt, section-size : 18pt,
@ -113,36 +112,6 @@
) )
] ]
#slide(
title: [Analysing Applications: Which Tools?],
foreground: eye-3(x: 3%, y: 5%)
)[
#set align(center+horizon)
#move(dx: -50pt, image("slides/imgs/apk-analysis.svg", width: 300pt))
]
// something is broken, so hack to keep the page number at the same
#counter("logical-slide").update( n => n - 1 )
#slide(
title: [Analysing Applications: Which Tools?],
)[
#set list(spacing: 3em)
#item-by-item[
- #cite(<Li2017>, form: "prose"): systematic literature review for Android static analysis, lists open-sourced tools
- #cite(<reaves_droid_2016>, form: "prose"): tests analysis tools, raises concerns about reusability and analysis of
real-world applications
]
]
// something is broken, so hack to keep the page number at the same
#counter("logical-slide").update( n => n - 1 )
#slide(
title: [Analysing Applications: Which Tools?],
)[
#highlight-block(pb1-text)
]
#slide( #slide(
title: [Obfuscation], title: [Obfuscation],
//foreground: eye-1(x: 95%, y: 85%, mirror: true) //foreground: eye-1(x: 95%, y: 85%, mirror: true)
@ -225,21 +194,100 @@
#slide( #slide(
title: [Obfuscation], title: [Obfuscation],
subtitle: [Deobfuscated], subtitle: [Deobfuscated],
foreground: {
place-fg(x: 44%, y: 55%, $ lr(}, size: #130pt) $ )
place-fg(x: 44%, y: 26%, $ lr(}, size: #110pt) $ )
arrow((385pt, 260pt), (450pt, 230pt))
arrow((385pt, 125pt), (450pt, 110pt))
},
)[ )[
#show: yes-codly #show: yes-codly
#codly( #grid(
skips: ((3, 10), (5, 10), (6, 10)), columns: (1fr, 4em, 1fr),
..default-codly [
) #scale(60%, reflow: true)[
#scale(100%)[ ```java
```java String DEX = "ZGV4CjA [...] EAAABEAwAA";
public class Foo { String className = "W5f3 [...] 3sls=";
public static String bar(String arg) { String methodName = "n6WGYJzjDrUvR9cYljlNlw==";
}
} ClassLoader cl = new InMemoryDexClassLoader(
ByteBuffer.wrap(Base64.decode(DEX, 2)),
Main.class.getClassLoader()
);
Class<?> loadedClass = this.cl.loadClass(decrypt(className));
Object obj = "FooBar";
Object ret = loadedClass.getMethod(
decrypt(methodName),
String.class
).invoke(null, obj);
```]
],
[],
[
#codly(
skips: ((3, 10),),
..default-codly
)
#scale(100%)[
```java
public class Foo {
public static String bar(String arg) {
}
}
```
```java
String ret = Foo.bar("FooBar"); String ret = Foo.bar("FooBar");
```]
]
)
]
```] #slide(
foreground: ghost-5(x: 10%, y: 7%)
)[
#set align(center+horizon)
#grid(
columns: (1fr, 1fr),
gutter: 2em,
[
== Dynamic Analysis
#item-by-item[
- Run the application
- _See_ dynamically loaded bytecode
- _See_ reflection calls
- Limited by code coverage
]
],
[
== Static Analysis
#item-by-item(start: 5)[
- Do *not* run the application
- *Not* limited by code coverage
- But only for the *code available*
//- Some values cannot be computed
]
],
//grid.cell(colspan: 2, uncover(8)[
// #text(size: 30pt)[Can we combine both?]
//]),
)
]
#slide(
title: [Analysing Applications: Which Tools?],
foreground: eye-3(x: 3%, y: 5%)
)[
#set align(center+horizon)
#move(dx: -50pt, image("slides/imgs/apk-analysis.svg", width: 300pt))
]
#slide(
title: [Problem Statement 1],
)[
#highlight-block(pb1-text)
] ]
#slide( #slide(
@ -299,13 +347,13 @@
#counter("logical-slide").update( n => n - 1 ) #counter("logical-slide").update( n => n - 1 )
#slide( #slide(
title: [Class Loading], title: [Problem Statement 3],
)[ )[
#highlight-block(pb2-text) #highlight-block(pb2-text)
] ]
#slide( #slide(
foreground: ghost-5(x: 10%, y: 7%) title: [Deobuscation],
)[ )[
#set align(center+horizon) #set align(center+horizon)
#grid( #grid(
@ -313,47 +361,55 @@
gutter: 2em, gutter: 2em,
[ [
== Dynamic Analysis == Dynamic Analysis
#item-by-item[
- Run the application Easier to solve Dynamic Code Loading and Reflection Calls
- _See_ dynamically loaded bytecode
- _See_ reflection calls
- Limited by code coverage
]
], ],
[ [
== Static Analysis == Static Analysis
#item-by-item(start: 5)[
- Do *not* run the application
- *Not* limited by code coverage
- But only for the *code available*
//- Some values cannot be computed
]
Better code coverage
], ],
grid.cell(colspan: 2, uncover(8)[ grid.cell(colspan: 2, uncover(2)[
#text(size: 30pt)[Can we combine both?] #text(size: 30pt)[Can we combine both?]
]), ]),
) )
] ]
#slide[ #counter("logical-slide").update( n => n - 1 )
#slide(
title: [Problem Statement 3],
)[
#highlight-block(pb3-text) #highlight-block(pb3-text)
] ]
#slide[ #slide(
#highlight-block(pb1-text) foreground: rotate(30deg, smallcaps(text(fill: pirat-color.red, size: 50pt)[Faire un dessin]))
)[
// TODO Outline / problematics / drawing
#highlight-block(pb2-text)
#highlight-block(pb3-text)
] ]
#new-section-slide([Tool Reusability]) #new-section-slide([Tool Reusability])
#slide(
title: [State of the Art],
)[
#set list(spacing: 0.5em)
Li #etal:
#v(0pt)
#item-by-item[
- Systematic literature review for Android static analysis
- Lists open-sourced tools
- Does not test the tools
]
#uncover("4-")[Reaves #etal:]
#v(0pt)
#item-by-item(start: 4)[
- Tests 7 Android analysis tools
- Tests analysing 16 real-world applications
- Raises concerns about reusability and analysis of real-world applications
]
]
#slide( #slide(
title: [Methodology] title: [Methodology]
)[ )[
@ -398,8 +454,6 @@
#set align(center+horizon) #set align(center+horizon)
#show figure.caption: none #show figure.caption: none
#scale(100%, get_figure(<fig:rasta-exit>)) #scale(100%, get_figure(<fig:rasta-exit>))
//#text(size: 25pt)[We check if the results *exist* after running a tool]
] ]
#counter("logical-slide").update( n => n - 1 ) #counter("logical-slide").update( n => n - 1 )
@ -445,6 +499,7 @@
#scale(100%, get_figure(<fig:rasta-exit>)) #scale(100%, get_figure(<fig:rasta-exit>))
] ]
/*
#counter("logical-slide").update( n => n - 1 ) #counter("logical-slide").update( n => n - 1 )
#slide( #slide(
title: [Results], title: [Results],
@ -505,34 +560,22 @@
#text(size: 22pt)[Finishing rate as a function of the bytecode size, for APKs discovered in 2022] #text(size: 22pt)[Finishing rate as a function of the bytecode size, for APKs discovered in 2022]
] ]
*/
#slide( #slide(
title: [Conclusion], title: [Conclusion],
foreground: {
place(
bottom + left,
text(fill: pirat-color.blue)[International Conference on Software and Systems Reuse (ICSR 2024)]
)
}
)[ )[
#set align(center) #v(1fr)
//#set align(center)
#item-by-item[ #item-by-item[
- Over 22 tools, 10 are usable (*less than half*) - Over 22 tools, 10 are usable
- Newer applications are harder to analyse - Newer applications are harder to analyse
- Applications with more bytecode are harder to analyse - Applications with more bytecode are harder to analyse
- Applications targetting more recent versions of Android are harder to analyse - Applications targetting more recent versions of Android are harder to analyse
] ]
] #v(1fr)
#align(center, text(fill: pirat-color.blue.darken(30%))[International Conference on Software and Systems Reuse (ICSR 2024)])
#slide[ #v(1em)
#set align(center)
// Sous titre dans la conclusion
#text(size: 22pt)[21st International Conference on Software and Systems Reuse (ICSR 2024)]
#v(2em)
#show regex("\[\d+\]"): none
#cite(<rasta>, form: "full")
] ]
#new-section-slide([Class Shadowing]) #new-section-slide([Class Shadowing])
@ -579,35 +622,107 @@
] ]
#slide( #slide(
title: [Android Ecosystem] title: [State of the Art],
//foreground: rotate(30deg, text(fill: pirat-color.red, size: 50pt)[State of the Art])
)[ )[
#set align(center+horizon) #set list(spacing: 3em)
#show figure.caption: none #item-by-item[
#grid( - Previous contributions focus on Java runtime
columns: (3fr, 1fr), - Android related contributions focus on Dynamic Code Loading
scale(reflow: true, get_figure(<fig:cl-archisdk>)), ]
[
#set align(left)
#set text(size: 20pt)
#set list(marker: [-])
=== Types of classes:
- APK Classes
- Platform Classes
- SDK Classes
- Hidden APIs
]
)
// TODO: hightlight
] ]
#for i in range(5) {
let strong-at(i, str-idxs, body) = {
if i in str-idxs {
strong(body)
} else {
body
}
}
slide(
title: [Android Ecosystem],
foreground: {
let c = white.transparentize(10%)
let place-rect(x, y, w, h) = place-fg(
x: x, y: y,
rect(
width: w,
height: h,
stroke: c,
fill: c,
)
)
// phone
if i in () {
place-rect(15%, 15%, 16.5%, 55%)
}
// Platform Classes
if i in (1,) {
place-rect(18%, 38%+4pt, 12%, 15%)
place-rect(21%, 54%, 8%, 5%)
}
// API access
if i in (1,4) {
place-rect(26%-4pt, 31%+2.3pt, 5%, 7%+2pt)
}
// APK file
if i in (2,3, 4) {
place-rect(19%+3pt, 22%, 9%, 9%+1pt)
place-rect(21%+5pt, 32%, 4%, 3%)
}
// doc
if i in (1,2,4) {
place-rect(31.5%, 45%, 30%, 25%)
}
// dev
if i in (2,4) {
place-rect(39.5%, 15%, 22%, 30%)
}
// Dev SDK
if i in (1,) {
place-rect(49.6%, 18%, 10%, 15%)
}
// Dev classes
if i in (3,) {
place-rect(41.5%-1pt, 18%, 8%+2pt, 10%)
}
// compil
if i in (2,3, 4) {
place-rect(31.5%, 22%, 8%, 5%)
}
}
)[
#set align(center+horizon)
#show figure.caption: none
#grid(
columns: (3fr, 1fr),
scale(reflow: true, get_figure(<fig:cl-archisdk>)),
[
#set align(left)
#set text(size: 20pt)
#set list(marker: [-])
=== Types of classes:
- #strong-at(i, (1,))[APK Classes]
- #strong-at(i, (2,))[Platform Classes]
- #strong-at(i, (3,))[SDK Classes]
- #strong-at(i, (4,))[Hidden APIs]
]
)
]
}
#slide( #slide(
title: [Android ClassLoaders] title: [Android ClassLoaders], // TODO CHANGE DRAWING
foreground: rotate(30deg, text(fill: pirat-color.red, size: 50pt)[DESSIN RUNTIME CL \ \ WITH DELEGATION])
)[ )[
/*
#set align(center+horizon) #set align(center+horizon)
#show figure.caption: none #show figure.caption: none
#scale(60%, reflow: true, get_figure(<fig:cl-class_loading_classes>)) #scale(60%, reflow: true, get_figure(<fig:cl-class_loading_classes>))
*/
] ]
#slide( #slide(
@ -704,6 +819,9 @@
=== `app.apk` === `app.apk`
#line(length: 30%) #line(length: 30%)
#show "classes.dex": set text(fill: pirat-color.red.darken(10%))
#show "classes2.dex": set text(fill: pirat-color.red.darken(10%))
#show "classes3.dex": set text(fill: pirat-color.red.darken(10%))
``` ```
AndroidManifest.xml AndroidManifest.xml
resources.arsc resources.arsc
@ -755,9 +873,7 @@
else: else:
return f"classes{index+1}.dex" return f"classes{index+1}.dex"
```) ```)
// Donner example
- `classes0.dex` ? - `classes0.dex` ?
- `classes1.dex` ?
- `classes02.dex` ? - `classes02.dex` ?
- `classes10.dex` ? - `classes10.dex` ?
] ]
@ -808,7 +924,7 @@
columns: (1fr, 1fr), columns: (1fr, 1fr),
column-gutter: 2em, column-gutter: 2em,
yes-codly[ yes-codly[
#scale(60%, reflow: true, #scale(55%, reflow: true,
```python ```python
def get_dex_name(index: int): def get_dex_name(index: int):
if index == 0: if index == 0:
@ -829,7 +945,9 @@
return load_from_file(dex_file, class_name) return load_from_file(dex_file, class_name)
else: else:
raise ClassNotFoundError() raise ClassNotFoundError()
```) // TODO: nomer And Cl Alg ```)
#v(-1em)
#smallcaps(text(size: 15pt)[Android Class Loading Algorithm])
], [ ], [
#set align(left) #set align(left)
#set text(size: 18pt) #set text(size: 18pt)
@ -852,6 +970,8 @@
#slide( #slide(
title: [Impact on Tools], title: [Impact on Tools],
// TODO: IF PR: Add REF
foreground: move(dx: 300pt, rotate(30deg, text(fill: pirat-color.red, size: 30pt)[We want PR!])),
)[ )[
#set align(center+horizon) #set align(center+horizon)
#show figure.caption: none #show figure.caption: none
@ -861,11 +981,10 @@
show table: set align(center+horizon) show table: set align(center+horizon)
it it
} }
#show "not working": "attack not successfull" #show "not working": "attack failed"
#show "working": "attack successfull" #show "working": "attack sucessful"
#show "works": "successfull" #show "works": "sucessful"
#scale(100%, reflow: true, get_figure(<tab:cl-results>)) #scale(100%, reflow: true, get_figure(<tab:cl-results>))
// TODO: IF PR: Add REF
] ]
#slide( #slide(
@ -887,33 +1006,67 @@
if i != 0 { counter("logical-slide").update( n => n - 1 ) } if i != 0 { counter("logical-slide").update( n => n - 1 ) }
slide( slide(
title: [In the Wild: 49 975 APKs], title: [In the Wild: 49 975 APKs],
foreground: eye-2(x: 8%, y: 67%, height: 70pt)
)[ )[
#set align(center+horizon) #set align(center+horizon)
#show figure.caption: none // TODO: Simplifier table, mettre nb apk dans titre
#show link.where(dest: <acr-sdk>): it => it.body // enlever SDK et 1ere partie 100%
#set table( //#scale(90%, reflow: true, get_figure(<tab:cl-shadow>))
#import "4_class_loader/X_var.typ": scan_50k, scan_only_shadow
#import "lib.typ": num
#let nb_col = 7
#scale(90%, reflow: true, table(
columns: nb_col,
stroke: none,
align: center+horizon,
fill: (x, y) => { fill: (x, y) => {
if ( if (
i == 1 and (x, y) in ((1, 13), (2, 13), (4, 13), (8, 13)) i == 1 and (x, y) in ((1, 4), (2, 4), (4, 4), (6, 4))
) or ( ) or (
i == 2 and (x, y) in ((1, 14), (2, 14)) i == 2 and (x, y) in ((1, 5), (2, 5))
) { ) {
highlight-color highlight-color
} else { } else {
none none
} }
} },
) inset: (x: 0% + 7pt, y: 0% + 5pt),
// TODO: Simplifier table, mettre nb apk dans titre table.hline(),
// enlever SDK et 1ere partie 100% table.header(
#scale(90%, reflow: true, get_figure(<tab:cl-shadow>)) table.cell(colspan: nb_col, inset: 3pt)[],
table.cell(rowspan: 2, eye-2(x: 35pt, y: 30pt, height: 70pt)),
table.vline(end: 3),
table.vline(start: 4),
table.cell(colspan: 3)[*Number of apps*],
table.vline(end: 3),
table.vline(start: 4),
table.cell(colspan: 2)[*Nb Shadow Classes*],
table.vline(end: 3),
table.vline(start: 4),
table.cell(rowspan: 2)[*Identical Code*],
[], [%], [% malware],
[Average], [Median],
),
table.hline(),
table.cell(colspan: nb_col, inset: 3pt)[],
..scan_only_shadow.map(e => (
[*#e.method*],
num(e.nbapp), [#e.ratioapp%], [#e.ratiomal%],
num(e.avgshadow), num(e.median),
[#e.id%]
)).flatten(),
table.cell(colspan: nb_col, inset: 3pt)[],
table.hline(),
))
] ]
} }
#slide( #slide(
title: [Conclusion] title: [Conclusion]
)[ )[
#v(1fr)
#item-by-item[ #item-by-item[
- We modeled the class loading algorithm - We modeled the class loading algorithm
- Static Analysis Tools did not - Static Analysis Tools did not
@ -921,28 +1074,18 @@
- Ambiguous cases exists in the wild - Ambiguous cases exists in the wild
- We did not find deliberate shadow attacks - We did not find deliberate shadow attacks
] ]
#v(1fr)
#align(center, text(fill: pirat-color.blue.darken(30%))[Digital Threats: Research and Practice])
#v(0.5em)
] ]
#slide[
#set align(center)
// Faire apparaitre a chaque étape
#text(size: 22pt)[Digital Threats: Research and Practice])
#v(2em)
#show regex("\[\d+\]"): none
#cite(<classloaderinthemiddle>, form: "full")
]
#new-section-slide([The Application of Theseus]) #new-section-slide([The Application of Theseus])
// TODO put everywhere Theseus Transformeur
#slide( #slide(
title: [Overview], title: [Overview],
foreground: { foreground: rotate(30deg, text(fill: pirat-color.red, size: 50pt)[Moche faire un dessin]),
set align(center+horizon)
rotate(30deg, text(fill: pirat-color.red, size: 30pt)[Moche faire un dessin])
}
)[ )[
// TODO: bien tout rappeler l'objectif // TODO: bien tout rappeler l'objectif
// TODO: SOA // TODO: SOA
@ -986,7 +1129,7 @@
if i != 0 { counter("logical-slide").update( n => n - 1 ) } if i != 0 { counter("logical-slide").update( n => n - 1 ) }
slide( slide(
title: [Reflection], title: [Transformation: Reflection],
//foreground: ghost-6(x: 80%, y: 15%, mirror: true) //foreground: ghost-6(x: 80%, y: 15%, mirror: true)
)[ )[
#show: yes-codly #show: yes-codly
@ -1189,10 +1332,9 @@
#set align(center+horizon) #set align(center+horizon)
#show figure.caption: none #show figure.caption: none
#scale(90%, reflow: true, get_figure(<fig:th-status-npatched-vs-patched>)) #scale(90%, reflow: true, get_figure(<fig:th-status-npatched-vs-patched>))
// Fleche original // TODO: Fleche original
// Fleche theseus // TODO: Fleche theseus
// //
// Theseus Transformeur
] ]
#slide( #slide(
@ -1235,10 +1377,10 @@
#counter("logical-slide").update( n => n - 1 ) #counter("logical-slide").update( n => n - 1 )
#slide( #slide(
title: [Futur Work] title: [Future Works]
)[ )[
#item-by-item[ #item-by-item[
- Benchmark to evaluate finishing rate - Benchmark of APKs to evaluate finishing rate
- Make tools reusing sources from Android Open Source Project - Make tools reusing sources from Android Open Source Project
- Require developpers to provide high coverage tests inputs with the APKs - Require developpers to provide high coverage tests inputs with the APKs
] ]
@ -1278,6 +1420,9 @@
/* /*
* RETOUR 1: * RETOUR 1:
* *
* Pas de retour en arriere
* Ne pas se retourner
*
* Bon premier jet. * Bon premier jet.
* *
* - slide text bof * - slide text bof
@ -1295,15 +1440,8 @@
* Plus d'état de l'art, dans chapitres? redonner contexte au debut des chapitre, en profiter pour l'état de l'art. Pas plus d'un ou deux papiers, si important. * Plus d'état de l'art, dans chapitres? redonner contexte au debut des chapitre, en profiter pour l'état de l'art. Pas plus d'un ou deux papiers, si important.
* Remplacer l'état de l'art dans l'intro par intuition et mettre soa dans chapitre * Remplacer l'état de l'art dans l'intro par intuition et mettre soa dans chapitre
* *
* Pb Statement: lisibilité!
*
* PQ: focu on dcl & refl * PQ: focu on dcl & refl
* *
* Obfuscation: fleche vers deobfuscation
*
* Intro, dessin pas a pas dans l'intro en fonction de ce qui se passe dans l'intro
* RQ1: Static, RQ2: Check coherence static / dynamic, RQ3: transformation * RQ1: Static, RQ2: Check coherence static / dynamic, RQ3: transformation
* *
* Mettre Theseus Tranformeur un peu partout!
*
*/ */

View file

@ -19,6 +19,8 @@ rgb("#E69426"),
pirat-color.red, pirat-color.red,
) )
#let etal = [_et al._]
#let highlight-block(body) = { #let highlight-block(body) = {
set text(fill: white) set text(fill: white)
block( block(
@ -73,22 +75,8 @@ pirat-color.red,
} }
*/ */
#let ghost( #let place-fg(x: 0pt, y: 0pt, body) = context {
img, x: 0pt, let s = measure(body)
y: 0pt,
mirror: false,
rot: 0deg,
height: 100pt
) = context {
let img = image(img, height: height)
if mirror {
img = scale(x: -100%, img)
}
if rot != 0deg {
img = rotate(rot, img)
}
let s = measure(img)
let hx = s.width / 2 let hx = s.width / 2
let hy = s.height / 2 let hy = s.height / 2
@ -96,10 +84,27 @@ pirat-color.red,
bottom + left, bottom + left,
dx: x - hx, dx: x - hx,
dy: -y + hy, dy: -y + hy,
img body
) )
} }
#let ghost(
img, x: 0pt,
y: 0pt,
mirror: false,
rot: 0deg,
height: 100pt
) = {
let img = image(img, height: height)
if mirror {
img = scale(x: -100%, img)
}
if rot != 0deg {
img = rotate(rot, img)
}
place-fg(x: x, y: y, img)
}
#let ghost-1 = ghost.with("imgs/ghosts/ghost-1.png") #let ghost-1 = ghost.with("imgs/ghosts/ghost-1.png")
#let ghost-2 = ghost.with("imgs/ghosts/ghost-2.png") #let ghost-2 = ghost.with("imgs/ghosts/ghost-2.png")
#let ghost-3 = ghost.with("imgs/ghosts/ghost-3.png") #let ghost-3 = ghost.with("imgs/ghosts/ghost-3.png")
@ -111,3 +116,30 @@ pirat-color.red,
#let eye-2 = ghost.with("imgs/ghosts/eye-2.png") #let eye-2 = ghost.with("imgs/ghosts/eye-2.png")
#let eye-3 = ghost.with("imgs/ghosts/eye-3.png") #let eye-3 = ghost.with("imgs/ghosts/eye-3.png")
#let eye-4 = ghost.with("imgs/ghosts/eye-4.png") #let eye-4 = ghost.with("imgs/ghosts/eye-4.png")
#let arrow(start, end, strk: 3pt + black) = {
let strk = stroke(strk)
start.at(1) = -start.at(1)
end.at(1) = -end.at(1)
let (xe, ye) = end
let (xs, ys) = start
let w = xe.pt() - xs.pt()
let h = ye.pt() - ys.pt()
let len = if w == 0pt and h == 0pt {
1pt
} else {
calc.sqrt(w*w + h*h)
}
let cos = w / len
let sin = h / len
place(bottom + left, line(start: start, end: end, stroke: strk))
place(bottom + left, polygon(
fill: strk.paint,
stroke: strk,
end,
(xe + strk.thickness * (sin - cos), ye - strk.thickness * (sin + cos )),
(xe - strk.thickness * (sin + cos), ye - strk.thickness * (sin - cos )),
))
}

View file

@ -43,9 +43,8 @@ cmyk(25%,7%,0%,0%),
// Data // Data
#let sns-polylux-template_title = state("title", none) #let sns-polylux-template_title = state("title", none)
#let sns-polylux-template_short-title = state("short-title", none)
#let sns-polylux-template_subtitle = state("subtitle", none) #let sns-polylux-template_subtitle = state("subtitle", none)
#let sns-polylux-template_event = state("event", none) #let sns-polylux-template_footer-text = state("footer-text", none)
#let sns-polylux-template_short-event = state("short-event", none) #let sns-polylux-template_short-event = state("short-event", none)
#let sns-polylux-template_authors = state("authors", none) #let sns-polylux-template_authors = state("authors", none)
@ -77,8 +76,7 @@ cmyk(25%,7%,0%,0%),
// Data // Data
title : none, title : none,
subtitle : none, subtitle : none,
short-title : none, footer-text : none,
event : none,
short-event : none, short-event : none,
logo-2 : none, logo-2 : none,
logo-1 : none, logo-1 : none,
@ -115,11 +113,7 @@ cmyk(25%,7%,0%,0%),
) )
sns-polylux-template_title.update(title) sns-polylux-template_title.update(title)
sns-polylux-template_subtitle.update(subtitle) sns-polylux-template_subtitle.update(subtitle)
if short-title != none { sns-polylux-template_short-title.update(short-title) } if footer-text != none { sns-polylux-template_footer-text.update(footer-text) }
else { sns-polylux-template_short-title.update(title) }
sns-polylux-template_event.update(event)
if short-event != none { sns-polylux-template_short-event.update(short-event) }
else { sns-polylux-template_short-event.update(event) }
sns-polylux-template_logo-2.update(logo-2) sns-polylux-template_logo-2.update(logo-2)
sns-polylux-template_logo-1.update(logo-1) sns-polylux-template_logo-1.update(logo-1)
sns-polylux-template_authors.update(authors) sns-polylux-template_authors.update(authors)
@ -171,7 +165,7 @@ cmyk(25%,7%,0%,0%),
let content = align(top + center, context( { let content = align(top + center, context( {
let title = sns-polylux-template_title.at(here()) let title = sns-polylux-template_title.at(here())
let subtitle = sns-polylux-template_subtitle.at(here()) let subtitle = sns-polylux-template_subtitle.at(here())
let event = sns-polylux-template_event.at(here()) let footer-text = sns-polylux-template_footer-text.at(here())
let authors = sns-polylux-template_authors.at(here()) let authors = sns-polylux-template_authors.at(here())
let title-size = sns-polylux-template_title-size.at(here()) let title-size = sns-polylux-template_title-size.at(here())
let logo = { let logo = {
@ -224,7 +218,7 @@ cmyk(25%,7%,0%,0%),
place( place(
bottom + center, bottom + center,
dy: -1cm, dy: -1cm,
text(fill: sns-polylux-template_background-color.at(here()), size: 22pt, event) text(fill: sns-polylux-template_background-color.at(here()), size: 22pt, footer-text)
) )
if logo != none { if logo != none {
place( place(
@ -454,8 +448,7 @@ cmyk(25%,7%,0%,0%),
// FOOTER // FOOTER
let footer = align(top + center, context( { let footer = align(top + center, context( {
let short-title = sns-polylux-template_short-title.at(here()) let footer-text = sns-polylux-template_footer-text.at(here())
let short-event = sns-polylux-template_short-event.at(here())
place(top + center, line(length: 100%-2cm, stroke: (paint: sns-polylux-template_colormap.at(here()).at(1), thickness: 2pt))) place(top + center, line(length: 100%-2cm, stroke: (paint: sns-polylux-template_colormap.at(here()).at(1), thickness: 2pt)))
@ -468,9 +461,8 @@ cmyk(25%,7%,0%,0%),
align(horizon,text(fill: sns-polylux-template_colormap.at(here()).at(2), size: 16pt, align(horizon,text(fill: sns-polylux-template_colormap.at(here()).at(2), size: 16pt,
grid( grid(
gutter: 0.8cm, gutter: 0.8cm,
columns: (0.7fr, 1fr, 0.7fr), columns: (1fr, 5em),
align(left, short-title), align(center, smallcaps(footer-text)),
align(center, smallcaps(short-event)),
align(right, if page-number [#toolbox.slide-number/#toolbox.last-slide-number]), align(right, if page-number [#toolbox.slide-number/#toolbox.last-slide-number]),
)) ))
)) ))