mirror of
https://github.com/typst/typst
synced 2025-07-27 14:27:56 +08:00
Compare commits
6 Commits
49873b9b43
...
a2e87185c1
Author | SHA1 | Date | |
---|---|---|---|
|
a2e87185c1 | ||
|
af2253ba16 | ||
|
6daae2e292 | ||
|
7f24cd9253 | ||
|
fea153a6fc | ||
|
cdb8a42c68 |
124
Cargo.lock
generated
124
Cargo.lock
generated
@ -181,9 +181,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.8.0"
|
version = "2.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -214,9 +214,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.21.0"
|
version = "1.23.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
|
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck_derive",
|
"bytemuck_derive",
|
||||||
]
|
]
|
||||||
@ -964,6 +964,69 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hayro"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"hayro-interpret",
|
||||||
|
"image",
|
||||||
|
"kurbo",
|
||||||
|
"rustc-hash",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hayro-font"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"phf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hayro-interpret"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.1",
|
||||||
|
"hayro-font",
|
||||||
|
"hayro-syntax",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"phf",
|
||||||
|
"qcms",
|
||||||
|
"skrifa",
|
||||||
|
"smallvec",
|
||||||
|
"yoke 0.8.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hayro-syntax"
|
||||||
|
version = "0.0.1"
|
||||||
|
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
|
||||||
|
dependencies = [
|
||||||
|
"flate2",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"rustc-hash",
|
||||||
|
"smallvec",
|
||||||
|
"zune-jpeg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hayro-write"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/LaurenzV/hayro?rev=e701f95#e701f9569157a2fe4ade68930dc9e9283782dcca"
|
||||||
|
dependencies = [
|
||||||
|
"flate2",
|
||||||
|
"hayro-syntax",
|
||||||
|
"log",
|
||||||
|
"pdf-writer",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -1200,9 +1263,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.25.5"
|
version = "0.25.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
|
checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
@ -1265,7 +1328,7 @@ version = "0.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
|
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"inotify-sys",
|
"inotify-sys",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -1361,7 +1424,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "krilla"
|
name = "krilla"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/LaurenzV/krilla?rev=20c14fe#20c14fefee5002566b3d6668b338bbe2168784e7"
|
source = "git+https://github.com/LaurenzV/krilla?rev=37b9a00#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@ -1370,6 +1433,7 @@ dependencies = [
|
|||||||
"float-cmp 0.10.0",
|
"float-cmp 0.10.0",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"gif",
|
"gif",
|
||||||
|
"hayro-write",
|
||||||
"image-webp",
|
"image-webp",
|
||||||
"imagesize",
|
"imagesize",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -1379,6 +1443,7 @@ dependencies = [
|
|||||||
"rustybuzz",
|
"rustybuzz",
|
||||||
"siphasher",
|
"siphasher",
|
||||||
"skrifa",
|
"skrifa",
|
||||||
|
"smallvec",
|
||||||
"subsetter",
|
"subsetter",
|
||||||
"tiny-skia-path",
|
"tiny-skia-path",
|
||||||
"xmp-writer",
|
"xmp-writer",
|
||||||
@ -1389,7 +1454,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "krilla-svg"
|
name = "krilla-svg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/LaurenzV/krilla?rev=20c14fe#20c14fefee5002566b3d6668b338bbe2168784e7"
|
source = "git+https://github.com/LaurenzV/krilla?rev=37b9a00#37b9a00bfac87ed0b347b7cf8e9d37a6f68fcccd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flate2",
|
"flate2",
|
||||||
"fontdb",
|
"fontdb",
|
||||||
@ -1402,9 +1467,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kurbo"
|
name = "kurbo"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f"
|
checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@ -1456,7 +1521,7 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
@ -1622,7 +1687,7 @@ version = "8.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
|
checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"filetime",
|
"filetime",
|
||||||
"fsevent-sys",
|
"fsevent-sys",
|
||||||
"inotify",
|
"inotify",
|
||||||
@ -1704,7 +1769,7 @@ version = "0.10.72"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
|
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1841,7 +1906,7 @@ version = "0.13.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ea27c5015ab81753fc61e49f8cde74999346605ee148bb20008ef3d3150e0dc"
|
checksum = "3ea27c5015ab81753fc61e49f8cde74999346605ee148bb20008ef3d3150e0dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -1999,7 +2064,7 @@ version = "0.9.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
|
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"getopts",
|
"getopts",
|
||||||
"memchr",
|
"memchr",
|
||||||
"unicase",
|
"unicase",
|
||||||
@ -2112,7 +2177,7 @@ version = "0.5.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2215,7 +2280,7 @@ version = "0.38.44"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
@ -2234,7 +2299,7 @@ version = "0.20.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
|
checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"core_maths",
|
"core_maths",
|
||||||
"log",
|
"log",
|
||||||
@ -2282,7 +2347,7 @@ version = "2.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@ -2445,9 +2510,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
@ -2855,7 +2920,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "typst-assets"
|
name = "typst-assets"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
source = "git+https://github.com/typst/typst-assets?rev=edf0d64#edf0d648376e29738a05a933af9ea99bb81557b1"
|
source = "git+https://github.com/typst/typst-assets?rev=fbf00f9#fbf00f9539fdb0825bef4d39fb57d5986c51b756"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typst-cli"
|
name = "typst-cli"
|
||||||
@ -2905,7 +2970,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "typst-dev-assets"
|
name = "typst-dev-assets"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
source = "git+https://github.com/typst/typst-dev-assets?rev=bfa947f#bfa947f3433d7d13a995168c40ae788a2ebfe648"
|
source = "git+https://github.com/typst/typst-dev-assets?rev=c6c2acf#c6c2acf6cdc31f99a23a478d3d614f8bf806a4f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typst-docs"
|
name = "typst-docs"
|
||||||
@ -3055,7 +3120,7 @@ name = "typst-library"
|
|||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"az",
|
"az",
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chinese-number",
|
"chinese-number",
|
||||||
"ciborium",
|
"ciborium",
|
||||||
@ -3067,6 +3132,7 @@ dependencies = [
|
|||||||
"fontdb",
|
"fontdb",
|
||||||
"glidesort",
|
"glidesort",
|
||||||
"hayagriva",
|
"hayagriva",
|
||||||
|
"hayro-syntax",
|
||||||
"icu_properties",
|
"icu_properties",
|
||||||
"icu_provider",
|
"icu_provider",
|
||||||
"icu_provider_blob",
|
"icu_provider_blob",
|
||||||
@ -3165,11 +3231,13 @@ version = "0.13.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"comemo",
|
"comemo",
|
||||||
|
"hayro",
|
||||||
"image",
|
"image",
|
||||||
"pixglyph",
|
"pixglyph",
|
||||||
"resvg",
|
"resvg",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
"ttf-parser",
|
"ttf-parser",
|
||||||
|
"typst-assets",
|
||||||
"typst-library",
|
"typst-library",
|
||||||
"typst-macros",
|
"typst-macros",
|
||||||
"typst-timing",
|
"typst-timing",
|
||||||
@ -3183,8 +3251,10 @@ dependencies = [
|
|||||||
"comemo",
|
"comemo",
|
||||||
"ecow",
|
"ecow",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
"hayro",
|
||||||
"image",
|
"image",
|
||||||
"ttf-parser",
|
"ttf-parser",
|
||||||
|
"typst-assets",
|
||||||
"typst-library",
|
"typst-library",
|
||||||
"typst-macros",
|
"typst-macros",
|
||||||
"typst-timing",
|
"typst-timing",
|
||||||
@ -3581,7 +3651,7 @@ version = "0.221.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083"
|
checksum = "9845c470a2e10b61dd42c385839cdd6496363ed63b5c9e420b5488b77bd22083"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
"indexmap 2.7.1",
|
"indexmap 2.7.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3716,7 +3786,7 @@ version = "0.33.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -32,8 +32,8 @@ typst-svg = { path = "crates/typst-svg", version = "0.13.1" }
|
|||||||
typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" }
|
typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" }
|
||||||
typst-timing = { path = "crates/typst-timing", version = "0.13.1" }
|
typst-timing = { path = "crates/typst-timing", version = "0.13.1" }
|
||||||
typst-utils = { path = "crates/typst-utils", version = "0.13.1" }
|
typst-utils = { path = "crates/typst-utils", version = "0.13.1" }
|
||||||
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "edf0d64" }
|
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "fbf00f9" }
|
||||||
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "bfa947f" }
|
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "c6c2acf" }
|
||||||
arrayvec = "0.7.4"
|
arrayvec = "0.7.4"
|
||||||
az = "1.2"
|
az = "1.2"
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
@ -61,6 +61,8 @@ fontdb = { version = "0.23", default-features = false }
|
|||||||
fs_extra = "1.3"
|
fs_extra = "1.3"
|
||||||
glidesort = "0.1.2"
|
glidesort = "0.1.2"
|
||||||
hayagriva = "0.8.1"
|
hayagriva = "0.8.1"
|
||||||
|
hayro-syntax = { git = "https://github.com/LaurenzV/hayro", rev = "e701f95" }
|
||||||
|
hayro = { git = "https://github.com/LaurenzV/hayro", rev = "e701f95" }
|
||||||
heck = "0.5"
|
heck = "0.5"
|
||||||
hypher = "0.1.4"
|
hypher = "0.1.4"
|
||||||
icu_properties = { version = "1.4", features = ["serde"] }
|
icu_properties = { version = "1.4", features = ["serde"] }
|
||||||
@ -72,8 +74,8 @@ image = { version = "0.25.5", default-features = false, features = ["png", "jpeg
|
|||||||
indexmap = { version = "2", features = ["serde"] }
|
indexmap = { version = "2", features = ["serde"] }
|
||||||
infer = { version = "0.19.0", default-features = false }
|
infer = { version = "0.19.0", default-features = false }
|
||||||
kamadak-exif = "0.6"
|
kamadak-exif = "0.6"
|
||||||
krilla = { git = "https://github.com/LaurenzV/krilla", rev = "20c14fe", default-features = false, features = ["raster-images", "comemo", "rayon"] }
|
krilla = { git = "https://github.com/LaurenzV/krilla", rev = "37b9a00", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] }
|
||||||
krilla-svg = { git = "https://github.com/LaurenzV/krilla", rev = "20c14fe" }
|
krilla-svg = { git = "https://github.com/LaurenzV/krilla", rev = "37b9a00"}
|
||||||
kurbo = "0.11"
|
kurbo = "0.11"
|
||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4"
|
||||||
lipsum = "0.9"
|
lipsum = "0.9"
|
||||||
|
@ -31,6 +31,7 @@ flate2 = { workspace = true }
|
|||||||
fontdb = { workspace = true }
|
fontdb = { workspace = true }
|
||||||
glidesort = { workspace = true }
|
glidesort = { workspace = true }
|
||||||
hayagriva = { workspace = true }
|
hayagriva = { workspace = true }
|
||||||
|
hayro-syntax = { workspace = true }
|
||||||
icu_properties = { workspace = true }
|
icu_properties = { workspace = true }
|
||||||
icu_provider = { workspace = true }
|
icu_provider = { workspace = true }
|
||||||
icu_provider_blob = { workspace = true }
|
icu_provider_blob = { workspace = true }
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
//! Image handling.
|
//! Image handling.
|
||||||
|
|
||||||
|
mod pdf;
|
||||||
mod raster;
|
mod raster;
|
||||||
mod svg;
|
mod svg;
|
||||||
|
|
||||||
|
pub use self::pdf::PdfImage;
|
||||||
pub use self::raster::{
|
pub use self::raster::{
|
||||||
ExchangeFormat, PixelEncoding, PixelFormat, RasterFormat, RasterImage,
|
ExchangeFormat, PixelEncoding, PixelFormat, RasterFormat, RasterImage,
|
||||||
};
|
};
|
||||||
@ -10,13 +12,15 @@ pub use self::svg::SvgImage;
|
|||||||
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
|
use hayro_syntax::LoadPdfError;
|
||||||
use typst_syntax::{Span, Spanned};
|
use typst_syntax::{Span, Spanned};
|
||||||
use typst_utils::LazyHash;
|
use typst_utils::{LazyHash, NonZeroExt};
|
||||||
|
|
||||||
use crate::diag::{At, LoadedWithin, SourceResult, StrResult, warning};
|
use crate::diag::{At, LoadedWithin, SourceResult, StrResult, bail, warning};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
Bytes, Cast, Content, Derived, NativeElement, Packed, Smart, StyleChain, cast, elem,
|
Bytes, Cast, Content, Derived, NativeElement, Packed, Smart, StyleChain, cast, elem,
|
||||||
@ -26,6 +30,7 @@ use crate::layout::{Length, Rel, Sizing};
|
|||||||
use crate::loading::{DataSource, Load, LoadSource, Loaded, Readable};
|
use crate::loading::{DataSource, Load, LoadSource, Loaded, Readable};
|
||||||
use crate::model::Figurable;
|
use crate::model::Figurable;
|
||||||
use crate::text::{LocalName, families};
|
use crate::text::{LocalName, families};
|
||||||
|
use crate::visualize::image::pdf::PdfDocument;
|
||||||
|
|
||||||
/// A raster or vector graphic.
|
/// A raster or vector graphic.
|
||||||
///
|
///
|
||||||
@ -79,8 +84,7 @@ pub struct ImageElem {
|
|||||||
/// format automatically, but that's not always possible).
|
/// format automatically, but that's not always possible).
|
||||||
///
|
///
|
||||||
/// Supported formats are `{"png"}`, `{"jpg"}`, `{"gif"}`, `{"svg"}`,
|
/// Supported formats are `{"png"}`, `{"jpg"}`, `{"gif"}`, `{"svg"}`,
|
||||||
/// `{"webp"}` as well as raw pixel data. Embedding PDFs as images is
|
/// `{"pdf"}`, `{"webp"}` as well as raw pixel data.
|
||||||
/// [not currently supported](https://github.com/typst/typst/issues/145).
|
|
||||||
///
|
///
|
||||||
/// When providing raw pixel data as the `source`, you must specify a
|
/// When providing raw pixel data as the `source`, you must specify a
|
||||||
/// dictionary with the following keys as the `format`:
|
/// dictionary with the following keys as the `format`:
|
||||||
@ -126,6 +130,11 @@ pub struct ImageElem {
|
|||||||
/// A text describing the image.
|
/// A text describing the image.
|
||||||
pub alt: Option<EcoString>,
|
pub alt: Option<EcoString>,
|
||||||
|
|
||||||
|
/// The page number that should be embedded as an image. This attribute only
|
||||||
|
/// has an effect for PDF files.
|
||||||
|
#[default(NonZeroUsize::ONE)]
|
||||||
|
pub page: NonZeroUsize,
|
||||||
|
|
||||||
/// How the image should adjust itself to a given area (the area is defined
|
/// How the image should adjust itself to a given area (the area is defined
|
||||||
/// by the `width` and `height` fields). Note that `fit` doesn't visually
|
/// by the `width` and `height` fields). Note that `fit` doesn't visually
|
||||||
/// change anything if the area's aspect ratio is the same as the image's
|
/// change anything if the area's aspect ratio is the same as the image's
|
||||||
@ -261,6 +270,45 @@ impl Packed<ImageElem> {
|
|||||||
)
|
)
|
||||||
.within(loaded)?,
|
.within(loaded)?,
|
||||||
),
|
),
|
||||||
|
ImageFormat::Vector(VectorFormat::Pdf) => {
|
||||||
|
let document = match PdfDocument::new(loaded.data.clone()) {
|
||||||
|
Ok(doc) => doc,
|
||||||
|
Err(e) => match e {
|
||||||
|
LoadPdfError::Encryption => {
|
||||||
|
bail!(
|
||||||
|
span,
|
||||||
|
"the PDF is encrypted or password-protected";
|
||||||
|
hint: "such PDFs are currently not supported";
|
||||||
|
hint: "preprocess the PDF to remove the encryption"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
LoadPdfError::Invalid => {
|
||||||
|
bail!(
|
||||||
|
span,
|
||||||
|
"the PDF could not be loaded";
|
||||||
|
hint: "perhaps the PDF file is malformed"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// The user provides the page number start from 1, but further
|
||||||
|
// down the pipeline, page numbers are 0-based.
|
||||||
|
let page_num = self.page.get(styles).get();
|
||||||
|
let page_idx = page_num - 1;
|
||||||
|
let num_pages = document.num_pages();
|
||||||
|
|
||||||
|
let Some(pdf_image) = PdfImage::new(document, page_idx) else {
|
||||||
|
let s = if num_pages == 1 { "" } else { "s" };
|
||||||
|
bail!(
|
||||||
|
span,
|
||||||
|
"page {page_num} does not exist";
|
||||||
|
hint: "the document only has {num_pages} page{s}"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageKind::Pdf(pdf_image)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Image::new(kind, self.alt.get_cloned(styles), self.scaling.get(styles)))
|
Ok(Image::new(kind, self.alt.get_cloned(styles), self.scaling.get(styles)))
|
||||||
@ -286,6 +334,7 @@ impl Packed<ImageElem> {
|
|||||||
"jpg" | "jpeg" => return Ok(ExchangeFormat::Jpg.into()),
|
"jpg" | "jpeg" => return Ok(ExchangeFormat::Jpg.into()),
|
||||||
"gif" => return Ok(ExchangeFormat::Gif.into()),
|
"gif" => return Ok(ExchangeFormat::Gif.into()),
|
||||||
"svg" | "svgz" => return Ok(VectorFormat::Svg.into()),
|
"svg" | "svgz" => return Ok(VectorFormat::Svg.into()),
|
||||||
|
"pdf" => return Ok(VectorFormat::Pdf.into()),
|
||||||
"webp" => return Ok(ExchangeFormat::Webp.into()),
|
"webp" => return Ok(ExchangeFormat::Webp.into()),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -373,6 +422,7 @@ impl Image {
|
|||||||
match &self.0.kind {
|
match &self.0.kind {
|
||||||
ImageKind::Raster(raster) => raster.format().into(),
|
ImageKind::Raster(raster) => raster.format().into(),
|
||||||
ImageKind::Svg(_) => VectorFormat::Svg.into(),
|
ImageKind::Svg(_) => VectorFormat::Svg.into(),
|
||||||
|
ImageKind::Pdf(_) => VectorFormat::Pdf.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,6 +431,7 @@ impl Image {
|
|||||||
match &self.0.kind {
|
match &self.0.kind {
|
||||||
ImageKind::Raster(raster) => raster.width() as f64,
|
ImageKind::Raster(raster) => raster.width() as f64,
|
||||||
ImageKind::Svg(svg) => svg.width(),
|
ImageKind::Svg(svg) => svg.width(),
|
||||||
|
ImageKind::Pdf(pdf) => pdf.width() as f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,6 +440,7 @@ impl Image {
|
|||||||
match &self.0.kind {
|
match &self.0.kind {
|
||||||
ImageKind::Raster(raster) => raster.height() as f64,
|
ImageKind::Raster(raster) => raster.height() as f64,
|
||||||
ImageKind::Svg(svg) => svg.height(),
|
ImageKind::Svg(svg) => svg.height(),
|
||||||
|
ImageKind::Pdf(pdf) => pdf.height() as f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,6 +449,7 @@ impl Image {
|
|||||||
match &self.0.kind {
|
match &self.0.kind {
|
||||||
ImageKind::Raster(raster) => raster.dpi(),
|
ImageKind::Raster(raster) => raster.dpi(),
|
||||||
ImageKind::Svg(_) => Some(Image::USVG_DEFAULT_DPI),
|
ImageKind::Svg(_) => Some(Image::USVG_DEFAULT_DPI),
|
||||||
|
ImageKind::Pdf(_) => Some(Image::DEFAULT_DPI),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,6 +488,8 @@ pub enum ImageKind {
|
|||||||
Raster(RasterImage),
|
Raster(RasterImage),
|
||||||
/// An SVG image.
|
/// An SVG image.
|
||||||
Svg(SvgImage),
|
Svg(SvgImage),
|
||||||
|
/// A PDF image.
|
||||||
|
Pdf(PdfImage),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RasterImage> for ImageKind {
|
impl From<RasterImage> for ImageKind {
|
||||||
@ -469,10 +524,20 @@ impl ImageFormat {
|
|||||||
return Some(Self::Vector(VectorFormat::Svg));
|
return Some(Self::Vector(VectorFormat::Svg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_pdf(data) {
|
||||||
|
return Some(Self::Vector(VectorFormat::Pdf));
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether the data looks like a PDF file.
|
||||||
|
fn is_pdf(data: &[u8]) -> bool {
|
||||||
|
let head = &data[..data.len().min(2048)];
|
||||||
|
memchr::memmem::find(head, b"%PDF-").is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks whether the data looks like an SVG or a compressed SVG.
|
/// Checks whether the data looks like an SVG or a compressed SVG.
|
||||||
fn is_svg(data: &[u8]) -> bool {
|
fn is_svg(data: &[u8]) -> bool {
|
||||||
// Check for the gzip magic bytes. This check is perhaps a bit too
|
// Check for the gzip magic bytes. This check is perhaps a bit too
|
||||||
@ -493,6 +558,9 @@ fn is_svg(data: &[u8]) -> bool {
|
|||||||
pub enum VectorFormat {
|
pub enum VectorFormat {
|
||||||
/// The vector graphics format of the web.
|
/// The vector graphics format of the web.
|
||||||
Svg,
|
Svg,
|
||||||
|
/// High-fidelity document and graphics format, with focus on exact
|
||||||
|
/// reproduction in print.
|
||||||
|
Pdf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> From<R> for ImageFormat
|
impl<R> From<R> for ImageFormat
|
||||||
|
98
crates/typst-library/src/visualize/image/pdf.rs
Normal file
98
crates/typst-library/src/visualize/image/pdf.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use hayro_syntax::page::Page;
|
||||||
|
use hayro_syntax::{LoadPdfError, Pdf};
|
||||||
|
|
||||||
|
use crate::foundations::Bytes;
|
||||||
|
|
||||||
|
/// A PDF document.
|
||||||
|
#[derive(Clone, Hash)]
|
||||||
|
pub struct PdfDocument(Arc<DocumentRepr>);
|
||||||
|
|
||||||
|
/// The internal representation of a `PdfDocument`.
|
||||||
|
struct DocumentRepr {
|
||||||
|
pdf: Arc<Pdf>,
|
||||||
|
data: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfDocument {
|
||||||
|
/// Loads a PDF document.
|
||||||
|
#[comemo::memoize]
|
||||||
|
#[typst_macros::time(name = "load pdf document")]
|
||||||
|
pub fn new(data: Bytes) -> Result<PdfDocument, LoadPdfError> {
|
||||||
|
let pdf = Arc::new(Pdf::new(Arc::new(data.clone()))?);
|
||||||
|
Ok(Self(Arc::new(DocumentRepr { data, pdf })))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the underlying PDF document.
|
||||||
|
pub fn pdf(&self) -> &Arc<Pdf> {
|
||||||
|
&self.0.pdf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of pages in the PDF.
|
||||||
|
pub fn num_pages(&self) -> usize {
|
||||||
|
self.0.pdf.pages().len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for DocumentRepr {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.data.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A specific page of a PDF acting as an image.
|
||||||
|
#[derive(Clone, Hash)]
|
||||||
|
pub struct PdfImage(Arc<ImageRepr>);
|
||||||
|
|
||||||
|
/// The internal representation of a `PdfImage`.
|
||||||
|
struct ImageRepr {
|
||||||
|
document: PdfDocument,
|
||||||
|
page_index: usize,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PdfImage {
|
||||||
|
/// Creates a new PDF image.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the page index is not valid.
|
||||||
|
#[comemo::memoize]
|
||||||
|
pub fn new(document: PdfDocument, page_index: usize) -> Option<PdfImage> {
|
||||||
|
let (width, height) = document.0.pdf.pages().get(page_index)?.render_dimensions();
|
||||||
|
Some(Self(Arc::new(ImageRepr { document, page_index, width, height })))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the underlying Typst PDF document.
|
||||||
|
pub fn document(&self) -> &PdfDocument {
|
||||||
|
&self.0.document
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the PDF page of the image.
|
||||||
|
pub fn page(&self) -> &Page {
|
||||||
|
&self.document().pdf().pages()[self.0.page_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the width of the image.
|
||||||
|
pub fn width(&self) -> f32 {
|
||||||
|
self.0.width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the height of the image.
|
||||||
|
pub fn height(&self) -> f32 {
|
||||||
|
self.0.height
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the page index of the image.
|
||||||
|
pub fn page_index(&self) -> usize {
|
||||||
|
self.0.page_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for ImageRepr {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.document.hash(state);
|
||||||
|
self.page_index.hash(state);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ use krilla::embed::EmbedError;
|
|||||||
use krilla::error::KrillaError;
|
use krilla::error::KrillaError;
|
||||||
use krilla::geom::PathBuilder;
|
use krilla::geom::PathBuilder;
|
||||||
use krilla::page::{PageLabel, PageSettings};
|
use krilla::page::{PageLabel, PageSettings};
|
||||||
|
use krilla::pdf::PdfError;
|
||||||
use krilla::surface::Surface;
|
use krilla::surface::Surface;
|
||||||
use krilla::{Document, SerializeSettings};
|
use krilla::{Document, SerializeSettings};
|
||||||
use krilla_svg::render_svg_glyph;
|
use krilla_svg::render_svg_glyph;
|
||||||
@ -363,6 +364,42 @@ fn finish(
|
|||||||
hint: "convert the image to 8 bit instead"
|
hint: "convert the image to 8 bit instead"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
KrillaError::Pdf(_, e, loc) => {
|
||||||
|
let span = to_span(loc);
|
||||||
|
match e {
|
||||||
|
// We already validated in `typst-library` that the page index is valid.
|
||||||
|
PdfError::InvalidPage(_) => bail!(
|
||||||
|
span,
|
||||||
|
"invalid page number for PDF file";
|
||||||
|
hint: "please report this as a bug"
|
||||||
|
),
|
||||||
|
PdfError::VersionMismatch(v) => {
|
||||||
|
let pdf_ver = v.as_str();
|
||||||
|
let config_ver = configuration.version();
|
||||||
|
let cur_ver = config_ver.as_str();
|
||||||
|
bail!(span,
|
||||||
|
"the version of the PDF is too high";
|
||||||
|
hint: "the current export target is {cur_ver}, while the PDF has version {pdf_ver}";
|
||||||
|
hint: "raise the export target to {pdf_ver} or higher";
|
||||||
|
hint: "or preprocess the PDF to convert it to a lower version"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KrillaError::DuplicateTagId(_, loc) => {
|
||||||
|
let span = to_span(loc);
|
||||||
|
bail!(span,
|
||||||
|
"duplicate tag id";
|
||||||
|
hint: "please report this as a bug"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
KrillaError::UnknownTagId(_, loc) => {
|
||||||
|
let span = to_span(loc);
|
||||||
|
bail!(span,
|
||||||
|
"unknown tag id";
|
||||||
|
hint: "please report this as a bug"
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,12 +572,12 @@ fn convert_error(
|
|||||||
}
|
}
|
||||||
// The below errors cannot occur yet, only once Typst supports full PDF/A
|
// The below errors cannot occur yet, only once Typst supports full PDF/A
|
||||||
// and PDF/UA. But let's still add a message just to be on the safe side.
|
// and PDF/UA. But let's still add a message just to be on the safe side.
|
||||||
ValidationError::MissingAnnotationAltText => error!(
|
ValidationError::MissingAnnotationAltText(_) => error!(
|
||||||
Span::detached(),
|
Span::detached(),
|
||||||
"{prefix} missing annotation alt text";
|
"{prefix} missing annotation alt text";
|
||||||
hint: "please report this as a bug"
|
hint: "please report this as a bug"
|
||||||
),
|
),
|
||||||
ValidationError::MissingAltText => error!(
|
ValidationError::MissingAltText(_) => error!(
|
||||||
Span::detached(),
|
Span::detached(),
|
||||||
"{prefix} missing alt text";
|
"{prefix} missing alt text";
|
||||||
hint: "make sure your images and equations have alt text"
|
hint: "make sure your images and equations have alt text"
|
||||||
@ -576,6 +613,13 @@ fn convert_error(
|
|||||||
"{prefix} missing document date";
|
"{prefix} missing document date";
|
||||||
hint: "set the date of the document"
|
hint: "set the date of the document"
|
||||||
),
|
),
|
||||||
|
ValidationError::EmbeddedPDF(loc) => {
|
||||||
|
error!(
|
||||||
|
to_span(*loc),
|
||||||
|
"embedding PDFs is currently not supported in this export mode";
|
||||||
|
hint: "try converting the PDF to an SVG before embedding it"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,14 @@ use std::sync::{Arc, OnceLock};
|
|||||||
|
|
||||||
use image::{DynamicImage, EncodableLayout, GenericImageView, Rgba};
|
use image::{DynamicImage, EncodableLayout, GenericImageView, Rgba};
|
||||||
use krilla::image::{BitsPerComponent, CustomImage, ImageColorspace};
|
use krilla::image::{BitsPerComponent, CustomImage, ImageColorspace};
|
||||||
|
use krilla::pdf::PdfDocument;
|
||||||
use krilla::surface::Surface;
|
use krilla::surface::Surface;
|
||||||
use krilla_svg::{SurfaceExt, SvgSettings};
|
use krilla_svg::{SurfaceExt, SvgSettings};
|
||||||
use typst_library::diag::{SourceResult, bail};
|
use typst_library::diag::{SourceResult, bail};
|
||||||
use typst_library::foundations::Smart;
|
use typst_library::foundations::Smart;
|
||||||
use typst_library::layout::{Abs, Angle, Ratio, Size, Transform};
|
use typst_library::layout::{Abs, Angle, Ratio, Size, Transform};
|
||||||
use typst_library::visualize::{
|
use typst_library::visualize::{
|
||||||
ExchangeFormat, Image, ImageKind, ImageScaling, RasterFormat, RasterImage,
|
ExchangeFormat, Image, ImageKind, ImageScaling, PdfImage, RasterFormat, RasterImage,
|
||||||
};
|
};
|
||||||
use typst_syntax::Span;
|
use typst_syntax::Span;
|
||||||
|
|
||||||
@ -60,6 +61,9 @@ pub(crate) fn handle_image(
|
|||||||
SvgSettings { embed_text: true, ..Default::default() },
|
SvgSettings { embed_text: true, ..Default::default() },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
ImageKind::Pdf(pdf) => {
|
||||||
|
surface.draw_pdf_page(&convert_pdf(pdf), size.to_krilla(), pdf.page_index())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if image.alt().is_some() {
|
if image.alt().is_some() {
|
||||||
@ -85,9 +89,9 @@ struct Repr {
|
|||||||
|
|
||||||
/// A wrapper around `RasterImage` so that we can implement `CustomImage`.
|
/// A wrapper around `RasterImage` so that we can implement `CustomImage`.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct PdfImage(Arc<Repr>);
|
struct PdfRasterImage(Arc<Repr>);
|
||||||
|
|
||||||
impl PdfImage {
|
impl PdfRasterImage {
|
||||||
pub fn new(raster: RasterImage) -> Self {
|
pub fn new(raster: RasterImage) -> Self {
|
||||||
Self(Arc::new(Repr {
|
Self(Arc::new(Repr {
|
||||||
raster,
|
raster,
|
||||||
@ -97,7 +101,7 @@ impl PdfImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for PdfImage {
|
impl Hash for PdfRasterImage {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
// `alpha_channel` and `actual_dynamic` are generated from the underlying `RasterImage`,
|
// `alpha_channel` and `actual_dynamic` are generated from the underlying `RasterImage`,
|
||||||
// so this is enough. Since `raster` is prehashed, this is also very cheap.
|
// so this is enough. Since `raster` is prehashed, this is also very cheap.
|
||||||
@ -105,7 +109,7 @@ impl Hash for PdfImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomImage for PdfImage {
|
impl CustomImage for PdfRasterImage {
|
||||||
fn color_channel(&self) -> &[u8] {
|
fn color_channel(&self) -> &[u8] {
|
||||||
self.0
|
self.0
|
||||||
.actual_dynamic
|
.actual_dynamic
|
||||||
@ -196,10 +200,15 @@ fn convert_raster(
|
|||||||
interpolate,
|
interpolate,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
krilla::image::Image::from_custom(PdfImage::new(raster), interpolate)
|
krilla::image::Image::from_custom(PdfRasterImage::new(raster), interpolate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[comemo::memoize]
|
||||||
|
fn convert_pdf(pdf: &PdfImage) -> PdfDocument {
|
||||||
|
PdfDocument::new(pdf.document().pdf().clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn exif_transform(image: &RasterImage, size: Size) -> (Transform, Size) {
|
fn exif_transform(image: &RasterImage, size: Size) -> (Transform, Size) {
|
||||||
let base = |hp: bool, vp: bool, mut base_ts: Transform, size: Size| {
|
let base = |hp: bool, vp: bool, mut base_ts: Transform, size: Size| {
|
||||||
if hp {
|
if hp {
|
||||||
|
@ -49,7 +49,6 @@ pub(crate) fn handle_link(
|
|||||||
fc.push_annotation(
|
fc.push_annotation(
|
||||||
LinkAnnotation::new(
|
LinkAnnotation::new(
|
||||||
rect,
|
rect,
|
||||||
None,
|
|
||||||
Target::Action(Action::Link(LinkAction::new(u.to_string()))),
|
Target::Action(Action::Link(LinkAction::new(u.to_string()))),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -64,7 +63,6 @@ pub(crate) fn handle_link(
|
|||||||
fc.push_annotation(
|
fc.push_annotation(
|
||||||
LinkAnnotation::new(
|
LinkAnnotation::new(
|
||||||
rect,
|
rect,
|
||||||
None,
|
|
||||||
Target::Destination(krilla::destination::Destination::Named(
|
Target::Destination(krilla::destination::Destination::Named(
|
||||||
nd.clone(),
|
nd.clone(),
|
||||||
)),
|
)),
|
||||||
@ -83,7 +81,6 @@ pub(crate) fn handle_link(
|
|||||||
fc.push_annotation(
|
fc.push_annotation(
|
||||||
LinkAnnotation::new(
|
LinkAnnotation::new(
|
||||||
rect,
|
rect,
|
||||||
None,
|
|
||||||
Target::Destination(krilla::destination::Destination::Xyz(
|
Target::Destination(krilla::destination::Destination::Xyz(
|
||||||
XyzDestination::new(index, pos.point.to_krilla()),
|
XyzDestination::new(index, pos.point.to_krilla()),
|
||||||
)),
|
)),
|
||||||
|
@ -13,11 +13,13 @@ keywords = { workspace = true }
|
|||||||
readme = { workspace = true }
|
readme = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
typst-assets = { workspace = true }
|
||||||
typst-library = { workspace = true }
|
typst-library = { workspace = true }
|
||||||
typst-macros = { workspace = true }
|
typst-macros = { workspace = true }
|
||||||
typst-timing = { workspace = true }
|
typst-timing = { workspace = true }
|
||||||
bytemuck = { workspace = true }
|
bytemuck = { workspace = true }
|
||||||
comemo = { workspace = true }
|
comemo = { workspace = true }
|
||||||
|
hayro = { workspace = true }
|
||||||
image = { workspace = true }
|
image = { workspace = true }
|
||||||
pixglyph = { workspace = true }
|
pixglyph = { workspace = true }
|
||||||
resvg = { workspace = true }
|
resvg = { workspace = true }
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use std::sync::Arc;
|
use hayro::{FontData, FontQuery, InterpreterSettings, RenderSettings, StandardFont};
|
||||||
|
|
||||||
use image::imageops::FilterType;
|
use image::imageops::FilterType;
|
||||||
use image::{GenericImageView, Rgba};
|
use image::{GenericImageView, Rgba};
|
||||||
|
use std::sync::Arc;
|
||||||
use tiny_skia as sk;
|
use tiny_skia as sk;
|
||||||
|
use tiny_skia::IntSize;
|
||||||
use typst_library::foundations::Smart;
|
use typst_library::foundations::Smart;
|
||||||
use typst_library::layout::Size;
|
use typst_library::layout::Size;
|
||||||
use typst_library::visualize::{Image, ImageKind, ImageScaling};
|
use typst_library::visualize::{Image, ImageKind, ImageScaling, PdfImage};
|
||||||
|
|
||||||
use crate::{AbsExt, State};
|
use crate::{AbsExt, State};
|
||||||
|
|
||||||
@ -59,9 +60,9 @@ pub fn render_image(
|
|||||||
/// Prepare a texture for an image at a scaled size.
|
/// Prepare a texture for an image at a scaled size.
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
fn build_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
|
fn build_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
|
||||||
let mut texture = sk::Pixmap::new(w, h)?;
|
let texture = match image.kind() {
|
||||||
match image.kind() {
|
|
||||||
ImageKind::Raster(raster) => {
|
ImageKind::Raster(raster) => {
|
||||||
|
let mut texture = sk::Pixmap::new(w, h)?;
|
||||||
let w = texture.width();
|
let w = texture.width();
|
||||||
let h = texture.height();
|
let h = texture.height();
|
||||||
|
|
||||||
@ -85,15 +86,63 @@ fn build_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
|
|||||||
let Rgba([r, g, b, a]) = src;
|
let Rgba([r, g, b, a]) = src;
|
||||||
*dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply();
|
*dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
texture
|
||||||
}
|
}
|
||||||
ImageKind::Svg(svg) => {
|
ImageKind::Svg(svg) => {
|
||||||
|
let mut texture = sk::Pixmap::new(w, h)?;
|
||||||
let tree = svg.tree();
|
let tree = svg.tree();
|
||||||
let ts = tiny_skia::Transform::from_scale(
|
let ts = tiny_skia::Transform::from_scale(
|
||||||
w as f32 / tree.size().width(),
|
w as f32 / tree.size().width(),
|
||||||
h as f32 / tree.size().height(),
|
h as f32 / tree.size().height(),
|
||||||
);
|
);
|
||||||
resvg::render(tree, ts, &mut texture.as_mut());
|
resvg::render(tree, ts, &mut texture.as_mut());
|
||||||
|
texture
|
||||||
}
|
}
|
||||||
}
|
ImageKind::Pdf(pdf) => build_pdf_texture(pdf, w, h)?,
|
||||||
|
};
|
||||||
|
|
||||||
Some(Arc::new(texture))
|
Some(Arc::new(texture))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep this in sync with `typst-svg`!
|
||||||
|
fn build_pdf_texture(pdf: &PdfImage, w: u32, h: u32) -> Option<sk::Pixmap> {
|
||||||
|
let select_standard_font = move |font: StandardFont| -> Option<(FontData, u32)> {
|
||||||
|
let bytes = match font {
|
||||||
|
StandardFont::Helvetica => typst_assets::pdf::SANS,
|
||||||
|
StandardFont::HelveticaBold => typst_assets::pdf::SANS_BOLD,
|
||||||
|
StandardFont::HelveticaOblique => typst_assets::pdf::SANS_ITALIC,
|
||||||
|
StandardFont::HelveticaBoldOblique => typst_assets::pdf::SANS_BOLD_ITALIC,
|
||||||
|
StandardFont::Courier => typst_assets::pdf::FIXED,
|
||||||
|
StandardFont::CourierBold => typst_assets::pdf::FIXED_BOLD,
|
||||||
|
StandardFont::CourierOblique => typst_assets::pdf::FIXED_ITALIC,
|
||||||
|
StandardFont::CourierBoldOblique => typst_assets::pdf::FIXED_BOLD_ITALIC,
|
||||||
|
StandardFont::TimesRoman => typst_assets::pdf::SERIF,
|
||||||
|
StandardFont::TimesBold => typst_assets::pdf::SERIF_BOLD,
|
||||||
|
StandardFont::TimesItalic => typst_assets::pdf::SERIF_ITALIC,
|
||||||
|
StandardFont::TimesBoldItalic => typst_assets::pdf::SERIF_BOLD_ITALIC,
|
||||||
|
StandardFont::ZapfDingBats => typst_assets::pdf::DING_BATS,
|
||||||
|
StandardFont::Symbol => typst_assets::pdf::SYMBOL,
|
||||||
|
};
|
||||||
|
Some((Arc::new(bytes), 0))
|
||||||
|
};
|
||||||
|
|
||||||
|
let interpreter_settings = InterpreterSettings {
|
||||||
|
font_resolver: Arc::new(move |query| match query {
|
||||||
|
FontQuery::Standard(s) => select_standard_font(*s),
|
||||||
|
FontQuery::Fallback(f) => select_standard_font(f.pick_standard_font()),
|
||||||
|
}),
|
||||||
|
warning_sink: Arc::new(|_| {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let render_settings = RenderSettings {
|
||||||
|
x_scale: w as f32 / pdf.width(),
|
||||||
|
y_scale: h as f32 / pdf.height(),
|
||||||
|
width: Some(w as u16),
|
||||||
|
height: Some(h as u16),
|
||||||
|
};
|
||||||
|
|
||||||
|
let hayro_pix = hayro::render(pdf.page(), &interpreter_settings, &render_settings);
|
||||||
|
|
||||||
|
sk::Pixmap::from_vec(hayro_pix.take_u8(), IntSize::from_wh(w, h)?)
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ keywords = { workspace = true }
|
|||||||
readme = { workspace = true }
|
readme = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
typst-assets = { workspace = true }
|
||||||
typst-library = { workspace = true }
|
typst-library = { workspace = true }
|
||||||
typst-macros = { workspace = true }
|
typst-macros = { workspace = true }
|
||||||
typst-timing = { workspace = true }
|
typst-timing = { workspace = true }
|
||||||
@ -21,6 +22,7 @@ base64 = { workspace = true }
|
|||||||
comemo = { workspace = true }
|
comemo = { workspace = true }
|
||||||
ecow = { workspace = true }
|
ecow = { workspace = true }
|
||||||
flate2 = { workspace = true }
|
flate2 = { workspace = true }
|
||||||
|
hayro = { workspace = true }
|
||||||
image = { workspace = true }
|
image = { workspace = true }
|
||||||
ttf-parser = { workspace = true }
|
ttf-parser = { workspace = true }
|
||||||
xmlparser = { workspace = true }
|
xmlparser = { workspace = true }
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use ecow::{EcoString, eco_format};
|
use ecow::{EcoString, eco_format};
|
||||||
|
use hayro::{FontData, FontQuery, InterpreterSettings, RenderSettings, StandardFont};
|
||||||
use image::{ImageEncoder, codecs::png::PngEncoder};
|
use image::{ImageEncoder, codecs::png::PngEncoder};
|
||||||
use typst_library::foundations::Smart;
|
use typst_library::foundations::Smart;
|
||||||
use typst_library::layout::{Abs, Axes};
|
use typst_library::layout::{Abs, Axes};
|
||||||
use typst_library::visualize::{
|
use typst_library::visualize::{
|
||||||
ExchangeFormat, Image, ImageKind, ImageScaling, RasterFormat,
|
ExchangeFormat, Image, ImageKind, ImageScaling, PdfImage, RasterFormat,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::SVGRenderer;
|
use crate::SVGRenderer;
|
||||||
@ -66,6 +69,25 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
ImageKind::Svg(svg) => ("svg+xml", svg.data()),
|
ImageKind::Svg(svg) => ("svg+xml", svg.data()),
|
||||||
|
ImageKind::Pdf(pdf) => {
|
||||||
|
// To make sure the image isn't pixelated, we always scale up so the
|
||||||
|
// lowest dimension has at least 1000 pixels. However, we only scale
|
||||||
|
// up as much so that the largest dimension doesn't exceed 3000
|
||||||
|
// pixels.
|
||||||
|
const MIN_RES: f32 = 1000.0;
|
||||||
|
const MAX_RES: f32 = 3000.0;
|
||||||
|
|
||||||
|
let base_width = pdf.width();
|
||||||
|
let w_scale = (MIN_RES / base_width).max(MAX_RES / base_width);
|
||||||
|
let base_height = pdf.height();
|
||||||
|
let h_scale = (MIN_RES / base_height).min(MAX_RES / base_height);
|
||||||
|
let total_scale = w_scale.min(h_scale);
|
||||||
|
let width = (base_width * total_scale).ceil() as u32;
|
||||||
|
let height = (base_height * total_scale).ceil() as u32;
|
||||||
|
|
||||||
|
buf = pdf_to_png(pdf, width, height);
|
||||||
|
("png", buf.as_slice())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut url = eco_format!("data:image/{format};base64,");
|
let mut url = eco_format!("data:image/{format};base64,");
|
||||||
@ -73,3 +95,45 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
|||||||
url.push_str(&data);
|
url.push_str(&data);
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep this in sync with `typst-png`!
|
||||||
|
fn pdf_to_png(pdf: &PdfImage, w: u32, h: u32) -> Vec<u8> {
|
||||||
|
let select_standard_font = move |font: StandardFont| -> Option<(FontData, u32)> {
|
||||||
|
let bytes = match font {
|
||||||
|
StandardFont::Helvetica => typst_assets::pdf::SANS,
|
||||||
|
StandardFont::HelveticaBold => typst_assets::pdf::SANS_BOLD,
|
||||||
|
StandardFont::HelveticaOblique => typst_assets::pdf::SANS_ITALIC,
|
||||||
|
StandardFont::HelveticaBoldOblique => typst_assets::pdf::SANS_BOLD_ITALIC,
|
||||||
|
StandardFont::Courier => typst_assets::pdf::FIXED,
|
||||||
|
StandardFont::CourierBold => typst_assets::pdf::FIXED_BOLD,
|
||||||
|
StandardFont::CourierOblique => typst_assets::pdf::FIXED_ITALIC,
|
||||||
|
StandardFont::CourierBoldOblique => typst_assets::pdf::FIXED_BOLD_ITALIC,
|
||||||
|
StandardFont::TimesRoman => typst_assets::pdf::SERIF,
|
||||||
|
StandardFont::TimesBold => typst_assets::pdf::SERIF_BOLD,
|
||||||
|
StandardFont::TimesItalic => typst_assets::pdf::SERIF_ITALIC,
|
||||||
|
StandardFont::TimesBoldItalic => typst_assets::pdf::SERIF_BOLD_ITALIC,
|
||||||
|
StandardFont::ZapfDingBats => typst_assets::pdf::DING_BATS,
|
||||||
|
StandardFont::Symbol => typst_assets::pdf::SYMBOL,
|
||||||
|
};
|
||||||
|
Some((Arc::new(bytes), 0))
|
||||||
|
};
|
||||||
|
|
||||||
|
let interpreter_settings = InterpreterSettings {
|
||||||
|
font_resolver: Arc::new(move |query| match query {
|
||||||
|
FontQuery::Standard(s) => select_standard_font(*s),
|
||||||
|
FontQuery::Fallback(f) => select_standard_font(f.pick_standard_font()),
|
||||||
|
}),
|
||||||
|
warning_sink: Arc::new(|_| {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let render_settings = RenderSettings {
|
||||||
|
x_scale: w as f32 / pdf.width(),
|
||||||
|
y_scale: h as f32 / pdf.height(),
|
||||||
|
width: Some(w as u16),
|
||||||
|
height: Some(h as u16),
|
||||||
|
};
|
||||||
|
|
||||||
|
let hayro_pix = hayro::render(pdf.page(), &interpreter_settings, &render_settings);
|
||||||
|
|
||||||
|
hayro_pix.take_png()
|
||||||
|
}
|
||||||
|
@ -665,12 +665,3 @@ applicable, contains possible workarounds.
|
|||||||
[`page` function]($page) which will force a page break. If you just want a few
|
[`page` function]($page) which will force a page break. If you just want a few
|
||||||
paragraphs to stretch into the margins, then reverting to the old margins, you
|
paragraphs to stretch into the margins, then reverting to the old margins, you
|
||||||
can use the [`pad` function]($pad) with negative padding.
|
can use the [`pad` function]($pad) with negative padding.
|
||||||
|
|
||||||
- **Include PDFs as images.** In LaTeX, it has become customary to insert vector
|
|
||||||
graphics as PDF or EPS files. Typst supports neither format as an image
|
|
||||||
format, but you can easily convert both into SVG files with [online
|
|
||||||
tools](https://cloudconvert.com/pdf-to-svg) or
|
|
||||||
[Inkscape](https://inkscape.org/). The web app will automatically convert PDF
|
|
||||||
files to SVG files upon uploading them. You can also use the
|
|
||||||
community-provided [`muchpdf` package](https://typst.app/universe/package/muchpdf)
|
|
||||||
to embed PDFs. It internally converts PDFs to SVGs on-the-fly.
|
|
||||||
|
@ -84,8 +84,9 @@ meaning in Typst. We can use `=`, `-`, `+`, and `_` to create headings, lists
|
|||||||
and emphasized text, respectively. However, having a special symbol for
|
and emphasized text, respectively. However, having a special symbol for
|
||||||
everything we want to insert into our document would soon become cryptic and
|
everything we want to insert into our document would soon become cryptic and
|
||||||
unwieldy. For this reason, Typst reserves markup symbols only for the most
|
unwieldy. For this reason, Typst reserves markup symbols only for the most
|
||||||
common things. Everything else is inserted with _functions._ For our image to
|
common things. Everything else is inserted with _functions._ For
|
||||||
show up on the page, we use Typst's [`image`] function.
|
[our image](https://github.com/typst/typst-dev-assets/blob/main/files/images/glacier.jpg)
|
||||||
|
to show up on the page, we use Typst's [`image`] function.
|
||||||
|
|
||||||
```example
|
```example
|
||||||
#image("glacier.jpg")
|
#image("glacier.jpg")
|
||||||
@ -125,19 +126,38 @@ mode. This means, you now have to remove the hash before the image function call
|
|||||||
The hash is only needed directly in markup (to disambiguate text from function
|
The hash is only needed directly in markup (to disambiguate text from function
|
||||||
calls).
|
calls).
|
||||||
|
|
||||||
The caption consists of arbitrary markup. To give markup to a function, we
|
The caption consists of arbitrary markup, and can also be a string. To give
|
||||||
enclose it in square brackets. This construct is called a _content block._
|
markup to a function, we enclose it in square brackets. This construct is called
|
||||||
|
a _content block._
|
||||||
|
|
||||||
```example
|
```example
|
||||||
#figure(
|
#figure(
|
||||||
image("glacier.jpg", width: 70%),
|
image("glacier.jpg", width: 70%),
|
||||||
caption: [
|
caption: box[
|
||||||
_Glaciers_ form an important part
|
_Glaciers_ form an important part
|
||||||
of the earth's climate system.
|
of the earth's climate system.
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Be careful** about putting the square brackets by themselves on separate
|
||||||
|
lines. This will introduce leading and trailing space around inline text inside
|
||||||
|
the brackets, that is hard to notice. Below are several caption examples: one
|
||||||
|
with extra undesired space, and 3 correct ones.
|
||||||
|
|
||||||
|
```example
|
||||||
|
#show rect: none
|
||||||
|
#figure(rect(), caption: [
|
||||||
|
Caption text
|
||||||
|
])
|
||||||
|
#figure(rect(), caption: box[
|
||||||
|
Caption text
|
||||||
|
])
|
||||||
|
#figure(rect(), caption: [Caption
|
||||||
|
text]) // Many spaces in markup counts as one.
|
||||||
|
#figure(rect(), caption: "Caption text") // Spaces in strings are displayed verbatim.
|
||||||
|
```
|
||||||
|
|
||||||
You continue to write your report and now want to reference the figure. To do
|
You continue to write your report and now want to reference the figure. To do
|
||||||
that, first attach a label to figure. A label uniquely identifies an element in
|
that, first attach a label to figure. A label uniquely identifies an element in
|
||||||
your document. Add one after the figure by enclosing some name in angle
|
your document. Add one after the figure by enclosing some name in angle
|
||||||
|
@ -110,8 +110,8 @@ font. For the purposes of the example, we'll also set another page size.
|
|||||||
margin: (x: 1.8cm, y: 1.5cm),
|
margin: (x: 1.8cm, y: 1.5cm),
|
||||||
)
|
)
|
||||||
#set text(
|
#set text(
|
||||||
|
size: 10pt,
|
||||||
font: "New Computer Modern",
|
font: "New Computer Modern",
|
||||||
size: 10pt
|
|
||||||
)
|
)
|
||||||
#set par(
|
#set par(
|
||||||
justify: true,
|
justify: true,
|
||||||
@ -235,19 +235,27 @@ Instead, you could maybe
|
|||||||
[define a custom function]($function/#defining-functions) that always yields the
|
[define a custom function]($function/#defining-functions) that always yields the
|
||||||
logo with its image. However, there is an even easier way:
|
logo with its image. However, there is an even easier way:
|
||||||
|
|
||||||
With show rules, you can redefine how Typst displays certain elements. You
|
With show rules, you can redefine how Typst displays certain elements. You can
|
||||||
specify which elements Typst should show differently and how they should look.
|
specify which elements Typst should show differently and how they should look.
|
||||||
Show rules can be applied to instances of text, many functions, and even the
|
Show rules can be applied to instances of text, many functions, and even the
|
||||||
whole document.
|
whole document.
|
||||||
|
|
||||||
```example
|
```example
|
||||||
#show "ArtosFlow": name => box[
|
// #show "ArtosFlow": name => {
|
||||||
#box(image(
|
// let logo = box(image(
|
||||||
|
// "logo.svg",
|
||||||
|
// height: 0.7em,
|
||||||
|
// ))
|
||||||
|
// [#logo #name]
|
||||||
|
// }
|
||||||
|
#show "ArtosFlow": name => {
|
||||||
|
box(image(
|
||||||
"logo.svg",
|
"logo.svg",
|
||||||
height: 0.7em,
|
height: 0.7em,
|
||||||
))
|
))
|
||||||
#name
|
" "
|
||||||
]
|
name
|
||||||
|
}
|
||||||
|
|
||||||
This report is embedded in the
|
This report is embedded in the
|
||||||
ArtosFlow project. ArtosFlow is a
|
ArtosFlow project. ArtosFlow is a
|
||||||
@ -256,18 +264,18 @@ project of the Artos Institute.
|
|||||||
|
|
||||||
There is a lot of new syntax in this example: We write the `{show}` keyword,
|
There is a lot of new syntax in this example: We write the `{show}` keyword,
|
||||||
followed by a string of text we want to show differently and a colon. Then, we
|
followed by a string of text we want to show differently and a colon. Then, we
|
||||||
write a function that takes the content that shall be shown as an argument.
|
write a function that takes the content as an argument that shall be shown.
|
||||||
Here, we called that argument `name`. We can now use the `name` variable in the
|
Here, we called that argument `name`. We can now use the `name` variable in
|
||||||
function's body to print the ArtosFlow name. Our show rule adds the logo image
|
the function's body to display the ArtosFlow name. Our show rule adds the logo
|
||||||
in front of the name and puts the result into a box to prevent linebreaks from
|
image to the left of the name and inserts a single space between the two.
|
||||||
occurring between logo and name. The image is also put inside of a box, so that
|
The image is put inside of a `box`, so that it does not appear in its own
|
||||||
it does not appear in its own paragraph.
|
paragraph, because `image` is a block-level element.
|
||||||
|
|
||||||
The calls to the first box function and the image function did not require a
|
<!-- The calls to the first box function and the image function did not require a -->
|
||||||
leading `#` because they were not embedded directly in markup. When Typst
|
<!-- leading `#` because they were not embedded directly in markup. When Typst -->
|
||||||
expects code instead of markup, the leading `#` is not needed to access
|
<!-- expects code instead of markup, the leading `#` is not needed to access -->
|
||||||
functions, keywords, and variables. This can be observed in parameter lists,
|
<!-- functions, keywords, and variables. This can be observed in parameter lists, -->
|
||||||
function definitions, and [code blocks]($scripting).
|
<!-- function definitions, and [code blocks]($scripting). -->
|
||||||
|
|
||||||
## Review
|
## Review
|
||||||
You now know how to apply basic formatting to your Typst documents. You learned
|
You now know how to apply basic formatting to your Typst documents. You learned
|
||||||
|
@ -6,7 +6,7 @@ description: Typst's tutorial.
|
|||||||
In the previous two chapters of this tutorial, you have learned how to write a
|
In the previous two chapters of this tutorial, you have learned how to write a
|
||||||
document in Typst and how to change its formatting. The report you wrote
|
document in Typst and how to change its formatting. The report you wrote
|
||||||
throughout the last two chapters got a straight A and your supervisor wants to
|
throughout the last two chapters got a straight A and your supervisor wants to
|
||||||
base a conference paper on it! The report will of course have to comply with the
|
base a conference paper on it! The paper will of course have to comply with the
|
||||||
conference's style guide. Let's see how we can achieve that.
|
conference's style guide. Let's see how we can achieve that.
|
||||||
|
|
||||||
Before we start, let's create a team, invite your supervisor and add them to the
|
Before we start, let's create a team, invite your supervisor and add them to the
|
||||||
@ -30,12 +30,12 @@ to find other users and try teams with them!
|
|||||||
The layout guidelines are available on the conference website. Let's take a look
|
The layout guidelines are available on the conference website. Let's take a look
|
||||||
at them:
|
at them:
|
||||||
|
|
||||||
- The font should be an 11pt serif font
|
- The font should be an 11 pt serif font
|
||||||
- The title should be in 17pt and bold
|
- The title should be in 17 pt and bold
|
||||||
- The paper contains a single-column abstract and two-column main text
|
- The paper contains a single-column abstract and two-column main text
|
||||||
- The abstract should be centered
|
- The abstract should be centered
|
||||||
- The main text should be justified
|
- The main text should be justified
|
||||||
- First level section headings should be 13pt, centered, and rendered in small
|
- First level section headings should be 13 pt, centered, and rendered in small
|
||||||
capitals
|
capitals
|
||||||
- Second level headings are run-ins, italicized and have the same size as the
|
- Second level headings are run-ins, italicized and have the same size as the
|
||||||
body text
|
body text
|
||||||
@ -51,7 +51,6 @@ Let's start by writing some set rules for the document.
|
|||||||
|
|
||||||
```example
|
```example
|
||||||
#set page(
|
#set page(
|
||||||
>>> margin: auto,
|
|
||||||
paper: "us-letter",
|
paper: "us-letter",
|
||||||
header: align(right)[
|
header: align(right)[
|
||||||
A fluid dynamic model for
|
A fluid dynamic model for
|
||||||
@ -61,8 +60,8 @@ Let's start by writing some set rules for the document.
|
|||||||
)
|
)
|
||||||
#set par(justify: true)
|
#set par(justify: true)
|
||||||
#set text(
|
#set text(
|
||||||
font: "Libertinus Serif",
|
|
||||||
size: 11pt,
|
size: 11pt,
|
||||||
|
font: "Libertinus Serif",
|
||||||
)
|
)
|
||||||
|
|
||||||
#lorem(600)
|
#lorem(600)
|
||||||
@ -72,12 +71,12 @@ You are already familiar with most of what is going on here. We set the text
|
|||||||
size to `{11pt}` and the font to Libertinus Serif. We also enable paragraph
|
size to `{11pt}` and the font to Libertinus Serif. We also enable paragraph
|
||||||
justification and set the page size to US letter.
|
justification and set the page size to US letter.
|
||||||
|
|
||||||
The `header` argument is new: With it, we can provide content to fill the top
|
The `header` field is new: with it, we can provide content to fill the top
|
||||||
margin of every page. In the header, we specify our paper's title as requested
|
margin of every page. In the header, we specify our paper's title as requested
|
||||||
by the conference style guide. We use the `align` function to align the text to
|
by the conference style guide. We use the `align` function to align the text to
|
||||||
the right.
|
the right.
|
||||||
|
|
||||||
Last but not least is the `numbering` argument. Here, we can provide a
|
Last but not least is the `numbering` field. Here, we can provide a
|
||||||
[numbering pattern]($numbering) that defines how to number the pages. By
|
[numbering pattern]($numbering) that defines how to number the pages. By
|
||||||
setting it to `{"1"}`, Typst only displays the bare page number. Setting it to
|
setting it to `{"1"}`, Typst only displays the bare page number. Setting it to
|
||||||
`{"(1/1)"}` would have displayed the current page and total number of pages
|
`{"(1/1)"}` would have displayed the current page and total number of pages
|
||||||
@ -89,35 +88,65 @@ Now, let's add a title and an abstract. We'll start with the title. We center
|
|||||||
align it and increase its font weight by enclosing it in `[*stars*]`.
|
align it and increase its font weight by enclosing it in `[*stars*]`.
|
||||||
|
|
||||||
```example
|
```example
|
||||||
>>> #set page(width: 300pt, margin: 30pt)
|
>>> #set page(
|
||||||
>>> #set text(font: "Libertinus Serif", 11pt)
|
>>> // paper: "us-letter",
|
||||||
#align(center, text(17pt)[
|
>>> width: 300pt,
|
||||||
|
>>> margin: 30pt,
|
||||||
|
>>> header: align(right)[
|
||||||
|
>>> A fluid dynamic model for
|
||||||
|
>>> glacier flow
|
||||||
|
>>> ],
|
||||||
|
>>> // numbering: "1",
|
||||||
|
>>> )
|
||||||
|
>>> #set par(justify: true)
|
||||||
|
>>> #set text(11pt, font: "Libertinus Serif")
|
||||||
|
<<< ...
|
||||||
|
#align(center, block(text(
|
||||||
|
17pt,
|
||||||
|
hyphenate: false
|
||||||
|
)[
|
||||||
*A fluid dynamic model
|
*A fluid dynamic model
|
||||||
for glacier flow*
|
for glacier flow*
|
||||||
])
|
]))
|
||||||
```
|
```
|
||||||
|
|
||||||
This looks right. We used the `text` function to override the previous text
|
This looks right. We used the `text` function to override the previous text
|
||||||
set rule locally, increasing the size to 17pt for the function's argument. Let's
|
set rule locally, increasing the size to 17 pt.
|
||||||
also add the author list: Since we are writing this paper together with our
|
Add explanation about block+hyphenate, which is pretty convoluted.
|
||||||
supervisor, we'll add our own and their name.
|
Let's also add the author list: Since we are writing this paper together with
|
||||||
|
our supervisor, we'll add our own and their name.
|
||||||
|
|
||||||
```example
|
```example
|
||||||
>>> #set page(width: 300pt, margin: 30pt)
|
>>> #set page(
|
||||||
>>> #set text(font: "Libertinus Serif", 11pt)
|
>>> // paper: "us-letter",
|
||||||
|
>>> width: 300pt,
|
||||||
|
>>> margin: 30pt,
|
||||||
|
>>> header: align(right)[
|
||||||
|
>>> A fluid dynamic model for
|
||||||
|
>>> glacier flow
|
||||||
|
>>> ],
|
||||||
|
>>> // numbering: "1",
|
||||||
|
>>> )
|
||||||
|
>>> #set par(justify: true)
|
||||||
|
>>> #set text(11pt, font: "Libertinus Serif")
|
||||||
>>>
|
>>>
|
||||||
>>> #align(center, text(17pt)[
|
>>> #align(center, block(text(
|
||||||
|
>>> 17pt,
|
||||||
|
>>> hyphenate: false
|
||||||
|
>>> )[
|
||||||
>>> *A fluid dynamic model
|
>>> *A fluid dynamic model
|
||||||
>>> for glacier flow*
|
>>> for glacier flow*
|
||||||
>>> ])
|
>>> ]))
|
||||||
|
<<< ...
|
||||||
#grid(
|
#grid(
|
||||||
columns: (1fr, 1fr),
|
columns: (1fr, 1fr),
|
||||||
align(center)[
|
align: center,
|
||||||
|
[
|
||||||
Therese Tungsten \
|
Therese Tungsten \
|
||||||
Artos Institute \
|
Artos Institute \
|
||||||
#link("mailto:tung@artos.edu")
|
#link("mailto:tung@artos.edu")
|
||||||
],
|
],
|
||||||
align(center)[
|
[
|
||||||
Dr. John Doe \
|
Dr. John Doe \
|
||||||
Artos Institute \
|
Artos Institute \
|
||||||
#link("mailto:doe@artos.edu")
|
#link("mailto:doe@artos.edu")
|
||||||
@ -127,45 +156,49 @@ supervisor, we'll add our own and their name.
|
|||||||
|
|
||||||
The two author blocks are laid out next to each other. We use the [`grid`]
|
The two author blocks are laid out next to each other. We use the [`grid`]
|
||||||
function to create this layout. With a grid, we can control exactly how large
|
function to create this layout. With a grid, we can control exactly how large
|
||||||
each column is and which content goes into which cell. The `columns` argument
|
each column is and which content goes into which cell. The `columns` field
|
||||||
takes an array of [relative lengths]($relative) or [fractions]($fraction). In
|
takes the number of columns, or an array of [relative lengths]($relative) or
|
||||||
this case, we passed it two equal fractional sizes, telling it to split the
|
[fractions]($fraction). In this case, we passed it two equal fractional sizes,
|
||||||
available space into two equal columns. We then passed two content arguments to
|
telling it to split the available space into two equal columns. We then passed
|
||||||
the grid function. The first with our own details, and the second with our
|
two content arguments to the grid function --- the first with our own details,
|
||||||
supervisors'. We again use the `align` function to center the content within the
|
and the second with our supervisor's. With grid, we can avoid using `align` on
|
||||||
column. The grid takes an arbitrary number of content arguments specifying the
|
each cell content to center them, and instead use the `align` field to do this
|
||||||
cells. Rows are added automatically, but they can also be manually sized with
|
for all cells automatically. The grid takes an arbitrary number of content
|
||||||
the `rows` argument.
|
arguments specifying the cells. Rows are added automatically, but they can also
|
||||||
|
be manually sized with the `rows` field.
|
||||||
|
|
||||||
Now, let's add the abstract. Remember that the conference wants the abstract to
|
Now, let's add the abstract. Remember that the conference wants the abstract to
|
||||||
be set ragged and centered.
|
be set ragged and centered.
|
||||||
|
|
||||||
```example:0,0,612,317.5
|
```example:0,0,612,317.5
|
||||||
>>> #set text(font: "Libertinus Serif", 11pt)
|
|
||||||
>>> #set par(justify: true)
|
|
||||||
>>> #set page(
|
>>> #set page(
|
||||||
>>> "us-letter",
|
>>> paper: "us-letter",
|
||||||
>>> margin: auto,
|
>>> header: align(right)[
|
||||||
>>> header: align(right + horizon)[
|
|
||||||
>>> A fluid dynamic model for
|
>>> A fluid dynamic model for
|
||||||
>>> glacier flow
|
>>> glacier flow
|
||||||
>>> ],
|
>>> ],
|
||||||
>>> numbering: "1",
|
>>> numbering: "1",
|
||||||
>>> )
|
>>> )
|
||||||
|
>>> #set par(justify: true)
|
||||||
|
>>> #set text(11pt, font: "Libertinus Serif")
|
||||||
>>>
|
>>>
|
||||||
>>> #align(center, text(17pt)[
|
>>> #align(center, block(text(
|
||||||
|
>>> 17pt,
|
||||||
|
>>> hyphenate: false
|
||||||
|
>>> )[
|
||||||
>>> *A fluid dynamic model
|
>>> *A fluid dynamic model
|
||||||
>>> for glacier flow*
|
>>> for glacier flow*
|
||||||
>>> ])
|
>>> ]))
|
||||||
>>>
|
>>>
|
||||||
>>> #grid(
|
>>> #grid(
|
||||||
>>> columns: (1fr, 1fr),
|
>>> columns: (1fr, 1fr),
|
||||||
>>> align(center)[
|
>>> align: center,
|
||||||
|
>>> [
|
||||||
>>> Therese Tungsten \
|
>>> Therese Tungsten \
|
||||||
>>> Artos Institute \
|
>>> Artos Institute \
|
||||||
>>> #link("mailto:tung@artos.edu")
|
>>> #link("mailto:tung@artos.edu")
|
||||||
>>> ],
|
>>> ],
|
||||||
>>> align(center)[
|
>>> [
|
||||||
>>> Dr. John Doe \
|
>>> Dr. John Doe \
|
||||||
>>> Artos Institute \
|
>>> Artos Institute \
|
||||||
>>> #link("mailto:doe@artos.edu")
|
>>> #link("mailto:doe@artos.edu")
|
||||||
@ -198,35 +231,33 @@ keyword:
|
|||||||
for glacier flow
|
for glacier flow
|
||||||
]
|
]
|
||||||
|
|
||||||
<<< ...
|
|
||||||
|
|
||||||
>>> #set text(font: "Libertinus Serif", 11pt)
|
|
||||||
>>> #set par(justify: true)
|
|
||||||
#set page(
|
#set page(
|
||||||
>>> "us-letter",
|
>>> paper: "us-letter",
|
||||||
>>> margin: auto,
|
<<< ...
|
||||||
header: align(
|
header: align(right, title),
|
||||||
right + horizon,
|
|
||||||
title
|
|
||||||
),
|
|
||||||
<<< ...
|
<<< ...
|
||||||
>>> numbering: "1",
|
>>> numbering: "1",
|
||||||
)
|
)
|
||||||
|
>>> #set par(justify: true)
|
||||||
#align(center, text(17pt)[
|
>>> #set text(11pt, font: "Libertinus Serif")
|
||||||
*#title*
|
|
||||||
])
|
|
||||||
|
|
||||||
<<< ...
|
<<< ...
|
||||||
|
|
||||||
|
#align(center, block(text(
|
||||||
|
17pt,
|
||||||
|
hyphenate: false,
|
||||||
|
strong(title),
|
||||||
|
)))
|
||||||
|
|
||||||
|
<<< ...
|
||||||
>>> #grid(
|
>>> #grid(
|
||||||
>>> columns: (1fr, 1fr),
|
>>> columns: (1fr, 1fr),
|
||||||
>>> align(center)[
|
>>> align: center,
|
||||||
|
>>> [
|
||||||
>>> Therese Tungsten \
|
>>> Therese Tungsten \
|
||||||
>>> Artos Institute \
|
>>> Artos Institute \
|
||||||
>>> #link("mailto:tung@artos.edu")
|
>>> #link("mailto:tung@artos.edu")
|
||||||
>>> ],
|
>>> ],
|
||||||
>>> align(center)[
|
>>> [
|
||||||
>>> Dr. John Doe \
|
>>> Dr. John Doe \
|
||||||
>>> Artos Institute \
|
>>> Artos Institute \
|
||||||
>>> #link("mailto:doe@artos.edu")
|
>>> #link("mailto:doe@artos.edu")
|
||||||
@ -247,55 +278,48 @@ and also within markup (prefixed by `#`, like functions). This way, if we decide
|
|||||||
on another title, we can easily change it in one place.
|
on another title, we can easily change it in one place.
|
||||||
|
|
||||||
## Adding columns and headings { #columns-and-headings }
|
## Adding columns and headings { #columns-and-headings }
|
||||||
The paper above unfortunately looks like a wall of lead. To fix that, let's add
|
The paper above unfortunately looks like a wall of lead(?). To fix that, let's add
|
||||||
some headings and switch our paper to a two-column layout. Fortunately, that's
|
some headings and switch our paper to a two-column layout. Fortunately, that's
|
||||||
easy to do: We just need to amend our `page` set rule with the `columns`
|
easy to do: we just need to amend our `page` set rule with the `columns` field.
|
||||||
argument.
|
|
||||||
|
|
||||||
By adding `{columns: 2}` to the argument list, we have wrapped the whole
|
By adding `{columns: 2}` to the argument list, we have wrapped the whole
|
||||||
document in two columns. However, that would also affect the title and authors
|
document in two columns. However, that would also affect the title and authors
|
||||||
overview. To keep them spanning the whole page, we can wrap them in a function
|
overview. To keep them spanning the whole page, we can wrap them in a function
|
||||||
call to [`{place}`]($place). Place expects an alignment and the content it
|
call to [`{place}`]($place). The `place` expects an alignment and the content it
|
||||||
should place as positional arguments. Using the named `{scope}` argument, we can
|
should place as positional arguments. Using the named `scope` field, we can
|
||||||
decide if the items should be placed relative to the current column or its
|
decide if the items should be placed relative to the current column or its
|
||||||
parent (the page). There is one more thing to configure: If no other arguments
|
parent (the page). There is one more thing to configure: If no other arguments
|
||||||
are provided, `{place}` takes its content out of the flow of the document and
|
are provided, `place` takes its content out of the flow of the document and
|
||||||
positions it over the other content without affecting the layout of other
|
positions it over the other content without affecting the layout of other
|
||||||
content in its container:
|
content in its container:
|
||||||
|
|
||||||
```example
|
```example
|
||||||
#place(
|
#place(top + center, rect())
|
||||||
top + center,
|
|
||||||
rect(fill: black),
|
|
||||||
)
|
|
||||||
#lorem(30)
|
#lorem(30)
|
||||||
```
|
```
|
||||||
|
|
||||||
If we hadn't used `{place}` here, the square would be in its own line, but here
|
If we hadn't used `place` here, the rectangle would be in its own line, but
|
||||||
it overlaps the few lines of text following it. Likewise, that text acts like as
|
here it overlaps the few lines of text following it. Likewise, that text acts
|
||||||
if there was no square. To change this behavior, we can pass the argument
|
like as if there was no rectangle. To change this behavior, we can pass the
|
||||||
`{float: true}` to ensure that the space taken up by the placed item at the top
|
argument `{float: true}` to ensure that the space taken up by the placed item
|
||||||
or bottom of the page is not occupied by any other content.
|
at the top or bottom of the page is not occupied by any other content.
|
||||||
|
|
||||||
```example:single
|
```example:single
|
||||||
>>> #let title = [
|
>>> #let title = [
|
||||||
>>> A fluid dynamic model
|
>>> A fluid dynamic model
|
||||||
>>> for glacier flow
|
>>> for glacier flow
|
||||||
>>> ]
|
>>> ]
|
||||||
>>>
|
<<< ...
|
||||||
>>> #set text(font: "Libertinus Serif", 11pt)
|
|
||||||
>>> #set par(justify: true)
|
|
||||||
>>>
|
|
||||||
#set page(
|
#set page(
|
||||||
>>> margin: auto,
|
|
||||||
paper: "us-letter",
|
paper: "us-letter",
|
||||||
header: align(
|
header: align(right, title),
|
||||||
right + horizon,
|
|
||||||
title
|
|
||||||
),
|
|
||||||
numbering: "1",
|
numbering: "1",
|
||||||
columns: 2,
|
columns: 2,
|
||||||
)
|
)
|
||||||
|
>>> #set par(justify: true)
|
||||||
|
>>> #set text(11pt, font: "Libertinus Serif")
|
||||||
|
<<< ...
|
||||||
|
|
||||||
#place(
|
#place(
|
||||||
top + center,
|
top + center,
|
||||||
@ -303,11 +327,7 @@ or bottom of the page is not occupied by any other content.
|
|||||||
scope: "parent",
|
scope: "parent",
|
||||||
clearance: 2em,
|
clearance: 2em,
|
||||||
)[
|
)[
|
||||||
>>> #text(
|
>>> #block(text(17pt, hyphenate: false, strong(title)))
|
||||||
>>> 17pt,
|
|
||||||
>>> weight: "bold",
|
|
||||||
>>> title,
|
|
||||||
>>> )
|
|
||||||
>>>
|
>>>
|
||||||
>>> #grid(
|
>>> #grid(
|
||||||
>>> columns: (1fr, 1fr),
|
>>> columns: (1fr, 1fr),
|
||||||
@ -324,10 +344,9 @@ or bottom of the page is not occupied by any other content.
|
|||||||
>>> )
|
>>> )
|
||||||
<<< ...
|
<<< ...
|
||||||
|
|
||||||
#par(justify: false)[
|
#set par(justify: false) // Put it above to remove hyphenate?
|
||||||
*Abstract* \
|
*Abstract* \
|
||||||
#lorem(80)
|
#lorem(80)
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
= Introduction
|
= Introduction
|
||||||
@ -337,14 +356,14 @@ or bottom of the page is not occupied by any other content.
|
|||||||
#lorem(200)
|
#lorem(200)
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, we also used the `clearance` argument of the `{place}` function
|
In this example, we also used the `clearance` argument of the `place` function
|
||||||
to provide the space between it and the body instead of using the [`{v}`]($v)
|
to provide the space between it and the body instead of using the [`v`]
|
||||||
function. We can also remove the explicit `{align(center, ..)}` calls around the
|
function. We can also remove the explicit `{align(center, ..)}` calls around the
|
||||||
various parts since they inherit the center alignment from the placement.
|
various parts since they inherit the center alignment from the placement.
|
||||||
|
|
||||||
Now there is only one thing left to do: Style our headings. We need to make them
|
Now there is only one thing left to do: style our headings. We need to make them
|
||||||
centered and use small capitals. Because the `heading` function does not offer
|
centered and use small capitals. For centering we can use a show-set rule, but
|
||||||
a way to set any of that, we need to write our own heading show rule.
|
to use small capitals we need to write our own heading show rule.
|
||||||
|
|
||||||
```example:50,250,265,270
|
```example:50,250,265,270
|
||||||
>>> #let title = [
|
>>> #let title = [
|
||||||
@ -352,37 +371,27 @@ a way to set any of that, we need to write our own heading show rule.
|
|||||||
>>> for glacier flow
|
>>> for glacier flow
|
||||||
>>> ]
|
>>> ]
|
||||||
>>>
|
>>>
|
||||||
>>> #set text(font: "Libertinus Serif", 11pt)
|
|
||||||
>>> #set par(justify: true)
|
|
||||||
>>> #set page(
|
>>> #set page(
|
||||||
>>> "us-letter",
|
>>> paper: "us-letter",
|
||||||
>>> margin: auto,
|
>>> header: align(right, title),
|
||||||
>>> header: align(
|
|
||||||
>>> right + horizon,
|
|
||||||
>>> title
|
|
||||||
>>> ),
|
|
||||||
>>> numbering: "1",
|
>>> numbering: "1",
|
||||||
>>> columns: 2,
|
>>> columns: 2,
|
||||||
>>> )
|
>>> )
|
||||||
#show heading: it => [
|
>>> #set par(justify: true)
|
||||||
#set align(center)
|
>>> #set text(11pt, font: "Libertinus Serif")
|
||||||
#set text(13pt, weight: "regular")
|
<<< ...
|
||||||
#block(smallcaps(it.body))
|
#show heading: set align(center)
|
||||||
]
|
#show heading: set text(13pt, weight: "regular")
|
||||||
|
#show heading: it => block(smallcaps(it.body))
|
||||||
|
|
||||||
<<< ...
|
<<< ...
|
||||||
>>>
|
|
||||||
>>> #place(
|
>>> #place(
|
||||||
>>> top + center,
|
>>> top + center,
|
||||||
>>> float: true,
|
>>> float: true,
|
||||||
>>> scope: "parent",
|
>>> scope: "parent",
|
||||||
>>> clearance: 2em,
|
>>> clearance: 2em,
|
||||||
>>> )[
|
>>> )[
|
||||||
>>> #text(
|
>>> #block(text(17pt, hyphenate: false, strong(title)))
|
||||||
>>> 17pt,
|
|
||||||
>>> weight: "bold",
|
|
||||||
>>> title,
|
|
||||||
>>> )
|
|
||||||
>>>
|
>>>
|
||||||
>>> #grid(
|
>>> #grid(
|
||||||
>>> columns: (1fr, 1fr),
|
>>> columns: (1fr, 1fr),
|
||||||
@ -398,10 +407,9 @@ a way to set any of that, we need to write our own heading show rule.
|
|||||||
>>> ]
|
>>> ]
|
||||||
>>> )
|
>>> )
|
||||||
>>>
|
>>>
|
||||||
>>> #par(justify: false)[
|
>>> #set par(justify: false)
|
||||||
>>> *Abstract* \
|
>>> *Abstract* \
|
||||||
>>> #lorem(80)
|
>>> #lorem(80)
|
||||||
>>> ]
|
|
||||||
>>> ]
|
>>> ]
|
||||||
>>>
|
>>>
|
||||||
>>> = Introduction
|
>>> = Introduction
|
||||||
@ -411,19 +419,26 @@ a way to set any of that, we need to write our own heading show rule.
|
|||||||
>>> #lorem(45)
|
>>> #lorem(45)
|
||||||
```
|
```
|
||||||
|
|
||||||
This looks great! We used a show rule that applies to all headings. We give it a
|
This looks great! We used a few rules that apply to all headings. First, we
|
||||||
function that gets passed the heading as a parameter. That parameter can be used
|
made headings centered, then we set font size to 13 pt and removed default
|
||||||
as content but it also has some fields like `title`, `numbers`, and `level` from
|
heading boldness by setting `weight` to `{"regular"}`. Lastly, there is a show
|
||||||
which we can compose a custom look. Here, we are center-aligning, setting the
|
rule with a closure, i.e., a callback function. We gave it a function that
|
||||||
font weight to `{"regular"}` because headings are bold by default, and use the
|
passes the heading as argument. That argument can be used as content but it
|
||||||
[`smallcaps`] function to render the heading's title in small capitals.
|
also has some fields like `title`, `numbers`, and `level`, from which we can
|
||||||
|
compose a custom look. Here, we use the [`smallcaps`] function to render the
|
||||||
|
heading's title in small capitals. Note that heading itself is wrapped in a
|
||||||
|
block by default, as it's a block-level element, and by using `{it.body}` we
|
||||||
|
are destroying the default structure of the heading. This means we strip away
|
||||||
|
not only the `block` "shell", but also any potential numbering and other
|
||||||
|
heading features. To restore it's semantic structure, we wrap
|
||||||
|
`{smallcaps(it.body)}` in a `block`. This way it will behave like usual.
|
||||||
|
|
||||||
The only remaining problem is that all headings look the same now. The
|
The only remaining problem is that all headings now look the same. The
|
||||||
"Motivation" and "Problem Statement" subsections ought to be italic run in
|
"Motivation" and "Problem Statement" subsections ought to be italic run-in
|
||||||
headers, but right now, they look indistinguishable from the section headings. We
|
headings, but right now, they look indistinguishable from the section headings.
|
||||||
can fix that by using a `where` selector on our set rule: This is a
|
We can fix that by using a `where` selector on our show rules: this is a
|
||||||
[method]($scripting/#methods) we can call on headings (and other
|
[method]($scripting/#methods) we can call on headings (and other elements) that
|
||||||
elements) that allows us to filter them by their level. We can use it to
|
allows us to filter them by their level (and other fields). We can use it to
|
||||||
differentiate between section and subsection headings:
|
differentiate between section and subsection headings:
|
||||||
|
|
||||||
```example:50,250,265,245
|
```example:50,250,265,245
|
||||||
@ -432,66 +447,49 @@ differentiate between section and subsection headings:
|
|||||||
>>> for glacier flow
|
>>> for glacier flow
|
||||||
>>> ]
|
>>> ]
|
||||||
>>>
|
>>>
|
||||||
>>> #set text(font: "Libertinus Serif", 11pt)
|
|
||||||
>>> #set par(justify: true)
|
|
||||||
>>> #set page(
|
>>> #set page(
|
||||||
>>> "us-letter",
|
>>> paper: "us-letter",
|
||||||
>>> margin: auto,
|
>>> header: align(right, title),
|
||||||
>>> header: align(
|
|
||||||
>>> right + horizon,
|
|
||||||
>>> title
|
|
||||||
>>> ),
|
|
||||||
>>> numbering: "1",
|
>>> numbering: "1",
|
||||||
>>> columns: 2,
|
>>> columns: 2,
|
||||||
>>> )
|
>>> )
|
||||||
>>>
|
>>> #set par(justify: true)
|
||||||
#show heading.where(
|
>>> #set text(11pt, font: "Libertinus Serif")
|
||||||
level: 1
|
<<< ...
|
||||||
): it => block(width: 100%)[
|
|
||||||
#set align(center)
|
|
||||||
#set text(13pt, weight: "regular")
|
|
||||||
#smallcaps(it.body)
|
|
||||||
]
|
|
||||||
|
|
||||||
#show heading.where(
|
#show heading.where(level: 1): set align(center)
|
||||||
level: 2
|
#show heading.where(level: 1): set text(13pt, weight: "regular")
|
||||||
): it => text(
|
#show heading.where(level: 1): it => block(smallcaps(it.body))
|
||||||
size: 11pt,
|
|
||||||
weight: "regular",
|
#show heading.where(level: 2): set text(11pt, weight: "regular", style: "italic")
|
||||||
style: "italic",
|
#show heading.where(level: 2): it => [#it.body.]
|
||||||
it.body + [.],
|
|
||||||
)
|
<<< ...
|
||||||
>>>
|
|
||||||
>>> #place(
|
>>> #place(
|
||||||
>>> top + center,
|
>>> top + center,
|
||||||
>>> float: true,
|
>>> float: true,
|
||||||
>>> scope: "parent",
|
>>> scope: "parent",
|
||||||
>>> clearance: 2em,
|
>>> clearance: 2em,
|
||||||
>>> )[
|
>>> )[
|
||||||
>>> #text(
|
>>> #block(text(17pt, hyphenate: false, strong(title)))
|
||||||
>>> 17pt,
|
>>>
|
||||||
>>> weight: "bold",
|
>>> #grid(
|
||||||
>>> title,
|
>>> columns: (1fr, 1fr),
|
||||||
|
>>> [
|
||||||
|
>>> Therese Tungsten \
|
||||||
|
>>> Artos Institute \
|
||||||
|
>>> #link("mailto:tung@artos.edu")
|
||||||
|
>>> ],
|
||||||
|
>>> [
|
||||||
|
>>> Dr. John Doe \
|
||||||
|
>>> Artos Institute \
|
||||||
|
>>> #link("mailto:doe@artos.edu")
|
||||||
|
>>> ]
|
||||||
>>> )
|
>>> )
|
||||||
>>>
|
>>>
|
||||||
>>> #grid(
|
>>> #set par(justify: false)
|
||||||
>>> columns: (1fr, 1fr),
|
>>> *Abstract* \
|
||||||
>>> [
|
>>> #lorem(80)
|
||||||
>>> Therese Tungsten \
|
|
||||||
>>> Artos Institute \
|
|
||||||
>>> #link("mailto:tung@artos.edu")
|
|
||||||
>>> ],
|
|
||||||
>>> [
|
|
||||||
>>> Dr. John Doe \
|
|
||||||
>>> Artos Institute \
|
|
||||||
>>> #link("mailto:doe@artos.edu")
|
|
||||||
>>> ]
|
|
||||||
>>> )
|
|
||||||
>>>
|
|
||||||
>>> #par(justify: false)[
|
|
||||||
>>> *Abstract* \
|
|
||||||
>>> #lorem(80)
|
|
||||||
>>> ]
|
|
||||||
>>> ]
|
>>> ]
|
||||||
>>>
|
>>>
|
||||||
>>> = Introduction
|
>>> = Introduction
|
||||||
@ -501,23 +499,26 @@ differentiate between section and subsection headings:
|
|||||||
>>> #lorem(45)
|
>>> #lorem(45)
|
||||||
```
|
```
|
||||||
|
|
||||||
This looks great! We wrote two show rules that each selectively apply to the
|
Excellent! We wrote several rules that selectively apply to the first and second
|
||||||
first and second level headings. We used a `where` selector to filter the
|
level headings. We used a `where` selector to filter the headings by their
|
||||||
headings by their level. We then rendered the subsection headings as run-ins. We
|
level. We then rendered the subsection headings as run-ins. We also
|
||||||
also automatically add a period to the end of the subsection headings.
|
automatically added a period to the end of the subsection headings. This time
|
||||||
|
we did not wrap result in a `block`, because we need the heading to be inline
|
||||||
|
with the following text.
|
||||||
|
|
||||||
Let's review the conference's style guide:
|
Let's review the conference's style guide:
|
||||||
- The font should be an 11pt serif font ✓
|
- The font should be an 11 pt serif font ✓
|
||||||
- The title should be in 17pt and bold ✓
|
- The title should be in 17 pt and bold ✓
|
||||||
- The paper contains a single-column abstract and two-column main text ✓
|
- The paper contains a single-column abstract and two-column main text ✓
|
||||||
- The abstract should be centered ✓
|
- The abstract should be centered ✓
|
||||||
- The main text should be justified ✓
|
- The main text should be justified ✓
|
||||||
- First level section headings should be centered, rendered in small caps and in
|
- First level section headings should be centered, rendered in small caps and in
|
||||||
13pt ✓
|
13 pt ✓
|
||||||
- Second level headings are run-ins, italicized and have the same size as the
|
- Second level headings are run-ins, italicized and have the same size as the
|
||||||
body text ✓
|
body text ✓
|
||||||
- Finally, the pages should be US letter sized, numbered in the center and the
|
- Finally, the pages should be US letter sized, numbered in the center of the
|
||||||
top right corner of each page should contain the title of the paper ✓
|
footer and the top right corner of each page should contain the title of the
|
||||||
|
paper ✓
|
||||||
|
|
||||||
We are now in compliance with all of these styles and can submit the paper to
|
We are now in compliance with all of these styles and can submit the paper to
|
||||||
the conference! The finished paper looks like this:
|
the conference! The finished paper looks like this:
|
||||||
@ -528,6 +529,62 @@ the conference! The finished paper looks like this:
|
|||||||
style="box-shadow: 0 4px 12px rgb(89 85 101 / 20%); width: 500px; max-width: 100%; display: block; margin: 24px auto;"
|
style="box-shadow: 0 4px 12px rgb(89 85 101 / 20%); width: 500px; max-width: 100%; display: block; margin: 24px auto;"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
Here is a full listing of the finished paper:
|
||||||
|
|
||||||
|
```example
|
||||||
|
#let title = [A fluid dynamic model for glacier flow]
|
||||||
|
|
||||||
|
#set page(
|
||||||
|
paper: "us-letter",
|
||||||
|
header: align(right, title),
|
||||||
|
numbering: "1",
|
||||||
|
columns: 2,
|
||||||
|
)
|
||||||
|
#set par(justify: true)
|
||||||
|
#set text(11pt, font: "Libertinus Serif")
|
||||||
|
|
||||||
|
#show heading.where(level: 1): set align(center)
|
||||||
|
#show heading.where(level: 1): set text(13pt, weight: "regular")
|
||||||
|
#show heading.where(level: 1): it => block(smallcaps(it.body))
|
||||||
|
|
||||||
|
#show heading.where(level: 2): set text(11pt, weight: "regular", style: "italic")
|
||||||
|
#show heading.where(level: 2): it => [#it.body.]
|
||||||
|
|
||||||
|
#place(top + center, float: true, scope: "parent", clearance: 2em)[
|
||||||
|
#block(text(17pt, hyphenate: false, strong(title)))
|
||||||
|
|
||||||
|
#grid(
|
||||||
|
columns: (1fr, 1fr),
|
||||||
|
[
|
||||||
|
Therese Tungsten \
|
||||||
|
Artos Institute \
|
||||||
|
#link("mailto:tung@artos.edu")
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Dr. John Doe \
|
||||||
|
Artos Institute \
|
||||||
|
#link("mailto:doe@artos.edu")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
#set par(justify: false)
|
||||||
|
*Abstract* \
|
||||||
|
#lorem(80)
|
||||||
|
]
|
||||||
|
|
||||||
|
= Introduction
|
||||||
|
#lorem(90)
|
||||||
|
|
||||||
|
== Motivation
|
||||||
|
#lorem(140)
|
||||||
|
|
||||||
|
== Problem Statement
|
||||||
|
#lorem(50)
|
||||||
|
|
||||||
|
= Related Work
|
||||||
|
#lorem(200)
|
||||||
|
```
|
||||||
|
|
||||||
## Review
|
## Review
|
||||||
You have now learned how to create headers and footers, how to use functions and
|
You have now learned how to create headers and footers, how to use functions and
|
||||||
scopes to locally override styles, how to create more complex layouts with the
|
scopes to locally override styles, how to create more complex layouts with the
|
||||||
|
@ -25,7 +25,10 @@ You are #amazed[beautiful]!
|
|||||||
|
|
||||||
This function takes a single argument, `term`, and returns a content block with
|
This function takes a single argument, `term`, and returns a content block with
|
||||||
the `term` surrounded by sparkles. We also put the whole thing in a box so that
|
the `term` surrounded by sparkles. We also put the whole thing in a box so that
|
||||||
the term we are amazed by cannot be separated from its sparkles by a line break.
|
the term we are amazed by cannot be separated from its sparkles by a line
|
||||||
|
break. Alternatively, you can use a
|
||||||
|
[shorthand](https://typst.app/docs/reference/symbols/#shorthands)
|
||||||
|
for a no-break space and write `{[✨~#term~✨]}`.
|
||||||
|
|
||||||
Many functions that come with Typst have optional named parameters. Our
|
Many functions that come with Typst have optional named parameters. Our
|
||||||
functions can also have them. Let's add a parameter to our function that lets us
|
functions can also have them. Let's add a parameter to our function that lets us
|
||||||
@ -34,7 +37,7 @@ parameter isn't given.
|
|||||||
|
|
||||||
```example
|
```example
|
||||||
#let amazed(term, color: blue) = {
|
#let amazed(term, color: blue) = {
|
||||||
text(color, box[✨ #term ✨])
|
text(color)[✨~#term~✨]
|
||||||
}
|
}
|
||||||
|
|
||||||
You are #amazed[beautiful]!
|
You are #amazed[beautiful]!
|
||||||
@ -43,7 +46,7 @@ I am #amazed(color: purple)[amazed]!
|
|||||||
|
|
||||||
Templates now work by wrapping our whole document in a custom function like
|
Templates now work by wrapping our whole document in a custom function like
|
||||||
`amazed`. But wrapping a whole document in a giant function call would be
|
`amazed`. But wrapping a whole document in a giant function call would be
|
||||||
cumbersome! Instead, we can use an "everything" show rule to achieve the same
|
cumbersome! Instead, we can use an "global" show rule to achieve the same
|
||||||
with cleaner code. To write such a show rule, put a colon directly after the
|
with cleaner code. To write such a show rule, put a colon directly after the
|
||||||
show keyword and then provide a function. This function is given the rest of the
|
show keyword and then provide a function. This function is given the rest of the
|
||||||
document as a parameter. The function can then do anything with this content.
|
document as a parameter. The function can then do anything with this content.
|
||||||
@ -52,7 +55,7 @@ just pass it by name to the show rule. Let's try it:
|
|||||||
|
|
||||||
```example
|
```example
|
||||||
>>> #let amazed(term, color: blue) = {
|
>>> #let amazed(term, color: blue) = {
|
||||||
>>> text(color, box[✨ #term ✨])
|
>>> text(color)[✨~#term~✨]
|
||||||
>>> }
|
>>> }
|
||||||
#show: amazed
|
#show: amazed
|
||||||
I choose to focus on the good
|
I choose to focus on the good
|
||||||
@ -68,69 +71,56 @@ powerful.
|
|||||||
|
|
||||||
## Embedding set and show rules { #set-and-show-rules }
|
## Embedding set and show rules { #set-and-show-rules }
|
||||||
To apply some set and show rules to our template, we can use `set` and `show`
|
To apply some set and show rules to our template, we can use `set` and `show`
|
||||||
within a content block in our function and then insert the document into
|
within a code block in our function and then insert the document into
|
||||||
that content block.
|
that code block.
|
||||||
|
|
||||||
```example
|
```example
|
||||||
#let template(doc) = [
|
#let template(doc) = {
|
||||||
#set text(font: "Inria Serif")
|
set text(font: "Inria Serif")
|
||||||
#show "something cool": [Typst]
|
show "something cool": [Typst]
|
||||||
#doc
|
doc
|
||||||
]
|
}
|
||||||
|
|
||||||
#show: template
|
#show: template
|
||||||
I am learning something cool today.
|
I am learning something cool today.
|
||||||
It's going great so far!
|
It's going great so far!
|
||||||
```
|
```
|
||||||
|
|
||||||
Just like we already discovered in the previous chapter, set rules will apply to
|
Just like we already discovered in the previous chapter, set rules will apply
|
||||||
everything within their content block. Since the everything show rule passes our
|
to everything within their scope. Since the global show rule passes our whole
|
||||||
whole document to the `template` function, the text set rule and string show
|
document to the `template` function, the text set rule and string show rule in
|
||||||
rule in our template will apply to the whole document. Let's use this knowledge
|
our template will apply to the whole document.
|
||||||
to create a template that reproduces the body style of the paper we wrote in the
|
|
||||||
previous chapter.
|
We used a curly-braced code block instead of a content block. This way, we
|
||||||
|
don't need to prefix all set rules and function calls with a `#`. This also
|
||||||
|
removes the implicit spaces that are naturally introduced in the markup mode.
|
||||||
|
In exchange, we cannot write markup directly in the code block anymore.
|
||||||
|
|
||||||
|
Let's use this knowledge to create a template that reproduces the body style of
|
||||||
|
the paper we wrote in the previous chapter.
|
||||||
|
|
||||||
```example
|
```example
|
||||||
#let conf(title, doc) = {
|
#let conf(title, doc) = {
|
||||||
set page(
|
set page(
|
||||||
paper: "us-letter",
|
paper: "us-letter",
|
||||||
>>> margin: auto,
|
header: align(right, title),
|
||||||
header: align(
|
|
||||||
right + horizon,
|
|
||||||
title
|
|
||||||
),
|
|
||||||
columns: 2,
|
columns: 2,
|
||||||
<<< ...
|
<<< ...
|
||||||
)
|
)
|
||||||
set par(justify: true)
|
set par(justify: true)
|
||||||
set text(
|
set text(
|
||||||
|
11pt,
|
||||||
font: "Libertinus Serif",
|
font: "Libertinus Serif",
|
||||||
size: 11pt,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Heading show rules.
|
// Heading show rules.
|
||||||
<<< ...
|
<<< ...
|
||||||
>>> show heading.where(
|
>>> show heading.where(level: 1): set align(center)
|
||||||
>>> level: 1
|
>>> show heading.where(level: 1): set text(13pt, weight: "regular")
|
||||||
>>> ): it => block(
|
>>> show heading.where(level: 1): it => block(smallcaps(it.body))
|
||||||
>>> align(center,
|
>>>
|
||||||
>>> text(
|
>>> show heading.where(level: 2): set text(11pt, weight: "regular", style: "italic")
|
||||||
>>> 13pt,
|
>>> show heading.where(level: 2): it => [#it.body.]
|
||||||
>>> weight: "regular",
|
|
||||||
>>> smallcaps(it.body),
|
|
||||||
>>> )
|
|
||||||
>>> ),
|
|
||||||
>>> )
|
|
||||||
>>> show heading.where(
|
|
||||||
>>> level: 2
|
|
||||||
>>> ): it => box(
|
|
||||||
>>> text(
|
|
||||||
>>> 11pt,
|
|
||||||
>>> weight: "regular",
|
|
||||||
>>> style: "italic",
|
|
||||||
>>> it.body + [.],
|
|
||||||
>>> )
|
|
||||||
>>> )
|
|
||||||
|
|
||||||
doc
|
doc
|
||||||
}
|
}
|
||||||
@ -154,24 +144,17 @@ previous chapter.
|
|||||||
>>> #lorem(200)
|
>>> #lorem(200)
|
||||||
```
|
```
|
||||||
|
|
||||||
We copy-pasted most of that code from the previous chapter. The two differences
|
We copied most of that code from the previous chapter. However, now we wrapped
|
||||||
are this:
|
everything in the function `conf` using a global show rule. The function applies
|
||||||
|
a few set and show rules and echoes the content it has been passed at the end.
|
||||||
|
|
||||||
1. We wrapped everything in the function `conf` using an everything show rule.
|
Also note where the title comes from: we previously had it inside of a variable.
|
||||||
The function applies a few set and show rules and echoes the content it has
|
|
||||||
been passed at the end.
|
|
||||||
|
|
||||||
2. Moreover, we used a curly-braced code block instead of a content block. This
|
|
||||||
way, we don't need to prefix all set rules and function calls with a `#`. In
|
|
||||||
exchange, we cannot write markup directly in the code block anymore.
|
|
||||||
|
|
||||||
Also note where the title comes from: We previously had it inside of a variable.
|
|
||||||
Now, we are receiving it as the first parameter of the template function. To do
|
Now, we are receiving it as the first parameter of the template function. To do
|
||||||
so, we passed a closure (that's a function without a name that is used right
|
so, we passed a closure (that's a function without a name that is used right
|
||||||
away) to the everything show rule. We did that because the `conf` function
|
away) to the global show rule. We did that because the `conf` function expects
|
||||||
expects two positional arguments, the title and the body, but the show rule will
|
two positional arguments: the title and the body, but the show rule will only
|
||||||
only pass the body. Therefore, we add a new function definition that allows us
|
pass the body. Therefore, we add a new function definition that allows us to set
|
||||||
to set a paper title and use the single parameter from the show rule.
|
a paper title and use the single parameter from the show rule.
|
||||||
|
|
||||||
## Templates with named arguments { #named-arguments }
|
## Templates with named arguments { #named-arguments }
|
||||||
Our paper in the previous chapter had a title and an author list. Let's add
|
Our paper in the previous chapter had a title and an author list. Let's add
|
||||||
@ -230,6 +213,9 @@ multiple arguments for the grid. We can do that by using the
|
|||||||
[`spread` operator]($arguments). It takes an array and applies each of its items
|
[`spread` operator]($arguments). It takes an array and applies each of its items
|
||||||
as a separate argument to the function.
|
as a separate argument to the function.
|
||||||
|
|
||||||
|
Let's also include some PDF metadata. We can achieve this by using
|
||||||
|
the [`document`] function and specifying fields such as `title` and `author`.
|
||||||
|
|
||||||
The resulting template function looks like this:
|
The resulting template function looks like this:
|
||||||
|
|
||||||
```typ
|
```typ
|
||||||
@ -239,31 +225,33 @@ The resulting template function looks like this:
|
|||||||
abstract: [],
|
abstract: [],
|
||||||
doc,
|
doc,
|
||||||
) = {
|
) = {
|
||||||
|
set document(title: title, author: authors.map(author => author.name))
|
||||||
// Set and show rules from before.
|
// Set and show rules from before.
|
||||||
>>> #set page(columns: 2)
|
|
||||||
<<< ...
|
<<< ...
|
||||||
|
|
||||||
set align(center)
|
{
|
||||||
text(17pt, title)
|
set align(center)
|
||||||
|
set par(justify: false)
|
||||||
|
|
||||||
let count = authors.len()
|
block(text(17pt, strong(title)))
|
||||||
let ncols = calc.min(count, 3)
|
|
||||||
grid(
|
|
||||||
columns: (1fr,) * ncols,
|
|
||||||
row-gutter: 24pt,
|
|
||||||
..authors.map(author => [
|
|
||||||
#author.name \
|
|
||||||
#author.affiliation \
|
|
||||||
#link("mailto:" + author.email)
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
|
|
||||||
par(justify: false)[
|
let count = authors.len()
|
||||||
*Abstract* \
|
let ncols = calc.min(count, 3)
|
||||||
#abstract
|
grid(
|
||||||
]
|
columns: (1fr,) * ncols,
|
||||||
|
row-gutter: 24pt,
|
||||||
|
..authors.map(author => [
|
||||||
|
#author.name \
|
||||||
|
#author.affiliation \
|
||||||
|
#link("mailto:" + author.email)
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
strong[Abstract]
|
||||||
|
linebreak()
|
||||||
|
abstract
|
||||||
|
}
|
||||||
|
|
||||||
set align(left)
|
|
||||||
doc
|
doc
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -291,72 +279,45 @@ call.
|
|||||||
>>> abstract: [],
|
>>> abstract: [],
|
||||||
>>> doc,
|
>>> doc,
|
||||||
>>> ) = {
|
>>> ) = {
|
||||||
>>> set text(font: "Libertinus Serif", 11pt)
|
>>> set document(title: title, author: authors.map(author => author.name))
|
||||||
>>> set par(justify: true)
|
>>> set page(
|
||||||
>>> set page(
|
>>> "us-letter",
|
||||||
>>> "us-letter",
|
>>> header: align(right, title),
|
||||||
>>> margin: auto,
|
>>> numbering: "1",
|
||||||
>>> header: align(
|
>>> columns: 2,
|
||||||
>>> right + horizon,
|
>>> )
|
||||||
>>> title
|
>>> set par(justify: true)
|
||||||
>>> ),
|
>>> set text(11pt, font: "Libertinus Serif")
|
||||||
>>> numbering: "1",
|
|
||||||
>>> columns: 2,
|
|
||||||
>>> )
|
|
||||||
>>>
|
>>>
|
||||||
>>> show heading.where(
|
>>> show heading.where(level: 1): set align(center)
|
||||||
>>> level: 1
|
>>> show heading.where(level: 1): set text(13pt, weight: "regular")
|
||||||
>>> ): it => block(
|
>>> show heading.where(level: 1): it => block(smallcaps(it.body))
|
||||||
>>> align(center,
|
|
||||||
>>> text(
|
|
||||||
>>> 13pt,
|
|
||||||
>>> weight: "regular",
|
|
||||||
>>> smallcaps(it.body),
|
|
||||||
>>> )
|
|
||||||
>>> ),
|
|
||||||
>>> )
|
|
||||||
>>> show heading.where(
|
|
||||||
>>> level: 2
|
|
||||||
>>> ): it => box(
|
|
||||||
>>> text(
|
|
||||||
>>> 11pt,
|
|
||||||
>>> weight: "regular",
|
|
||||||
>>> style: "italic",
|
|
||||||
>>> it.body + [.],
|
|
||||||
>>> )
|
|
||||||
>>> )
|
|
||||||
>>>
|
>>>
|
||||||
>>> place(
|
>>> show heading.where(level: 2): set text(11pt, weight: "regular", style: "italic")
|
||||||
>>> top,
|
>>> show heading.where(level: 2): it => [#it.body.]
|
||||||
>>> float: true,
|
>>>
|
||||||
>>> scope: "parent",
|
>>> place(top + center, float: true, scope: "parent", clearance: 2em, {
|
||||||
>>> clearance: 2em,
|
>>> set par(justify: false)
|
||||||
>>> {
|
>>> block(text(17pt, title))
|
||||||
>>> set align(center)
|
>>> let count = calc.min(authors.len(), 3)
|
||||||
>>> text(17pt, title)
|
>>> grid(
|
||||||
>>> let count = calc.min(authors.len(), 3)
|
>>> columns: (1fr,) * count,
|
||||||
>>> grid(
|
>>> row-gutter: 24pt,
|
||||||
>>> columns: (1fr,) * count,
|
>>> ..authors.map(author => [
|
||||||
>>> row-gutter: 24pt,
|
>>> #author.name \
|
||||||
>>> ..authors.map(author => [
|
>>> #author.affiliation \
|
||||||
>>> #author.name \
|
>>> #link("mailto:" + author.email)
|
||||||
>>> #author.affiliation \
|
>>> ]),
|
||||||
>>> #link("mailto:" + author.email)
|
>>> )
|
||||||
>>> ]),
|
>>> strong[Abstract]
|
||||||
>>> )
|
>>> linebreak()
|
||||||
>>> par(justify: false)[
|
>>> abstract
|
||||||
>>> *Abstract* \
|
>>> })
|
||||||
>>> #abstract
|
>>> doc
|
||||||
>>> ]
|
>>> }
|
||||||
>>> },
|
|
||||||
>>> )
|
|
||||||
>>> doc
|
|
||||||
>>>}
|
|
||||||
<<< #import "conf.typ": conf
|
<<< #import "conf.typ": conf
|
||||||
#show: conf.with(
|
#show: conf.with(
|
||||||
title: [
|
title: [Towards Improved Modelling],
|
||||||
Towards Improved Modelling
|
|
||||||
],
|
|
||||||
authors: (
|
authors: (
|
||||||
(
|
(
|
||||||
name: "Theresa Tungsten",
|
name: "Theresa Tungsten",
|
||||||
@ -397,7 +358,7 @@ that define reusable document styles. You've made it far and learned a lot. You
|
|||||||
can now use Typst to write your own documents and share them with others.
|
can now use Typst to write your own documents and share them with others.
|
||||||
|
|
||||||
We are still a super young project and are looking for feedback. If you have any
|
We are still a super young project and are looking for feedback. If you have any
|
||||||
questions, suggestions or you found a bug, please let us know
|
questions, suggestions, or you found a bug, please let us know
|
||||||
in the [Forum](https://forum.typst.app/),
|
in the [Forum](https://forum.typst.app/),
|
||||||
on our [Discord server](https://discord.gg/2uDybryKPe),
|
on our [Discord server](https://discord.gg/2uDybryKPe),
|
||||||
on [GitHub](https://github.com/typst/typst/),
|
on [GitHub](https://github.com/typst/typst/),
|
||||||
|
BIN
tests/ref/image-pdf-multiple-pages.png
Normal file
BIN
tests/ref/image-pdf-multiple-pages.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
BIN
tests/ref/image-pdf.png
Normal file
BIN
tests/ref/image-pdf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
@ -258,7 +258,7 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
|
|||||||
--- image-png-but-pixmap-format ---
|
--- image-png-but-pixmap-format ---
|
||||||
#image(
|
#image(
|
||||||
read("/assets/images/tiger.jpg", encoding: none),
|
read("/assets/images/tiger.jpg", encoding: none),
|
||||||
// Error: 11-18 expected "png", "jpg", "gif", "webp", dictionary, "svg", or auto
|
// Error: 11-18 expected "png", "jpg", "gif", "webp", dictionary, "svg", "pdf", or auto
|
||||||
format: "rgba8",
|
format: "rgba8",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -289,3 +289,16 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
|
|||||||
..rotations.map(v => raw(str(v), lang: "typc")),
|
..rotations.map(v => raw(str(v), lang: "typc")),
|
||||||
..rotations.map(rotated)
|
..rotations.map(rotated)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
--- image-pdf ---
|
||||||
|
#image("/assets/images/matplotlib.pdf")
|
||||||
|
|
||||||
|
--- image-pdf-multiple-pages ---
|
||||||
|
#image("/assets/images/diagrams.pdf", page: 1)
|
||||||
|
#image("/assets/images/diagrams.pdf", page: 3)
|
||||||
|
#image("/assets/images/diagrams.pdf", page: 2)
|
||||||
|
|
||||||
|
--- image-pdf-invalid-page ---
|
||||||
|
// Error: 2-49 page 2 does not exist
|
||||||
|
// Hint: 2-49 the document only has 1 page
|
||||||
|
#image("/assets/images/matplotlib.pdf", page: 2)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user