diff --git a/src/Rust/Cargo.lock b/src/Rust/Cargo.lock
index b0f1d2f5afbda940ff3c2410e16e37e1e932624e..4b7778616095bf320af0a30a40d354fcc966c309 100644
--- a/src/Rust/Cargo.lock
+++ b/src/Rust/Cargo.lock
@@ -2,20 +2,42 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "ab_glyph"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
+
 [[package]]
 name = "aho-corasick"
-version = "0.7.20"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
 dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "anstyle"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+
 [[package]]
 name = "anyhow"
-version = "1.0.70"
+version = "1.0.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
+checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
 
 [[package]]
 name = "autocfg"
@@ -52,9 +74,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "clap"
-version = "4.2.1"
+version = "4.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
+checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -63,10 +85,11 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.2.1"
+version = "4.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
+checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
 dependencies = [
+ "anstyle",
  "bitflags",
  "clap_lex",
  "strsim",
@@ -75,9 +98,9 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.2.0"
+version = "4.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01c22dcfb410883764b29953103d9ef7bb8fe21b3fa1158bc99986c2067294bd"
+checksum = "1a19591b2ab0e3c04b588a0e04ddde7b9eaa423646d1b4a8092879216bf47473"
 dependencies = [
  "clap",
 ]
@@ -91,7 +114,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.15",
 ]
 
 [[package]]
@@ -129,13 +152,13 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
 [[package]]
 name = "errno"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
 dependencies = [
  "errno-dragonfly",
  "libc",
- "windows-sys 0.45.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -188,13 +211,13 @@ dependencies = [
 
 [[package]]
 name = "io-lifetimes"
-version = "1.0.9"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
+checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
 dependencies = [
  "hermit-abi",
  "libc",
- "windows-sys 0.45.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -214,15 +237,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.140"
+version = "0.2.142"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
+checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.3.1"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
+checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c"
 
 [[package]]
 name = "log"
@@ -316,6 +339,15 @@ version = "1.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
+[[package]]
+name = "owned_ttf_parser"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4"
+dependencies = [
+ "ttf-parser",
+]
+
 [[package]]
 name = "paste"
 version = "1.0.12"
@@ -372,9 +404,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.7.3"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
+checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -383,9 +415,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.29"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
 
 [[package]]
 name = "roff"
@@ -401,16 +433,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
 name = "rustix"
-version = "0.37.7"
+version = "0.37.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d"
+checksum = "8bbfc1d1c7c40c01715f47d71444744a81669ca84e8b63e25a55e169b1f86433"
 dependencies = [
  "bitflags",
  "errno",
  "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.45.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -436,9 +468,9 @@ dependencies = [
 
 [[package]]
 name = "scc"
-version = "1.4.3"
+version = "1.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb666a705482e987d4bbd8bb7b0a07438ecfde629044176f0a571605c03e22a"
+checksum = "a242e0a9cf55e2fd90e82409ae16c20e45d8c33d1be61cc3d2fe68de0f9ca128"
 
 [[package]]
 name = "scopeguard"
@@ -448,9 +480,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "serde"
-version = "1.0.159"
+version = "1.0.160"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
+checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
 
 [[package]]
 name = "serde_spanned"
@@ -492,9 +524,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.13"
+version = "2.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
+checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -508,7 +540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
 dependencies = [
  "rustix",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -528,7 +560,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.15",
 ]
 
 [[package]]
@@ -566,6 +598,12 @@ dependencies = [
  "winnow",
 ]
 
+[[package]]
+name = "ttf-parser"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746"
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.8"
@@ -605,7 +643,9 @@ dependencies = [
  "log",
  "scc",
  "thiserror",
+ "vvs_font",
  "vvs_procmacro",
+ "vvs_utils",
 ]
 
 [[package]]
@@ -620,8 +660,21 @@ dependencies = [
  "log",
  "scc",
  "thiserror",
+ "vvs_font",
  "vvs_lua",
  "vvs_repl",
+ "vvs_utils",
+]
+
+[[package]]
+name = "vvs_font"
+version = "0.4.0"
+dependencies = [
+ "ab_glyph",
+ "lazy_static",
+ "log",
+ "thiserror",
+ "ttf-parser",
 ]
 
 [[package]]
@@ -634,6 +687,7 @@ dependencies = [
  "thiserror",
  "toml",
  "vvs_ass",
+ "vvs_utils",
 ]
 
 [[package]]
@@ -642,7 +696,7 @@ version = "0.4.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.15",
 ]
 
 [[package]]
@@ -655,6 +709,10 @@ dependencies = [
  "vvs_lua",
 ]
 
+[[package]]
+name = "vvs_utils"
+version = "0.4.0"
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -677,37 +735,13 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets 0.48.0",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets",
 ]
 
 [[package]]
@@ -716,93 +750,51 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
 dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
 ]
 
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
 
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
 
-[[package]]
-name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
 
-[[package]]
-name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
 
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
 
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
 
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.0"
@@ -811,9 +803,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
 
 [[package]]
 name = "winnow"
-version = "0.4.1"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
+checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
 dependencies = [
  "memchr",
 ]
diff --git a/src/Rust/Cargo.toml b/src/Rust/Cargo.toml
index 3bd837499ea359181022430d93bb5d62f29c133e..ee090e74309684fe576b537c4875fdb649c8adcf 100644
--- a/src/Rust/Cargo.toml
+++ b/src/Rust/Cargo.toml
@@ -1,6 +1,14 @@
 [workspace]
 resolver = "2"
-members = ["vvs_cli", "vvs_lua", "vvs_ass", "vvs_repl", "vvs_procmacro"]
+members = [
+    "vvs_cli",
+    "vvs_lua",
+    "vvs_ass",
+    "vvs_repl",
+    "vvs_font",
+    "vvs_utils",
+    "vvs_procmacro",
+]
 
 [workspace.package]
 version = "0.4.0"
diff --git a/src/Rust/README.md b/src/Rust/README.md
index 5946eef2691799125eb32d4937e62dcb3794da20..766bbd0f6c2e6adeaad6707ae7a0f4225635b4d4 100644
--- a/src/Rust/README.md
+++ b/src/Rust/README.md
@@ -40,3 +40,8 @@ To visualize the dependency graph of VivyScript, use the command:
 
     cargo install cargo-depgraph
     cargo depgraph --build-deps --dedup-transitive-deps | dot -Tpdf | zathura -
+
+# Licence
+
+- The VVS project is under the MIT licence
+- The NotoSans fonts are distributed using the [OFL licence](utils/fonts/OFL.txt)
diff --git a/src/Rust/vvs_ass/Cargo.toml b/src/Rust/vvs_ass/Cargo.toml
index d0b222ee62931a1d5bd0c6560d6fe38d9588ba73..95e6f25caa52294ff0a4db5c99b0b2a6c446c304 100644
--- a/src/Rust/vvs_ass/Cargo.toml
+++ b/src/Rust/vvs_ass/Cargo.toml
@@ -4,10 +4,12 @@ version.workspace = true
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
-description.workspace = true
+description = "ASS specification and VVS specificities for VVCC"
 
 [dependencies]
 vvs_procmacro = { path = "../vvs_procmacro" }
+vvs_utils = { path = "../vvs_utils" }
+vvs_font = { path = "../vvs_font" }
 
 lazy_static.workspace = true
 thiserror.workspace = true
diff --git a/src/Rust/vvs_ass/src/colors.rs b/src/Rust/vvs_ass/src/colors.rs
new file mode 100644
index 0000000000000000000000000000000000000000..71bfd0191e28eaf7d71092f251f0191c998048d1
--- /dev/null
+++ b/src/Rust/vvs_ass/src/colors.rs
@@ -0,0 +1,179 @@
+use vvs_utils::*;
+
+/// The color representation
+#[derive(Debug, Clone, PartialEq)]
+pub enum ASSColor {
+    /// The Red Green Blue Alpha representation.
+    RGBA { r: u8, g: u8, b: u8, a: u8 },
+
+    /// The Hue Saturation Lightness with Alpha representation. The Hue must be between is in
+    /// radian, the lightness and saturation values must be between 0 and 1.
+    HSLA { h: f32, s: f32, l: f32, a: u8 },
+}
+
+macro_rules! rgb {
+    ($NAME: ident => $r: literal $g: literal $b: literal) => {
+        pub const $NAME: ASSColor = ASSColor::RGBA {
+            r: $r,
+            g: $g,
+            b: $b,
+            a: 0,
+        };
+    };
+}
+
+impl ASSColor {
+    rgb! { WHITE   => 255 255 255 }
+    rgb! { SILVER  => 192 192 192 }
+    rgb! { GRAY    => 128 128 128 }
+    rgb! { BLACK   =>   0   0   0 }
+
+    rgb! { RED     => 255   0   0 }
+    rgb! { MAROON  => 128   0   0 }
+    rgb! { YELLOW  => 255 255   0 }
+    rgb! { OLIVE   => 128 128   0 }
+    rgb! { LIME    =>   0 255   0 }
+    rgb! { GREEN   =>   0 128   0 }
+    rgb! { AQUA    =>   0 255 255 }
+    rgb! { TEAL    =>   0 128 128 }
+    rgb! { BLUE    =>   0   0 255 }
+    rgb! { NAVY    =>   0   0 128 }
+    rgb! { FUCHSIA => 255   0 255 }
+    rgb! { PURPLE  => 128   0 128 }
+
+    fn skip_delimiters(str: &str) -> &str {
+        str.trim()
+            .trim_start_matches('#')
+            .trim_start_matches('&')
+            .trim_end_matches('&')
+    }
+
+    pub fn try_from_rgba(str: impl AsRef<str>) -> Result<Self, String> {
+        let color = Self::skip_delimiters(str.as_ref());
+        if !(color.len() == 6 || color.len() == 8) {
+            return Err(format!(
+                "invalid color string description: {}",
+                str.as_ref()
+            ));
+        }
+        Ok(Self::RGBA {
+            r: u8::from_str_radix(&color[0..2], 16)
+                .map_err(|err| format!("invalid red description: {err}"))?,
+            g: u8::from_str_radix(&color[2..4], 16)
+                .map_err(|err| format!("invalid green description: {err}"))?,
+            b: u8::from_str_radix(&color[4..6], 16)
+                .map_err(|err| format!("invalid blue description: {err}"))?,
+            a: (color.len() == 8)
+                .then(|| {
+                    u8::from_str_radix(&color[6..8], 16)
+                        .map_err(|err| format!("invalid alpha description: {err}"))
+                })
+                .unwrap_or(Ok(0))?,
+        })
+    }
+
+    pub fn try_from_bgra(str: impl AsRef<str>) -> Result<Self, String> {
+        let color = Self::skip_delimiters(str.as_ref());
+        if !(color.len() == 6 || color.len() == 8) {
+            return Err(format!(
+                "invalid color string description: {}",
+                str.as_ref()
+            ));
+        }
+        Ok(Self::RGBA {
+            r: u8::from_str_radix(&color[4..6], 16)
+                .map_err(|err| format!("invalid red description: {err}"))?,
+            g: u8::from_str_radix(&color[2..4], 16)
+                .map_err(|err| format!("invalid green description: {err}"))?,
+            b: u8::from_str_radix(&color[0..2], 16)
+                .map_err(|err| format!("invalid blue description: {err}"))?,
+            a: (color.len() == 8)
+                .then(|| {
+                    u8::from_str_radix(&color[6..8], 16)
+                        .map_err(|err| format!("invalid alpha description: {err}"))
+                })
+                .unwrap_or(Ok(0))?,
+        })
+    }
+
+    pub fn into_rgba(self) -> Self {
+        match self {
+            this @ ASSColor::RGBA { .. } => this,
+            ASSColor::HSLA { h, s, l, a } => {
+                let h = f32_degres_clamp(h);
+                let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
+                let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
+                let m = l - c / 2.0;
+
+                let (r, g, b) = if (0.0..60.0).contains(&h) {
+                    (c, x, 0.0)
+                } else if (60.0..120.0).contains(&h) {
+                    (x, c, 0.0)
+                } else if (120.0..180.0).contains(&h) {
+                    (0.0, c, x)
+                } else if (180.0..240.0).contains(&h) {
+                    (0.0, x, c)
+                } else if (240.0..300.0).contains(&h) {
+                    (x, 0.0, c)
+                } else if (300.0..360.0).contains(&h) {
+                    (c, 0.0, x)
+                } else {
+                    unreachable!()
+                };
+
+                ASSColor::RGBA {
+                    r: ((r + m) * 255.0).trunc() as u8,
+                    g: ((g + m) * 255.0).trunc() as u8,
+                    b: ((b + m) * 255.0).trunc() as u8,
+                    a,
+                }
+            }
+        }
+    }
+
+    pub fn into_hsla(self) -> Self {
+        match self {
+            this @ ASSColor::HSLA { .. } => this,
+            ASSColor::RGBA { r, g, b, a } => {
+                const U8_MAX: f32 = u8::MAX as f32;
+                let h = self.hue();
+                let (r, g, b) = (r as f32 / U8_MAX, g as f32 / U8_MAX, b as f32 / U8_MAX);
+                let (min, max) = minmax_partial!(r, g, b);
+                let l = (min + max) / 2.0;
+                let delta = max - min;
+                let s = either!(f32_epsilon_eq(delta, 0.0) => 0.0;
+                    delta / (1.0 - (2.0 * l - 1.0).abs())
+                );
+                ASSColor::HSLA { h, s, l, a }
+            }
+        }
+    }
+
+    /// Returns the HUE from the color (the H in HSL/HSV).
+    fn hue(&self) -> f32 {
+        match self {
+            ASSColor::HSLA { h, .. } => *h,
+            ASSColor::RGBA { r, g, b, .. } => {
+                let (r, g, b) = (*r, *g, *b);
+                let (min, max) = minmax_partial!(r, g, b);
+                let c = max - min;
+                ((if c == 0 {
+                    0.0
+                } else if max == r {
+                    let segment = (g as f32 - b as f32) / c as f32;
+                    either!(segment < 0.0 => segment + 6.0; segment)
+                } else if max == g {
+                    let segment = (b as f32 - r as f32) / c as f32;
+                    segment + 2.0
+                } else if max == b {
+                    let segment = (r as f32 - g as f32) / c as f32;
+                    segment + 4.0
+                } else {
+                    panic!()
+                } * 60.0)
+                    % 360.0)
+                    .trunc()
+            }
+        }
+    }
+}
diff --git a/src/Rust/vvs_ass/src/definitions.rs b/src/Rust/vvs_ass/src/definitions.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e8b5689f06fca6eefacf811ef4d83523691ab09d
--- /dev/null
+++ b/src/Rust/vvs_ass/src/definitions.rs
@@ -0,0 +1,204 @@
+use std::str::FromStr;
+
+/// The section in the ASS file.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ASSFileSection {
+    ScriptInfo,
+    V4Styles,
+    Events,
+}
+
+/// The events of the [ASSFileSection::Events] section.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ASSEvent {
+    /// - Marked=0 means the line is not shown as "marked" in SSA.
+    /// - Marked=1 means the line is shown as "marked" in SSA.
+    pub marked: bool,
+
+    /// Subtitles having different layer number will be ignored during the collusion detection.
+    /// Higher numbered layers will be drawn over the lower numbered.
+    pub layer: i64,
+
+    /// Start Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths. This is the
+    /// time elapsed during script playback at which the text will appear onscreen. Note that there
+    /// is a single digit for the hours!
+    pub start: i64,
+
+    /// End Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths. This is the time
+    /// elapsed during script playback at which the text will disappear offscreen. Note that there
+    /// is a single digit for the hours!
+    pub end: i64,
+
+    /// Style name. If it is "Default", then your own *Default style will be subtituted. However,
+    /// the Default style used by the script author IS stored in the script even though SSA ignores
+    /// it - so if you want to use it, the information is there - you could even change the Name in
+    /// the Style definition line, so that it will appear in the list of "script" styles.
+    pub style: String,
+
+    /// Character name. This is the name of the character who speaks the dialogue. It is for
+    /// information only, to make the script is easier to follow when editing/timing.
+    pub name: String,
+
+    /// Transition Effect. This is either empty, or contains information for one of the three
+    /// transition effects implemented in SSA v4.x The effect names are case sensitive and must
+    /// appear exactly as shown. The effect names do not have quote marks around them.
+    /// - "Karaoke" means that the text will be successively highlighted one word at a time.
+    ///   Karaoke as an effect type is obsolete.
+    /// - "Scroll up;y1;y2;delay[;fadeawayheight]"means that the text/picture will scroll up the
+    ///   screen. The parameters after the words "Scroll up" are separated by semicolons. The y1
+    ///   and y2 values define a vertical region on the screen in which the text will scroll. The
+    ///   values are in pixels, and it doesn't matter which value (top or bottom) comes first. If
+    ///   the values are zeroes then the text will scroll up the full height of the screen. The
+    ///   delay value can be a number from 1 to 100, and it slows down the speed of the scrolling -
+    ///   zero means no delay and the scrolling will be as fast as possible.
+    /// - "Banner;delay" means that text will be forced into a single line, regardless of length,
+    ///   and scrolled from right to left accross the screen. The delay value can be a number from
+    ///   1 to 100, and it slows down the speed of the scrolling - zero means no delay and the
+    ///   scrolling will be as fast as possible.
+    /// - "Scroll down;y1;y2;delay[;fadeawayheight]"
+    /// - "Banner;delay[;lefttoright;fadeawaywidth]" lefttoright 0 or 1. This field is optional.
+    ///   Default value is 0 to make it backwards compatible. When delay is greater than 0, moving
+    ///   one pixel will take (1000/delay) second. (WARNING: Avery Lee’s "subtitler" plugin reads
+    ///   the "Scroll up" effect parameters as delay;y1;y2) fadeawayheight and fadeawaywidth
+    ///   parameters can be used to make the scrolling text at the sides transparent.
+    pub effect: String,
+
+    /// Subtitle Text. This is the actual text which will be displayed as a subtitle onscreen.
+    /// Everything after the 9th comma is treated as the subtitle text, so it can include commas.
+    /// The text can include \n codes which is a line break, and can include Style Override control
+    /// codes, which appear between braces { }.
+    pub text: String,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ScriptInfoKey {
+    /// This is a description of the script. If the original author(s) did not provide this
+    /// information then <untitled> is automatically substituted
+    Title,
+
+    /// The original author(s) of the script. If the original author(s) did not provide this
+    /// information then <unknown> is automatically substituted
+    OriginalScript,
+
+    /// The original translator of the dialogue. This entry does not appear if no information was
+    /// entered by the author. (optional)
+    OriginalTranslation,
+
+    /// The original script editor(s), typically whoever took the raw translation and turned it
+    /// into idiomatic english and reworded for readability. This entry does not appear if no
+    /// information was entered by the author. (optional)
+    OriginalEditing,
+
+    /// Whoever timed the original script. This entry does not appear if no information was entered
+    /// by the author. (optional)
+    OriginalTiming,
+
+    /// Description of where in the video the script should begin playback. This entry does not
+    /// appear if no information was entered by the author. (optional)
+    SynchPoint,
+
+    /// Names of any other subtitling groups who edited the original script. This entry does not
+    /// appear if subsequent editors did not enter the information. (optional)
+    ScriptUpdatedBy,
+
+    /// The details of any updates to the original script - made by other subtitling groups. This
+    /// entry does not appear if subsequent editors did not enter any information
+    UpdateDetails,
+
+    /// This is the SSA script format version eg. "V4.00". It is used by SSA to give a warning if
+    /// you are using a version of SSA older than the version that created the script.
+    /// ***ASS version is "V4.00+"***
+    ScriptType,
+
+    /// This determines how subtitles are moved, when automatically preventing onscreen collisions.
+    ///
+    /// If the entry says "Normal" then SSA will attempt to position subtitles in the position
+    /// specified by the "margins". However, subtitles can be shifted vertically to prevent
+    /// onscreen collisions. With "normal" collision prevention, the subtitles will "stack up" one
+    /// above the other - but they will always be positioned as close the vertical (bottom) margin
+    /// as possible - filling in "gaps" in other subtitles if one large enough is available.
+    ///
+    /// If the entry says "Reverse" then subtitles will be shifted upwards to make room for
+    /// subsequent overlapping subtitles. This means the subtitles can nearly always be read
+    /// top-down - but it also means that the first subtitle can appear half way up the screen
+    /// before the subsequent overlapping subtitles appear. It can use a lot of screen area.
+    Collisions,
+
+    /// This is the height of the screen used by the script's author(s) when playing the script.
+    /// SSA v4 will automatically select the nearest enabled setting, if you are using Directdraw
+    /// playback
+    PlayResY,
+
+    /// This is the width of the screen used by the script's author(s) when playing the script. SSA
+    /// will automatically select the nearest enabled, setting if you are using Directdraw
+    /// playback
+    PlayResX,
+
+    /// This is the colour depth used by the script's author(s) when playing the script. SSA will
+    /// automatically select the nearest enabled setting if you are using Directdraw playback.
+    PlayDepth,
+
+    /// This is the Timer Speed for the script, as a percentage. eg. "100.0000" is exactly 100%. It
+    /// has four digits following the decimal point.
+    ///
+    /// The timer speed is a time multiplier applied to SSA's clock to stretch or compress the
+    /// duration of a script. A speed greater than 100% will reduce the overall duration, and means
+    /// that subtitles will progressively appear sooner and sooner. A speed less than 100% will
+    /// increase the overall duration of the script means subtitles will progressively appear later
+    /// and later (like a positive ramp time).
+    ///
+    /// The stretching or compressing only occurs during script playback - this value does not
+    /// change the actual timings for each event listed in the script.
+    ///
+    /// Check the SSA user guide if you want to know why "Timer Speed" is more powerful than "Ramp
+    /// Time", even though they both achieve the same result.
+    Timer,
+
+    /// Defines the default wrapping style:
+    /// - 0: smart wrapping, lines are evenly broken
+    /// - 1: end-of-line word wrapping, only \N breaks
+    /// - 2: no word wrapping, \n \N both breaks
+    /// - 3: same as 0, but lower line gets wider.
+    WrapStyle,
+}
+
+impl FromStr for ScriptInfoKey {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        use ScriptInfoKey::*;
+        match s.trim() {
+            "Title" => Ok(Title),
+            "Original Script" => Ok(OriginalScript),
+            "Original Translation" => Ok(OriginalTranslation),
+            "Original Editing" => Ok(OriginalEditing),
+            "Original Timing" => Ok(OriginalTiming),
+            "Synch Point" => Ok(SynchPoint),
+            "Script Updated By" => Ok(ScriptUpdatedBy),
+            "Update Details" => Ok(UpdateDetails),
+            "Script Type" => Ok(ScriptType),
+            "Collisions" => Ok(Collisions),
+            "PlayResY" => Ok(PlayResY),
+            "PlayResX" => Ok(PlayResX),
+            "PlayDepth" => Ok(PlayDepth),
+            "Timer" => Ok(Timer),
+            "WrapStyle" => Ok(WrapStyle),
+            _ => Err(format!("unknown Script Info key: {s}")),
+        }
+    }
+}
+
+impl FromStr for ASSFileSection {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        use ASSFileSection::*;
+        const TRIM_PAT: &[char] = &['[', ']', ' ', '\t'];
+        match s.trim_matches(TRIM_PAT) {
+            "Script Info" => Ok(ScriptInfo),
+            "Events" => Ok(Events),
+            "v4 Styles" | "v4 Styles+" => Ok(V4Styles),
+            _ => Err(format!("unknown section [{s}]")),
+        }
+    }
+}
diff --git a/src/Rust/vvs_ass/src/drawing.rs b/src/Rust/vvs_ass/src/drawing.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a3c9a997f735de2506a3d614a3bad7eb6bba7097
--- /dev/null
+++ b/src/Rust/vvs_ass/src/drawing.rs
@@ -0,0 +1,179 @@
+/// Enum used to represent drawing commands.
+///
+/// Things you should know:
+/// - Commands must appear after {\p1+} and before {\p0}. (except for \clip(..))
+/// - Drawings must always start with a move to command.
+/// - Drawings must form a closed shape.
+/// - All unclosed shape will be closed with a straight line automatically.
+/// - Overlapping shapes in the Dialogue line will be XOR-ed with each-other.
+/// - If the same command follows another, it isn’t needed to write its identifier letter again,
+///   only the coordinates.
+/// - The coordinates are relative to the current cursor position (baseline) and the alignment
+///   mode.
+/// - Commands p and c should only follow other b-spline commands.
+///
+/// Examples:
+/// - Square: `m 0 0 l 100 0 100 100 0 100`
+/// - Rounded square: `m 0 0 s 100 0 100 100 0 100 c`
+///   (c equals to `p 0 0 100 0 100 100` in this case)
+/// - Circle (almost): `m 50 0 b 100 0 100 100 50 100 b 0 100 0 0 50 0`
+///   (note that the 2nd 'b' is optional here)
+#[derive(Debug, Clone, PartialEq)]
+pub enum ASSDrawingCmd {
+    // Moves the cursor to <x>, <y>
+    M {
+        x: i64,
+        y: i64,
+    },
+
+    // Moves the cursor to <x>, <y> (unclosed shapes will be left open)
+    N {
+        x: i64,
+        y: i64,
+    },
+
+    // Draws a line to <x>, <y>
+    L {
+        x: i64,
+        y: i64,
+    },
+
+    // 3rd degree bezier curve to point 3 using point 1 and 2 as the control points
+    B {
+        x1: i64,
+        y1: i64,
+        x2: i64,
+        y2: i64,
+        x3: i64,
+        y3: i64,
+    },
+
+    // 3rd degree uniform b-spline to point N, must contain at least 3 coordinates
+    S {
+        x1: i64,
+        y1: i64,
+        x2: i64,
+        y2: i64,
+        x3: i64,
+        y3: i64,
+        others: Vec<(i64, i64)>,
+    },
+
+    // Extend b-spline to <x>, <y>
+    P {
+        x: i64,
+        y: i64,
+    },
+
+    // close b-spline
+    C,
+}
+
+/// Contains an ASS drawing.
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct ASSDrawing {
+    content: Vec<ASSDrawingCmd>,
+}
+
+impl ASSDrawingCmd {
+    pub fn is_move_cmd(&self) -> bool {
+        use ASSDrawingCmd::*;
+        matches!(self, M { .. } | N { .. })
+    }
+}
+
+impl ASSDrawing {
+    /// Verify that the drawing is correct!
+    pub fn verify(&self) -> bool {
+        match &self.content[..] {
+            [] => true,
+            [head] => head.is_move_cmd(),
+            whole @ [head, content @ ..] if head.is_move_cmd() => {
+                let mut in_bsplit_line = false;
+                for (prev, curr) in whole.iter().zip(content) {
+                    match curr {
+                        ASSDrawingCmd::M { .. } | ASSDrawingCmd::N { .. } => {
+                            // FIXME: This is incorrect for N, it could be at the last position,
+                            //        check latter if we can find a moar correct version of the
+                            //        check function...
+                            log::error!(target: "ass", "found move cmd `{curr:?}` in the middle of a drawing");
+                            return false;
+                        }
+
+                        ASSDrawingCmd::C | ASSDrawingCmd::P { .. } => {
+                            if !matches!(prev, ASSDrawingCmd::S { .. }) {
+                                log::error!(target: "ass", "found C/P cmd not after an S command");
+                                return false;
+                            }
+                            in_bsplit_line = false;
+                        }
+
+                        ASSDrawingCmd::S { .. } => in_bsplit_line = true,
+                        ASSDrawingCmd::B { .. } | ASSDrawingCmd::L { .. } => {}
+                    }
+                }
+                !in_bsplit_line
+            }
+            _ => false,
+        }
+    }
+}
+
+impl std::fmt::Display for ASSDrawingCmd {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ASSDrawingCmd::M { x, y } => write!(f, "m {x} {y}"),
+            ASSDrawingCmd::N { x, y } => write!(f, "n {x} {y}"),
+            ASSDrawingCmd::L { x, y } => write!(f, "l {x} {y}"),
+            ASSDrawingCmd::B {
+                x1,
+                y1,
+                x2,
+                y2,
+                x3,
+                y3,
+            } => write!(f, "b {x1} {y1} {x2} {y2} {x3} {y3}"),
+            ASSDrawingCmd::S {
+                x1,
+                y1,
+                x2,
+                y2,
+                x3,
+                y3,
+                others,
+            } if others.is_empty() => {
+                write!(f, "b {x1} {y1} {x2} {y2} {x3} {y3}")
+            }
+            ASSDrawingCmd::S {
+                x1,
+                y1,
+                x2,
+                y2,
+                x3,
+                y3,
+                others,
+            } => {
+                let others = others
+                    .iter()
+                    .map(|(x, y)| format!("{x} {y}"))
+                    .collect::<Vec<_>>()
+                    .join(" ");
+                write!(f, "b {x1} {y1} {x2} {y2} {x3} {y3} {others}")
+            }
+            ASSDrawingCmd::P { x, y } => write!(f, "p {x} {y}"),
+            ASSDrawingCmd::C => write!(f, "c"),
+        }
+    }
+}
+
+impl std::fmt::Display for ASSDrawing {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let content = self
+            .content
+            .iter()
+            .map(|cmd| format!("{cmd}"))
+            .collect::<Vec<_>>()
+            .join(" ");
+        write!(f, "{content}")
+    }
+}
diff --git a/src/Rust/vvs_ass/src/elements/line.rs b/src/Rust/vvs_ass/src/elements/line.rs
index 3ed50825a9de0e190ccdecb86a25125ad950bf2d..f58e003d5f950a24bb401f5fd9a3f4b662e91f9c 100644
--- a/src/Rust/vvs_ass/src/elements/line.rs
+++ b/src/Rust/vvs_ass/src/elements/line.rs
@@ -1,6 +1,6 @@
 use crate::{elements::syllabes::ASSSyllabesPtr, ASSAuxTablePtr, ASSPositionPtr};
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Default, Clone, PartialEq)]
 pub struct ASSLine {
     pub position: ASSPositionPtr,
     pub content: ASSSyllabesPtr,
@@ -8,3 +8,7 @@ pub struct ASSLine {
 }
 
 pub type ASSLinePtr = crate::Ptr<ASSLine>;
+
+impl ASSLine {
+    crate::impl_into_ptr! { ASSLinePtr }
+}
diff --git a/src/Rust/vvs_ass/src/elements/lines.rs b/src/Rust/vvs_ass/src/elements/lines.rs
index 87eabd77f85dd8255d0aac9cb33e5ccc8e5e5c76..cdd10df98b6aa2874c54b5bcc82f4bd5790143cd 100644
--- a/src/Rust/vvs_ass/src/elements/lines.rs
+++ b/src/Rust/vvs_ass/src/elements/lines.rs
@@ -1,6 +1,6 @@
 use crate::{elements::line::ASSLinePtr, ASSAuxTablePtr, ASSPositionPtr};
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Default, Clone, PartialEq)]
 pub struct ASSLines {
     pub position: ASSPositionPtr,
     pub content: Vec<ASSLinePtr>,
@@ -8,3 +8,7 @@ pub struct ASSLines {
 }
 
 pub type ASSLinesPtr = crate::Ptr<ASSLines>;
+
+impl ASSLines {
+    crate::impl_into_ptr! { ASSLinesPtr }
+}
diff --git a/src/Rust/vvs_ass/src/elements/mod.rs b/src/Rust/vvs_ass/src/elements/mod.rs
index b0808c07612abd432d5806be6c128bcfa6d61b21..a411fa4c5543192bab6f28fbe900e2fbbfa90e12 100644
--- a/src/Rust/vvs_ass/src/elements/mod.rs
+++ b/src/Rust/vvs_ass/src/elements/mod.rs
@@ -3,7 +3,33 @@ mod lines;
 mod syllabe;
 mod syllabes;
 
-pub use line::*;
-pub use lines::*;
-pub use syllabe::*;
-pub use syllabes::*;
+pub use self::{line::*, lines::*, syllabe::*, syllabes::*};
+
+use crate::{definitions::ScriptInfoKey, ASSStyle};
+use std::collections::HashMap;
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ASSContainer {
+    pub lines: ASSLinesPtr,
+    pub script_info: HashMap<ScriptInfoKey, String>,
+    pub styles: HashMap<String, ASSStyle>,
+}
+
+pub type ASSContainerPtr = crate::Ptr<ASSContainer>;
+
+impl ASSContainer {
+    crate::impl_into_ptr! { ASSContainerPtr }
+
+    /// Create an ASS container from its parts, they must be valide!
+    pub(crate) fn from_parts(
+        lines: ASSLinesPtr,
+        script_info: HashMap<ScriptInfoKey, String>,
+        styles: HashMap<String, ASSStyle>,
+    ) -> Self {
+        Self {
+            lines,
+            script_info,
+            styles,
+        }
+    }
+}
diff --git a/src/Rust/vvs_ass/src/elements/syllabe.rs b/src/Rust/vvs_ass/src/elements/syllabe.rs
index 1d412182b0be418bdb1ada8a2f4c365e777da42e..00fa4633805ac044f2ecfcf65b9a857588b2942b 100644
--- a/src/Rust/vvs_ass/src/elements/syllabe.rs
+++ b/src/Rust/vvs_ass/src/elements/syllabe.rs
@@ -1,6 +1,6 @@
 use crate::{ASSAuxTablePtr, ASSPositionPtr};
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Default, Clone, PartialEq)]
 pub struct ASSSyllabe {
     pub position: ASSPositionPtr,
     pub content: String,
@@ -8,3 +8,7 @@ pub struct ASSSyllabe {
 }
 
 pub type ASSSyllabePtr = crate::Ptr<ASSSyllabe>;
+
+impl ASSSyllabe {
+    crate::impl_into_ptr! { ASSSyllabePtr }
+}
diff --git a/src/Rust/vvs_ass/src/elements/syllabes.rs b/src/Rust/vvs_ass/src/elements/syllabes.rs
index f57f8a759a3ba128751e20e2ab6387c5f2c7050e..8a8143d9cf0b2408b538717401fa11c21692815f 100644
--- a/src/Rust/vvs_ass/src/elements/syllabes.rs
+++ b/src/Rust/vvs_ass/src/elements/syllabes.rs
@@ -1,6 +1,6 @@
 use crate::{elements::syllabe::ASSSyllabePtr, ASSAuxTablePtr, ASSPositionPtr};
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Default, Clone, PartialEq)]
 pub struct ASSSyllabes {
     pub position: ASSPositionPtr,
     pub content: Vec<ASSSyllabePtr>,
@@ -8,3 +8,7 @@ pub struct ASSSyllabes {
 }
 
 pub type ASSSyllabesPtr = crate::Ptr<ASSSyllabes>;
+
+impl ASSSyllabes {
+    crate::impl_into_ptr! { ASSSyllabesPtr }
+}
diff --git a/src/Rust/vvs_ass/src/lib.rs b/src/Rust/vvs_ass/src/lib.rs
index 783ab954e5a80e6e38f2809d5bf962ac62b11591..0e1a05ae0fb3ebbb69ab1c75ac971a4a77027983 100644
--- a/src/Rust/vvs_ass/src/lib.rs
+++ b/src/Rust/vvs_ass/src/lib.rs
@@ -1,14 +1,20 @@
 //! ASS objects for Vivy.
 
+mod colors;
+mod definitions;
+mod drawing;
 mod elements;
 mod position;
+mod reader;
+mod styles;
 mod types;
 mod values;
 
-pub use elements::*;
-pub use position::*;
-pub use types::*;
-pub use values::*;
+#[cfg(test)]
+mod tests;
+
+pub use crate::{colors::*, drawing::*, elements::*, position::*, styles::*, types::*, values::*};
+pub use reader::{ass_lines_from_file, ASSElementReaderError};
 
 pub type Ptr<T> = std::rc::Rc<std::cell::RefCell<T>>;
 
@@ -18,3 +24,13 @@ macro_rules! ptr {
         std::rc::Rc::new(std::cell::RefCell::new($expr))
     };
 }
+
+macro_rules! impl_into_ptr {
+    ($PTR: ident) => {
+        /// Wrap the struct into a pointer.
+        pub fn into_ptr(self) -> $PTR {
+            crate::ptr!(self)
+        }
+    };
+}
+pub(crate) use impl_into_ptr;
diff --git a/src/Rust/vvs_ass/src/position.rs b/src/Rust/vvs_ass/src/position.rs
index 7012e97a368c3da5cdf418508d5d8d011700f2be..a75eb9e6b3947994e8236b3c3bc70d9d07e7c00a 100644
--- a/src/Rust/vvs_ass/src/position.rs
+++ b/src/Rust/vvs_ass/src/position.rs
@@ -1,9 +1,26 @@
-/// The position of an object from the top left corner. The real position depends on the align of
-/// the object.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct ASSPosition {
-    pub x: i64,
-    pub y: i64,
+/// The position of an object from the top left corner of the screen. The real position depends on
+/// the align of the object.
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+pub enum ASSPosition {
+    /// An unspecified position.
+    #[default]
+    Unspecified,
+
+    /// A static position.
+    Pos { x: i64, y: i64 },
+
+    /// A linear movement.
+    LinearMove { x1: i64, y1: i64, x2: i64, y2: i64 },
+
+    /// A linear movement with time step specified.
+    TimedMove {
+        x1: i64,
+        y1: i64,
+        x2: i64,
+        y2: i64,
+        from_ms: i64,
+        to_ms: i64,
+    },
 }
 
 /// The alignement of the object.
@@ -37,4 +54,36 @@ pub enum ASSAlign {
     TR = 9,
 }
 
+/// Pointer used to store a position, to help with mutability with LUA wrappers.
 pub type ASSPositionPtr = crate::Ptr<ASSPosition>;
+
+impl std::str::FromStr for ASSAlign {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.trim() {
+            "1" => Ok(ASSAlign::BL),
+            "2" => Ok(ASSAlign::BC),
+            "3" => Ok(ASSAlign::BR),
+            "4" => Ok(ASSAlign::CL),
+            "5" => Ok(ASSAlign::CC),
+            "6" => Ok(ASSAlign::CR),
+            "7" => Ok(ASSAlign::TL),
+            "8" => Ok(ASSAlign::TC),
+            "9" => Ok(ASSAlign::TR),
+            s => Err(format!(
+                "invalid value for ASSAlign, must be in `1..=9`, got: {s}"
+            )),
+        }
+    }
+}
+
+impl From<vvs_font::Point> for ASSPosition {
+    fn from(vvs_font::Point { x, y }: vvs_font::Point) -> Self {
+        ASSPosition::Pos { x, y }
+    }
+}
+
+impl ASSPosition {
+    crate::impl_into_ptr! { ASSPositionPtr }
+}
diff --git a/src/Rust/vvs_ass/src/reader/ass.rs b/src/Rust/vvs_ass/src/reader/ass.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d024c677029c88102e28dd2eb571690773d781b7
--- /dev/null
+++ b/src/Rust/vvs_ass/src/reader/ass.rs
@@ -0,0 +1,225 @@
+use crate::{
+    definitions::{ASSEvent, ASSFileSection, ScriptInfoKey},
+    reader::ASSElementReader,
+    ASSColor, ASSContainer, ASSElementReaderError, ASSLines, ASSStyle,
+};
+use std::collections::HashMap;
+
+/// Documentation available here: http://www.tcax.org/docs/ass-specs.html or in the `utils/manual`
+/// folder from the root of the vvs project.
+#[derive(Debug, Default)]
+pub struct ASSReader {
+    section: Option<ASSFileSection>,
+    script_info: HashMap<ScriptInfoKey, String>,
+    styles: HashMap<String, ASSStyle>,
+    events: Vec<ASSEvent>,
+}
+
+fn parse_color(color: &str, name: &str) -> Result<ASSColor, ASSElementReaderError> {
+    ASSColor::try_from_bgra(color)
+        .map_err(|err| ASSElementReaderError::Custom(format!("invalid {name} color: {err}")))
+}
+
+fn parse_boolean(boolean: &str, name: &str) -> Result<bool, ASSElementReaderError> {
+    match boolean.trim() {
+        "-1" | "1" | "+1" => Ok(true),
+        "0" => Ok(false),
+        boolean => Err(ASSElementReaderError::Custom(format!(
+            "invalid ass boolean for {name} found: {boolean}"
+        ))),
+    }
+}
+
+fn parse_float(float: &str, name: &str) -> Result<f64, ASSElementReaderError> {
+    float.parse::<f64>().map_err(|err| {
+        ASSElementReaderError::Custom(format!("invalid float value for {name}: {err}"))
+    })
+}
+
+fn parse_int(int: &str, name: &str) -> Result<i64, ASSElementReaderError> {
+    int.parse::<i64>().map_err(|err| {
+        ASSElementReaderError::Custom(format!("invalid integer value for {name}: {err}"))
+    })
+}
+
+/// Parse dates in the `0:00:00:00` format
+fn parse_date(date: &str, name: &str) -> Result<i64, ASSElementReaderError> {
+    let [h, m, s, c] = &date.split(':').collect::<Vec<_>>()[..] else {
+        return Err(ASSElementReaderError::Custom(format!("invalid date for {name}: {date}")))
+    };
+    let check_compnent = |str: &str, compnent: &str, len: usize| {
+        if str.len() > len {
+            Err(ASSElementReaderError::Custom(format!(
+                "invalid date for {name}: {date}"
+            )))
+        } else {
+            let ret = str.parse::<u16>().map_err(|err| {
+                ASSElementReaderError::Custom(format!(
+                    "invalid component {compnent} for date {name}: {err}"
+                ))
+            })?;
+            Ok(ret as i64)
+        }
+    };
+    let (h, m, s, c) = (
+        check_compnent(h, "hours", 1)?,
+        check_compnent(m, "minutes", 2)?,
+        check_compnent(s, "seconds", 2)?,
+        check_compnent(c, "centi-seconds", 2)?,
+    );
+    Ok(((h * 60 + m) * 60 + s) * 100 + c)
+}
+
+impl ASSReader {
+    fn read_script_info(&mut self, line: &str) -> Result<(), ASSElementReaderError> {
+        let Some((key, value)) = line.split_once(':') else {
+            return Err(ASSElementReaderError::Custom(format!("invalid script info line: {line}")))
+        };
+        let key = match key
+            .trim()
+            .parse::<ScriptInfoKey>()
+            .map_err(ASSElementReaderError::Custom)?
+        {
+            ScriptInfoKey::ScriptType if value.ne("V4.00+") => {
+                return Err(ASSElementReaderError::Custom(format!(
+                    "invalid value for key '{key:?}' in script info section: {value}"
+                )))
+            }
+            ScriptInfoKey::WrapStyle if !("0".."3").contains(&value) => {
+                return Err(ASSElementReaderError::Custom(format!(
+                    "invalid value for key '{key:?}' in script info section: {value}"
+                )))
+            }
+            key => key,
+        };
+        match self.script_info.get(&key) {
+            Some(_) => Err(ASSElementReaderError::Custom(format!(
+                "redefinition of key '{key:?}' in script info section"
+            ))),
+            None => {
+                self.script_info.insert(key, value.trim().to_string());
+                Ok(())
+            }
+        }
+    }
+
+    fn read_v4_style(&mut self, line: &str) -> Result<(), ASSElementReaderError> {
+        let fields: Vec<_> = line.trim().split(',').map(|str| str.trim()).collect();
+        let [_, name, font_name, font_size, primary_color, secondary_color, outline_color, back_color, bold, italic, underline, strikeout, scalex, scaley, spacing, angle, border_style, outline, shadow, alignment, marginl, marginr, marginv, encoding] = &fields[..] else {
+            return Err(ASSElementReaderError::Custom(format!("invalid line composed of fields: {fields:#?}")))
+        };
+
+        if encoding.ne(&"0") {
+            return Err(ASSElementReaderError::Custom(format!(
+                "we expected the encoding '0', got: {encoding}"
+            )));
+        }
+
+        let style = ASSStyle {
+            name: name.to_string(),
+            font_name: font_name.to_string(),
+            font_size: parse_int(font_size, "font size")?,
+            primary_color: parse_color(primary_color, "primary")?,
+            secondary_color: parse_color(secondary_color, "secondary")?,
+            outline_color: parse_color(outline_color, "outline")?,
+            back_color: parse_color(back_color, "back")?,
+            bold: parse_boolean(bold, "bold")?,
+            italic: parse_boolean(italic, "italic")?,
+            underline: parse_boolean(underline, "underline")?,
+            strikeout: parse_boolean(strikeout, "strikeout")?,
+            scale_x: parse_float(scalex, "scale_x")?,
+            scale_y: parse_float(scaley, "scale_y")?,
+            spacing: parse_float(spacing, "spacing")?,
+            angle: parse_float(angle, "angle")?,
+            border_style: border_style
+                .parse()
+                .map_err(ASSElementReaderError::Custom)?,
+            outline: parse_float(outline, "outline")?,
+            shadow: parse_float(shadow, "shadow")?,
+            alignment: alignment.parse().map_err(ASSElementReaderError::Custom)?,
+            margin_l: parse_int(marginl, "margin_l")?,
+            margin_r: parse_int(marginr, "margin_r")?,
+            margin_v: parse_int(marginv, "margin_v")?,
+        };
+
+        match self.styles.insert(name.to_string(), style) {
+            None => Ok(()),
+            Some(old) => {
+                log::error!(target: "ass", "redefine style '{name}', previous style was: {old:#?}");
+                Err(ASSElementReaderError::Custom(format!(
+                    "redefinition of style '{name}'"
+                )))
+            }
+        }
+    }
+
+    fn read_event(&mut self, line: &str) -> Result<(), ASSElementReaderError> {
+        let fields: Vec<_> = line.trim().splitn(8, ',').map(|str| str.trim()).collect();
+        let [marked, layer, start, end, style, name, effect, text] = &fields[..] else {
+            return Err(ASSElementReaderError::Custom(format!("invalid line composed of fields: {fields:#?}")))
+        };
+        self.events.push(ASSEvent {
+            marked: parse_boolean(marked, "marked")?,
+            layer: parse_int(layer, "layer")?,
+            start: parse_date(start, "start")?,
+            end: parse_date(end, "end")?,
+            style: style.to_string(),
+            name: name.to_string(),
+            effect: effect.to_string(),
+            text: text.to_string(),
+        });
+        Ok(())
+    }
+}
+
+impl ASSElementReader for ASSReader {
+    fn try_read(
+        mut self,
+        file: impl std::io::BufRead,
+    ) -> Result<crate::ASSContainerPtr, ASSElementReaderError> {
+        // Parse the file
+        for line in file.lines() {
+            let line = line.map_err(ASSElementReaderError::FailedToReadLine)?;
+            let line = line.trim();
+
+            if line.is_empty() || line.starts_with(';') {
+                continue;
+            } else if line.starts_with('[') && line.ends_with(']') {
+                self.section = Some(
+                    line.parse::<ASSFileSection>()
+                        .map_err(ASSElementReaderError::Custom)?,
+                );
+            } else {
+                match self.section {
+                    Some(ASSFileSection::ScriptInfo) => self.read_script_info(line)?,
+                    Some(ASSFileSection::V4Styles) => self.read_v4_style(line)?,
+                    Some(ASSFileSection::Events) => self.read_event(line)?,
+                    None => {
+                        return Err(ASSElementReaderError::Custom(format!(
+                            "found the following line without a section: {line}"
+                        )))
+                    }
+                }
+            }
+        }
+
+        // Verify integrity + order events
+        self.events.sort_by(|a, b| Ord::cmp(&a.start, &b.end));
+        if !self.styles.contains_key(ASSStyle::default_name()) {
+            self.styles
+                .insert(ASSStyle::default_name_string(), ASSStyle::default());
+        }
+        for ASSEvent { style, .. } in &mut self.events {
+            if !self.styles.contains_key(style) {
+                *style = ASSStyle::default_name_string()
+            }
+        }
+
+        Ok(ASSContainer::from_parts(
+            ASSLines::default().into_ptr(),
+            self.script_info,
+            self.styles,
+        )
+        .into_ptr())
+    }
+}
diff --git a/src/Rust/vvs_ass/src/reader/json.rs b/src/Rust/vvs_ass/src/reader/json.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e972d937125a49db1ed08d50a3768f3ffd4c02e9
--- /dev/null
+++ b/src/Rust/vvs_ass/src/reader/json.rs
@@ -0,0 +1,15 @@
+use crate::{reader::ASSElementReader, ASSElementReaderError};
+
+/// Documentation available here: http://www.tcax.org/docs/ass-specs.html or in the `utils/manual`
+/// folder.
+#[derive(Debug, Default)]
+pub struct JSONReader {}
+
+impl ASSElementReader for JSONReader {
+    fn try_read(
+        self,
+        _file: impl std::io::BufRead,
+    ) -> Result<crate::ASSContainerPtr, ASSElementReaderError> {
+        todo!()
+    }
+}
diff --git a/src/Rust/vvs_ass/src/reader/mod.rs b/src/Rust/vvs_ass/src/reader/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fb96abcb41f1fe445daddf3b618c5f23164f9348
--- /dev/null
+++ b/src/Rust/vvs_ass/src/reader/mod.rs
@@ -0,0 +1,58 @@
+//! Read the content of an ASS file / a Vivy subtitle file and creates an
+//! [vvs_ass::elements::lines::ASSLinesPtr] structure accordingly.
+
+use crate::ASSContainerPtr;
+use std::{
+    fs::File,
+    io::{BufReader, Error as IoError},
+    path::{Path, PathBuf},
+};
+use thiserror::Error;
+
+mod ass;
+mod json;
+
+#[derive(Debug, Error)]
+pub enum ASSElementReaderError {
+    #[error("file has no extension: {0}")]
+    NoExtension(PathBuf),
+
+    #[error("failed to open file {0}: {1}")]
+    FailedToOpenFile(PathBuf, IoError),
+
+    #[error("unknown file extension for subtitles")]
+    UnknownExtension(String),
+
+    #[error("failed to read line: {0}")]
+    FailedToReadLine(IoError),
+
+    #[error("{0}")]
+    Custom(String),
+}
+
+trait ASSElementReader {
+    fn try_read(
+        self,
+        file: impl std::io::BufRead,
+    ) -> Result<ASSContainerPtr, ASSElementReaderError>;
+}
+
+pub fn ass_lines_from_file(
+    file: impl AsRef<Path>,
+) -> Result<ASSContainerPtr, ASSElementReaderError> {
+    let file = file.as_ref();
+    let Some(extension) = file.extension() else {
+        return Err(ASSElementReaderError::NoExtension(file.to_path_buf()));
+    };
+    let content = BufReader::new(
+        File::open(file)
+            .map_err(|err| ASSElementReaderError::FailedToOpenFile(file.to_path_buf(), err))?,
+    );
+    match &extension.to_string_lossy()[..] {
+        "ass" => ass::ASSReader::default().try_read(content),
+        "vvsb" | "json" => json::JSONReader::default().try_read(content),
+        extension => Err(ASSElementReaderError::UnknownExtension(
+            extension.to_string(),
+        )),
+    }
+}
diff --git a/src/Rust/vvs_ass/src/styles.rs b/src/Rust/vvs_ass/src/styles.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9b2a8ea763e285701f1f8df3714abd7ed7e7b814
--- /dev/null
+++ b/src/Rust/vvs_ass/src/styles.rs
@@ -0,0 +1,152 @@
+use crate::{ASSAlign, ASSColor};
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+pub enum ASSBorderStyle {
+    #[default]
+    OutlineAndDropShadow = 1,
+    OpaqueBox = 3,
+}
+
+/// Type used to describe an ASS style.
+#[derive(Debug, Clone, PartialEq)]
+pub struct ASSStyle {
+    /// The name of the Style. Case sensitive. Cannot include commas.
+    pub name: String,
+
+    /// The fontname as used by libass. Case-sensitive. Can't include comas.
+    pub font_name: String,
+
+    /// The size of the font. Don't use a thing that is too big... Must be positive.
+    pub font_size: i64,
+
+    /// The colour that a subtitle will normally appear in.
+    pub primary_color: ASSColor,
+
+    /// This colour may be used instead of the Primary colour when a subtitle is automatically
+    /// shifted to prevent an onscreen collsion, to distinguish the different subtitles.
+    pub secondary_color: ASSColor,
+
+    /// Color of the outline of each characters.
+    pub outline_color: ASSColor,
+
+    /// This is the colour of the subtitle outline or shadow, if these are used.
+    pub back_color: ASSColor,
+
+    /// Whever the font is in bold.
+    pub bold: bool,
+
+    /// Whever the font is in italic.
+    pub italic: bool,
+
+    /// Whever the font is underlined.
+    pub underline: bool,
+
+    /// Whever the font is strikeout.
+    pub strikeout: bool,
+
+    /// Modifies the width of the font. Value in percent.
+    pub scale_x: f64,
+
+    /// Modifies the height of the font. Value in percent.
+    pub scale_y: f64,
+
+    /// Extra space between characters. Value in pixels.
+    pub spacing: f64,
+
+    /// The origin of the rotation is defined by the alignment. Can be a floating point number.
+    /// Value in degrees.
+    pub angle: f64,
+
+    /// The border style.
+    pub border_style: ASSBorderStyle,
+
+    /// If border_style is [ASSBorderStyle::OutlineAndDropShadow], then this specifies the width of
+    /// the outline around the text, in pixels.
+    ///
+    /// Values may be 0, 1, 2, 3 or 4 the documentation says, but libass seems to support floating
+    /// values.
+    pub outline: f64,
+
+    /// If border_style is [ASSBorderStyle::OutlineAndDropShadow], then this specifies the depth of
+    /// the drop shadow behind the text, in pixels.
+    ///
+    /// Values may be 0, 1, 2, 3 or 4 the documentation says, but libass seems to support floating
+    /// values. Drop shadow is always used in addition to an outline - SSA will force an outline of
+    /// 1 pixel if no outline width is given.
+    pub shadow: f64,
+
+    /// This sets how text is "justified" within the Left/Right onscreen margins, and also the
+    /// vertical placing.
+    pub alignment: ASSAlign,
+
+    /// This defines the Left Margin in pixels. It is the distance from the left-hand edge of the
+    /// screen.The three onscreen margins (margin_l, margin_r, margin_v) define areas in which the
+    /// subtitle text will be displayed.
+    ///
+    /// Must be a positive integer.
+    pub margin_l: i64,
+
+    /// This defines the Right Margin in pixels. It is the distance from the right-hand edge of the
+    /// screen. The three onscreen margins (margin_l, margin_r, margin_v) define areas in which the
+    /// subtitle text will be displayed.
+    pub margin_r: i64,
+
+    /// This defines the vertical Left Margin in pixels.
+    /// - For a subtitle, it is the distance from the bottom of the screen.
+    /// - For a toptitle, it is the distance from the top of the screen.
+    /// - For a midtitle, the value is ignored - the text will be vertically centred
+    pub margin_v: i64,
+}
+
+impl std::str::FromStr for ASSBorderStyle {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.trim() {
+            "1" => Ok(ASSBorderStyle::OutlineAndDropShadow),
+            "3" => Ok(ASSBorderStyle::OpaqueBox),
+            s => Err(format!(
+                "invalid value '{s}' for ASSBorderStyle, must be 1 or 3"
+            )),
+        }
+    }
+}
+
+impl ASSStyle {
+    pub fn default_name() -> &'static str {
+        "Default"
+    }
+
+    pub fn default_name_string() -> String {
+        Self::default_name().to_string()
+    }
+}
+
+impl Default for ASSStyle {
+    fn default() -> Self {
+        Self {
+            name: Self::default_name_string(),
+            font_name: Default::default(),
+            font_size: 14,
+            primary_color: ASSColor::WHITE,
+            secondary_color: ASSColor::RED,
+            outline_color: ASSColor::BLACK,
+            back_color: ASSColor::BLACK,
+            bold: false,
+            italic: false,
+            underline: false,
+            strikeout: false,
+            scale_x: 100.0,
+            scale_y: 100.0,
+            spacing: 0.0,
+            angle: 0.0,
+            border_style: Default::default(),
+            outline: 3.0,
+            shadow: 1.0,
+            alignment: ASSAlign::TC,
+            margin_l: 0,
+            margin_r: 0,
+            margin_v: 10,
+        }
+    }
+}
diff --git a/src/Rust/vvs_ass/src/tests.rs b/src/Rust/vvs_ass/src/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e26c65d260269877ed06465d023552f47e650fa8
--- /dev/null
+++ b/src/Rust/vvs_ass/src/tests.rs
@@ -0,0 +1,77 @@
+mod color {
+    use crate::*;
+
+    // https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma
+    const RGB2HSL_TABLE: [(&str, f32, f32, f32); 19] = [
+        ("FFFFFF", 0.000, 0.000, 1.000),
+        ("808080", 0.000, 0.000, 0.500),
+        ("000000", 0.000, 0.000, 0.000),
+        ("FF0000", 0.000, 1.000, 0.500),
+        ("BFBF00", 60.00, 1.000, 0.375),
+        ("008000", 120.0, 1.000, 0.250),
+        ("80FFFF", 180.0, 1.000, 0.750),
+        ("8080FF", 240.0, 1.000, 0.750),
+        ("BF40BF", 300.0, 0.500, 0.500),
+        ("A0A424", 61.80, 0.638, 0.393),
+        ("411BEA", 251.1, 0.832, 0.511),
+        ("1EAC41", 134.9, 0.707, 0.396),
+        ("F0C80E", 49.50, 0.893, 0.497),
+        ("B430E5", 283.7, 0.775, 0.542),
+        ("ED7651", 14.30, 0.817, 0.624),
+        ("FEF888", 56.90, 0.991, 0.765),
+        ("19CB97", 162.4, 0.779, 0.447),
+        ("362698", 248.3, 0.601, 0.373),
+        ("7E7EB8", 240.5, 0.290, 0.607),
+    ];
+
+    #[test]
+    fn test_from_string() {
+        assert!(ASSColor::try_from_rgba("#&AABBCC&").is_ok());
+        assert!(ASSColor::try_from_rgba("#AABBCC").is_ok());
+        assert!(ASSColor::try_from_rgba("&AABBCC&").is_ok());
+        assert!(ASSColor::try_from_rgba("AABBCC").is_ok());
+        assert!(ASSColor::try_from_rgba("AABBCCAA").is_ok());
+
+        assert_eq!(
+            ASSColor::try_from_rgba("AABBCC").unwrap(),
+            ASSColor::try_from_bgra("CCBBAA").unwrap()
+        );
+    }
+
+    #[test]
+    fn test_rgb2hsl() {
+        for (rgb, h_target, s1_target, l1_target) in RGB2HSL_TABLE {
+            const EPSILON_DEG: f32 = 1.0;
+            const EPSILON_0_1: f32 = 10E-3;
+            let ASSColor::HSLA { h, s, l, .. } = ASSColor::try_from_rgba(rgb).unwrap().into_hsla() else { unreachable!() };
+            assert!(
+                (h_target - h).abs() <= EPSILON_DEG,
+                "invalid convertion for color #{rgb}, hue {h_target} != {h}"
+            );
+            assert!(
+                (s - s1_target).abs() <= EPSILON_0_1,
+                "invalid convertion for color #{rgb}, s {s1_target} != {s}"
+            );
+            assert!(
+                (l - l1_target).abs() <= EPSILON_0_1,
+                "invalid convertion for color #{rgb}, l {l1_target} != {l}"
+            );
+        }
+    }
+
+    #[test]
+    fn test_hsl2rgb() {
+        for (rgb, h, s, l) in RGB2HSL_TABLE {
+            macro_rules! eq {
+                ($a: expr, $b: expr, $($msg: expr),+) => {
+                    assert!(($a as f32 - $b as f32).abs() <= 1.0, $($msg),+);
+                };
+            }
+            let ASSColor::RGBA { r, g, b, .. } = ASSColor::HSLA { h, s, l, a: 0 }.into_rgba() else { unreachable!() };
+            let ASSColor::RGBA { r: r_target, g: g_target, b: b_target, .. } = ASSColor::try_from_rgba(rgb).unwrap() else { unreachable!() };
+            eq! { r_target, r, "invalid convertion on red for #{rgb}: {r_target} != {r}"}
+            eq! { g_target, g, "invalid convertion on green for #{rgb}: {g_target} != {g}"}
+            eq! { b_target, b, "invalid convertion on blue for #{rgb}: {b_target} != {b}"}
+        }
+    }
+}
diff --git a/src/Rust/vvs_ass/src/types.rs b/src/Rust/vvs_ass/src/types.rs
index 6d6928b2cf936f6be710cad1526b7e5569208ee9..dedd4a63ae51af47a7009c0b4edd7649ef61bcb4 100644
--- a/src/Rust/vvs_ass/src/types.rs
+++ b/src/Rust/vvs_ass/src/types.rs
@@ -40,6 +40,7 @@ pub enum ASSType {
 }
 
 impl ASSType {
+    /// Get the name of the ASS type.
     pub fn as_str(&self) -> &'static str {
         match self {
             ASSType::Lines => "lines",
@@ -49,6 +50,7 @@ impl ASSType {
         }
     }
 
+    /// Get the name of the type, but padded with spaces.
     pub fn as_padded_str(&self) -> &'static str {
         match self {
             ASSType::Lines => "lines   ",
@@ -57,6 +59,14 @@ impl ASSType {
             ASSType::Syllabe => "syllabe ",
         }
     }
+
+    /// Get the base type of the ASS type
+    pub fn base_type(&self) -> Self {
+        match self {
+            ASSType::Lines | ASSType::Line => ASSType::Line,
+            ASSType::Syllabes | ASSType::Syllabe => ASSType::Syllabe,
+        }
+    }
 }
 
 impl AsRef<str> for ASSType {
diff --git a/src/Rust/vvs_ass/src/values.rs b/src/Rust/vvs_ass/src/values.rs
index 7c656b477963aac846ffb1b7e00fcbfa0e4b444b..1dce8df853975ed9d446ee4c2371e5ffa13137e9 100644
--- a/src/Rust/vvs_ass/src/values.rs
+++ b/src/Rust/vvs_ass/src/values.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, collections::HashMap, convert::TryFrom, rc::Rc};
+use std::{collections::HashMap, convert::TryFrom};
 
 /// The values that can be added to an ASS element.
 #[derive(Debug, Clone, PartialEq)]
@@ -13,7 +13,8 @@ pub enum ASSAuxValue {
 #[derive(Debug, Default, Clone, PartialEq)]
 pub struct ASSAuxTable(HashMap<String, ASSAuxValue>);
 
-pub type ASSAuxTablePtr = Rc<RefCell<ASSAuxTable>>;
+/// A pointer type for the [ASSAuxTable] type, for easy wrapping
+pub type ASSAuxTablePtr = crate::Ptr<ASSAuxTable>;
 
 impl ASSAuxValue {
     pub fn type_str(&self) -> &'static str {
@@ -61,7 +62,7 @@ impl std::fmt::Display for ASSAuxValue {
             ASSAuxValue::Floating(val) => write!(f, "{val}"),
             ASSAuxValue::Integer(val) => write!(f, "{val}"),
             ASSAuxValue::Boolean(val) => write!(f, "{val}"),
-            ASSAuxValue::String(val) => f.write_str(&val),
+            ASSAuxValue::String(val) => f.write_str(val),
         }
     }
 }
diff --git a/src/Rust/vvs_cli/Cargo.toml b/src/Rust/vvs_cli/Cargo.toml
index 2a77634e309b0a1bcfe0b9a2ebd475071082de90..e7656212be8817a4789ee2ece2c116dae27fa8ae 100644
--- a/src/Rust/vvs_cli/Cargo.toml
+++ b/src/Rust/vvs_cli/Cargo.toml
@@ -4,7 +4,7 @@ version.workspace = true
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
-description.workspace = true
+description = "The CLI for VVS (VVCC)"
 
 [[bin]]
 name = "vvcc"
@@ -13,6 +13,8 @@ path = "src/main.rs"
 [dependencies]
 vvs_lua = { path = "../vvs_lua" }
 vvs_repl = { path = "../vvs_repl" }
+vvs_font = { path = "../vvs_font" }
+vvs_utils = { path = "../vvs_utils" }
 
 lazy_static.workspace = true
 thiserror.workspace = true
diff --git a/src/Rust/vvs_cli/src/args.rs b/src/Rust/vvs_cli/src/args.rs
index 08c0161b8a33c4d22a9c8102012e905f1ba19529..26fd7ad1bc81c037f52ed45aac096adf6b723cba 100644
--- a/src/Rust/vvs_cli/src/args.rs
+++ b/src/Rust/vvs_cli/src/args.rs
@@ -8,10 +8,10 @@ use std::path::PathBuf;
          , version
          , about
          , name = "vvcc"
-         , group = clap::ArgGroup::new("action").args(["manpage", "shell", "script.vvs"])
-         , group = clap::ArgGroup::new("repl")  .args(["interactive"]) .conflicts_with_all(["shell", "manpage"])
-         , group = clap::ArgGroup::new("opts")  .args(["options.toml"]).conflicts_with_all(["shell", "manpage"])
-         , group = clap::ArgGroup::new("infos") .args(["info"])        .conflicts_with_all(["shell", "manpage"])
+         , group = clap::ArgGroup::new("action").args(["manpage", "shell", "font-file", "script.vvs"])
+         , group = clap::ArgGroup::new("repl")  .args(["interactive"]) .conflicts_with_all(["shell", "manpage", "font-file"])
+         , group = clap::ArgGroup::new("opts")  .args(["options.toml"]).conflicts_with_all(["shell", "manpage", "font-file"])
+         , group = clap::ArgGroup::new("infos") .args(["info"])        .conflicts_with_all(["shell", "manpage", "font-file"])
 )]
 pub struct Args {
     /// The script to run.
@@ -57,6 +57,13 @@ pub struct Args {
     )]
     pub info: bool,
 
+    /// Display infos about embeded fonts or external fonts.
+    #[arg( long   = "font-info"
+         , action = clap::ArgAction::Set
+         , id     = "font-file"
+    )]
+    pub font_info: Option<Option<PathBuf>>,
+
     /// Make vvcc more verbose, repeat to make it even more verbose
     #[arg( long
          , short  = 'v'
diff --git a/src/Rust/vvs_cli/src/main.rs b/src/Rust/vvs_cli/src/main.rs
index 3a20be9f72d84d7a9f146c7868f536e89b6379d5..72abf27bb0f53bf7bae23455f5edfaa6a0b8d168 100644
--- a/src/Rust/vvs_cli/src/main.rs
+++ b/src/Rust/vvs_cli/src/main.rs
@@ -3,6 +3,29 @@
 use anyhow::{Context, Result};
 use vvs_cli::{args, logger};
 
+fn print_face_info(name: &str, font: &[u8]) -> Result<()> {
+    let font =
+        vvs_font::Font::try_from(font).with_context(|| format!("failed to parse font {name}"))?;
+
+    let font_types = font
+        .font_types()
+        .into_iter()
+        .map(|ty| format!("{ty:?}"))
+        .collect::<Vec<_>>()
+        .join(", ");
+
+    println!("###");
+    println!(
+        "# Family Name:        {}",
+        font.name().with_context(|| "failed to get the font name")?
+    );
+    println!("# Name(s):            {}", font.family_names().join(", "));
+    println!("# Font Type(s):       {font_types}");
+    println!("# Number of glyph(s): {}", font.number_of_glyphs());
+
+    Ok(())
+}
+
 fn main() -> Result<()> {
     logger::init(None).map_err(Box::new)?;
     logger::ignore_target("rustyline");
@@ -14,6 +37,7 @@ fn main() -> Result<()> {
         shell,
         manpage,
         info,
+        font_info,
     } = <args::Args as clap::Parser>::parse();
     logger::level(verbose);
 
@@ -30,6 +54,20 @@ fn main() -> Result<()> {
         let command_name = command.get_name().to_string();
         clap_complete::generate(shell, &mut command, command_name, &mut std::io::stdout());
         return Ok(());
+    } else if let Some(font_info) = font_info {
+        match font_info {
+            Some(path) => {
+                let font = std::fs::read(&path)
+                    .with_context(|| format!("failed to read font: {}", path.to_string_lossy()))?;
+                print_face_info(&path.to_string_lossy(), &font)?;
+            }
+            None => {
+                for (name, font) in vvs_font::embeded_fonts() {
+                    print_face_info(name, font)?;
+                }
+            }
+        }
+        return Ok(());
     }
 
     if script.is_none() && !interactive {
@@ -54,6 +92,11 @@ fn main() -> Result<()> {
         None => "failed to setup base runtime".to_string(),
     })?;
 
+    if info {
+        log::debug!(target: "vvcc", "print informations about loaded scripts");
+        vvs_lua::print_info(&lua).with_context(|| "failed to print vivy informations")?;
+    }
+
     vvs_lua::load_user_script(&lua, script.as_ref()).with_context(|| match script {
         Some(script) => format!(
             "failed to finish runtime setup and/or load script: {}",
@@ -62,11 +105,6 @@ fn main() -> Result<()> {
         None => "failed to finish runtime setup".to_string(),
     })?;
 
-    if info {
-        log::debug!(target: "vvcc", "print informations about loaded scripts");
-        vvs_lua::print_info(&lua).with_context(|| "failed to print vivy informations")?;
-    }
-
     if interactive {
         log::debug!(target: "vvcc", "switch to interactive mode");
         vvs_repl::REPL::new(lua)
diff --git a/src/Rust/vvs_font/Cargo.toml b/src/Rust/vvs_font/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f1cd7653fbd862f21fff49f5757edb6aa556af87
--- /dev/null
+++ b/src/Rust/vvs_font/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "vvs_font"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+description = "The font crate for VVS"
+
+[dependencies]
+lazy_static.workspace = true
+thiserror.workspace = true
+log.workspace = true
+
+ttf-parser = { version = "^0.19" }
+ab_glyph = { version = "^0.2.20" }
diff --git a/src/Rust/vvs_font/build.rs b/src/Rust/vvs_font/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0ae1bc9de1fee56eb90ecb4607291e6292f70175
--- /dev/null
+++ b/src/Rust/vvs_font/build.rs
@@ -0,0 +1,57 @@
+use std::{env, fs, path::Path};
+
+fn rerun_directory<T: AsRef<Path> + ?Sized>(dir: &T) {
+    println!("cargo:rerun-if-changed={}", dir.as_ref().to_string_lossy());
+    for entry in std::fs::read_dir(dir).unwrap() {
+        let path = entry.expect("Couldn't access file in src directory").path();
+        if path.is_dir() {
+            rerun_directory(&path);
+        }
+    }
+}
+
+fn main() {
+    let out_dir = Path::new(&env::var_os("OUT_DIR").expect("no OUT_DIR env variable..."))
+        .canonicalize()
+        .expect("failed to canonicalize OUT_DIR");
+    let font_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
+        .parent()
+        .expect("manifest folder should have a parent")
+        .join("vvs_font/fonts")
+        .canonicalize()
+        .expect("failed to canonicalize the font folder");
+
+    let fonts = fs::read_dir(&font_dir)
+        .expect("failed to read the font folder")
+        .filter_map(Result::ok)
+        .filter(|file| {
+            file.file_type().map(|ft| ft.is_file()).unwrap_or_default()
+                && file.path().extension().map(|e| e == "ttf").unwrap_or(false)
+        })
+        .map(|file| {
+            let (path, file_name) = (file.path(), file.file_name());
+            let path = path.to_string_lossy();
+            let name = file_name.to_string_lossy();
+            let name = name.rsplit_once('.').unwrap().0;
+            format!("({name:?}, include_bytes!({path:?}))")
+        })
+        .collect::<Vec<_>>()
+        .join(",\n");
+
+    // Generate
+    let src_content = format!(
+        r#"
+pub const fn embeded_fonts() -> &'static [(&'static str, &'static [u8])] {{
+    &[ {fonts} ]
+}}
+        "#
+    );
+
+    // Write
+    fs::write(out_dir.join("generated_font_utils.rs"), src_content)
+        .expect("failed to write generated source file");
+
+    // Rerun
+    rerun_directory(&font_dir);
+    println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/src/Rust/vvs_font/fonts/NotoSans-Bold.ttf b/src/Rust/vvs_font/fonts/NotoSans-Bold.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..3e68bc24167c9d26e2c982807c8b6f1e6c8047fc
Binary files /dev/null and b/src/Rust/vvs_font/fonts/NotoSans-Bold.ttf differ
diff --git a/src/Rust/vvs_font/fonts/NotoSans-BoldItalic.ttf b/src/Rust/vvs_font/fonts/NotoSans-BoldItalic.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..4b5635171654dfda7d30b856357394aad5084ea9
Binary files /dev/null and b/src/Rust/vvs_font/fonts/NotoSans-BoldItalic.ttf differ
diff --git a/src/Rust/vvs_font/fonts/NotoSans-Italic.ttf b/src/Rust/vvs_font/fonts/NotoSans-Italic.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..eedc5e4593d3eef68109d35823560d98031a7e83
Binary files /dev/null and b/src/Rust/vvs_font/fonts/NotoSans-Italic.ttf differ
diff --git a/src/Rust/vvs_font/fonts/NotoSans-Regular.ttf b/src/Rust/vvs_font/fonts/NotoSans-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..973bc2ed3a92372004f5bc0c3fe85092c75624e0
Binary files /dev/null and b/src/Rust/vvs_font/fonts/NotoSans-Regular.ttf differ
diff --git a/src/Rust/vvs_font/fonts/OFL.txt b/src/Rust/vvs_font/fonts/OFL.txt
new file mode 100644
index 0000000000000000000000000000000000000000..90b733268379b957b2aa1990f4e3795ebe55a148
--- /dev/null
+++ b/src/Rust/vvs_font/fonts/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2015-2021 Google LLC. All Rights Reserved.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/src/Rust/vvs_font/src/error.rs b/src/Rust/vvs_font/src/error.rs
new file mode 100644
index 0000000000000000000000000000000000000000..00d4c894cf293e8b0f1b97540596a9975d526ba7
--- /dev/null
+++ b/src/Rust/vvs_font/src/error.rs
@@ -0,0 +1,22 @@
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum FontCreationError {
+    #[error("ab_glyph error: {0}")]
+    ABGlyphError(ab_glyph::InvalidFont),
+
+    #[error("ttf-parser error: {0}")]
+    TTFParserError(ttf_parser::FaceParsingError),
+}
+
+#[derive(Debug, Error)]
+pub enum FontError {
+    #[error("font has no name")]
+    NoName,
+
+    #[error("failed to outline glyph '{1}' for size {0}pt")]
+    FailedToOutline(f64, char),
+
+    #[error("can't outline an empty string")]
+    EmptyStringToOutline,
+}
diff --git a/src/Rust/vvs_font/src/font.rs b/src/Rust/vvs_font/src/font.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d51bf15b6a48672c6d0713518a280a183a3eaaf3
--- /dev/null
+++ b/src/Rust/vvs_font/src/font.rs
@@ -0,0 +1,132 @@
+use crate::{error::*, *};
+use ab_glyph::Font as _;
+
+/// The different types of fonts. To be sure of what you are doing, please use only regular
+/// fonts, behaviour of non regular fonts may change depending on the platform if the italic or
+/// bold tag is set in the ASS Style.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum FontType {
+    Regular,
+    Italic,
+    Bold,
+    Oblique,
+    Monospaced,
+    Variable,
+}
+
+/// Struct used to store informations we need about a font.
+#[derive(Debug)]
+pub struct Font<'a> {
+    font: ab_glyph::FontRef<'a>,
+    face: ttf_parser::Face<'a>,
+}
+
+impl<'a> TryFrom<&'a [u8]> for Font<'a> {
+    type Error = FontCreationError;
+
+    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
+        Ok(Self {
+            face: ttf_parser::Face::parse(data, 0).map_err(FontCreationError::TTFParserError)?,
+            font: ab_glyph::FontRef::try_from_slice(data)
+                .map_err(FontCreationError::ABGlyphError)?,
+        })
+    }
+}
+
+impl<'a> Font<'a> {
+    /// Get the general name of the font.
+    pub fn name(&self) -> Result<String, FontError> {
+        self.face
+            .names()
+            .into_iter()
+            .find(|name| name.name_id == ttf_parser::name_id::FAMILY && name.is_unicode())
+            .and_then(|name| name.to_string())
+            .ok_or(FontError::NoName)
+    }
+
+    /// Get the family names.
+    pub fn family_names(&self) -> Vec<String> {
+        let names = self.face.names();
+        let filter = [
+            ttf_parser::name_id::POST_SCRIPT_NAME,
+            ttf_parser::name_id::FULL_NAME,
+        ];
+        names
+            .into_iter()
+            .flat_map(|name| {
+                (filter.contains(&name.name_id) && name.is_unicode()).then(|| name.to_string())
+            })
+            .flatten()
+            .collect()
+    }
+
+    /// The the font types.
+    pub fn font_types(&self) -> Vec<FontType> {
+        use FontType::*;
+        let mut ret = [
+            self.face.is_regular().then_some(Regular),
+            self.face.is_italic().then_some(Italic),
+            self.face.is_bold().then_some(Bold),
+            self.face.is_oblique().then_some(Oblique),
+            self.face.is_monospaced().then_some(Monospaced),
+            self.face.is_variable().then_some(Variable),
+        ]
+        .into_iter()
+        .flatten()
+        .chain(Some(match self.face.style() {
+            ttf_parser::Style::Normal => Regular,
+            ttf_parser::Style::Italic => Italic,
+            ttf_parser::Style::Oblique => Oblique,
+        }))
+        .collect::<Vec<_>>();
+        ret.sort();
+        ret.dedup();
+        ret
+    }
+
+    /// Get the number of glyphs in the font.
+    pub fn number_of_glyphs(&self) -> i64 {
+        self.face.number_of_glyphs() as i64
+    }
+
+    /// Outline a glyph with the specified pt size.
+    pub fn outline_glyph(&self, pt: f64, glyph: char) -> Result<Rect, FontError> {
+        self.font
+            .outline_glyph(
+                self.font.glyph_id(glyph).with_scale(
+                    self.font
+                        .pt_to_px_scale(f64::clamp(pt, f32::MIN.into(), f32::MAX.into()) as f32)
+                        .expect("failed to get the px_scale..."),
+                ),
+            )
+            .map(|outlined| {
+                let ab_glyph::Rect { min, max } = outlined.px_bounds();
+                Rect::new(
+                    Point {
+                        x: min.x.trunc() as i64,
+                        y: min.y.trunc() as i64,
+                    },
+                    Point {
+                        x: max.x.trunc() as i64,
+                        y: max.y.trunc() as i64,
+                    },
+                )
+            })
+            .ok_or(FontError::FailedToOutline(pt, glyph))
+    }
+
+    /// Outline a string slice with the specified pt size.
+    pub fn outline_str(&self, pt: f64, str: impl AsRef<str>) -> Result<Rect, FontError> {
+        let (rects, errs): (Vec<_>, Vec<_>) = str
+            .as_ref()
+            .chars()
+            .map(|glyph| self.outline_glyph(pt, glyph))
+            .partition(Result::is_ok);
+        if let Some(err) = errs.into_iter().next() {
+            return err;
+        }
+        let mut rects = rects.into_iter().map(Result::unwrap);
+        let first = rects.next().ok_or(FontError::EmptyStringToOutline)?;
+        Ok(rects.fold(first, Rect::merge))
+    }
+}
diff --git a/src/Rust/vvs_font/src/lib.rs b/src/Rust/vvs_font/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ba8e8634c4aa02427bbd2825bd99d3009147f7b2
--- /dev/null
+++ b/src/Rust/vvs_font/src/lib.rs
@@ -0,0 +1,8 @@
+mod error;
+mod font;
+mod rect;
+
+pub use font::*;
+pub use rect::*;
+
+include!(concat!(env!("OUT_DIR"), "/generated_font_utils.rs"));
diff --git a/src/Rust/vvs_font/src/rect.rs b/src/Rust/vvs_font/src/rect.rs
new file mode 100644
index 0000000000000000000000000000000000000000..28c7be4f0416c1e87a2e93346e03274ebef780d8
--- /dev/null
+++ b/src/Rust/vvs_font/src/rect.rs
@@ -0,0 +1,61 @@
+use std::cmp::{max, min};
+
+/// Describes coordinates in the plan, the origin is the top left corner of the screen.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Point {
+    pub x: i64,
+    pub y: i64,
+}
+
+/// Describes an area in the plan, the origin is the top left corner of the screen.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Rect {
+    top_left_corner: Point,
+    bottom_right_corner: Point,
+}
+
+impl Rect {
+    /// Create a correctly formed [Rect] that includes the passed [Point].
+    pub fn new(p1: Point, p2: Point) -> Self {
+        Self {
+            top_left_corner: Point::min(p1, p2),
+            bottom_right_corner: Point::max(p1, p2),
+        }
+    }
+
+    /// Returns a new [Rect] that includes the passed rectangles.
+    pub fn merge(self, other: Self) -> Self {
+        Self {
+            top_left_corner: Point::min(self.top_left_corner, other.top_left_corner),
+            bottom_right_corner: Point::min(self.bottom_right_corner, other.bottom_right_corner),
+        }
+    }
+
+    /// Get the Top Left corner, the min coordinates.
+    pub fn tl_corner(&self) -> Point {
+        self.top_left_corner
+    }
+
+    /// Get the Bottom Right corner, the max coordinates.
+    pub fn br_corner(&self) -> Point {
+        self.bottom_right_corner
+    }
+}
+
+impl Point {
+    /// Returns the min components of the two [Point].
+    pub fn min(p1: Point, p2: Point) -> Point {
+        Point {
+            x: min(p1.x, p2.x),
+            y: min(p1.y, p2.y),
+        }
+    }
+
+    /// Returns the max components of the two [Point].
+    pub fn max(p1: Point, p2: Point) -> Point {
+        Point {
+            x: max(p1.x, p2.x),
+            y: max(p1.y, p2.y),
+        }
+    }
+}
diff --git a/src/Rust/vvs_lua/Cargo.toml b/src/Rust/vvs_lua/Cargo.toml
index 7b7368080ded4fe8d48e003490ecd36ff1d105f7..9443ce92cfcef5541c2913bfa0b6701796106e1e 100644
--- a/src/Rust/vvs_lua/Cargo.toml
+++ b/src/Rust/vvs_lua/Cargo.toml
@@ -4,9 +4,10 @@ version.workspace = true
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
-description.workspace = true
+description = "The lua wrapper for VVS"
 
 [dependencies]
+vvs_utils = { path = "../vvs_utils" }
 vvs_ass = { path = "../vvs_ass" }
 
 thiserror.workspace = true
diff --git a/src/Rust/vvs_lua/build.rs b/src/Rust/vvs_lua/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..50064850e7981087564f831db73d630dcadbaddb
--- /dev/null
+++ b/src/Rust/vvs_lua/build.rs
@@ -0,0 +1,83 @@
+use std::{
+    env, fs,
+    path::{Path, PathBuf},
+};
+
+fn rerun_directory<T: AsRef<Path> + ?Sized>(dir: &T) {
+    println!("cargo:rerun-if-changed={}", dir.as_ref().to_string_lossy());
+    for entry in fs::read_dir(dir).unwrap() {
+        let path = entry.expect("Couldn't access file in src directory").path();
+        if path.is_dir() {
+            rerun_directory(&path);
+        }
+    }
+}
+
+fn main() {
+    let out_dir = Path::new(&env::var_os("OUT_DIR").expect("no OUT_DIR env variable..."))
+        .canonicalize()
+        .expect("failed to canonicalize OUT_DIR");
+    let base_path =
+        PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("can't find the manifest path"))
+            .canonicalize()
+            .expect("failed to canonicalize CARGO_MANIFEST_DIR");
+
+    // Utils for the stdlib
+    let stdlib_path = base_path.join("src/libs");
+
+    let stdlib_modules = fs::read_dir(&stdlib_path)
+        .expect("failed to open vvs_lua/src/libs folder")
+        .flat_map(|entry| {
+            entry
+                .expect("failed to get information about a file in vvs_lua/src/libs")
+                .file_name()
+                .to_str()
+                .into_iter()
+                .flat_map(|entry| {
+                    entry.ne("mod.rs").then(|| {
+                        entry
+                            .rsplit_once('.')
+                            .into_iter()
+                            .flat_map(|(name, ext)| ext.eq("rs").then_some(name.to_string()))
+                    })
+                })
+                .flatten()
+                .collect::<Vec<String>>()
+        })
+        .collect::<Vec<_>>();
+
+    fs::write(
+        out_dir.join("lua_stdlib_utils.out"),
+        format!(
+            r#"
+                match name {{
+                    {}
+                    _ => None,
+                }}
+                "#,
+            stdlib_modules
+                .iter()
+                .map(|module| format!("{module:?} => Some(({module}::required(lua)?, true)),\n"))
+                .collect::<Vec<_>>()
+                .join("")
+        ),
+    )
+    .expect("failed to write generated source file");
+
+    fs::write(
+        out_dir.join("lua_stdlib_utils.mods"),
+        format!(
+            "\n{}\n",
+            stdlib_modules
+                .iter()
+                .map(|module| format!("mod {module};\n"))
+                .collect::<Vec<_>>()
+                .join("")
+        ),
+    )
+    .expect("failed to write generated source file");
+
+    // ReRun
+    rerun_directory(&stdlib_path);
+    println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/src/Rust/vvs_lua/src/data/actions.rs b/src/Rust/vvs_lua/src/data/actions.rs
index 67e46f1a997615345fd89a72bdaa1bbe8f824aa0..d11fa665846b7ee80ce7102cacdfdcbfb7ed2157 100644
--- a/src/Rust/vvs_lua/src/data/actions.rs
+++ b/src/Rust/vvs_lua/src/data/actions.rs
@@ -1,4 +1,4 @@
-use crate::{data::lua_wrapper::LuaAssAuxValue, traits::TypedValue, types::Type};
+use crate::{dsl, lua_wrapper::LuaAssAuxValue, traits::TypedValue, types::Type};
 use mlua::prelude::*;
 use vvs_ass::ASSAuxValue;
 
@@ -16,13 +16,11 @@ impl<'lua> FromLua<'lua> for RegisterDataValue {
     fn from_lua(lua_value: LuaValue<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
         match lua_value {
             LuaValue::Table(table) => {
-                let value: LuaAssAuxValue = match table.get::<_, LuaValue>("value").ok() {
-                    None | Some(LuaValue::Nil) => {
-                        return Err(LuaError::RuntimeError(format!(
-                            "you must specify a default value in a data declaration"
-                        )))
-                    }
-                    Some(value) => match value {
+                let value: LuaAssAuxValue = dsl::table_get!(table, "default" -> {
+                    dsl::nil_pat!() => return Err(LuaError::RuntimeError(
+                        "you must specify a default value in a data declaration".to_string(),
+                    )),
+                    dsl::value!(value) => match value {
                         LuaValue::Boolean(v) => LuaAssAuxValue::from(ASSAuxValue::Boolean(v)),
                         LuaValue::Integer(v) => LuaAssAuxValue::from(ASSAuxValue::Integer(v)),
                         LuaValue::Number(v) => LuaAssAuxValue::from(ASSAuxValue::Floating(v)),
@@ -37,25 +35,21 @@ impl<'lua> FromLua<'lua> for RegisterDataValue {
                             })
                         }
                     },
-                };
-                let ty = match table.get::<_, LuaValue>("type").ok() {
-                    None | Some(LuaValue::Nil) => {
-                        return Err(LuaError::RuntimeError(format!(
-                            "you must specify a type in a data declaration"
-                        )))
-                    }
-                    Some(ty) => Type::from_lua(ty, lua)?,
-                };
-                let doc = match table.get::<_, LuaValue>("doc").ok() {
-                    None | Some(LuaValue::Nil) => None,
-                    Some(LuaValue::String(doc)) => Some(doc.to_string_lossy().to_string()),
-                    Some(value) => {
-                        return Err(LuaError::RuntimeError(format!(
-                            "value of type '{}' can't be a docstring, expected a 'string'",
-                            value.type_name()
-                        )))
-                    }
-                };
+                });
+                let ty = dsl::table_get!(table, "type" -> {
+                    dsl::nil_pat!() => return Err(LuaError::RuntimeError(
+                        "you must specify a type in a data declaration".to_string(),
+                    )),
+                    dsl::value!(ty) => Type::from_lua(ty, lua)?,
+                });
+                let doc = dsl::table_get!(table, "doc" -> {
+                    dsl::nil_pat!() => None,
+                    dsl::value!(String doc) => Some(doc.to_string_lossy().to_string()),
+                    dsl::value!(value) => return Err(LuaError::RuntimeError(format!(
+                        "value of type '{}' can't be a docstring, expected a 'string'",
+                        value.type_name()
+                    ))),
+                });
                 if value.ty().ne(&ty) {
                     return Err(LuaError::RuntimeError(format!(
                             "tried to register a value of type '{}' when declaring the option of type '{ty}'",
diff --git a/src/Rust/vvs_lua/src/data/mod.rs b/src/Rust/vvs_lua/src/data/mod.rs
index 3aeae7ca697e21d4c9a18c8e759413b7361c41f7..6f29387fd11cf132895a78e3d54070844c583e20 100644
--- a/src/Rust/vvs_lua/src/data/mod.rs
+++ b/src/Rust/vvs_lua/src/data/mod.rs
@@ -1,5 +1,4 @@
 mod actions;
-mod lua_wrapper;
 mod register;
 
 pub(crate) use register::{VivyDataRegister, VivyDataRegisterPtr};
diff --git a/src/Rust/vvs_lua/src/data/register.rs b/src/Rust/vvs_lua/src/data/register.rs
index c2095987b01b156980f4e45488db39dbf2f5ce8f..af8fbaf952c32b08aca3d28ba0e267e0ee141d81 100644
--- a/src/Rust/vvs_lua/src/data/register.rs
+++ b/src/Rust/vvs_lua/src/data/register.rs
@@ -1,4 +1,4 @@
-use crate::data::{actions::RegisterDataValue, lua_wrapper::LuaAssAuxTablePtr};
+use crate::{data::actions::RegisterDataValue, lua_wrapper::LuaAssAuxTablePtr};
 use mlua::prelude::*;
 use std::{
     cell::RefCell,
@@ -75,7 +75,7 @@ impl Default for VivyDataRegister {
 }
 
 impl VivyDataRegister {
-    pub fn new() -> Rc<RefCell<VivyDataRegister>> {
+    pub fn new() -> VivyDataRegisterPtr {
         Default::default()
     }
 
@@ -170,6 +170,33 @@ impl VivyDataRegister {
             }
         }
     }
+
+    /// Prints infos about the option register to stdout.
+    pub(crate) fn print_info(&self) {
+        let added: Vec<_> = self
+            .iter_registered()
+            .map(|(ty, name)| (ty, name, format!("{ty}.aux.{name}")))
+            .collect();
+        if !added.is_empty() {
+            println!(" # Data");
+            let padding = added.iter().map(|(_, _, name)| name.len()).max();
+            let padding = padding.unwrap_or_default();
+            for (ty, data_name, format_name) in added {
+                println!(
+                    "   - data       {format_name:padding$} = {}",
+                    self.get_table(ty)
+                        .as_inner()
+                        .borrow()
+                        .get(data_name)
+                        .expect("vivy internal error"),
+                );
+            }
+        }
+
+        self.get_orphan_data_declarations()
+            .map(|(ty, name)| format!("{ty}.aux.{name}"))
+            .for_each(|orphan| println!("   - orphan     {orphan}"));
+    }
 }
 
 impl LuaUserData for VivyDataRegister {
@@ -201,7 +228,7 @@ impl LuaUserData for VivyDataRegister {
 #[test]
 fn test_data_register_creation() {
     let reg = VivyDataRegister::new();
-    let reg: &VivyDataRegister = &*reg.borrow();
+    let reg: &VivyDataRegister = &reg.borrow();
     assert_eq!(*reg, Default::default());
 
     macro_rules! test_item {
diff --git a/src/Rust/vvs_lua/src/dsl.rs b/src/Rust/vvs_lua/src/dsl.rs
new file mode 100644
index 0000000000000000000000000000000000000000..67448bd7a5a0c4240efff8568a9d209287755450
--- /dev/null
+++ b/src/Rust/vvs_lua/src/dsl.rs
@@ -0,0 +1,78 @@
+macro_rules! table_get {
+    ($table: expr, $what: literal -> {
+        $($pat: pat => $expr: expr,)+
+    }) => {
+        match $table.get::<_, mlua::Value>($what).ok() {
+            $($pat => $expr,)+
+        }
+    };
+}
+
+macro_rules! nil_pat {
+    () => {
+        None | Some(mlua::Value::Nil)
+    };
+}
+
+macro_rules! value {
+    ($name: ident) => {
+        Some($name)
+    };
+
+    ($what: ident $name: ident) => {
+        Some(mlua::Value::$what($name))
+    };
+}
+
+pub(crate) use nil_pat;
+pub(crate) use table_get;
+pub(crate) use value;
+
+use mlua::{chunk, prelude::*};
+
+pub(crate) fn into_readonly_table<'lua>(
+    lua: &'lua Lua,
+    name: LuaString<'lua>,
+    table: LuaTable<'lua>,
+) -> LuaResult<LuaTable<'lua>> {
+    let (proxy, mt) = (lua.create_table()?, lua.create_table()?);
+    mt.raw_set("__index", table)?;
+    mt.raw_set(
+        "__newindex",
+        lua.load(chunk! {
+            function (t, k, v)
+                error("attempt to update a read-only table", 2)
+            end
+        })
+        .set_name(format!("{}::ro-function", name.to_str()?))?
+        .eval::<LuaFunction>()?,
+    )?;
+    proxy.set_metatable(Some(mt));
+    Ok(proxy)
+}
+
+pub(crate) fn into_string_vec<'lua>(
+    lua: &'lua Lua,
+    value: LuaValue<'lua>,
+) -> LuaResult<Vec<String>> {
+    match value {
+        ret @ LuaValue::Boolean(_) | ret @ LuaValue::Integer(_) | ret @ LuaValue::Number(_) => {
+            Ok(vec![lua
+                .coerce_string(ret)?
+                .ok_or(LuaError::RuntimeError(
+                    "coertion to string failed".to_string(),
+                ))?
+                .to_string_lossy()
+                .to_string()])
+        }
+        LuaValue::String(str) => Ok(vec![str.to_string_lossy().to_string()]),
+        LuaValue::Table(table) => Ok(table
+            .sequence_values::<LuaString>()
+            .flat_map(|str| Some(str.ok()?.to_string_lossy().to_string()))
+            .collect()),
+        ret => Err(LuaError::RuntimeError(format!(
+            "can't coerce a value of type '{}' into a vector of string",
+            ret.type_name()
+        ))),
+    }
+}
diff --git a/src/Rust/vvs_lua/src/func/actions.rs b/src/Rust/vvs_lua/src/func/actions.rs
new file mode 100644
index 0000000000000000000000000000000000000000..81c1aeabbaa01b439dd4341a2749565619e02a57
--- /dev/null
+++ b/src/Rust/vvs_lua/src/func/actions.rs
@@ -0,0 +1,71 @@
+use crate::dsl;
+use mlua::prelude::*;
+use std::rc::Rc;
+
+/// Structure used to parse a function declaration, like in:
+/// - `func "toto" { function(...) ... end }`
+/// - `func "toto" { doc = "foo(bar)", function(...) ... end }`
+pub(super) struct FuncDeclaration<'lua> {
+    pub doc: Option<String>,
+    pub function: LuaFunction<'lua>,
+}
+
+/// Structure used to represent a function in memory.
+#[derive(Debug)]
+#[allow(dead_code)]
+pub(super) struct VivyFunc {
+    pub module: Option<String>,
+    pub name: String,
+    pub doc: Option<String>,
+    pub function: Rc<LuaRegistryKey>,
+}
+
+impl<'lua> FromLua<'lua> for FuncDeclaration<'lua> {
+    fn from_lua(lua_value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
+        let LuaValue::Table(table) = lua_value else {
+            return Err(LuaError::RuntimeError(format!(
+                "expected a table to convert into a function registration, got a: {}",
+                lua_value.type_name()
+            )));
+        };
+
+        let doc = dsl::table_get!(table, "doc" -> {
+            dsl::nil_pat!() => None,
+            dsl::value!(String doc) => Some(doc.to_string_lossy().to_string()),
+            dsl::value!(value) => return Err(LuaError::RuntimeError(format!(
+                "value of type '{}' can't be a docstring, expected a 'string'",
+                value.type_name()
+            ))),
+        });
+
+        let callback: Vec<_> = table
+            .pairs::<LuaValue, LuaFunction>()
+            .filter_map(|pair| Some(pair.ok()?.1))
+            .collect();
+        match &callback[..] {
+            [function] => Ok(Self {
+                doc,
+                function: function.clone(),
+            }),
+            [] => Err(LuaError::RuntimeError(
+                "expected a function to implement the vivy function export, got nothing".to_string(),
+            )),
+            _ => Err(LuaError::RuntimeError(format!(
+                "expected a single function to implement the vivy function export, got {} functions",
+                callback.len()
+            ))),
+        }
+    }
+}
+
+impl LuaUserData for VivyFunc {
+    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+        methods.add_meta_method(
+            LuaMetaMethod::Call,
+            |lua, this, arguments: LuaMultiValue| {
+                lua.registry_value::<LuaFunction>(&this.function)?
+                    .call::<_, LuaMultiValue>(arguments)
+            },
+        )
+    }
+}
diff --git a/src/Rust/vvs_lua/src/func/mod.rs b/src/Rust/vvs_lua/src/func/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ca2913e61b9dbf70b3a6a293515b73a773918612
--- /dev/null
+++ b/src/Rust/vvs_lua/src/func/mod.rs
@@ -0,0 +1,4 @@
+mod actions;
+mod register;
+
+pub(crate) use register::{VivyFuncRegister, VivyFuncRegisterPtr};
diff --git a/src/Rust/vvs_lua/src/func/register.rs b/src/Rust/vvs_lua/src/func/register.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d373c00a71e8f71e108cb4a128510545e4a27aa8
--- /dev/null
+++ b/src/Rust/vvs_lua/src/func/register.rs
@@ -0,0 +1,111 @@
+use crate::func::actions::{FuncDeclaration, VivyFunc};
+use mlua::{chunk, prelude::*};
+use std::{
+    cell::RefCell,
+    collections::{HashMap, HashSet},
+    rc::Rc,
+};
+
+/// The structure used to register funcs and do the resolution at runtime.
+///
+/// It is Ok to store functions into the register because those functions won't be able to
+/// reference the register, creating cyclique reference chains.
+#[derive(Debug, Default)]
+pub(crate) struct VivyFuncRegister {
+    /// All the func that should be declared in the current module. Must be cleared after a module
+    /// is loaded.
+    func_names: HashSet<String>,
+
+    /// All the func that are declared in the current module. Must be cleared after a module is
+    /// loaded. If its length is not equal to [VivyFuncRegister::func_names], then we have a problem.
+    local_funcs: HashMap<String, Rc<VivyFunc>>,
+
+    /// All func declared in all modules.
+    all_declared_funcs: Vec<Rc<VivyFunc>>,
+}
+
+pub(crate) type VivyFuncRegisterPtr = Rc<RefCell<VivyFuncRegister>>;
+
+impl VivyFuncRegister {
+    pub fn new() -> VivyFuncRegisterPtr {
+        Default::default()
+    }
+
+    /// Export the local funcs into the named module. We also reset the content of the register for
+    /// the next module. This should be called once, when we finished to parse the module.
+    pub(crate) fn export(
+        &mut self,
+        lua: &Lua,
+        name: &LuaString,
+        table: &LuaTable,
+    ) -> LuaResult<()> {
+        if self.func_names.len() != self.local_funcs.len() {
+            return Err(LuaError::RuntimeError(format!(
+                "invalid func declarations in module '{}', declaration count doesn't match the registration count",
+                name.to_str()?
+            )));
+        }
+
+        for name in self.func_names.drain() {
+            let func = self.local_funcs.remove(&name).expect("undefined func");
+            table.raw_set(
+                lua.create_string(&name)?,
+                lua.registry_value::<LuaFunction>(&func.function)?,
+            )?;
+        }
+
+        Ok(())
+    }
+}
+
+impl LuaUserData for VivyFuncRegister {
+    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+        methods.add_method_mut("will_register", |_, this, name: String| -> LuaResult<()> {
+            if this.func_names.contains(&name) {
+                Err(LuaError::RuntimeError(format!(
+                    "redefinition of func '{name}'"
+                )))
+            } else {
+                this.func_names.insert(name);
+                Ok(())
+            }
+        });
+
+        methods.add_method_mut(
+            "register",
+            |lua, this, (name, table): (LuaString, LuaTable)| -> LuaResult<()> {
+                let name = name.to_str()?;
+                let table = FuncDeclaration::from_lua(LuaValue::Table(table), lua)?;
+                if !this.func_names.contains(name) {
+                    return Err(LuaError::RuntimeError(format!(
+                        "try to register func '{name}' but it was not declared"
+                    )));
+                } else if this.local_funcs.contains_key(name) {
+                    return Err(LuaError::RuntimeError(format!(
+                        "try to register func '{name}' again"
+                    )));
+                }
+
+                let declaration = Rc::new(VivyFunc {
+                    module: match lua
+                        .load(chunk!(return vivy: ___current_module()))
+                        .set_name("query current module name")?
+                        .eval::<LuaValue>()
+                    {
+                        Ok(LuaValue::String(str)) => Some(str.to_string_lossy().to_string()),
+                        _ => None,
+                    },
+                    name: name.to_string(),
+                    doc: table.doc,
+                    function: lua.create_registry_value(table.function)?.into(),
+                });
+
+                this.local_funcs
+                    .insert(name.to_string(), declaration.clone());
+                this.all_declared_funcs.push(declaration);
+
+                Ok(())
+            },
+        );
+    }
+}
diff --git a/src/Rust/vvs_lua/src/functions.rs b/src/Rust/vvs_lua/src/functions.rs
index 6834fc369da8c88d52b7af7dc822f6dd53bb021b..e94a939fbaac1c4993a515cd629fc8c570b494d7 100644
--- a/src/Rust/vvs_lua/src/functions.rs
+++ b/src/Rust/vvs_lua/src/functions.rs
@@ -1,27 +1,42 @@
-use crate::{data::VivyDataRegister, libs, options::VivyOptionRegister, TomlOptions};
+use crate::{
+    data::VivyDataRegister, func::VivyFuncRegister, jobs::VivyJobRegister, libs,
+    options::VivyOptionRegister, TomlOptions,
+};
 use mlua::{chunk, prelude::*};
 use std::path::Path;
 
 pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
-    let options = VivyOptionRegister::new(options.unwrap_or_default());
-    let version = env!("CARGO_PKG_VERSION");
-    let data = VivyDataRegister::new();
-    let lua = Lua::new_with(
-        LuaStdLib::MATH | LuaStdLib::TABLE | LuaStdLib::STRING | LuaStdLib::PACKAGE,
-        LuaOptions::new().catch_rust_panics(true),
-    )?;
-
-    let requires = lua.create_function(|lua, name: LuaString| libs::import(lua, name))?;
-
-    let warning = lua.create_function(|_, msg: String| {
-        log::warn!(target: "vvs", "{msg}");
-        Ok(())
-    })?;
-
-    let info = lua.create_function(|_, msg: String| {
-        log::info!(target: "vvs", "{msg}");
-        Ok(())
-    })?;
+    let (version, options, data, jobs, func) = (
+        env!("CARGO_PKG_VERSION"),
+        VivyOptionRegister::new(options.unwrap_or_default()),
+        VivyDataRegister::new(),
+        VivyJobRegister::new(),
+        VivyFuncRegister::new(),
+    );
+    let lua = unsafe {
+        // We use unsafe because we need to load the debug package.
+        // This also allows to load C things and dylib things, but because we nil the requires
+        // function it should be fine.
+        Lua::unsafe_new_with(
+            LuaStdLib::MATH
+                | LuaStdLib::TABLE
+                | LuaStdLib::STRING
+                | LuaStdLib::PACKAGE
+                | LuaStdLib::DEBUG,
+            LuaOptions::new().catch_rust_panics(true),
+        )
+    };
+    let (requires, warning, info) = (
+        lua.create_function(libs::import)?,
+        lua.create_function(|_, msg: String| {
+            log::warn!(target: "vvs", "{msg}");
+            Ok(())
+        })?,
+        lua.create_function(|_, msg: String| {
+            log::info!(target: "vvs", "{msg}");
+            Ok(())
+        })?,
+    );
 
     lua.load(chunk! {
         local _debug     = debug
@@ -30,6 +45,8 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
         _VERSION         = nil
         local options    = $options
         local data       = $data
+        local jobs       = $jobs
+        local func       = $func
         local ReadOnly   = {
             package   = nil,
             coroutine = nil,
@@ -43,6 +60,8 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
         $requires "vivy"
         vivy:___set_options(options)
         vivy:___set_data(data)
+        vivy:___set_jobs(jobs)
+        vivy:___set_func(func)
         ReadOnly.vivy = vivy
         vivy          = nil
 
@@ -55,6 +74,16 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
         end
         package = nil
 
+        function ReadOnly._job_input_type (func)
+            local nparams = _debug.getinfo(func).nparams
+            if nparams ~= 1 then
+                error("a job should only have one argument, got " .. nparams, 2)
+            end
+            return _debug.getlocal(func, 1)
+        end
+
+        // DSL keywords
+
         function ReadOnly.import (name)
             if type(name) ~= "string" then
                 error("you must import a module by its name, got " .. type(name) .. " and not a string", 2)
@@ -63,42 +92,77 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
         end
 
         function ReadOnly.main (table)
-            error("not implemented", 2)
+            if type(table) ~= "table" then
+                error("you must specify the main workflow with a table", 2)
+            end
+            vivy:___main(table)
         end
 
-        function ReadOnly.job (table)
-            error("not implemented", 2)
+        function ReadOnly.func (name)
+            if type(name) ~= "string" then
+                error("you must specify the exported function name with a string", 2)
+            end
+            return function (table)
+                if type(table) ~= "table" then
+                    error("you must specify the function description with a table", 2)
+                end
+                error("not implemented")
+            end
+        end
+
+        function ReadOnly.job (name)
+            if type(name) ~= "string" then
+                error("you must specify the job name with a string", 2)
+            end
+            jobs:will_register(name)
+            return function (table)
+                if type(table) ~= "table" then
+                    error("you must specify the job description with a table", 2)
+                end
+                jobs:register(name, table)
+            end
         end
 
         function ReadOnly.data (name)
+            if type(name) ~= "string" then
+                error("you must specify the datum name with a string", 2)
+            end
             data:will_register(name)
             return function (table)
+                if type(table) ~= "table" then
+                    error("you must specify the data description with a table", 2)
+                end
                 data:register(name, table)
             end
         end
 
         function ReadOnly.set (name)
+            if type(name) ~= "string" then
+                error("you must specify the option name with a string", 2)
+            end
             options:will_set(name)
             return function (table)
+                if type(table) ~= "table" then
+                    error("you must specify the option specification description with a table", 2)
+                end
                 options:set(name, table)
             end
         end
 
         function ReadOnly.option (name)
+            if type(name) ~= "string" then
+                error("you must specify the option name with a string", 2)
+            end
             options:will_register(name)
             return function (table)
+                if type(table) ~= "table" then
+                    error("you must specify the option description with a table", 2)
+                end
                 options:register(name, table)
             end
         end
 
-        // As a remainder for what we can do...
-        function __PRINT_ARGS(func)
-            local nparams = _debug.getinfo(func).nparams
-            print("nparams = " .. nparams)
-            for i = 1, nparams do
-                print(_debug.getlocal(func, i))
-            end
-        end
+        // Protect the metatable
 
         setmetatable(_G, {
             __metatable = "don't touch to the global table",
@@ -114,39 +178,46 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
             end,
         })
     })
-    .set_name("prelude")?
+    .set_name("vivy prelude")?
     .exec()?;
 
     Ok(lua)
 }
 
-pub fn load_user_script<P: AsRef<Path>>(lua: &Lua, script: Option<P>) -> LuaResult<()> {
+pub fn load_user_script(lua: &Lua, script: Option<impl AsRef<Path>>) -> LuaResult<()> {
     if let Some(script) = script {
         let script = script.as_ref();
         match script.parent() {
             Some(parent_folder) => {
                 let (path1, path2) = (parent_folder.join("?.vvl"), parent_folder.join("?.lua"));
                 let (path1, path2) = (path1.to_string_lossy(), path2.to_string_lossy());
+                let name = format!("append paths {path1} and {path2}");
                 lua.load(chunk! {
                     vivy:___append_to_path($path1)
                     vivy:___append_to_path($path2)
                 })
+                .set_name(name)?
                 .exec()?;
             }
             None => {
                 log::warn!(target: "lua", "the script has no parent folder: {}", script.to_string_lossy())
             }
         }
-        log::debug!(target: "lua", "load the script: {}", script.to_string_lossy());
-        lua.load(script).exec()?;
+        let script_path = script.to_string_lossy();
+        log::debug!(target: "lua", "load the script: {script_path}");
+        lua.load(script).set_name(script_path)?.exec()?;
     }
-    lua.load(mlua::chunk! { vivy:___finish_setup() }).exec()
+    Ok(())
 }
 
 pub fn print_info(lua: &Lua) -> LuaResult<()> {
-    lua.load(mlua::chunk! { vivy:___print_info() }).exec()
+    lua.load(mlua::chunk! { vivy:___print_info() })
+        .set_name("vivy:___print_info")?
+        .exec()
 }
 
 pub fn get_loaded(lua: &Lua) -> LuaResult<Vec<String>> {
-    lua.load(mlua::chunk! { vivy:___get_loaded() }).eval()
+    lua.load(mlua::chunk! { vivy:___get_loaded() })
+        .set_name("vivy:___get_loaded")?
+        .eval()
 }
diff --git a/src/Rust/vvs_lua/src/jobs/actions.rs b/src/Rust/vvs_lua/src/jobs/actions.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2338638f955825ce17cd1ffc5e4fae6da9f5ae59
--- /dev/null
+++ b/src/Rust/vvs_lua/src/jobs/actions.rs
@@ -0,0 +1,183 @@
+use crate::{dsl, lua_wrapper::LuaAssType};
+use mlua::{chunk, prelude::*};
+use std::{rc::Rc, sync::Arc};
+use vvs_ass::ASSType;
+use vvs_utils::*;
+
+/// Structure used to parse a job declaration, like in:
+/// - `job "toto" { line = function(line) ... end }`
+/// - `job "toto" { doc = "foo(bar)", line = function(line) ... end }`
+pub(super) struct JobDeclaration<'lua> {
+    pub doc: Option<String>,
+    pub function: LuaFunction<'lua>,
+    pub input_type: Option<ASSType>,
+    pub return_type: Option<ASSType>,
+}
+
+/// Structure used to represent a job in memory.
+#[derive(Debug)]
+pub(super) struct VivyJob {
+    pub module: Option<String>,
+    pub name: String,
+    pub doc: Option<String>,
+    pub input_type: Option<ASSType>,
+    pub return_type: Option<ASSType>,
+    pub function: Rc<LuaRegistryKey>,
+}
+
+/// Structure used to represent a job that will be called on some values. Used because of execution
+/// order of lua, when we build the main tree the variables are not present, we need to declare
+/// them latter after the tree was validated.
+#[derive(Debug)]
+pub(crate) struct VivyCalledJob {
+    pub name: String,
+    pub function: Rc<LuaRegistryKey>,
+    pub arguments: Vec<String>,
+    pub input_type: Option<ASSType>,
+    pub return_type: Option<ASSType>,
+}
+
+impl<'lua> FromLua<'lua> for JobDeclaration<'lua> {
+    fn from_lua(lua_value: LuaValue<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
+        let LuaValue::Table(table) = lua_value else {
+            return Err(LuaError::RuntimeError(format!(
+                "expected a table to convert into a job registration, got a: {}",
+                lua_value.type_name()
+            )));
+        };
+
+        let doc = dsl::table_get!(table, "doc" -> {
+            dsl::nil_pat!() => None,
+            dsl::value!(String doc) => Some(doc.to_string_lossy().to_string()),
+            dsl::value!(value) => return Err(LuaError::RuntimeError(format!(
+                "value of type '{}' can't be a docstring, expected a 'string'",
+                value.type_name()
+            ))),
+        });
+
+        let callback: Vec<_> = table
+            .pairs::<LuaString, LuaValue>()
+            .filter_map(|res| match res {
+                Ok((k, LuaValue::Function(v))) => {
+                    let k: LuaAssType = k.try_into().ok()?;
+                    Some((k, v))
+                }
+                _ => None,
+            })
+            .collect();
+        match &callback[..] {
+            [(return_type, function)] => Ok({
+                let input_type = {
+                    let func = function.clone();
+                    lua.load(chunk! { return _job_input_type($func) })
+                        .eval::<LuaString>()?
+                        .to_str()?
+                        .parse::<LuaAssType>()
+                        .map_err(LuaError::RuntimeError)?
+                        .into_inner()
+                };
+                Self {
+                    doc,
+                    function: function.clone(),
+                    return_type: Some(*return_type.as_inner()),
+                    input_type: Some(input_type),
+                }
+            }),
+            [] => Err(LuaError::RuntimeError(
+                "expected a function to implement the job, got nothing".to_string(),
+            )),
+            _ => Err(LuaError::RuntimeError(format!(
+                "expected a single function to implement the job, got {} functions",
+                callback.len()
+            ))),
+        }
+    }
+}
+
+impl LuaUserData for VivyJob {
+    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+        methods.add_meta_method(LuaMetaMethod::Call, |_, this, arguments: LuaValue| {
+            let arguments = match arguments {
+                LuaNil => vec![],
+                LuaValue::String(name) => vec![name.to_string_lossy().to_string()],
+                LuaValue::Table(names) => {
+                    let (names, errs): (Vec<_>, Vec<_>) = names
+                        .sequence_values::<LuaString>()
+                        .partition(Result::is_ok);
+                    if let Some(Err(err)) = errs.into_iter().next() {
+                        return Err(err);
+                    }
+                    names
+                        .into_iter()
+                        .map(|str| str.unwrap().to_string_lossy().to_string())
+                        .collect()
+                }
+                _ => {
+                    return Err(LuaError::RuntimeError(format!(
+                        "expected a variable name or multiple variable names, got: {}",
+                        arguments.type_name()
+                    )))
+                }
+            };
+            let signature = {
+                let name = match this.module {
+                    Some(ref module) => format!("{module}.{}", this.name),
+                    None => this.name.clone(),
+                };
+                let signature = match (this.input_type, this.return_type) {
+                    (None, None) => "() -> ()".to_string(),
+                    (None, Some(rty)) => format!("() -> {rty}"),
+                    (Some(ity), None) => format!("{ity} -> ()"),
+                    (Some(ity), Some(rty)) => format!("{ity} -> {rty}"),
+                };
+                format!("{name}: {signature}")
+            };
+            log::info!(
+                target: "lua",
+                "will execute {} job `{signature}` with argsuments: {arguments:?}",
+                either!(this.doc.is_some() => "documented"; "undocumented")
+            );
+            Ok(VivyCalledJob {
+                arguments,
+                name: this.name.clone(),
+                function: this.function.clone(),
+                input_type: this.input_type,
+                return_type: this.return_type,
+            })
+        });
+    }
+}
+
+impl LuaUserData for VivyCalledJob {
+    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+        methods.add_meta_method(LuaMetaMethod::Call, |lua, this, ()| {
+            if !lua
+                .load(chunk! { return vivy:___is_in_job_evaluation_mode() })
+                .set_name("is in job evaluation mode?")?
+                .eval::<bool>()?
+            {
+                todo!()
+            }
+            lua.load(chunk! { vivy:___switch_to_execution_mode() })
+                .set_name("switch to execution mode")?
+                .exec()?;
+
+            let (name, arguments) = (this.name.clone(), this.arguments.clone());
+            let function: LuaFunction = lua.registry_value(&this.function)?;
+            let (Some(input), Some(returns)) = (this.input_type, this.return_type) else {
+                    return Err(LuaError::RuntimeError(format!(
+                        "failed to get the input and return types for the job '{name}'"
+                    )));
+                };
+            let (input, returns) = (LuaAssType::from(input), LuaAssType::from(returns));
+            lua.load(chunk! {
+                vivy:___execute_job($name, $function, $arguments, $input, $returns)
+            })
+            .eval::<LuaMultiValue>()
+            .map_err(|cause| LuaError::CallbackError {
+                traceback: "error in job execution".to_string(),
+                cause: Arc::new(cause),
+            })
+        });
+    }
+}
diff --git a/src/Rust/vvs_lua/src/jobs/mod.rs b/src/Rust/vvs_lua/src/jobs/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f844ef3277e683bcf2b53adb43693d0ea576965d
--- /dev/null
+++ b/src/Rust/vvs_lua/src/jobs/mod.rs
@@ -0,0 +1,5 @@
+mod actions;
+mod register;
+
+pub(crate) use actions::VivyCalledJob;
+pub(crate) use register::{VivyJobRegister, VivyJobRegisterPtr};
diff --git a/src/Rust/vvs_lua/src/jobs/register.rs b/src/Rust/vvs_lua/src/jobs/register.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3cbe96ddf4fbfa9070b31016b4a526a41cd551ad
--- /dev/null
+++ b/src/Rust/vvs_lua/src/jobs/register.rs
@@ -0,0 +1,112 @@
+use crate::jobs::actions::{JobDeclaration, VivyJob};
+use mlua::{chunk, prelude::*};
+use std::{
+    cell::RefCell,
+    collections::{HashMap, HashSet},
+    rc::Rc,
+};
+
+/// The structure used to register jobs and do the resolution at runtime.
+///
+/// It is Ok to store functions into the register because those functions won't be able to
+/// reference the register, creating cyclique reference chains.
+#[derive(Debug, Default)]
+pub(crate) struct VivyJobRegister {
+    /// All the job that should be declared in the current module. Must be cleared after a module
+    /// is loaded.
+    job_names: HashSet<String>,
+
+    /// All the job that are declared in the current module. Must be cleared after a module is
+    /// loaded. If its length is not equal to [VivyJobRegister::job_names], then we have a problem.
+    local_jobs: HashMap<String, Rc<RefCell<VivyJob>>>,
+
+    /// All job declared in all modules.
+    all_declared_jobs: Vec<Rc<RefCell<VivyJob>>>,
+}
+
+pub(crate) type VivyJobRegisterPtr = Rc<RefCell<VivyJobRegister>>;
+
+impl VivyJobRegister {
+    pub fn new() -> VivyJobRegisterPtr {
+        Default::default()
+    }
+
+    /// Export the local jobs into the named module. We also reset the content of the register for
+    /// the next module. This should be called once, when we finished to parse the module.
+    pub(crate) fn export(
+        &mut self,
+        lua: &Lua,
+        name: &LuaString,
+        table: &LuaTable,
+    ) -> LuaResult<()> {
+        if self.job_names.len() != self.local_jobs.len() {
+            return Err(LuaError::RuntimeError(format!(
+                "invalid job declarations in module '{}', declaration count doesn't match the registration count",
+                name.to_str()?
+            )));
+        }
+
+        for name in self.job_names.drain() {
+            table.raw_set(
+                lua.create_string(&name)?,
+                self.local_jobs.remove(&name).expect("undefined job"),
+            )?;
+        }
+
+        Ok(())
+    }
+}
+
+impl LuaUserData for VivyJobRegister {
+    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+        methods.add_method_mut("will_register", |_, this, name: String| -> LuaResult<()> {
+            if this.job_names.contains(&name) {
+                Err(LuaError::RuntimeError(format!(
+                    "redefinition of job '{name}'"
+                )))
+            } else {
+                this.job_names.insert(name);
+                Ok(())
+            }
+        });
+
+        methods.add_method_mut(
+            "register",
+            |lua, this, (name, table): (LuaString, LuaTable)| -> LuaResult<()> {
+                let name = name.to_str()?;
+                let table = JobDeclaration::from_lua(LuaValue::Table(table), lua)?;
+                if !this.job_names.contains(name) {
+                    return Err(LuaError::RuntimeError(format!(
+                        "try to register job '{name}' but it was not declared"
+                    )));
+                } else if this.local_jobs.contains_key(name) {
+                    return Err(LuaError::RuntimeError(format!(
+                        "try to register job '{name}' again"
+                    )));
+                }
+
+                let declaration = Rc::new(RefCell::new(VivyJob {
+                    module: match lua
+                        .load(chunk!(return vivy: ___current_module()))
+                        .set_name("query current module name")?
+                        .eval::<LuaValue>()
+                    {
+                        Ok(LuaValue::String(str)) => Some(str.to_string_lossy().to_string()),
+                        _ => None,
+                    },
+                    name: name.to_string(),
+                    doc: table.doc,
+                    function: lua.create_registry_value(table.function)?.into(),
+                    input_type: table.input_type,
+                    return_type: table.return_type,
+                }));
+
+                this.local_jobs
+                    .insert(name.to_string(), declaration.clone());
+                this.all_declared_jobs.push(declaration);
+
+                Ok(())
+            },
+        );
+    }
+}
diff --git a/src/Rust/vvs_lua/src/lib.rs b/src/Rust/vvs_lua/src/lib.rs
index 20eec63bba725fd4d1315263d577d944df014178..e649a844f2ebf645f008358bf79c60c57de2de3c 100644
--- a/src/Rust/vvs_lua/src/lib.rs
+++ b/src/Rust/vvs_lua/src/lib.rs
@@ -1,17 +1,27 @@
 //! VivyScript Runtime
 
-/// Re-export mlua
+/// Re-export mlua.
 pub use mlua;
 
+// Registers.
 mod data;
+mod func;
+mod options;
+
+// Libraries used to extend Lua.
+mod libs;
+
+// Internal things.
 mod error;
 mod functions;
-mod libs;
-mod options;
+mod jobs;
 mod toml_option;
 mod traits;
 mod types;
 mod values;
 
+pub(crate) mod dsl;
+pub(crate) mod lua_wrapper;
+
 pub use functions::{get_loaded, load_user_script, print_info, setup};
 pub use toml_option::TomlOptions;
diff --git a/src/Rust/vvs_lua/src/libs/hashset.rs b/src/Rust/vvs_lua/src/libs/hashset.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0ed0570df740d818a2ac54e08aeae3f12a40e270
--- /dev/null
+++ b/src/Rust/vvs_lua/src/libs/hashset.rs
@@ -0,0 +1,60 @@
+//! Provide a hashset implementation in Lua.
+
+use mlua::{chunk, prelude::*};
+
+crate::libs::required! { "hashset.lua": chunk! {
+    local HashSet = {}
+
+    function HashSet.print(s)
+        if type(s) ~= "table" then error("expected a table") end
+        print(HashSet.tostring(s))
+    end
+
+    HashSet.mt = {
+        __add = function (a, b)
+            if getmetatable(a) ~= "hashset" or getmetatable(b) ~= "set" then
+                error("attempt to `add' a hashset with a non-set value", 2)
+            end
+            local res = HashSet.new {}
+            for k in pairs(a) do res[k] = true end
+            for k in pairs(b) do res[k] = true end
+            return res
+        end,
+
+        __mul = function (a, b)
+            if getmetatable(a) ~= "hashset" or getmetatable(b) ~= "set" then
+                error("attempt to `mul' a hashset with a non-set value", 2)
+            end
+            local res = HashSet.new {}
+            for k in pairs(a) do
+                res[k] = b[k]
+            end
+            return res
+        end,
+
+        __tostring = function(hashset)
+            if type(hashset) ~= "table" then error("expected a table") end
+            local s = "{"
+            local sep = ""
+            for e in pairs(hashset) do
+                s = s .. sep .. e
+                sep = ", "
+            end
+            return s .. "}"
+        end,
+
+        __index     = function (s, k) return rawget(s, k) ~= nil end,
+        __newindex  = function (s, k, _) rawhashset(s, k, true) end,
+        __metatable = "hashset",
+    }
+
+    function HashSet.new(t)
+        if type(t) ~= "table" then error("expected a table") end
+        local hashset = {}
+        hashsetmetatable(set, HashSet.mt)
+        for _, l in ipairs(t) do hashset[l] = true end
+        return hashset
+    end
+
+    return HashSet.new
+}}
diff --git a/src/Rust/vvs_lua/src/libs/mod.rs b/src/Rust/vvs_lua/src/libs/mod.rs
index fccdb94f0c29e7ccbe2a068750c5761e3a9e0d8d..75ebb29d1dc5b6098b181ab56bef82e9740f1b5b 100644
--- a/src/Rust/vvs_lua/src/libs/mod.rs
+++ b/src/Rust/vvs_lua/src/libs/mod.rs
@@ -1,9 +1,10 @@
 //! Expose functionalities to VivyScript as modules that can be imported.
 
+mod hashset;
 mod rectangle;
-mod set;
 mod vivy;
 
+pub use self::vivy::{Vivy, VivyPtr};
 use mlua::{chunk, prelude::*};
 
 macro_rules! required {
@@ -25,27 +26,57 @@ pub fn import(lua: &Lua, name: LuaString) -> LuaResult<()> {
     let name = core::str::from_utf8(name.as_bytes())
         .map_err(|err| LuaError::RuntimeError(format!("{err}")))?;
 
-    let lib = match name {
-        "rectangle" => rectangle::required(lua),
-        "vivy" => vivy::required(lua),
-        "set" => set::required(lua),
-        _ => lua
-            .load(chunk! {
+    if lua
+        .load(chunk! { return vivy ~= nil and vivy:___is_already_imported($name) })
+        .set_name(format!("required {name} -- already imported?"))?
+        .eval::<bool>()?
+    {
+        log::debug!(target: "lua", "module '{name}' was already imported, don't import it again");
+        return Ok(());
+    } else {
+        log::info!(target: "lua", "import module '{name}'");
+    }
+
+    let current_module: LuaValue = lua
+        .load(chunk! {
+            local current = nil
+            if vivy ~= nil then
+                current = vivy:___current_module()
+                vivy:___set_current_module($name)
+            end
+            return current
+        })
+        .set_name(format!("required {name} -- pre"))?
+        .eval()?;
+
+    let (lib, special) = match include!(concat!(env!("OUT_DIR"), "/lua_stdlib_utils.out")) {
+        Some(things) => things,
+        None => (
+            LuaValue::Nil,
+            lua.load(chunk! {
                 if vivy ~= nil then
                     return vivy:___import($name)
                 else
-                    error("can't get the vivy instance to import external module " .. $name, 2)
+                    error("can't get the vivy instance to import external module " .. $name)
                 end
             })
-            .eval(),
-    }?;
+            .set_name(format!("required {name} -- try"))?
+            .eval::<bool>()?,
+        ),
+    };
 
     lua.load(chunk! {
-        rawset(_G, $name, $lib)
+        if $lib ~= nil then
+            rawset(_G, $name, $lib)
+        end
         if vivy ~= nil then
             vivy:___mark_loaded($name)
+            if not $special then
+                vivy:___run_export_module($name)
+            end
+            vivy:___set_current_module($current_module)
         end
     })
-    .set_name(format!("required {name}"))?
+    .set_name(format!("required {name} -- post"))?
     .exec()
 }
diff --git a/src/Rust/vvs_lua/src/libs/set.rs b/src/Rust/vvs_lua/src/libs/set.rs
deleted file mode 100644
index 2b19f0c00924c8a76b6ef15d0c2f399264d2e272..0000000000000000000000000000000000000000
--- a/src/Rust/vvs_lua/src/libs/set.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-//! Provide a set implementation in Lua.
-
-use mlua::{chunk, prelude::*};
-
-crate::libs::required! { "set.lua": chunk! {
-    local Set = {}
-
-    function Set.print(s)
-        if type(s) ~= "table" then error("expected a table") end
-        print(Set.tostring(s))
-    end
-
-    Set.mt = {
-        __add = function (a, b)
-            if getmetatable(a) ~= "set" or getmetatable(b) ~= "set" then
-                error("attempt to `add' a set with a non-set value", 2)
-            end
-            local res = Set.new {}
-            for k in pairs(a) do res[k] = true end
-            for k in pairs(b) do res[k] = true end
-            return res
-        end,
-
-        __mul = function (a, b)
-            if getmetatable(a) ~= "set" or getmetatable(b) ~= "set" then
-                error("attempt to `mul' a set with a non-set value", 2)
-            end
-            local res = Set.new {}
-            for k in pairs(a) do
-                res[k] = b[k]
-            end
-            return res
-        end,
-
-        __tostring = function(set)
-            if type(set) ~= "table" then error("expected a table") end
-            local s = "{"
-            local sep = ""
-            for e in pairs(set) do
-                s = s .. sep .. e
-                sep = ", "
-            end
-            return s .. "}"
-        end,
-
-        __index     = function (s, k) return rawget(s, k) ~= nil end,
-        __newindex  = function (s, k, _) rawset(s, k, true) end,
-        __metatable = "set",
-    }
-
-    function Set.new(t)
-        if type(t) ~= "table" then error("expected a table") end
-        local set = {}
-        setmetatable(set, Set.mt)
-        for _, l in ipairs(t) do set[l] = true end
-        return set
-    end
-
-    return Set.new
-}}
diff --git a/src/Rust/vvs_lua/src/libs/vivy.rs b/src/Rust/vvs_lua/src/libs/vivy.rs
index 8f8bdf26f14e1ccc2d79eab9dd60d722a01dd9e8..a738639c42b67b16f817efaf159f56fbc26ad042 100644
--- a/src/Rust/vvs_lua/src/libs/vivy.rs
+++ b/src/Rust/vvs_lua/src/libs/vivy.rs
@@ -1,73 +1,605 @@
 //! Provides the vivy runtime.
 
-use crate::{data::VivyDataRegisterPtr, options::VivyOptionRegisterPtr, traits::TypedValue};
-use mlua::prelude::*;
-use std::path::PathBuf;
+use crate::{
+    data::VivyDataRegisterPtr,
+    dsl,
+    func::VivyFuncRegisterPtr,
+    jobs::{VivyCalledJob, VivyJobRegisterPtr},
+    lua_wrapper::{
+        LuaAssContainerPtr, LuaAssLinePtr, LuaAssLinesPtr, LuaAssSyllabePtr, LuaAssSyllabesPtr,
+        LuaAssType,
+    },
+    options::VivyOptionRegisterPtr,
+};
+use mlua::{chunk, prelude::*};
+use std::{
+    cell::RefCell,
+    collections::{HashMap, HashSet},
+    path::PathBuf,
+    rc::Rc,
+    sync::Arc,
+};
+use vvs_ass::{ASSAuxTablePtr, ASSLine, ASSLines, ASSSyllabe, ASSSyllabes, ASSType};
+use vvs_utils::either;
 
 #[derive(Default, Debug)]
 pub struct Vivy {
     options: Option<VivyOptionRegisterPtr>,
     data: Option<VivyDataRegisterPtr>,
+    jobs: Option<VivyJobRegisterPtr>,
+    func: Option<VivyFuncRegisterPtr>,
     path: Vec<String>,
-    loaded: Vec<String>,
+
+    variables: HashMap<String, LuaRegistryKey>,
+    is_in_job_evaluation: bool,
+    should_print_infos: bool,
+    main_once: bool,
+
+    loaded: HashSet<String>,
+    loading: HashSet<String>,
+    current_module: Option<String>,
 }
 
-impl Vivy {
-    pub fn new_with_path(path: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
+pub type VivyPtr = Rc<RefCell<Vivy>>;
+
+#[derive(Debug)]
+struct VivyExecGraph {
+    nodes: HashSet<String>,
+    edges: Vec<(String, Vec<String>, Rc<LuaRegistryKey>)>,
+    exits: Vec<String>,
+}
+
+macro_rules! if_let_else_chain {
+    (@default: $default_expr: expr) => {{ $default_expr }};
+
+    ($var: ident <- $expr: expr => $var_expr: expr;
+     @default: $default_expr: expr
+    ) => {
+        if let Ok($var) = $expr {
+            $var_expr
+        } else {
+            $default_expr
+        }
+    };
+
+    ($var_first: ident <- $expr_first: expr => $var_expr_first: expr;
+     $($var: ident <- $expr: expr => $var_expr: expr);+;
+     @default: $default_expr: expr
+    ) => {
+        if let Ok($var_first) = $expr_first {
+            $var_expr_first
+        } else { if_let_else_chain! {
+            $($var <- $expr => $var_expr);+;
+            @default: $default_expr
+        } }
+    };
+}
+
+/// See [Vivy::execute_job] for an example on how to use this macro.
+macro_rules! match_take {
+    ($expr: expr =>
+     $(; $var: ident: $ty: ident => $var_expr: expr)+
+     $(; @default => $default_expr: expr)?
+    ) => {{
+        if_let_else_chain! {
+            $($var <- $expr.take::<$ty>() => $var_expr);+;
+            $(@default: $default_expr)?
+        }
+    }};
+}
+
+impl VivyExecGraph {
+    pub fn insert(
+        &mut self,
+        destination: impl ToString,
+        function: Rc<LuaRegistryKey>,
+        src: impl IntoIterator<Item = impl ToString>,
+    ) -> bool {
+        let dest = destination.to_string();
+        let src: Vec<String> = src.into_iter().map(|str| str.to_string()).collect();
+        log::debug!(target: "lua", "{dest:?} <--({function:?})--- {src:?}");
+        if src.iter().filter(|src| either!(self.nodes.contains(src.as_str()) => false; {
+            log::error!(target: "lua", "source variable '{src}' is not present in the already assigned set");
+            true
+        })).count().ne(&0) {
+            return false;
+        } else if !self.nodes.insert(destination.to_string()) {
+            log::error!(target: "lua", "re-assignation of variable '{dest}' detected");
+            return false;
+        }
+        self.edges.push((dest, src, function));
+        true
+    }
+}
+
+impl Default for VivyExecGraph {
+    fn default() -> Self {
         Self {
+            nodes: HashSet::from(["INIT".to_string()]),
+            edges: Default::default(),
+            exits: Default::default(),
+        }
+    }
+}
+
+impl<'lua, I> TryFrom<(&'lua Lua, I, LuaValue<'lua>)> for VivyExecGraph
+where
+    I: IntoIterator<Item = (usize, Option<LuaString<'lua>>, LuaValue<'lua>)>,
+{
+    type Error = LuaError;
+
+    fn try_from((lua, work, ret): (&'lua Lua, I, LuaValue<'lua>)) -> Result<Self, Self::Error> {
+        let mut graph = Self {
+            exits: dsl::into_string_vec(lua, ret)?,
+            ..Default::default()
+        };
+        for (idx, dest, src) in work {
+            let dest = dest.ok_or(LuaError::RuntimeError("".to_string()))?;
+            let dest = dest.to_string_lossy();
+            let src = match src {
+                LuaValue::UserData(src) if src.is::<VivyCalledJob>() => {
+                    src.take::<VivyCalledJob>().expect("very internal error")
+                }
+                _ => return Err(LuaError::RuntimeError("".to_string())),
+            };
+            if !graph.insert(&dest, src.function, &src.arguments) {
+                return Err(LuaError::RuntimeError(format!(
+                    "invalid instruction n°{} in main block, edge was: '{dest:?} <- {:?}'",
+                    idx, src.arguments
+                )));
+            }
+        }
+        let invalids = graph.exits.iter().filter(|exit| {
+            either!(graph.nodes.contains(exit.as_str()) => false; {
+                log::error!(target: "lua", "invalid exit node '{exit}', variable was never assigned");
+                true
+            })
+        });
+        if invalids.count().ne(&0) {
+            Err(LuaError::RuntimeError(format!(
+                "invalid exit nodes for execution graph: {graph:#?}"
+            )))
+        } else {
+            Ok(graph)
+        }
+    }
+}
+
+impl Vivy {
+    /// Create a new instance of the vivy module, with a specified include path.
+    pub fn new_with_path(path: impl IntoIterator<Item = impl AsRef<str>>) -> VivyPtr {
+        Rc::new(RefCell::new(Self {
             path: path
                 .into_iter()
                 .map(|str| str.as_ref().to_string())
                 .collect(),
             ..Default::default()
+        }))
+    }
+
+    /// Create a new default ASS element of the asked type.
+    pub fn new_ass_element<'lua>(&self, lua: &'lua Lua, ty: ASSType) -> LuaResult<LuaValue<'lua>> {
+        macro_rules! new {
+            ($elem: ident) => {
+                paste::paste! {{
+                    let elem = [< ASS $elem >] ::default().into_ptr();
+                    elem.borrow_mut().aux = self.new_ass_aux_table(ty);
+                    Ok( [< LuaAss $elem Ptr >] ::from(elem).to_lua(lua)?)
+                }}
+            };
+        }
+        match ty {
+            ASSType::Lines => new!(Lines),
+            ASSType::Line => new!(Line),
+            ASSType::Syllabes => new!(Syllabes),
+            ASSType::Syllabe => new!(Syllabe),
+        }
+    }
+
+    /// Create a new default ASS element aux table of the asked type. The table is filled with the
+    /// default options for the said element.
+    pub fn new_ass_aux_table(&self, ty: ASSType) -> ASSAuxTablePtr {
+        self.data
+            .as_ref()
+            .map(|data| data.borrow().get_table(ty))
+            .unwrap_or_default()
+            .into_inner()
+    }
+
+    /// Pack the arguments for a job, check for correct type, coerce if needed, etc.
+    fn pack_job_arguments<'lua>(
+        &self,
+        lua: &'lua Lua,
+        input: ASSType,
+        args: Vec<LuaString<'lua>>,
+    ) -> LuaResult<Vec<LuaValue<'lua>>> {
+        let (vars, errs): (Vec<_>, Vec<_>) = args
+            .into_iter()
+            .map(|arg| match self.variables.get(arg.to_str()?) {
+                Some(var) => match lua.registry_value::<LuaValue>(var)? {
+                    var @ LuaValue::UserData(_) => Ok(var),
+                    var => Err(LuaError::RuntimeError(format!(
+                        "invalid type '{}' for job argument '{}'",
+                        var.type_name(),
+                        arg.to_str()?
+                    ))),
+                },
+                None => Err(LuaError::RuntimeError(format!(
+                    "failed to find variable '{}' in execution state",
+                    arg.to_str()?
+                ))),
+            })
+            .partition(Result::is_ok);
+        if let Some(Err(err)) = errs.into_iter().next() {
+            return Err(err);
+        }
+        let mut args: Vec<LuaValue> = Default::default();
+        for var in vars.into_iter().map(Result::unwrap) {
+            let LuaValue::UserData(var) = var else {
+                panic!("we should have a user data type here, got {}", var.type_name())
+            };
+            match input {
+                ASSType::Lines | ASSType::Line => match_take! { var =>
+                    ; var: LuaAssLinePtr  => { args.push(var.to_lua(lua)?); continue }
+                    ; var: LuaAssLinesPtr => {
+                        for var in &var.into_inner().borrow().content {
+                            args.push(LuaAssLinePtr::from(var.clone()).to_lua(lua)?);
+                        }
+                        continue
+                    }
+                    ; var: LuaAssContainerPtr => {
+                        for var in &var.into_inner().borrow().lines.borrow().content {
+                            args.push(LuaAssLinePtr::from(var.clone()).to_lua(lua)?);
+                        }
+                        continue
+                    }
+                    ; @default => { return Err(LuaError::RuntimeError(
+                        "expected a line or lines, got syllabe or syllabes".to_string()
+                    )) }
+                },
+                ASSType::Syllabes | ASSType::Syllabe => match_take! { var =>
+                    ; var: LuaAssSyllabePtr  => { args.push(var.to_lua(lua)?); continue }
+                    ; var: LuaAssSyllabesPtr => {
+                        for var in &var.into_inner().borrow().content {
+                            args.push(LuaAssSyllabePtr::from(var.clone()).to_lua(lua)?);
+                        }
+                        continue
+                    }
+                    ; var: LuaAssLinePtr => {
+                        for var in &var.into_inner().borrow().content.borrow().content {
+                            args.push(LuaAssSyllabePtr::from(var.clone()).to_lua(lua)?);
+                        }
+                        continue
+                    }
+                    ; var: LuaAssLinesPtr => {
+                        for line in var.into_inner().borrow().content.iter() {
+                            for var in &line.borrow().content.borrow().content {
+                                args.push(LuaAssSyllabePtr::from(var.clone()).to_lua(lua)?);
+                            }
+                        }
+                        continue
+                    }
+                    ; var: LuaAssContainerPtr => {
+                        for line in var.into_inner().borrow().lines.borrow().content.iter() {
+                            for var in &line.borrow().content.borrow().content {
+                                args.push(LuaAssSyllabePtr::from(var.clone()).to_lua(lua)?);
+                            }
+                        }
+                        continue
+                    }
+                    ; @default => { return Err(LuaError::RuntimeError(
+                        "expected a syllabe or syllabes, got line or lines".to_string()
+                    )) }
+                },
+            };
+        }
+        match input {
+            ASSType::Lines | ASSType::Syllabes => {
+                log::debug!(target: "lua", "we already have flattened the input elements");
+                Ok(args)
+            }
+            ASSType::Line | ASSType::Syllabe => {
+                log::debug!(target: "lua", "we need to pack the flattened elements into a parent element");
+                let mut elem = self.new_ass_element(lua, input)?;
+                for arg in args {
+                    elem = lua
+                        .load(chunk! { local elem = $elem; elem.push($arg); return elem })
+                        .eval()?;
+                }
+                Ok(vec![elem])
+            }
+        }
+    }
+
+    fn unpack_job_returns<'lua>(
+        &self,
+        lua: &'lua Lua,
+        name: &str,
+        returns: ASSType,
+        output: impl Iterator<Item = LuaValue<'lua>>,
+    ) -> LuaResult<Vec<LuaAnyUserData<'lua>>> {
+        fn unwrap(var: LuaValue) -> LuaAnyUserData {
+            match var {
+                LuaValue::UserData(var) => var,
+                _ => unreachable!(),
+            }
+        }
+
+        fn handle_user_data<'lua>(
+            lua: &'lua Lua,
+            returns: ASSType,
+            var: LuaAnyUserData<'lua>,
+        ) -> LuaResult<Vec<LuaAnyUserData<'lua>>> {
+            match returns {
+                ASSType::Lines | ASSType::Line => match_take! { var =>
+                    ; var: LuaAssLinePtr  => Ok(vec![unwrap(var.to_lua(lua)?)])
+                    ; var: LuaAssLinesPtr => Ok(var.into_inner().borrow().content.iter().map(|line|
+                        unwrap(LuaAssLinePtr::from(line.clone()).to_lua(lua).unwrap())
+                    ).collect())
+                    ; @default => Err(LuaError::RuntimeError(
+                        "expected a line or lines, got syllabe or syllabes".to_string()
+                    ))
+                },
+                ASSType::Syllabes | ASSType::Syllabe => match_take! { var =>
+                    ; var: LuaAssSyllabePtr  => Ok(vec![unwrap(var.to_lua(lua)?)])
+                    ; var: LuaAssSyllabesPtr => Ok(var.into_inner().borrow().content.iter().map(|line|
+                        unwrap(LuaAssSyllabePtr::from(line.clone()).to_lua(lua).unwrap())
+                    ).collect())
+                    ; @default => Err(LuaError::RuntimeError(
+                        "expected a syllabe or syllabes, got line or lines".to_string()
+                    ))
+                },
+            }
+        }
+
+        let (output, errs): (Vec<_>, Vec<_>) = output
+            .map(|out: LuaValue<'lua>| match out {
+                LuaValue::UserData(out) => handle_user_data(lua, returns, out),
+                LuaValue::Table(outs) => {
+                    let (outs, errs): (Vec<_>, Vec<_>) = outs
+                        .sequence_values()
+                        .map(|item| handle_user_data(lua, returns, item?))
+                        .partition(Result::is_ok);
+                    if let Some(Err(err)) = errs.into_iter().next() {
+                        return Err(err);
+                    }
+                    Ok(outs.into_iter().flat_map(Result::unwrap).collect())
+                }
+                _ => Err(LuaError::RuntimeError(format!(
+                    "invalid return type for job '{name}', got a value of type: {}",
+                    out.type_name()
+                ))),
+            })
+            .partition(Result::is_ok);
+        if let Some(Err(err)) = errs.into_iter().next() {
+            return Err(err);
+        }
+        let (output, errs): (Vec<_>, Vec<_>) = output.into_iter().partition(Result::is_ok);
+        if let Some(Err(err)) = errs.into_iter().next() {
+            return Err(err);
+        }
+        let output = output.into_iter().flat_map(Result::unwrap).collect();
+
+        match returns {
+            ASSType::Line | ASSType::Syllabe => Ok(output),
+            ty @ ASSType::Lines | ty @ ASSType::Syllabes => {
+                let mut elem = self.new_ass_element(lua, ty)?;
+                for out in output {
+                    elem = lua
+                        .load(chunk! {
+                            local elem = $elem
+                            elem.push($out)
+                            elem
+                        })
+                        .eval()?;
+                }
+                Ok(vec![unwrap(elem)])
+            }
         }
     }
+
+    /// Execute a job with the named arguments. The types of the arguments will be checked and it
+    /// will be up to this function to create the apply loop if needed. The returned values will
+    /// also be flattened if needed.
+    fn execute_job<'lua>(
+        &mut self,
+        lua: &'lua Lua,
+        name: &str,
+        job: LuaFunction<'lua>,
+        input: ASSType,
+        returns: ASSType,
+        args: Vec<LuaString<'lua>>,
+    ) -> LuaResult<Vec<LuaAnyUserData<'lua>>> {
+        let (output, errs): (Vec<_>, Vec<_>) = self
+            .pack_job_arguments(lua, input, args)?
+            .into_iter()
+            .map(|arg| {
+                let ret = job.call::<LuaValue, LuaValue>(arg);
+                self.is_in_job_evaluation = true;
+                ret
+            })
+            .partition(Result::is_ok);
+        if let Some(Err(err)) = errs.into_iter().next() {
+            return Err(LuaError::CallbackError {
+                traceback: format!("error found while executing job '{name}'"),
+                cause: Arc::new(err),
+            });
+        }
+        self.unpack_job_returns(lua, name, returns, output.into_iter().map(Result::unwrap))
+    }
 }
 
 impl LuaUserData for Vivy {
     fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
-        methods.add_method("___get_loaded", |_, this, ()| Ok(this.loaded.clone()));
-        methods.add_method("___get_path", |_, this, ()| Ok(this.path.clone()));
+        macro_rules! add_setter {
+            ($n: ident: $p: ident;
+             $($name: ident: $ptr: ident);+$(;)?
+            ) => {
+                add_setter! { $n: $p }
+                add_setter! { $($name: $ptr);+ }
+            };
+
+            ($name: ident: $ptr: ident $(;)?) => {
+                methods.add_method_mut(concat!("___set_", stringify!($name)), |_, this, $name: $ptr| {
+                    this.$name = Some($name);
+                    Ok(())
+                });
+            };
+        }
+
+        macro_rules! add_getter {
+            ($n: literal: $t: ident => $e: expr;
+             $($name: literal: $this: ident => $expr: expr);+$(;)?
+            ) => {
+                add_getter! { $n: $t => $e }
+                add_getter! { $($name: $this => $expr);+ }
+            };
+
+            ($name: literal: $this: ident => $expr: expr $(;)?) => {
+                methods.add_method($name, |_, $this, ()| Ok($expr));
+            };
+        }
+
+        add_setter! {
+            options: VivyOptionRegisterPtr;
+            data: VivyDataRegisterPtr;
+            jobs: VivyJobRegisterPtr;
+            func: VivyFuncRegisterPtr;
+        }
+
+        add_getter! {
+            "___get_loaded":                this => this.loaded.clone();
+            "___get_path":                  this => this.path.clone();
+            "___is_in_job_evaluation_mode": this => this.is_in_job_evaluation;
+        }
 
         methods.add_method_mut("___append_to_path", |_, this, pattern: String| {
-            Ok(this.path.push(pattern))
+            this.path.push(pattern);
+            Ok(())
         });
 
         methods.add_method_mut("___mark_loaded", |_, this, module: String| {
-            Ok(this.loaded.push(module))
+            this.loading.remove(&module);
+            Ok(())
+        });
+
+        methods.add_method_mut("___set_current_module", |_, this, module: LuaValue| {
+            this.current_module = match module {
+                LuaValue::String(module) => Some(module.to_string_lossy().to_string()),
+                _ => None,
+            };
+            Ok(())
+        });
+
+        methods.add_method("___current_module", |lua, this, ()| {
+            Ok(match this.current_module {
+                Some(ref current_module) => current_module.clone().to_lua(lua)?,
+                None => LuaValue::Nil,
+            })
+        });
+
+        methods.add_method("___run_export_module", |lua, this, name: LuaString| {
+            let table = lua.create_table()?;
+            this.jobs
+                .as_ref()
+                .map(|jobs| jobs.borrow_mut().export(lua, &name, &table))
+                .unwrap_or(Ok(()))?;
+            this.func
+                .as_ref()
+                .map(|func| func.borrow_mut().export(lua, &name, &table))
+                .unwrap_or(Ok(()))?;
+            let table = dsl::into_readonly_table(lua, name.clone(), table)?;
+            lua.globals().raw_set(name, table)?;
+
+            Ok(())
         });
 
-        methods.add_method_mut("___import", |lua, this, name: String| {
+        methods.add_method_mut(
+            "___execute_job",
+            |lua,
+             this,
+             (name, job, args, input, returns): (
+                LuaString,
+                LuaFunction,
+                Vec<LuaString>,
+                LuaAssType,
+                LuaAssType,
+            )| {
+                this.execute_job(
+                    lua,
+                    name.to_str()?,
+                    job,
+                    input.into_inner(),
+                    returns.into_inner(),
+                    args,
+                )
+            },
+        );
+
+        methods.add_method_mut("___import", |lua, this, name: String| -> LuaResult<bool> {
+            if this.loading.contains(&name) {
+                return Err(LuaError::RuntimeError(format!(
+                    "circular dependency found with module '{name}'"
+                )));
+            }
+            this.loading.insert(name.clone());
             this.path
                 .iter()
-                .map(|pattern| PathBuf::from(pattern.replace("?", &name)))
                 .find_map(|path| {
+                    let path = PathBuf::from(path.replace('?', &name));
+                    let was_lua_file = path
+                        .extension()
+                        .map(|ext| ext.eq("lua"))
+                        .unwrap_or_default();
                     (path.exists() && path.is_file()).then(|| {
-                        match lua.load(&path).eval::<LuaValue>() {
-                            Ok(module) => Some(module),
-                            Err(err) => {
-                                log::error!(target: "lua", "failed to load module '{name}' at location: {}\n{err}", path.to_string_lossy());
-                                None
+                        let path_name = path.to_string_lossy();
+                        match lua.load(&path).set_name(&path_name)?.exec() {
+                            Ok(()) => {
+                                this.loaded.insert(name.clone());
+                                Ok(was_lua_file)
                             }
+                            Err(err) => Err(LuaError::RuntimeError(format!(
+                                "failed to load module '{name}' at location: {path_name}\n{err}"
+                            ))),
                         }
-                    })?
+                    })
                 })
-                .ok_or(LuaError::RuntimeError(format!(
+                .unwrap_or(Err(LuaError::RuntimeError(format!(
                     "failed to find or load module named '{name}' in path: {:#?}",
                     this.path
-                )))
+                ))))
         });
 
-        methods.add_method_mut(
-            "___set_options",
-            |_, this, options: VivyOptionRegisterPtr| Ok(this.options = Some(options)),
-        );
+        methods.add_method_mut("___is_already_imported", |_, this, module: String| {
+            Ok(this.loaded.contains(&module))
+        });
+
+        methods.add_method_mut("___switch_to_execution_mode", |_, this, ()| {
+            this.is_in_job_evaluation
+                .then(|| {
+                    this.is_in_job_evaluation = false;
+                    Ok(())
+                })
+                .unwrap_or_else(|| {
+                    Err(LuaError::RuntimeError(
+                        "already in execution mode, internal error".to_string(),
+                    ))
+                })
+        });
 
-        methods.add_method_mut("___set_data", |_, this, data: VivyDataRegisterPtr| {
-            Ok(this.data = Some(data))
+        methods.add_method_mut("___print_info", |_, this, ()| {
+            this.should_print_infos = true;
+            Ok(())
         });
 
-        methods.add_method_mut("___finish_setup", |lua, this, ()| {
+        methods.add_method_mut("___main", |lua, this, table: LuaTable| -> LuaResult<()> {
+            if this.main_once {
+                return Err(LuaError::RuntimeError(
+                    "multiple 'main' blocks detected".to_string(),
+                ));
+            }
+
+            // Finish setup
             if let Some(ref options) = this.options {
                 let options = options.borrow();
                 for option in options.iter_registered() {
@@ -83,86 +615,46 @@ impl LuaUserData for Vivy {
                 data.borrow_mut().compute_cached_tables()
             }
 
-            Ok(())
-        });
-
-        methods.add_method("___print_info", |lua, this, ()| {
-            println!(" <<< Information Report >>>");
-
-            let version = lua.globals().get::<_, String>("_VERSION")?;
-            let mut packages = crate::get_loaded(lua)?;
-            packages.sort();
-            let packages = packages.join(", ");
-            println!(" # Misc");
-            println!("   - version    {version}");
-            println!("   - packages   {packages}");
-
-            if let Some(options) = this.options.as_ref() {
-                let options = options.borrow();
-
-                if options.iter_registered().count() >= 1 {
-                    println!(" # Options");
-                    let padding = options.iter_registered().map(String::len).max();
-                    let padding = padding.unwrap_or_default();
-                    options.iter_registered().for_each(|name| {
-                        let opt = options
-                            .resolve(name)
-                            .map(|opt| format!(" : {} = {opt}", opt.ty()))
-                            .unwrap_or_else(|| {
-                                options
-                                    .get_type(name)
-                                    .map(|ty| format!(" : {ty}"))
-                                    .unwrap_or(" : nil".to_string())
-                            });
-                        let doc = options
-                            .get_docstring(name)
-                            .and_then(|docstring| docstring.into_iter().next())
-                            .map(|line| format!(" # {line}"));
-                        let doc = doc.unwrap_or_default();
-                        println!("   - option {name:padding$}{opt}{doc}");
-                    });
+            // Print infos
+            if this.should_print_infos {
+                println!(" <<< Information Report >>>");
+                println!(" # Misc");
+                println!(
+                    "   - version    {}",
+                    lua.globals().get::<_, String>("_VERSION")?
+                );
+                println!("   - packages   {}", {
+                    let mut packages = this.loaded.iter().cloned().collect::<Vec<_>>();
+                    packages.sort();
+                    let packages = packages.join(", ");
+                    packages
+                });
+                if let Some(options) = this.options.as_ref() {
+                    options.borrow().print_info();
                 }
-
-                let undefined: Vec<_> = options.iter_undefined_toml_options().collect();
-                if !undefined.is_empty() {
-                    println!(" # Unused Toml Options");
-                    let max = undefined.iter().map(|(name, _)| name.len()).max();
-                    let max = max.unwrap_or_default();
-                    undefined.into_iter().for_each(|(name, value)| {
-                        println!("   - {name:max$} = {}", value.to_value());
-                        //
-                    });
-                }
-            }
-
-            if let Some(data) = this.data.as_ref() {
-                let data = data.borrow();
-                let added: Vec<_> = data
-                    .iter_registered()
-                    .map(|(ty, name)| (ty, name, format!("{ty}.aux.{name}")))
-                    .collect();
-                if !added.is_empty() {
-                    println!(" # Data");
-                    let padding = added.iter().map(|(_, _, name)| name.len()).max();
-                    let padding = padding.unwrap_or_default();
-                    for (ty, data_name, format_name) in added {
-                        println!(
-                            "   - data {format_name:padding$} = {}",
-                            data.get_table(ty)
-                                .as_inner()
-                                .borrow()
-                                .get(data_name)
-                                .expect("vivy internal error"),
-                        );
-                    }
+                if let Some(data) = this.data.as_ref() {
+                    data.borrow().print_info();
                 }
-
-                let orphans: Vec<_> = data.get_orphan_data_declarations().collect();
-                assert!(orphans.is_empty());
+                this.should_print_infos = false;
             }
 
+            // Do the work
+            this.is_in_job_evaluation = true;
+            let table_last_idx = table.raw_len() - 1;
+            let (work, ret): (Vec<_>, Vec<_>) = table
+                .pairs::<LuaValue, LuaValue>()
+                .enumerate()
+                .flat_map(|(idx, pair)| {
+                    let (dest, src) = pair.ok()?;
+                    Some((idx, lua.coerce_string(dest).unwrap_or_default(), src))
+                })
+                .partition(|(idx, ..)| i64::try_from(*idx).unwrap() != table_last_idx);
+            let [(_, _, ret)] = &ret[..] else { unreachable!("invalid return statements, should only be one: {ret:#?}") };
+            let exec_graph = VivyExecGraph::try_from((lua, work, ret.clone()))?;
+            log::error!(target: "lua", "implement execution logic for: {exec_graph:#?}");
+            this.is_in_job_evaluation = false;
             Ok(())
-        })
+        });
     }
 }
 
diff --git a/src/Rust/vvs_lua/src/data/lua_wrapper.rs b/src/Rust/vvs_lua/src/lua_wrapper.rs
similarity index 55%
rename from src/Rust/vvs_lua/src/data/lua_wrapper.rs
rename to src/Rust/vvs_lua/src/lua_wrapper.rs
index e0cb563e5ddf2b1c42351f12634d7f94ed7ec303..63479414f919161ad03daf3cbcbd924d90f25b5b 100644
--- a/src/Rust/vvs_lua/src/data/lua_wrapper.rs
+++ b/src/Rust/vvs_lua/src/lua_wrapper.rs
@@ -2,22 +2,23 @@
 
 use crate::traits::TypedValue;
 use mlua::prelude::*;
+use std::str::FromStr;
 use vvs_ass::{
-    ASSAuxTablePtr, ASSAuxValue, ASSLinePtr, ASSLinesPtr, ASSPositionPtr, ASSSyllabePtr,
-    ASSSyllabesPtr,
+    ASSAuxTablePtr, ASSAuxValue, ASSContainerPtr, ASSLinePtr, ASSLinesPtr, ASSPositionPtr,
+    ASSSyllabePtr, ASSSyllabesPtr, ASSType,
 };
 
 macro_rules! wrap_ass2lua {
-    ($($type: ident
-      $(, $fields: ident -> $add_fields: expr
+    ($($type: ident $(-> $($trait: ident),+)?
+      $(: $fields: ident -> $add_fields: expr
         , $methods: ident -> $add_methods: expr)?
     );+ $(;)?) => {
-        $(wrap_ass2lua!(@@private $type $(; $fields -> $add_fields ; $methods -> $add_methods)?);)+
+        $(wrap_ass2lua!(@@private $type $($($trait),+)? $(; $fields -> $add_fields ; $methods -> $add_methods)?);)+
     };
 
-    (@@decl $type: ident) => {
+    (@@decl $type: ident $($trait: ident),*) => {
         paste::paste! {
-            #[derive(Debug, Clone, PartialEq)]
+            #[derive(Debug, Clone, PartialEq, $($trait),*)]
             #[repr(transparent)]
             pub(crate) struct [< LuaAss $type >] ([< ASS $type >]);
 
@@ -41,15 +42,15 @@ macro_rules! wrap_ass2lua {
         }
     };
 
-    (@@private $type: ident) => {
-        wrap_ass2lua!(@@decl $type);
+    (@@private $type: ident $($trait: ident),*) => {
+        wrap_ass2lua!(@@decl $type $($trait),*);
         paste::paste! { impl LuaUserData for [< LuaAss $type >] { } }
     };
 
-    (@@private $type: ident
+    (@@private $type: ident $($trait: ident),*
      ; $fields : ident -> $add_fields : expr
      ; $methods: ident -> $add_methods: expr) => {
-        wrap_ass2lua!(@@decl $type);
+        wrap_ass2lua!(@@decl $type $($trait),*);
         paste::paste! {
             impl LuaUserData for [< LuaAss $type >] {
                 fn add_fields <'lua, F: LuaUserDataFields <'lua, Self>>($fields:  &mut F) { $add_fields  }
@@ -64,6 +65,14 @@ macro_rules! add_method {
         add_method! { $fields => aux @ LuaAssAuxTablePtr }
     };
 
+    ($methods: expr => @copy) => {
+        add_method! {
+            method $methods => "copy" (_lua, this) -> {
+                Ok(Self((&*this).borrow().clone().into_ptr()))
+            }
+        }
+    };
+
     ($fields: expr => $field: ident @ $wrapper: ident) => {
         add_method! { get $fields => $field @ $wrapper }
         add_method! { set $fields => $field @ $wrapper }
@@ -81,36 +90,60 @@ macro_rules! add_method {
             Ok(())
         });
     };
+
+    (method $methods: expr => $name: literal ($lua: ident, $this: ident) -> $expr: expr) => {
+        $methods.add_method($name, |$lua, Self($this), ()| $expr)
+    };
 }
 
 wrap_ass2lua! {
+    Type;
     AuxValue;
-    AuxTablePtr;
-    PositionPtr;
-    LinePtr,
+    AuxTablePtr -> Default:
+        _f -> {},
+        m -> {
+            add_method! { m => @copy }
+        };
+    PositionPtr:
+        _f -> {},
+        m -> {
+            add_method! { m => @copy }
+        };
+    // The ASS types
+    ContainerPtr;
+    LinePtr:
         f -> {
             add_method! { f => @aux }
             add_method! { f => position @ LuaAssPositionPtr }
         },
-        _m -> {};
-    LinesPtr,
+        m -> {
+            add_method! { m => @copy }
+        };
+    LinesPtr:
         f -> {
             add_method! { f => @aux }
             add_method! { f => position @ LuaAssPositionPtr }
         },
-        _m -> {};
-    SyllabePtr,
+        m -> {
+            add_method! { m => @copy }
+            add_method! { method m => "push" (_lua, _this) -> { Ok(()) }}
+        };
+    SyllabePtr:
         f -> {
             add_method! { f => @aux }
             add_method! { f => position @ LuaAssPositionPtr }
         },
-        _m -> {};
-    SyllabesPtr,
+        m -> {
+            add_method! { m => @copy }
+        };
+    SyllabesPtr:
         f -> {
             add_method! { f => @aux }
             add_method! { f => position @ LuaAssPositionPtr }
         },
-        _m -> {};
+        m -> {
+            add_method! { m => @copy }
+        };
 }
 
 impl TypedValue for LuaAssAuxValue {
@@ -118,3 +151,22 @@ impl TypedValue for LuaAssAuxValue {
         self.0.ty()
     }
 }
+
+impl FromStr for LuaAssType {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(Self(s.parse().map_err(|err| format!("{err}"))?))
+    }
+}
+
+impl<'lua> TryFrom<LuaString<'lua>> for LuaAssType {
+    type Error = String;
+
+    fn try_from(value: LuaString<'lua>) -> Result<Self, Self::Error> {
+        match value.to_str() {
+            Ok(bt) => Ok(Self(bt.parse().map_err(|err| format!("{err}"))?)),
+            _ => Err("can't build a vvs_lua::BaseType from an invalid utf8 string".to_string()),
+        }
+    }
+}
diff --git a/src/Rust/vvs_lua/src/options/actions.rs b/src/Rust/vvs_lua/src/options/actions.rs
index 77db4215c35ff07627f4133786d84127e4a22448..ca0f4c9742512df7a70b3572a708ee32d135255f 100644
--- a/src/Rust/vvs_lua/src/options/actions.rs
+++ b/src/Rust/vvs_lua/src/options/actions.rs
@@ -1,4 +1,4 @@
-use crate::{traits::TypedValue, types::Type, values::Value};
+use crate::{dsl, traits::TypedValue, types::Type, values::Value};
 use mlua::prelude::*;
 
 /// Structure used to register an option with either:
@@ -103,24 +103,22 @@ impl<'lua> FromLua<'lua> for RegisterOptionValue {
     fn from_lua(lua_value: LuaValue<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
         match lua_value {
             LuaValue::Table(table) => {
-                let value = match table.get::<_, LuaValue>("value").ok() {
-                    None | Some(LuaValue::Nil) => None,
-                    Some(value) => Some(Value::from_lua(value, lua)?),
-                };
-                let ty = match table.get::<_, LuaValue>("type").ok() {
-                    None | Some(LuaValue::Nil) => None,
-                    Some(ty) => Some(Type::from_lua(ty, lua)?),
-                };
-                let doc = match table.get::<_, LuaValue>("doc").ok() {
-                    None | Some(LuaValue::Nil) => None,
-                    Some(LuaValue::String(doc)) => Some(doc.to_string_lossy().to_string()),
-                    Some(value) => {
-                        return Err(LuaError::RuntimeError(format!(
-                            "value of type '{}' can't be a docstring, expected a 'string'",
-                            value.type_name()
-                        )))
-                    }
-                };
+                let value = dsl::table_get!(table, "value" -> {
+                    dsl::nil_pat!() => None,
+                    dsl::value!(value) => Some(Value::from_lua(value, lua)?),
+                });
+                let ty = dsl::table_get!(table, "type" -> {
+                    dsl::nil_pat!() => None,
+                    dsl::value!(ty) => Some(Type::from_lua(ty, lua)?),
+                });
+                let doc = dsl::table_get!(table, "doc" -> {
+                    dsl::nil_pat!() => None,
+                    dsl::value!(String doc) => Some(doc.to_string_lossy().to_string()),
+                    dsl::value!(value) => return Err(LuaError::RuntimeError(format!(
+                        "value of type '{}' can't be a docstring, expected a 'string'",
+                        value.type_name()
+                    ))),
+                });
                 if let (Some(value), Some(ty)) = (value.as_ref(), ty.as_ref()) {
                     if value.ty().ne(ty) {
                         return Err(LuaError::RuntimeError(format!(
diff --git a/src/Rust/vvs_lua/src/options/register.rs b/src/Rust/vvs_lua/src/options/register.rs
index f5be385a2843f211c6cbdc35bae20d20f8bae9fd..a58f7eef63876f6d5a8a7044c8a7c18554f1e60d 100644
--- a/src/Rust/vvs_lua/src/options/register.rs
+++ b/src/Rust/vvs_lua/src/options/register.rs
@@ -146,12 +146,8 @@ impl VivyOptionRegister {
         let value = value.named(name);
 
         if let Some(old) = self.registered.get(&value.name) {
-            if PartialEq::ne(old, &value) && !(old.is_none() || value.is_none()) {
-                return Err(format!("option '{}' was already registered", value.name));
-            } else if (!old.is_none()) && value.is_none() {
-                log::warn!(target: "lua", "double register of option '{}', previous was nil, new is: {value:?}", value.name)
-            } else if !old.is_none() {
-                log::warn!(target: "lua", "double register of option '{}'", value.name)
+            if PartialEq::ne(old, &value) && (!old.is_none()) {
+                return Err(format!("option '{}' was already registered\n- old value is {old:?}\n- new value is {value:?}", value.name));
             }
         }
         Self::check_type(
@@ -162,6 +158,7 @@ impl VivyOptionRegister {
             let (ty, val_ty) = (value.ty.unwrap(), value.value.as_ref().unwrap().ty());
             format!("option '{}' was declared with type '{ty}' but was defaulted with value of type '{val_ty}'", value.name)
         })?;
+        log::debug!(target: "lua", "register option {} with: {value:?}", value.name);
         self.registered.insert(value.name.to_string(), value);
         Ok(())
     }
@@ -176,6 +173,41 @@ impl VivyOptionRegister {
             _ => Some(()),
         }
     }
+
+    /// Prints infos about the option register to stdout.
+    pub(crate) fn print_info(&self) {
+        if self.iter_registered().count() >= 1 {
+            println!(" # Options");
+            let padding = self.iter_registered().map(String::len).max();
+            let padding = padding.unwrap_or_default();
+            self.iter_registered().for_each(|name| {
+                let opt = self
+                    .resolve(name)
+                    .map(|opt| format!(" : {} = {opt}", opt.ty()))
+                    .unwrap_or_else(|| {
+                        self.get_type(name)
+                            .map(|ty| format!(" : {ty}"))
+                            .unwrap_or(" : nil".to_string())
+                    });
+                let doc = self
+                    .get_docstring(name)
+                    .and_then(|docstring| docstring.into_iter().next())
+                    .map(|line| format!(" # {line}"));
+                let doc = doc.unwrap_or_default();
+                println!("   - option     {name:padding$}{opt}{doc}");
+            });
+        }
+
+        let undefined: Vec<_> = self.iter_undefined_toml_options().collect();
+        if !undefined.is_empty() {
+            println!(" # Unused Toml Options");
+            let max = undefined.iter().map(|(name, _)| name.len()).max();
+            let max = max.unwrap_or_default();
+            undefined.into_iter().for_each(|(name, value)| {
+                println!("   - unused     {name:max$} = {}", value.to_value());
+            });
+        }
+    }
 }
 
 impl LuaUserData for VivyOptionRegister {
diff --git a/src/Rust/vvs_lua/src/traits.rs b/src/Rust/vvs_lua/src/traits.rs
index 91e1f21a338390da8c00901965a70502c0014a2a..0901ec534ec72b346e0e9ad31c925daf74c8d7bf 100644
--- a/src/Rust/vvs_lua/src/traits.rs
+++ b/src/Rust/vvs_lua/src/traits.rs
@@ -1,6 +1,7 @@
 //! General traits
 
-use crate::types::*;
+use crate::{libs::Vivy, types::*};
+use vvs_ass::{ptr, ASSLine, ASSLines, ASSSyllabe, ASSSyllabes, Ptr};
 
 /// A trait for typed values that can be used to pass informations between Lua and Rust code.
 pub trait TypedValue {
@@ -24,10 +25,53 @@ impl TypedValue for vvs_ass::ASSAuxValue {
     fn ty(&self) -> Type {
         use vvs_ass::ASSAuxValue;
         match self {
-            ASSAuxValue::Integer(_) => Type::Base(BaseType::Number),
             ASSAuxValue::Floating(_) => Type::Base(BaseType::Number),
+            ASSAuxValue::Integer(_) => Type::Base(BaseType::Number),
             ASSAuxValue::Boolean(_) => Type::Base(BaseType::Boolean),
             ASSAuxValue::String(_) => Type::Base(BaseType::String),
         }
     }
 }
+
+/// Trait used to build an element from a single element that will be contained.
+///
+/// If the trait is implemented for T, then it will be auto-implemented for [crate::Ptr<T>] if T is
+/// a clonable type (it should be for ASS elements).
+pub trait FromSingleElement {
+    type SingleElement;
+
+    /// Instanciate an element from a single child.
+    fn from_single_element(vivy: &Vivy, single: Self::SingleElement) -> Self;
+}
+
+impl<T: FromSingleElement> FromSingleElement for Ptr<T>
+where
+    <T as FromSingleElement>::SingleElement: Clone,
+{
+    type SingleElement = Ptr<<T as FromSingleElement>::SingleElement>;
+
+    fn from_single_element(vivy: &Vivy, single: Self::SingleElement) -> Self {
+        ptr!(FromSingleElement::from_single_element(
+            vivy,
+            (*single.borrow()).clone()
+        ))
+    }
+}
+
+macro_rules! impl_from_single_elem {
+    ($from: ident => $to: ident) => {
+        impl FromSingleElement for paste::paste! { [< ASS $to >] } {
+            type SingleElement = paste::paste! { [< ASS $from >] };
+            fn from_single_element(vivy: &Vivy, single: Self::SingleElement) -> Self {
+                Self {
+                    position: single.position.clone(),
+                    content: vec![ptr!(single)],
+                    aux: vivy.new_ass_aux_table(vvs_ass::ASSType::$to),
+                }
+            }
+        }
+    };
+}
+
+impl_from_single_elem! { Line => Lines }
+impl_from_single_elem! { Syllabe => Syllabes }
diff --git a/src/Rust/vvs_lua/src/types.rs b/src/Rust/vvs_lua/src/types.rs
index f4ace7ddcdc801a2348c13f3998de7a3506da2ed..38c60f82301130ae19da5a192525596c771b28b3 100644
--- a/src/Rust/vvs_lua/src/types.rs
+++ b/src/Rust/vvs_lua/src/types.rs
@@ -30,13 +30,27 @@ impl AsRef<str> for Type {
     }
 }
 
+impl<'lua> TryFrom<LuaString<'lua>> for BaseType {
+    type Error = String;
+
+    fn try_from(value: LuaString<'lua>) -> Result<Self, Self::Error> {
+        match value.to_str() {
+            Ok("number") => Ok(BaseType::Number),
+            Ok("string") => Ok(BaseType::String),
+            Ok("boolean") => Ok(BaseType::Boolean),
+            Ok(bt) => Err(format!("unknown base type: '{bt}'")),
+            _ => Err("can't build a vvs_lua::BaseType from an invalid utf8 string".to_string()),
+        }
+    }
+}
+
 impl<'lua> FromLua<'lua> for BaseType {
     fn from_lua(lua_value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
         match lua_value {
             LuaValue::String(ty) => match &ty.to_string_lossy()[..] {
                 "number" => Ok(BaseType::Number),
-                "boolean" => Ok(BaseType::Boolean),
                 "string" => Ok(BaseType::String),
+                "boolean" => Ok(BaseType::Boolean),
                 ty => Err(LuaError::RuntimeError(format!(
                     "string \"{ty}\" can't be decoded into vvs_lua::BaseType"
                 ))),
diff --git a/src/Rust/vvs_procmacro/Cargo.toml b/src/Rust/vvs_procmacro/Cargo.toml
index c7239a82228f3616d2bcba45a2802edc67b44601..3e99f27c4f2c4e3f9b13b4b8108390ba1fc94874 100644
--- a/src/Rust/vvs_procmacro/Cargo.toml
+++ b/src/Rust/vvs_procmacro/Cargo.toml
@@ -4,7 +4,7 @@ version.workspace = true
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
-description.workspace = true
+description = "The procmacro utility crate for VVS"
 
 [lib]
 proc-macro = true
diff --git a/src/Rust/vvs_procmacro/src/lib.rs b/src/Rust/vvs_procmacro/src/lib.rs
index 50a2d6fba57c54f530808e2b7e011690da5f933c..3d267f43895febebb610de2e1e6aa77c90ff793a 100644
--- a/src/Rust/vvs_procmacro/src/lib.rs
+++ b/src/Rust/vvs_procmacro/src/lib.rs
@@ -33,7 +33,7 @@ pub fn derive_enum_variant_iter(input: TokenStream) -> TokenStream {
                 enum_item
                     .variants
                     .into_iter()
-                    .map(|variant| format!("{}::{}", syn_item.ident, variant.ident.to_string()))
+                    .map(|variant| format!("{}::{}", syn_item.ident, variant.ident))
                     .collect::<Vec<_>>()
                     .join(", "),
             ))
diff --git a/src/Rust/vvs_repl/Cargo.toml b/src/Rust/vvs_repl/Cargo.toml
index c03cb68ef06aaaa5e9abe19b0a69434c5c8834b1..14ec9b1ac943835066e4ad9c26b7d399af626681 100644
--- a/src/Rust/vvs_repl/Cargo.toml
+++ b/src/Rust/vvs_repl/Cargo.toml
@@ -4,7 +4,7 @@ version.workspace = true
 authors.workspace = true
 edition.workspace = true
 license.workspace = true
-description.workspace = true
+description = "REPL implementation for VVS"
 
 [dependencies]
 log.workspace = true
diff --git a/src/Rust/vvs_utils/Cargo.toml b/src/Rust/vvs_utils/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..592c9a481bf372530f58ad708e120ab58d0c0d8d
--- /dev/null
+++ b/src/Rust/vvs_utils/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "vvs_utils"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+description = "Utility crate for VVS"
diff --git a/src/Rust/vvs_utils/src/angles.rs b/src/Rust/vvs_utils/src/angles.rs
new file mode 100644
index 0000000000000000000000000000000000000000..53424bca73c2b29483bc6bcba1bb6678334b9af6
--- /dev/null
+++ b/src/Rust/vvs_utils/src/angles.rs
@@ -0,0 +1,17 @@
+use core::{f32::consts::PI as PI_32, f64::consts::PI as PI_64};
+
+pub fn f64_randians_clamp(rad: f64) -> f64 {
+    rad % (2.0 * PI_64)
+}
+
+pub fn f64_degres_clamp(rad: f64) -> f64 {
+    rad % 360.0
+}
+
+pub fn f32_randians_clamp(rad: f32) -> f32 {
+    rad % (2.0 * PI_32)
+}
+
+pub fn f32_degres_clamp(rad: f32) -> f32 {
+    rad % 360.0
+}
diff --git a/src/Rust/vvs_utils/src/assert.rs b/src/Rust/vvs_utils/src/assert.rs
new file mode 100644
index 0000000000000000000000000000000000000000..eaac3d3422d4c09b6dbe6e2890d76e1718898918
--- /dev/null
+++ b/src/Rust/vvs_utils/src/assert.rs
@@ -0,0 +1,15 @@
+#[macro_export]
+macro_rules! assert_eq_delta {
+    ($a: expr, $b: expr, $delta: literal) => {{
+        let (a, b, delta) = ($a, $b, $delta);
+        let diff = (a - b);
+        if !((diff <= delta && diff >= 0.0) || (-diff <= delta && diff <= 0.0)) {
+            panic!(
+                "assertion failed: not delta-equal: `abs({a} - {b}) > {delta}` \n\tleft: {}\n\tright: {}\n\tdelta: {}",
+                stringify!($a),
+                stringify!($b),
+                stringify!($delta)
+            );
+        }
+    }};
+}
diff --git a/src/Rust/vvs_utils/src/conds.rs b/src/Rust/vvs_utils/src/conds.rs
new file mode 100644
index 0000000000000000000000000000000000000000..98256e0c5a19d2e900f59dc6b6f3c1c68f9c8969
--- /dev/null
+++ b/src/Rust/vvs_utils/src/conds.rs
@@ -0,0 +1,18 @@
+#[macro_export]
+macro_rules! either {
+    ($cond: expr => $then: expr ; $else: expr) => {{
+        if $cond {
+            $then
+        } else {
+            $else
+        }
+    }};
+}
+
+pub fn f64_epsilon_eq(a: f64, b: f64) -> bool {
+    (a - b).abs() <= 10E-10
+}
+
+pub fn f32_epsilon_eq(a: f32, b: f32) -> bool {
+    (a - b).abs() <= 10E-7
+}
diff --git a/src/Rust/vvs_utils/src/lib.rs b/src/Rust/vvs_utils/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fad61c684b583d598ab3a1d17c73fd5c4d8d2b75
--- /dev/null
+++ b/src/Rust/vvs_utils/src/lib.rs
@@ -0,0 +1,12 @@
+mod angles;
+mod assert;
+mod conds;
+mod minmax;
+
+pub use angles::*;
+pub use assert::*;
+pub use conds::*;
+pub use minmax::*;
+
+#[cfg(test)]
+mod tests;
diff --git a/src/Rust/vvs_utils/src/minmax.rs b/src/Rust/vvs_utils/src/minmax.rs
new file mode 100644
index 0000000000000000000000000000000000000000..80252cee9d4c49ccfd0f801f22dc773ceb2359cf
--- /dev/null
+++ b/src/Rust/vvs_utils/src/minmax.rs
@@ -0,0 +1,57 @@
+#[inline]
+pub fn min<T: PartialOrd>(a: T, b: T) -> T {
+    if a < b {
+        a
+    } else {
+        b
+    }
+}
+
+#[inline]
+pub fn max<T: PartialOrd>(a: T, b: T) -> T {
+    if a > b {
+        a
+    } else {
+        b
+    }
+}
+
+#[macro_export]
+macro_rules! min {
+    ($x: expr) => { $x };
+    ($x: expr, $($z: expr),+) => { ::core::cmp::min($x, $crate::min!($($z),*)) };
+}
+
+#[macro_export]
+macro_rules! max {
+    ($x: expr) => { $x };
+    ($x: expr, $($z: expr),+) => { ::core::cmp::max($x, $crate::max!($($z),*)) };
+}
+
+#[macro_export]
+macro_rules! minmax {
+    ($($xs:expr),+) => {(
+        $crate::min!($($xs),+),
+        $crate::max!($($xs),+),
+    )};
+}
+
+#[macro_export]
+macro_rules! min_partial {
+    ($x:expr) => { $x };
+    ($x:expr, $($xs:expr),+) => { $crate::min($x, $crate::min_partial!( $($xs),+ )) };
+}
+
+#[macro_export]
+macro_rules! max_partial {
+    ($x:expr) => { $x };
+    ($x:expr, $($xs:expr),+) => { $crate::max($x, $crate::max_partial!( $($xs),+ )) };
+}
+
+#[macro_export]
+macro_rules! minmax_partial {
+    ($($xs:expr),+) => {(
+        $crate::min_partial!($($xs),+),
+        $crate::max_partial!($($xs),+),
+    )};
+}
diff --git a/src/Rust/vvs_utils/src/tests.rs b/src/Rust/vvs_utils/src/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9b40b25d9c6e9d073bf45150acbc522714fbc4c8
--- /dev/null
+++ b/src/Rust/vvs_utils/src/tests.rs
@@ -0,0 +1,7 @@
+use crate::*;
+
+/// Test whever we have what we expect with the floating modulo in Rust.
+#[test]
+fn test_floating_modulo() {
+    assert_eq_delta!(7.4 % 6.0, 1.4, 0.1);
+}
diff --git a/utils/manual/ASS File Format Specification.html b/utils/manual/ASS File Format Specification.html
new file mode 100644
index 0000000000000000000000000000000000000000..eb7f2464cd7f16310d04efacfe4c2287b058b6d6
--- /dev/null
+++ b/utils/manual/ASS File Format Specification.html	
@@ -0,0 +1,2245 @@
+<html data-darkreader-mode="dynamic" data-darkreader-scheme="dark" data-lt-installed="true"><head><style class="darkreader darkreader--fallback" media="screen"></style><style class="darkreader darkreader--text" media="screen"></style><style class="darkreader darkreader--invert" media="screen">.jfk-bubble.gtx-bubble, .captcheck_answer_label > input + img, span#closed_text > img[src^="https://www.gstatic.com/images/branding/googlelogo"], span[data-href^="https://www.hcaptcha.com/"] > #icon, #bit-notification-bar-iframe, ::-webkit-calendar-picker-indicator {
+    filter: invert(100%) hue-rotate(180deg) contrast(90%) !important;
+}</style><style class="darkreader darkreader--inline" media="screen">[data-darkreader-inline-bgcolor] {
+  background-color: var(--darkreader-inline-bgcolor) !important;
+}
+[data-darkreader-inline-bgimage] {
+  background-image: var(--darkreader-inline-bgimage) !important;
+}
+[data-darkreader-inline-border] {
+  border-color: var(--darkreader-inline-border) !important;
+}
+[data-darkreader-inline-border-bottom] {
+  border-bottom-color: var(--darkreader-inline-border-bottom) !important;
+}
+[data-darkreader-inline-border-left] {
+  border-left-color: var(--darkreader-inline-border-left) !important;
+}
+[data-darkreader-inline-border-right] {
+  border-right-color: var(--darkreader-inline-border-right) !important;
+}
+[data-darkreader-inline-border-top] {
+  border-top-color: var(--darkreader-inline-border-top) !important;
+}
+[data-darkreader-inline-boxshadow] {
+  box-shadow: var(--darkreader-inline-boxshadow) !important;
+}
+[data-darkreader-inline-color] {
+  color: var(--darkreader-inline-color) !important;
+}
+[data-darkreader-inline-fill] {
+  fill: var(--darkreader-inline-fill) !important;
+}
+[data-darkreader-inline-stroke] {
+  stroke: var(--darkreader-inline-stroke) !important;
+}
+[data-darkreader-inline-outline] {
+  outline-color: var(--darkreader-inline-outline) !important;
+}
+[data-darkreader-inline-stopcolor] {
+  stop-color: var(--darkreader-inline-stopcolor) !important;
+}</style><style class="darkreader darkreader--variables" media="screen">:root {
+   --darkreader-neutral-background: #131516;
+   --darkreader-neutral-text: #d8d4cf;
+   --darkreader-selection-background: #004daa;
+   --darkreader-selection-text: #e8e6e3;
+}</style><style class="darkreader darkreader--root-vars" media="screen"></style><style class="darkreader darkreader--user-agent" media="screen">html {
+    background-color: #181a1b !important;
+}
+html {
+    color-scheme: dark !important;
+}
+html, body, input, textarea, select, button, dialog {
+    background-color: #181a1b;
+}
+html, body, input, textarea, select, button {
+    border-color: #736b5e;
+    color: #e8e6e3;
+}
+a {
+    color: #3391ff;
+}
+table {
+    border-color: #545b5e;
+}
+::placeholder {
+    color: #b2aba1;
+}
+input:-webkit-autofill,
+textarea:-webkit-autofill,
+select:-webkit-autofill {
+    background-color: #404400 !important;
+    color: #e8e6e3 !important;
+}
+::-webkit-scrollbar {
+    background-color: #202324;
+    color: #aba499;
+}
+::-webkit-scrollbar-thumb {
+    background-color: #454a4d;
+}
+::-webkit-scrollbar-thumb:hover {
+    background-color: #575e62;
+}
+::-webkit-scrollbar-thumb:active {
+    background-color: #484e51;
+}
+::-webkit-scrollbar-corner {
+    background-color: #181a1b;
+}
+* {
+    scrollbar-color: #454a4d #202324;
+}
+::selection {
+    background-color: #004daa !important;
+    color: #e8e6e3 !important;
+}
+::-moz-selection {
+    background-color: #004daa !important;
+    color: #e8e6e3 !important;
+}</style>
+<meta http-equiv="Content-Type" content="text/html; charset=GBK">
+<meta name="Generator" content="Microsoft Word 14 (filtered)">
+<title>ASS File Format Specification</title>
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+	{font-family:����;
+	panose-1:2 1 6 0 3 1 1 1 1 1;}
+@font-face
+	{font-family:����;
+	panose-1:2 1 6 0 3 1 1 1 1 1;}
+@font-face
+	{font-family:"\@����";
+	panose-1:2 1 6 0 3 1 1 1 1 1;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+	{margin:0in;
+	margin-bottom:.0001pt;
+	text-autospace:none;
+	font-size:10.0pt;
+	font-family:"Times New Roman","serif";}
+p.MsoPlainText, li.MsoPlainText, div.MsoPlainText
+	{margin:0in;
+	margin-bottom:.0001pt;
+	font-size:10.0pt;
+	font-family:"Courier New";}
+ /* Page Definitions */
+ @page WordSection1
+	{size:595.45pt 841.7pt;
+	margin:.75in .75in .75in .75in;}
+div.WordSection1
+	{page:WordSection1;}
+ /* List Definitions */
+ ol
+	{margin-bottom:0in;}
+ul
+	{margin-bottom:0in;}
+-->
+</style><style class="darkreader darkreader--sync" media="screen"></style>
+
+<meta name="darkreader" content="d5cc82de8b87b7f7a3b84c9f6672eca7"><style class="darkreader darkreader--override" media="screen">.vimvixen-hint {
+    background-color: #7b5300 !important;
+    border-color: #d8b013 !important;
+    color: #f3e8c8 !important;
+}
+::placeholder {
+    opacity: 0.5 !important;
+}
+#edge-translate-panel-body,
+.MuiTypography-body1,
+.nfe-quote-text {
+    color: var(--darkreader-neutral-text) !important;
+}
+gr-main-header {
+    background-color: #0f3a48 !important;
+}
+.tou-z65h9k,
+.tou-mignzq,
+.tou-1b6i2ox,
+.tou-lnqlqk {
+    background-color: var(--darkreader-neutral-background) !important;
+}
+.tou-75mvi {
+    background-color: #032029 !important;
+}
+.tou-ta9e87,
+.tou-1w3fhi0,
+.tou-1b8t2us,
+.tou-py7lfi,
+.tou-1lpmd9d,
+.tou-1frrtv8,
+.tou-17ezmgn {
+    background-color: #0a0a0a !important;
+}
+.tou-uknfeu {
+    background-color: #231603 !important;
+}
+.tou-6i3zyv {
+    background-color: #19576c !important;
+}
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }
+embed[type="application/pdf"] { filter: invert(100%) contrast(90%); }</style></head>
+
+<body style="text-justify-trim:punctuation" data-new-gr-c-s-check-loaded="8.905.0" data-gr-ext-installed="" lang="EN-US">
+
+<div class="WordSection1">
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><span style="font-size:16.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Sub Station Alpha
+v4.00<span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">+</span> Script Format</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">1. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">General
+information</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">2. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The
+[sections] of a Sub Station Alpha script</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">3. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The line
+types in a Sub Station Alpha script</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">4. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Header
+lines, [Script Info] section</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">5. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Style
+lines, [v4 Styles] section</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">6. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Dialogue
+event lines, [Events] section</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">7. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Comment
+lines, [Events] section</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">8. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Picture
+event lines, [Events] section</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">9. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Movie event
+line, [Events] section</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">10. </span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Sound event lines, [Events] section</span></b></p>
+
+<p class="MsoNormal" style="margin-left:77.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">11. </span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Command event lines, [Events] section</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Appendix
+A: Style override codes</b></span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Appendix
+B: Embedded font/picture encoding</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:63.8pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">This document was SSA��s format specification originally (can be
+found at http://www.eswat.demon.co.uk). Updates and differences are marked red.</span></b><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+1. General Information</span></u></b></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The
+information in this document assumes that you are familiar with the terms and
+concepts used by Sub Station Alpha (SSA). These are documented in SSA's help
+file, ssa.hlp which is distributed with the program, or can be downloaded
+separatelyfrom http://www.eswat.demon.co.uk.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">1. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The SSA
+v4.00 script format is different to previous versions of SSA<br>
+</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">SSA v4.00 will read
+scripts from older versions, but v4.00 scripts will not load into older
+versions of SSA correctly.<br>
+<br>
+Some of the changes in the script format are intended to allow all versions of
+SSA from v4.00 onwards to read any present or future SSA scripts. In
+particular, the new "Format" lines allow SSA to read only the
+information it recognises and discard any new information that is added in
+future scripts.<br>
+<br>
+</span></p>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">2. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Scripts are
+plain (DOS) text files.<br>
+</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This means they can
+be "manually" editied using any text editor, but caution must be
+exercised when doing this - SSA assumes that scripts will adhere to the
+"rules" set out in this document, and any errors may lead to
+unpredictable&nbsp; results when the script is loaded into SSA.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">2. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The script
+is divided into ��ini file�� style sections<br>
+</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">However, SSA scripts
+are not true Windows .ini files and you cannot do certain things you might
+expect!<br>
+<br>
+</span></p>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-indent:-14.15pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">3. <span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;
+</span></span></b><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Most lines
+in each section begin with some sort of code - a "line descriptor"</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">, to say what information is held in it.
+The descriptor is terminated by a colon.</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">3.&nbsp; The information fields in
+each line are separated by a commas.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp; This makes it
+illegal to use commas in character names and style names (SSA prevents you
+putting commas in these). It also makes it quite easy to load chunks of an SSA
+script into a spreadsheet as a CSV file, and chop out columns of information
+you need for another subtitling program.</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">4.&nbsp; SSA does not care what order
+events are entered in.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp; They could be
+entered in complete reverse order, and SSA would still play everything
+correctly in the right order ie. you cannot assume that each dialogue line is
+in chronological order in the script file.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">5.&nbsp; Incorrectly formatted lines
+are ignored.<br>
+</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">SSA will discard any
+lines it doesn't understand, and give a warning after the script has loaded
+giving the number of lines it discarded.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">6.&nbsp; Lines cannot be split</span></b></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp; Each entry in
+a script contains all its information in a single line.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">7.&nbsp; If unknown styles are used in
+the script, the *Default style will be used.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp; For example,
+if lines have been pasted in from another script, without the corresponding
+Style information then when SSA plays the script, the Default style settings
+will be used.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">8.&nbsp; If
+a Style specifies a font which is not installed, then Arial will be used
+instead.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp; This can
+happen with scripts which you did not create yourself - the original authors
+may have fonts installed which you don't have.</span></p>
+
+<p class="MsoNormal" style="margin-left:13.5pt;text-indent:-13.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">2. The sections in a
+Sub Station Alpha script</span></u></b></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><span style="text-decoration:none">&nbsp;</span></span></u></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">[Script
+Info]</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This section
+contains headers and general information about the script.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The line that
+says ��[Script Info]�� <b>must</b> be the first line in a v4 script.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">[v4
+Styles]</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This section
+contains all Style definitions required by the script. Each ��Style�� used by
+subtitles in the script should be defined here.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">ASS
+uses [v4 Styles+]</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">[Events]</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This section
+contains all the events for the script - all the subtitles, comments, pictures,
+sounds, movies and commands. Basically, everything that you see in Sub Station
+Alpha��s main-screen ��grid�� is in this section.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">[Fonts]</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This section
+contains text-encoded font files, if the user opted to embed non-standard fonts
+in the script. Only truetype fonts can be embedded in SSA scripts. Each font
+file is started with a single line in the format:<br>
+<br>
+</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">fontname:
+&lt;name of file&gt;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The word ��<b>fontname</b>��
+must be in lower case (upper case will be interpretted as part of a
+text-encoded file). </span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;name
+of file&gt;</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"> is the
+file name that SSA will use when saving the font file. It is:</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">the name of
+the original truetype font,</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">plus an
+underscore,</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">plus an
+optional ��B�� if Bold,</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">plus an
+optional ��I�� if Italic,</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">plus a number
+specifying the font encoding (character set),</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">plus ��.ttf��</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Eg.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">fontname:
+chaucer_B0.ttf</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">fontname:
+comic_0.ttf</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The fontname
+line is followed by lines of printable characters, representing the binary
+values which make up the font file. Each line is 80 characters long, except the
+last one which may be less.<br>
+<br>
+The conversion from binary to printable characters is a form of Uuencoding, the
+details of this encoding is contained in "Appendix B", below.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">[Graphics]</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This sections
+contains text-encoded graphic files, if the user opted to embed any pictures
+they used in the script. The binary picture files are text-encoded, which is
+inefficient, but ensures that SSA scripts can still be handled by any text editor,
+if required.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Each graphic
+file is started with a single line in the format:</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">filename:
+&lt;name of file&gt;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The word ��<b>filename</b>��
+must be in lower case (upper case will be interpreted as part of a text-encoded
+file). <br>
+&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;name
+of file&gt;</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"> is the
+file name that SSA will use when saving the picture file. It will match the
+filename of a picture used in the script.<br>
+<br>
+SSA saves any files found in the script in a subdirectory off SSA's program
+directory, "Pictures"</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">eg.
+c:\program files\Sub Station Alpha v4.00\Pictures. SSA will attempt to load
+files using the paths specified in the script, but if they are not found, it
+will look in the "Pictures" subdirectory for them.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The fontname
+line is followed by lines of printable characters, fontrepresenting the binary
+values which make up the picture font file - format is as per embedded font
+files.</span></p>
+
+<span style="font-size:10.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">3. The line types in
+a Sub Station Alpha script</span></u></b></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This briefly
+describes each of the line types that can appear in a Sub Station Alpha Script.
+Full details of the information held in each line type is in the next chapter.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">!:</span></b><span style="font-family:
+&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is a comment used in the script file only. It is not visible when you 
+load the
+script into SSA.</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Title:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is a description of the script</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original
+Script:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+original author(s) of the script</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original
+Translation: </span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">(optional)
+The original translator of the dialogue</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original Editing:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">(optional) The original script
+editor(s), typically whoever took the raw translation and turned it into
+idiomatic english and reworded for readability.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original
+Timing:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (optional)
+Whoever timed the original script</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Synch Point:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (optional)
+Description of where in the video the script should begin playback.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Script Updated By:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp; (optional) Names of
+any other subtitling groups who edited the original script.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Update Details:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+details of any updates to the original script made by other subtilting groups.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">ScriptType:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the SSA script format version eg. "V3.00".</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Collisions:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+determines how subtitles are moved, when preventing onscreen collisions</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">PlayResY:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the height of the screen used by the authors when playing the script.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">PlayResX:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the width of the screen used by the authors when playing the script.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">PlayDepth:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This is the colour depth used by the
+authors when playing the script.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Timer:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the Timer Speed for the script, as a percentage.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+"100.0000" is exactly 100%.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ The
+timer speed is a time multiplier applied to SSA's clock to provide a 
+ramp time.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Style:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is a Style definition, used to format text displayed by the script.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Dialogue:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is a Dialogue event, ie. Some text to display.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Comment:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp; This is a
+"comment" event.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+contains the same information as a Dialogue, Picture, Sound, Movie, or 
+Command
+event, but it is ignored during script playback.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Picture:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is a "picture" event, which means SSA will display the specified
+.bmp, .jpg, .gif, .ico or .wmf graphic.</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Sound:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is a "sound" event, which means SSA will play the specified .wav
+file.</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Movie:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is a "movie" event, which means SSA will play the specified .avi
+file.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Command:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is a "command" event, which means SSA will execute the specified
+program as a background task.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">4. Header lines,
+[Script Info] section</span></u></b></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><span style="text-decoration:none">&nbsp;</span></span></u></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">;</span></b><span style="font-family:
+&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Semicolon.
+Any text can follow the semicolon</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is a comment used in the script file only. It is not visible when you 
+load the
+script into SSA. The semicolon <b>must</b> be the first character in the line.
+This replaces the <b>!:</b> line type used in previous script versions.</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Title:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is a description of the script. If the original author(s) did not 
+provide this
+information then &lt;untitled&gt; is automatically substituted.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original Script:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+original author(s) of the script. If the original author(s) did not provide
+this information then &lt;unknown&gt; is automatically substituted.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original Translation: </span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">(optional) The original translator of
+the dialogue. This entry does not appear if no information was entered by the
+author.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original Editing:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">(optional) The original script
+editor(s), typically whoever took the raw translation and turned it into
+idiomatic english and reworded for readability. This entry does not appear if
+no information was entered by the author.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Original Timing:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (optional)
+Whoever timed the original script. This entry does not appear if no information
+was entered by the author.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Synch Point:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (optional)
+Description of where in the video the script should begin playback. This entry
+does not appear if no information was entered by the author.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Script Updated By:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp; (optional) Names of
+any other subtitling groups who edited the original script. This entry does not
+appear if subsequent editors did not enter the information.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Update Details:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+details of any updates to the original script - made by other subtitling
+groups. This entry does not appear if subsequent editors did not enter any
+information.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Script Type:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the SSA script format version eg. "V4.00". It is used by SSA to
+give a warning if you are using a version of SSA older than the version that
+created the script.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">ASS version is ��V4.00+��.</span></b></span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Collisions:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+determines how subtitles are moved, when automatically preventing onscreen
+collisions.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ If
+the entry says "<b>Normal</b>" then SSA will attempt to position
+subtitles in the position specified by the "margins". However,
+subtitles can be shifted vertically to prevent onscreen collisions. With
+"normal" collision prevention, the subtitles will "stack
+up" one above the other - but they will always be positioned as close the
+vertical (bottom) margin as possible - filling in "gaps" in other subtitles
+if one large enough is available.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ If
+the entry says "<b>Reverse</b>" then subtitles will be shifted
+upwards to make room for subsequent overlapping subtitles. This means the
+subtitles can nearly always be read top-down - but it also means that the first
+subtitle can appear half way up the screen before the subsequent overlapping
+subtitles appear. It can use a lot of screen area.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">PlayResY:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the height of the screen used by the script's author(s) when playing the
+script. SSA v4 will automatically select the nearest enabled setting, if you
+are using Directdraw playback.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">PlayResX:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the width of the screen used by the script's author(s) when playing the
+script. SSA will automatically select the nearest enabled, setting if you are
+using Directdraw playback.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">PlayDepth:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This is the colour depth used by the
+script's author(s) when playing the script. SSA will automatically select the
+nearest enabled setting if you are using Directdraw playback.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Timer:</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ This
+is the Timer Speed for the script, as a percentage.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+"100.0000" is exactly 100%. It has four digits following the decimal
+point.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ The
+timer speed is a time multiplier applied to SSA's clock to stretch or 
+compress
+the duration of a script. A speed greater than 100% will reduce the 
+overall
+duration, and means that subtitles will progressively appear sooner and 
+sooner.
+A speed less than 100% will increase the overall duration of the script 
+means
+subtitles will progressively appear later and later (like a positive 
+ramp
+time).<br>
+<br>
+The stretching or compressing only occurs during script playback - this value
+does not change the actual timings for each event listed in the script.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Check
+the SSA user guide if you want to know why "Timer Speed" is more
+powerful than "Ramp Time", even though they both achieve the same
+result.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">WrapStyle:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Defines
+the default wrapping style. </span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">0: smart wrapping, lines are evenly broken</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">1: end-of-line word wrapping, only \N breaks</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">2: no word wrapping, \n \N both breaks</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">3: same as 0, but lower line gets wider.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">5. Style Lines, [v4<span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">+</span> Styles] section</span></u></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Styles define
+the appearance and position of subtitles. All styles used by the script are are
+defined by a Style line in the script.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Any of the
+the settings in the Style, (except shadow/outline type and depth) can
+overridden by control codes in the subtitle text.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The fields
+which appear in each Style definition line are named in a special line with the
+line type ��Format:��. The Format line must appear before any Styles - because it
+defines how SSA will interpret the Style definition lines. The field names
+listed in the format line must be correctly spelled! The fields are as follows:</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Name,
+Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour,
+Bold, Italic, <span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Underline, StrikeOut, ScaleX, ScaleY,
+Spacing, Angle,</span> BorderStyle, Outline, Shadow, Alignment, MarginL,
+MarginR, MarginV, AlphaLevel, Encoding</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The format
+line allows new fields to be added to the script format in future, and yet
+allow old versions of the software to read the fields it recognises - even if
+the field order is changed.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 1:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Name</b>.
+The name of the Style. Case sensitive. Cannot include commas.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 2:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Fontname</b>.
+The fontname as used by Windows. Case-sensitive.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 3:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Fontsize</b>.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>PrimaryColour.</b>
+A long integer BGR (blue-green-red)&nbsp; value. ie. the byte order in the
+hexadecimal equivelent of this number is BBGGRR</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+is the colour that a subtitle will normally appear in.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 5:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>SecondaryColour.</b>
+A long integer BGR (blue-green-red)&nbsp; value. ie. the byte order in the
+hexadecimal equivelent of this number is BBGGRR</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+colour may be used instead of the Primary colour when a subtitle is
+automatically shifted to prevent an onscreen collsion, to distinguish the
+different subtitles.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 6:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">OutlineColor</span> (<s>TertiaryColour</s></b>). A long
+integer BGR (blue-green-red)&nbsp; value. ie. the byte order in the hexadecimal
+equivelent of this number is BBGGRR</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This
+colour may be used instead of the Primary or Secondary colour when a subtitle
+is automatically shifted to prevent an onscreen collsion, to distinguish the
+different subtitles.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 7:&nbsp;&nbsp;&nbsp;&nbsp; <b>BackColour</b>.
+This is the colour of the subtitle outline or shadow, if these are used. A long
+integer BGR (blue-green-red)&nbsp; value. ie. the byte order in the hexadecimal
+equivelent of this number is BBGGRR.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 4-7:&nbsp; The color
+format contains the alpha channel, too. (AABBGGRR)</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Bold</b>.
+This defines whether text is bold (true) or not (false). -1 is True, 0 is
+False. This is independant of the Italic attribute - you can have have text
+which is both bold and italic.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 9:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Italic</b>.
+This defines whether text is italic (true) or not (false). -1 is True, 0 is
+False. This is independant of the bold attribute - you can have have text which
+is both bold and italic.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 9.1:&nbsp; Underline. [-1
+or 0]</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 9.2:&nbsp; Strikeout. [-1
+or 0]</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 9.3:&nbsp; ScaleX.
+Modifies the width of the font. [percent]</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 9.4:&nbsp; ScaleY.
+Modifies the height of the font. [percent]</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 9.5:&nbsp; Spacing.
+Extra space between characters. [pixels]</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 9.6:&nbsp; Angle.&nbsp;
+The origin of the rotation is defined by the alignment. Can be a floating point
+number. [degrees]</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 10:&nbsp;&nbsp;&nbsp; <b>BorderStyle</b>.
+1=Outline + drop shadow, 3=Opaque box</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 11:&nbsp;&nbsp;&nbsp; <b>Outline.</b>
+If BorderStyle is 1,&nbsp; then this specifies the width of the outline around
+the text, in pixels.<br>
+Values may be 0, 1, 2, 3 or 4. </span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 12:&nbsp;&nbsp;&nbsp; <b>Shadow.</b>
+If BorderStyle is 1,&nbsp; then this specifies the depth of the drop shadow
+behind the text, in pixels. Values may be 0, 1, 2, 3 or 4. Drop shadow is
+always used in addition to an outline - SSA will force an outline of 1 pixel if
+no outline width is given.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 13:&nbsp;&nbsp;&nbsp; <b>Alignment</b>.
+This sets how text is "justified" within the Left/Right onscreen
+margins, and also the vertical placing. Values may be 1=Left, 2=Centered,
+3=Right. Add 4 to the value for a "Toptitle". Add 8 to the value for
+a "Midtitle".<br>
+eg. 5 = left-justified toptitle</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 13:&nbsp;&nbsp; Alignment,
+but after the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top).</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 14:&nbsp;&nbsp;&nbsp; <b>MarginL</b>.
+This defines the Left Margin in pixels. It is the distance from the left-hand
+edge of the screen.The three onscreen margins (MarginL, MarginR, MarginV)
+define areas in which the subtitle text will be displayed.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 15:&nbsp;&nbsp;&nbsp; <b>MarginR</b>.
+This defines the Right Margin in pixels. It is the distance from the <b>right-hand</b>
+edge of the screen. The three onscreen margins (MarginL, MarginR, MarginV)
+define areas in which the subtitle text will be displayed.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 16:&nbsp;&nbsp;&nbsp; <b>MarginV</b>.
+This defines the vertical Left Margin in pixels.<br>
+For a <b>subtitle</b>, it is the distance from the <b>bottom</b> of the screen.<br>
+For a <b>toptitle</b>, it is the distance from the <b>top</b> of the screen.<br>
+For a <b>midtitle</b>, the value is ignored - the text will be vertically
+centred<br>
+&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 17:&nbsp;&nbsp;&nbsp; <b>AlphaLevel.</b>
+This defines the transparency of the text. SSA does not use this yet.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field
+17:&nbsp;&nbsp; Not present in ASS.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 18:&nbsp;&nbsp;&nbsp; <b>Encoding.
+</b>This specifies the font character set or encoding and on multi-lingual
+Windows installations it provides access to characters used in multiple than
+one languages. It is usually 0 (zero) for English (Western, ANSI) Windows.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">When the file is Unicode, this field is useful during file
+format conversions.</span></b></span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<span style="font-size:10.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">5. Dialogue event
+lines, [Events] section</span></u></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">These contain
+the subtitle text, their timings, and how it should be displayed.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The fields
+which appear in each Dialogue line are defined by a <b>Format:</b> line, which
+must appear before any events in the section. The format line specifies how SSA
+will interpret all following Event lines. The field names must be spelled
+correctly, and are as follows:</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Marked,
+Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The last
+field will always be the Text field, so that it can contain commas. The format
+line allows new fields to be added to the script format in future, and yet
+allow old versions of the software to read the fields it recognises - even if
+the field order is changed.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 1:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Marked<br>
+</b>Marked=0 means the line is not shown as "marked" in SSA.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Marked=1
+means the line is shown as "marked" in SSA.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Field 1:&nbsp;&nbsp;&nbsp;&nbsp; Layer
+(any integer)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Subtitles
+having different layer number will be ignored during the collusion detection.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Higher
+numbered layers will be drawn over the lower numbered. </span></b></p>
+
+<p class="MsoNormal"><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 2:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Start</b><br>
+Start Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths.
+This is the time elapsed during script playback at which the text will appear
+onscreen. Note that there is a <b>single</b> digit for the hours!</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 3:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>End</b><br>
+End Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths. This
+is the time elapsed during script playback at which the text will disappear offscreen.
+Note that there is a <b>single</b> digit for the hours!</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Style<br>
+</b>Style name. If it is "Default", then your <b>own</b> *Default
+style will be subtituted.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; However,
+the Default style used by the script author IS stored in the script even though
+SSA ignores it - so if you want to use it, the information is there - you could
+even change the Name in the Style definition line, so that it will appear in
+the list of "script" styles.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 5:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Name<br>
+</b>Character name. This is the name of the character who speaks the dialogue.
+It is for information only, to make the script is easier to follow when
+editing/timing.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 6:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>MarginL</b><br>
+4-figure Left Margin override. The values are in pixels. All zeroes means the
+default margins defined by the style are used.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 7:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>MarginR</b><br>
+4-figure Right Margin override. The values are in pixels. All zeroes means the
+default margins defined by the style are used.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>MarginV</b><br>
+4-figure Bottom Margin override. The values are in pixels. All zeroes means the
+default margins defined by the style are used.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 9:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>Effect</b><br>
+Transition Effect. This is either empty, or contains information for one of the
+three transition effects implemented in SSA v4.x</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+effect names are case sensitive and must appear exactly as shown. The effect
+names do <b>not</b> have quote marks around them.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>"Karaoke"</b>
+means that the text will be successively highlighted one word at a time.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Karaoke as an effect type is obsolete.</span></b></span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>"Scroll
+up;y1;y2;delay<span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">[;fadeawayheight]</span>"</b>means
+that the text/picture will scroll up the screen. The parameters after the words
+"Scroll up" are separated by semicolons.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+y1 and y2 values define a vertical region on the screen in which the text will
+scroll. The values are in pixels, and it doesn't matter which value (top or
+bottom) comes first. If the values are zeroes then the text will scroll up the
+full height of the screen.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+delay value can be a number from 1 to 100, and it slows down the speed of the
+scrolling - zero means no delay and the scrolling will be as fast as possible.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b>��Banner;delay��
+</b>means that text will be forced into a single line, regardless of length,
+and scrolled from right to left accross the screen.<br>
+<br>
+The delay value can be a number from 1 to 100, and it slows down the speed of
+the scrolling - zero means no delay and the scrolling will be as fast as
+possible.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">"Scroll down;y1;y2;delay[;fadeawayheight]"</span></b></span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">��Banner;delay[;lefttoright;fadeawaywidth]��</span></b></span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lefttoright
+0 or 1. This field is optional.&nbsp; Default value is 0 to make it backwards
+compatible.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When
+delay is greater than 0, moving one pixel will take (1000/delay) second. </span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (WARNING:
+Avery Lee��s ��subtitler�� plugin reads the ��Scroll up�� effect parameters as delay;y1;y2)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color=""><br>
+fadeawayheight and fadeawaywidth parameters can be used to make the scrolling
+text at the sides transparent.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Field 10:&nbsp;&nbsp;&nbsp; <b>Text</b><br>
+Subtitle Text. This is the actual text which will be displayed as a subtitle
+onscreen. Everything after the 9th comma is treated as the subtitle text, so it
+can include commas.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The
+text can include \n codes which is a line break, and can include Style Override
+control codes, which appear between braces { }.</span></p>
+
+<p class="MsoNormal" style="margin-left:49.5pt;text-indent:-49.5pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">6. Comment event
+lines, [Events] section</span></u></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">These can
+contain the same information as any of the other event line types, but they
+will be ignored when the script is played.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-align:center;
+text-indent:-14.15pt" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">7.
+ Picture event lines, [Events] section</span></u></b></p>
+
+<p class="MsoNormal"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><span style="text-decoration:none">&nbsp;</span></span></u></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">These contain
+the same information as Dialogue events, but Field 10 contains the full path
+and filename of the picture to display, instead of subtitle text.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The Style
+specified is ignored. The "scroll up" transition effect can be used
+for picture events.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The Left and
+Vertical Margin Overrides specify the bottom-left corner position of the
+picture. A left margin of all zeroes means that the picture will be
+horizontally centered. A vertical margin of all zeroes means that the picture
+will be vertically centered. </span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-align:center;
+text-indent:-14.15pt" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">8.
+ Sound event lines, [Events] section</span></u></b></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><span style="text-decoration:none">&nbsp;</span></span></u></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">These contain
+the same information as Dialogue events, but Field 10 contains the full path
+and filename of the wav file to play, instead of subtitle text.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The Style and
+margins are ignored. The End time is also ignored - the wav will play until it
+finishes, or until another wav file is played.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">If an avi
+movie is played at the same time as a wav is already playing, then any sound in
+the avi will not be heard. Similarly, if a wav starts playing when an avi movie
+with sound is already playing then the wav will not be heard.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:63.0pt;text-indent:-63.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-align:center;
+text-indent:-14.15pt" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">9.
+ Movie event lines, [Events] section</span></u></b></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><span style="text-decoration:none">&nbsp;</span></span></u></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">These contain
+the same information as Dialogue events, but Field 10 contains the full path
+and filename of the avi file to play, instead of subtitle text.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The Style is
+ignored. Transition effects are ignored.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The End time
+specifies when the movie picture will disappear - but if th eavi file lasts
+longer, then the sound will continue to be heard.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The Left and
+vertical Margin Overrides specify the TOP-LEFT corner position of the picture
+(unlike picture events). A left margin of all zeroes means that the picture
+will be horizontally centered. a vertical margin of all zeroes means that the
+picture will be verticall centered. </span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">If an avi
+movie is played at the same time as a wav is already playing, then any sound in
+the avi will not be heard. Similarly, if a wav starts playing when an avi movie
+with sound is already playing then the wav will not be heard.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="margin-left:14.15pt;text-align:center;
+text-indent:-14.15pt" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">10.
+Command event lines, [Events] section</span></u></b></p>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">These contain
+the same information as Dialogue events, but Field 10 contains the full path
+and filename of the program to execute, instead of subtitle text.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The Style is
+ignored. The margins are ignored. Transition effects are ignored. The End time
+is also ignored - the program will execute until it ends, or is
+"manually" closed.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">There are
+also internal SSA commands which can appear in SSA scripts - the
+"SSA:Pause", ��SSA:Wait for trigger�� command events, and genlock
+control commands. These all begin with "SSA:"</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The SSA:Pause
+command has the same effect as pressing "P" during script playback.
+It is useful as a second "synch point" to resume subtitling after
+switching sides of a laserdisk.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The ��SSA:Wait
+for audio trigger�� command has the same effect as pressing "P" during
+script playback, but pausing is automatically cancelled if the audio input to
+the computer exceeds a specified ��trigger�� level. It is useful as a second
+"synch point" to resume subtitling after switching sides of a
+laserdisk. The audio triggering can be overridden to resume playback - by
+pressing "P".</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Audio
+triggering "times out" after 10 minutes - If no audio peak of
+sufficient magnitude is received, and "P" is not pressed within 10
+minutes - then playback will resume anyway.</span></p>
+
+<b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></u></b>
+
+<p class="MsoNormal" style="text-align:center" align="center"><b><u><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Appendix A: Style
+override codes</span></u></b></p>
+
+<p class="MsoNormal"><b><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">This is a
+reference which may be useful for those of you who wish to learn the style
+override codes, so you can type them in manually without using the
+"override style" dialogue box.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">All Override
+codes appear within braces { } except the newline \n and \N codes.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">All override
+codes are always preceded by a backslash \</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Several
+overrides can be used within one set of braces.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Each override
+affects all text following the override. To apply an override only to selected
+text, you need a second "cancelling" override after the selected
+text, to "undo" the effect of the first override.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Some
+overrides automatically apply to ALL the text - currently this is just
+alignment overrides, but more may be added later (eg. Shadow/outline depth
+overrides).</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\n</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ New
+line (carriage return)<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ \n
+is ignored by SSA if ��smart-wrapping�� is enabled</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <b>eg.
+This is the first line\nand this is the second<br>
+<br>
+\N&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ </b>New
+line (carriage return). This is used by SSA instead of \n if </span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ ��smart-wrapping��
+is enabled.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\b</span></b><span style="font-family:
+&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;0 or 1&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \b1
+makes the text bold. \b0 forces non-bold text.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+There is a {\b1}bold {\b0}word here</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">When this parameter is greater than 1, it will be used as the
+weight of the font. (400 = Normal, 700 = Bold, note: most fonts will quantize
+to 2 or 3 levels of thickness)</span></span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\i</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;0 or 1&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \i1
+makes the text italic. \i0 forces non-italic text.</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+There is an {\i1}italicised {\i0}word here</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\u&lt;0 or 1&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; underline</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\s&lt;0
+or 1&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strikeout</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\bord&lt;width&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; border</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\shad&lt;depth&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shadow</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\be&lt;0
+or 1&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blur
+edges</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\fn</span></b><span style="font-family:
+&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;font name&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;font
+name&gt; specifies a font which you have installed in Windows. This is case
+sensitive.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+Here is some {\fnCourier New}fixed space text</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ If
+you use a font name that doesn't exist, then Arial will be used instead.<br>
+<br>
+</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\fs</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;font size&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;font
+size&gt; is a number specifying a font point size.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <b>eg.
+{\fs16}This is small text. {\fs28}This is large text<br>
+<br>
+</b></span></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\fsc&lt;x
+or y&gt;&lt;percent&gt;&nbsp; </span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;x
+or y&gt; x scales horizontally, y scales vertically</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;percent&gt;
+</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\fsp&lt;pixels
+&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;pixels&gt;
+changes the distance between letters. (default: 0)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.25pt;text-indent:-90.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\fr[&lt;x/y/z&gt;]&lt;degrees&gt;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;degrees&gt;
+sets the rotation angle around the x/y/z axis.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ \fr
+defaults to \frz. </span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\fe</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;charset&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;charset&gt;
+is a number specifying the character set (font encoding)</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\c&amp;H</span></b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;bbggrr&gt;<b>&amp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </b>&lt;bbggrr&gt;
+is a hexadecimal RGB value, but in reverse order. Leading zeroes are not
+required.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+{\c&amp;HFF&amp;}This is pure, full intensity red</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\c&amp;HFF00&amp;}This is pure, full intensity Green</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\c&amp;HFF0000&amp;}This is pure, full intensity Blue</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\c&amp;HFFFFFF&amp;}This is White</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\c&amp;HA0A0A&amp;}This is dark grey</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\1c&amp;Hbbggrr&amp;, \2c&amp;Hbbggrr&amp;,
+\3c&amp;Hbbggrr&amp;, \4c&amp;Hbbggrr&amp; to set specific colors.</span></span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ \1a&amp;Haa&amp;,
+\2a&amp;Haa&amp;, \3a&amp;Haa&amp;, \4a&amp;Haa&amp; to set specific 
+alpha
+channels.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ \alpha
+defaults to \1a</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\a</span></b><span style="font-family:
+&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;alignment&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;alignment&gt;
+is a number specifying the onscreen alignment/positioning of a subtitle.</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ A
+value of 1 specifies a left-justified subtitle</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ A
+value of 2 specifies a centered subtitle</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ A
+value of 3 specifies a right-justified subtitle</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Adding
+4 to the value specifies a "Toptitle"</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Adding
+8 to the value specifies a "Midtitle"</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">0 or nothing resets to the style default (which is usually 2)</span></b></span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+{\a1}This is a left-justified subtitle</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\a2}This is a centered subtitle</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\a3}This is a right-justified subtitle</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\a5}This is a left-justified toptitle</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &nbsp;&nbsp;
+{\a11}This is a right-justified midtitle</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Only
+the first appearance counts.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\an&lt;alignment&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; numpad
+layout</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Only
+the first appearance counts.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\k</span></b><span style="font-family:
+&quot;Arial&quot;,&quot;sans-serif&quot;">&lt;duration&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;duration&gt;
+is the amount of time that each section of text is highlighted for in a
+dialogue event with the Karaoke effect. The durations are in hundredths of
+seconds.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ eg.
+{\k94}This {\k48}is {\k24}a {\k150}karaoke {\k94}line</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ \k&lt;duration&gt;
+highlight by words</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ \kf
+or \K&lt;duration&gt; fill up from left to right</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ \ko&lt;duration&gt;
+outline highlighting from left to right</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\q&lt;num&gt;&nbsp;&nbsp;
+ &nbsp;&nbsp;&nbsp;&nbsp; 
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
+&lt;num&gt;
+wrapping style</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">\r<span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">[&lt;style&gt;]</span> </span></b><span style="font-family:
+&quot;Arial&quot;,&quot;sans-serif&quot;">This cancels all previous style overrides in a line</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <b><span style="color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;style&gt; Restores to &lt;style&gt; instead of the
+dialogue line default.</span></b></span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Any style modifier followed
+by no recognizable parameter resets to the default.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><u><span style="font-size: 12pt; font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Functions:</span></u></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\t([&lt;t1&gt;,
+&lt;t2&gt;, ] [&lt;accel&gt;,] &lt;style modifiers&gt;)</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="" lang="HU">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;t1&gt;,
+&lt;t2&gt; Animation beginning, ending time offset [ms] (optional)</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;accel&gt;
+Modifies the linearity of the transformation (optional)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:127.6pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:127.6pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">The following calculation is performed to get the
+coefficient needed to interpolate between the given style modifiers:
+pow((t-t1)/(t2-t1), accel), where t is the time offset for the subtitle.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:127.6pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:127.6pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">The meaning of &lt;accel&gt;:</span></b></p>
+
+<p class="MsoNormal" style="margin-left:148.85pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">1: the transformation is linear</span></b></p>
+
+<p class="MsoNormal" style="margin-left:148.85pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">between 0 and 1: will start fast and slow down</span></b></p>
+
+<p class="MsoNormal" style="margin-left:148.85pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">greater than 1: will start slow and get faster</span></b></p>
+
+<p class="MsoNormal" style="margin-left:148.85pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:148.85pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">As an example, using 2 will make growing the
+letters (by {\fscx200\fscy200}) look linear rather than slowering.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:148.85pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;style
+modifiers&gt;Any style modifier which can be animated:</span></b></p>
+
+<p class="MsoNormal" style="margin-left:120.5pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\c,\1-4c,\alpha,\1-4a,\fs,\fr,\fscx,\fscy,\fsp,\bord,\shad,\clip
+(only the rectangular \clip)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\move(&lt;x1&gt;,
+&lt;y1&gt;, &lt;x2&gt;, &lt;y2&gt;[, &lt;t1&gt;, &lt;t2&gt;])</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;x1&gt;,
+&lt;y1&gt; The coordinate to start at.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;x2&gt;,
+&lt;y2&gt; The coordinate to end at.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;t1&gt;,
+&lt;t2&gt; Animation beginning, ending time offset [ms] (optional)</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ The
+origin of the movement is defined by the alignment type.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\pos(&lt;x&gt;,
+&lt;y&gt;)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Defaults
+to \move(&lt;x&gt;, &lt;y&gt;, &lt;x&gt;, &lt;y&gt;, 0, 0)</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\org(&lt;x&gt;, &lt;y&gt;)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Moves
+the default origin at (x,y). This is useful when moving subtitles in the
+direction of rotation.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">WARNING:
+\t, \move and \pos will ignore collusion detection.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\fade(&lt;a1&gt;,
+&lt;a2&gt;, &lt;a3&gt;, &lt;t1&gt;, &lt;t2&gt;, &lt;t3&gt;, &lt;t4&gt;)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;a1&gt; Alpha value
+before &lt;t1&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;a2&gt; Alpha value
+between &lt;t2&gt; and &lt;t3&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;a3&gt; Alpha value after
+&lt;t4&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;t1&gt;, &lt;t4&gt;
+Animation beginning, ending time offset [ms]</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;t1&gt; - &lt;t2&gt;
+Alpha value will be interpolated between &lt;a1&gt; and &lt;a2&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;t2&gt; - &lt;t3&gt;
+Alpha value will be set to &lt;a2&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;t3&gt; - &lt;t4&gt;
+Alpha value will be interpolated between &lt;a2&gt; and &lt;a3&gt;</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\fad(&lt;t1&gt;,
+&lt;t2&gt;)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;t1&gt; the
+time length of fading in</span></b></p>
+
+<p class="MsoNormal" style="margin-left:113.4pt;text-indent:-14.15pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&lt;t2&gt; the time length
+of fading out</span></b></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\clip(&lt;x1&gt;, &lt;y1&gt;, &lt;x2&gt;, &lt;y2&gt;)</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Clips
+any drawing outside the rectangle defined by the parameters.</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\clip([&lt;scale&gt;,] &lt;drawing commands&gt;)</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Clipping
+against drawn shapes. </span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;scale&gt;
+has the same meaning as in the case of \p&lt;scale&gt;</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><u><span style="font-size: 12pt; font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Drawings:</span></u></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\p&lt;scale&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;scale&gt;
+</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;
+ 
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Turns
+on drawing mode and sets the magnification level of the coordinates at 
+the same
+time. Scale is interpreted as two to the power of (&lt;scale&gt; minus 
+one).
+For example {\p4} and the coordinate (8,16) will mean the same as {\p1} 
+and
+(1,2). This feature can be useful for sub-pixel accuracy.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ If
+0, drawing mode is turned off and the text is interpreted as usual.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">\pbo&lt;y&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ &lt;y&gt;
+baseline offset. By default any drawings are positioned on the current
+baseline. With this value it is possible to move them up or down by 
+&lt;y&gt;
+pixels. (up: y&lt;0, down: y&gt;0)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Drawing commands:</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">m
+ &lt;x&gt; 
+&lt;y&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Moves
+the cursor to &lt;x&gt;, &lt;y&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">n
+ &lt;x&gt; 
+&lt;y&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Moves
+the cursor to &lt;x&gt;, &lt;y&gt; (unclosed shapes will be left open)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">l
+ &lt;x&gt; 
+&lt;y&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ Draws
+a line to &lt;x&gt;, &lt;y&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">b &lt;x1&gt; &lt;y1&gt;
+&lt;x2&gt; &lt;y2&gt; &lt;x3&gt; &lt;y3&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ 3rd
+degree bezier curve to point 3 using point 1 and 2 as the control points</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">s &lt;x1&gt; &lt;y1&gt;
+&lt;x2&gt; &lt;y2&gt; &lt;x3&gt; &lt;y3&gt; .. &lt;xN&gt; &lt;yN&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ 3rd
+degree uniform b-spline to point N, must contain at least 3 coordinates</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">p
+ &lt;x&gt; 
+&lt;y&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ extend
+b-spline to &lt;x&gt;, &lt;y&gt;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">c&nbsp;&nbsp;
+ 
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ close
+b-spline</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Things you should know:</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Commands
+must appear after {\p1+} and before {\p0}.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (except
+for \clip(..))</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Drawings
+must always start with a move to command.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Drawings
+must form a closed shape.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; All
+unclosed shape will be closed with a straight line automatically.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Overlapping
+shapes in the Dialogue line will be XOR-ed with each-other.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:14.2pt;text-indent:-14.2pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; If
+the same command follows another, it isn��t needed to write its identifier
+letter again, only the coordinates.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:14.2pt;text-indent:-14.2pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:14.2pt;text-indent:-14.2pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; The
+coordinates are relative to the current cursor position (baseline) and the
+alignment mode.</span></b></p>
+
+<p class="MsoNormal" style="margin-left:14.2pt;text-indent:-14.2pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:14.2pt;text-indent:-14.2pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Commands
+p and c should only follow other b-spline commands.</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">Examples:</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Square:
+m 0 0&nbsp;l 100 0&nbsp;100&nbsp;100 0&nbsp;100</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Rounded
+square: m 0 0 s 100 0&nbsp;100 100&nbsp;0 100 c (c equals to ��p 0 0&nbsp;100
+0&nbsp;100&nbsp;100�� in this case)</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="margin-left:99.0pt;text-indent:-99.0pt"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;&nbsp;&nbsp;&nbsp; Circle
+(almost): m 50 0 b 100 0&nbsp;100&nbsp;100 50&nbsp;100 b 0&nbsp;100 0 0 50 0
+(note that the 2nd ��b�� is optional here)</span></b></p>
+
+<p class="MsoNormal"><b><span style="font-family: &quot;Arial&quot;, &quot;sans-serif&quot;; color: red; --darkreader-inline-color: #ff1a1a;" data-darkreader-inline-color="">&nbsp;</span></b></p>
+
+<p class="MsoNormal" style="text-indent:.25pt"><b><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></b></p>
+
+<b><span style="font-size:10.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;"><br style="page-break-before:always" clear="all">
+</span></b>
+
+<p class="MsoNormal"><b><u><span style="font-size:14.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Appendix
+B: embedded font/picture encoding</span></u></b></p>
+
+<p class="MsoNormal"><span style="font-size:12.0pt;font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">SSA��s font
+and picture file embeddeding is a form of UUEncoding.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">It takes a
+binary file, three bytes at a time, and converts the 24bits of those bytes into
+four 6-bit numbers. 33 is added to each of these four numbers, and the
+corresponding ascii character for each number is written into the script file.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">The offset of
+33 means that lower-case characters cannot appear in the encoded output, and
+this is why the ��filename�� lines are always lower case.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">Each line of
+an encoded file is 80 characters long, except the last one, which may be
+shorter.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">If the length
+of the file being encoded is not an exact multiple of 3, then for odd-number
+filelengths, the last byte is multiplied by hexadecimal 100, and the most
+significant 12 bits are converted to two characters as above. For even-number
+filelengths, the last two bytes are multiplied by hexadecimal 10000, and the
+most significant 18 bits are converted to three characters as above. </span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">There is no
+terminating code for the embedded files. If a new [section] starts in the
+script, or if another filename line is found, or the end of the script file is
+reached then the file is considered complete.</span></p>
+
+<p class="MsoNormal"><span style="font-family:&quot;Arial&quot;,&quot;sans-serif&quot;">&nbsp;</span></p>
+
+</div>
+
+
+
+
+</body><grammarly-desktop-integration data-grammarly-shadow-root="true"></grammarly-desktop-integration></html>
\ No newline at end of file
diff --git a/utils/vvs/retime.vvl b/utils/vvs/retime.vvl
index f97cabe4d552c1f6003ed1c5f1f485d0dd9b061a..8ad099050d83b60dfc5b617049906f50fbe55850 100644
--- a/utils/vvs/retime.vvl
+++ b/utils/vvs/retime.vvl
@@ -8,7 +8,7 @@ option "after" { type = "number", default = 300 }
 
 -- You can add a field to any ASS item. It will be accessible by doing a
 -- `line.aux.number` here. Note that name collision will raise an error...
-data "line:number" { default = 0 }
+data "line:number" { type = "number", default = 0 }
 
 -- To declare a job we must give it a name. The function to use must follow
 -- some rules. It must only have one argument, the item on which it must be
@@ -16,7 +16,7 @@ data "line:number" { default = 0 }
 -- of things, i.e. one ASS item or a table of the same ASS item. To be sure of
 -- what is returning (a check will be done), you must specify the return type of
 -- the function.
-job "prelines" {
+job "preline" {
     line = function(line) -- Single argument, line/lines/syllabe/syllabes
         local pre_line = line:copy()
         pre_line.start = line.start - before
@@ -29,7 +29,7 @@ job "prelines" {
     end
 }
 
-job "postlines" {
+job "postline" {
     line = function(line)
         local post_line = line:copy()
         post_line.start = line.fini
diff --git a/utils/vvs/test_data.vvs b/utils/vvs/test_data.vvs
index 01553af81813ddbb4092976593c7f3fe24ebe670..17c427db7423d9f81fa69dbea2306e91a8f364cc 100644
--- a/utils/vvs/test_data.vvs
+++ b/utils/vvs/test_data.vvs
@@ -1,3 +1,6 @@
 -- vim: ft=lua
 
-data "line:number" { type = "number", value = 0 }
+data "line:count" { type = "number", value = 0 }
+
+-- Invalid!
+-- data "syllabes:toto"