From 7e418ad1482102e7e53a854f99e6a91283df48a2 Mon Sep 17 00:00:00 2001 From: Johann Birnick Date: Fri, 20 Dec 2024 22:56:21 -0800 Subject: [PATCH] implement title element --- crates/typst-library/src/model/mod.rs | 3 ++ crates/typst-library/src/model/title.rs | 68 ++++++++++++++++++++++++ tests/ref/html/title-and-heading.html | 15 ++++++ tests/ref/html/title-basic.html | 12 +++++ tests/ref/title-and-heading.png | Bin 0 -> 1673 bytes tests/ref/title-basic.png | Bin 0 -> 679 bytes tests/ref/title-show-rule.png | Bin 0 -> 2136 bytes tests/suite/model/title.typ | 12 +++++ 8 files changed, 110 insertions(+) create mode 100644 crates/typst-library/src/model/title.rs create mode 100644 tests/ref/html/title-and-heading.html create mode 100644 tests/ref/html/title-basic.html create mode 100644 tests/ref/title-and-heading.png create mode 100644 tests/ref/title-basic.png create mode 100644 tests/ref/title-show-rule.png create mode 100644 tests/suite/model/title.typ diff --git a/crates/typst-library/src/model/mod.rs b/crates/typst-library/src/model/mod.rs index 7dad51c39..ab00bbf4d 100644 --- a/crates/typst-library/src/model/mod.rs +++ b/crates/typst-library/src/model/mod.rs @@ -20,6 +20,7 @@ mod reference; mod strong; mod table; mod terms; +mod title; pub use self::bibliography::*; pub use self::cite::*; @@ -39,6 +40,7 @@ pub use self::reference::*; pub use self::strong::*; pub use self::table::*; pub use self::terms::*; +pub use self::title::*; use crate::foundations::{category, Category, Scope}; @@ -57,6 +59,7 @@ pub fn define(global: &mut Scope) { global.define_elem::(); global.define_elem::(); global.define_elem::(); + global.define_elem::(); global.define_elem::(); global.define_elem::(); global.define_elem::(); diff --git a/crates/typst-library/src/model/title.rs b/crates/typst-library/src/model/title.rs new file mode 100644 index 000000000..2dd42e19c --- /dev/null +++ b/crates/typst-library/src/model/title.rs @@ -0,0 +1,68 @@ +use crate::{ + diag::SourceResult, + engine::Engine, + foundations::{ + elem, Content, NativeElement, Packed, Show, ShowSet, Smart, StyleChain, Styles, + TargetElem, + }, + html::{tag, HtmlElem}, + introspection::Locatable, + layout::{AlignElem, Alignment, BlockBody, BlockElem, Em}, + text::{FontWeight, TextElem, TextSize}, +}; + +/// A document title. +/// +/// Should be used to display the main title of the whole document, and should +/// occur only once per document. +/// +/// Shows as `h1` in HTML. In contrast, a heading of level 1 +/// (created with `= Some Heading`) will show as `h2`. +/// +/// # Example +/// ```example +/// #title[Interstellar Mail Delivery] +/// +/// = Introduction +/// In recent years, ... +/// ``` +#[elem(Locatable, Show, ShowSet)] +pub struct TitleElem { + /// The content of the title. + #[required] + pub body: Content, +} + +impl Show for Packed { + #[typst_macros::time(name = "title", span = self.span())] + fn show(&self, _engine: &mut Engine, styles: StyleChain) -> SourceResult { + let html = TargetElem::target_in(styles).is_html(); + + let span = self.span(); + let realized = self.body().clone(); + + Ok(if html { + HtmlElem::new(tag::h1).with_body(Some(realized)).pack().spanned(span) + } else { + let realized = BlockBody::Content(realized); + BlockElem::new().with_body(Some(realized)).pack().spanned(span) + }) + } +} + +impl ShowSet for Packed { + fn show_set(&self, _styles: StyleChain) -> Styles { + const SIZE: Em = Em::new(1.6); + const ABOVE: Em = Em::new(1.125); + const BELOW: Em = Em::new(0.75); + + let mut out = Styles::new(); + out.set(TextElem::set_size(TextSize(SIZE.into()))); + out.set(TextElem::set_weight(FontWeight::BOLD)); + out.set(BlockElem::set_above(Smart::Custom(ABOVE.into()))); + out.set(BlockElem::set_below(Smart::Custom(BELOW.into()))); + out.set(BlockElem::set_sticky(true)); + out.set(AlignElem::set_alignment(Alignment::CENTER)); + out + } +} diff --git a/tests/ref/html/title-and-heading.html b/tests/ref/html/title-and-heading.html new file mode 100644 index 000000000..a6aa7ec0c --- /dev/null +++ b/tests/ref/html/title-and-heading.html @@ -0,0 +1,15 @@ + + + + + + + +

+ A cool title +

+

+ Some level one heading +

+ + \ No newline at end of file diff --git a/tests/ref/html/title-basic.html b/tests/ref/html/title-basic.html new file mode 100644 index 000000000..7b44c7f28 --- /dev/null +++ b/tests/ref/html/title-basic.html @@ -0,0 +1,12 @@ + + + + + + + +

+ Some Title +

+ + \ No newline at end of file diff --git a/tests/ref/title-and-heading.png b/tests/ref/title-and-heading.png new file mode 100644 index 0000000000000000000000000000000000000000..3cf9654b513cf1761fca608996c3ea46d87ad377 GIT binary patch literal 1673 zcmV;426p+0P)DQ_f4B%JtV?R^ZZ+$=nOkOK8KSn1?w0vh5^*El#6{OwmJ~B}#a$4HN?UX; zAjHFf%Z1=VWri;pXtG691O=6q5vAp!DAJALC2+sXD36*U2nKDYx!;*HbI#1~oX^aE z&Yb_bj~}V~%&-|Y!#`!X@fLlRyX|FfzFj39eYp8W?Ev8*hY#p0;~JSqN6kR8^6NOQ zoFUr=2A6BpLi%i=KL-Z;`YNj%^i^CZd{E7yqX@MYSU*6x zd1A|W6-H+no=g7S1m`y_^7}yp+!4A{5k3*kGUsxz+bF)32W*X z9KRVqHI|ut@P2P?}Ot83KuI)KpSB6aa$bHM*J%0Gp|hh zhMkBh9iZO7vau zTKk&npG|74og%O1OqYBvIvCE2XKyg^56FnhO!0;>8YAO|f}=DuSE`t+@&B9ry$gQQ z!T-Vk-U%Bmr@%{+#Y$C3u%xg_Uh`A4dVyb=tvJbxO1rBhC%%9_GB3StzvW}s^O7hEwOM2Bq@@|VkGwk=1P66au zi_R2kF?@z&30$_^3dCC2&zOAfrmM`Y2FMt?G;j2)d^JHuG|X2o*B01WIZaIK1+GpD z-*ptAIQqSdke-;BeIil6zd)x?xJmKexN86y67}&-oYp4S0vKum&W0t{a89S!NGbV-FaSlqr0D_L+hx1Jec)jAX?^gah$B(H{PO@ zAj`T9!zvjx_&D#X`1Zpu-sUL^58yAK@(;Omd#TH0F)Wjd_{sqm+Yu=T2NTw+7-p+O z1qEfag}pNO_76mPH|*LD?~rsp(Il<<_1A+1uTi<|QSafZbWo1hFPpUT0nbSmfs)J> zSqLujU;ZTqO6Kp5F)~PS{S+06hg#G6rIp?C1;IGkr#j1@{&P?{X&q(W^^fGS`i3VZ z!{j2H+MItA9GX@>NN`F(kbl{ZpdH6Jw|&!5D0=1)j6FXv~RH TTHUk@00000NkvXXu0mjf*HAEw>5o+Z7b+v<3@mlFf%*mGa-c>;yOD7Qi&T-K6qug10(- zrB%LI0N+l69Od|f`h7gx`2re!Qh-CM&=z=1q!|)FSpfHrf>wC|odA#~X+WfH@T{9! z-v)p&IqYO5^cTfmA50Cs!;_c;Ex>ePR9pj?(>`*8DhbX=2F$}VE7lJ8D0geuDO9Q4 zDF^8WZwdpVw+1%WfGJSL{dAKC*e(T& z;Nbbs`6C=Jm(6go^0s!k9m6NpF;}Km0&rf% zL4uqH!c`8hH4ZwyXIcYyE5FM7UhyyzurxE<`6h!g;`XY;Q??Bih-8dT_=>o09AKfg z{Yq2%5qbmF79%C%8q(S5se*gCC~CP&jWsY0<#~mz0H&9PW$&R&Yp7^x)fu!Os`Pqo zKc?E;N0XSV(HZnwBQ)v^x_W3R>Y}E?plgEm;?hC;b+W^D*be{gegS=W_@5h9{zCu& N002ovPDHLkV1ksQHM0N! literal 0 HcmV?d00001 diff --git a/tests/ref/title-show-rule.png b/tests/ref/title-show-rule.png new file mode 100644 index 0000000000000000000000000000000000000000..9b0c5296d9ff1745cbcb43848d0f615869efb93e GIT binary patch literal 2136 zcmV-e2&eanP)?*1zsclYLgErs-J5k;-6$y%x2C6T(MA;2y%Y^wD(be~?n+M-CF+(&6y4QD zOBRjGvaHK;mRcp0h?0;d6`_epYLUrB=DWY&_czZo=aEdZ6EQufXI_8hGda(B&pgj$ z@|<~$ek6~%!mh9@{1XaS9$q^8cd;wKNGo+}xM*fGv~uq<^@7vFYXT8*z`PX$+)q(w${@=4tq&@G z=gH>kNCap)q$L3OOfAvF^fqctZ{0F6FY_Mb5dsU@>_V9gfwqnom1|Ua9*wX zWEBlU3JK+pgWz$4!^|9sHa+u5nZh%Lloxq*A!Qk_5K{W{dWn=UUXw`4^#lMcB`Fl7 z1Qk}6dZMmnHc4gvyl#_{gzO7q9~r`b;5C4h>Aa#ynFc}=V&g!X%VACGQ>gnnpTJ!x zd6HE+vNx&=FXq*kl=pc>5G(Zsq3uyN7^L?&tSet0fSUQXC*FWG4S0wDzv1VI9fK-9 zQem6o*YuNJA?6sAnvh1FlBN!Z<3VZ{WfNP>VT0A)sGQZ`F}|b;Lw3ip@NkeO*LHjR z7gUmoXehE`vKqnDz0$F88<2)n6FxR}Tq)_IkgX@8IAj;dDw1aqITr2-Qu}JcD-bu3 zF5Pyj_aIwGR$Y09IT#)Zl6zs*@LI%Y$)+s|Ugbn|)5GwwVDaX;)_%B8b>RiTrjxFu zoWp#}b%=DMP?$i{n<)8|5XTBDUB^ zHrqR@t2bW0CbubfiOq=i2c_ZUujo>Ab5PvI{Ef~>H$kgsC+;R`e{0Zrd`P5)Q-0Gs z5H-OIjQ1dxUC;X!bV#BR?IL6H0= zrjs6K-yh$#;ggl0?$4lVe0N~Y<|CP8{DWYY@{;>xxmRW(Rv~^-$&1}LSCA(6$kvaO zPuy{R72yiI!mh9@>i8#t z&2cQ82sOZR5_W_1I@y!&4boi4!jk}?$L14Pa&k7d1;u}JcINRd5eNXeB+LV;6Df)% zpKJz#w7{`&Uw~LrQb1WlY^Mnzm45)~Wztn1m=F*|HvK_b zjR1hw>7-fSTK0F3J zg76%kL+yoo@W4}!gnNP1kRGrDqyTOHiRUw96AIEi!;SjDO0xPgIJLHLSM9%n(N^QF zSUgYfiU*H-hu9Ck!1`R~nf0~__tz3O2LQWDMk_(`xMRHi{*dVI0g~=DG`!CfLGcQC zb>_H!ZUFC7E#Wv&UL%9b5MYU=v^fMR+pDUGC=pcz?0CzkyhL22;EgnT{&6A*OXA5)FtDPR;qR?UDZ@2XQf!-rR| z^