From 097eae5f4e8fdb1d07e7bc0d4a8d6f02e967d837 Mon Sep 17 00:00:00 2001 From: trivernis Date: Thu, 17 Dec 2020 16:12:47 +0100 Subject: [PATCH] Add theme config option Signed-off-by: trivernis --- Cargo.lock | 45 +++++++ Cargo.toml | 1 + src/format/assets/base.scss | 159 +++++++++++++++++++++++++ src/format/assets/dark-ocean.scss | 7 ++ src/format/assets/dark-solarized.scss | 7 ++ src/format/assets/light-github.scss | 7 ++ src/format/assets/light-ocean.scss | 7 ++ src/format/assets/light-solarized.scss | 7 ++ src/format/html/assets/style.css | 149 ----------------------- src/format/html/html_writer.rs | 11 +- src/format/html/to_html.rs | 15 ++- src/format/mod.rs | 1 + src/format/style.rs | 53 +++++++++ src/main.rs | 5 +- src/settings/format_settings.rs | 11 ++ 15 files changed, 325 insertions(+), 160 deletions(-) create mode 100644 src/format/assets/base.scss create mode 100644 src/format/assets/dark-ocean.scss create mode 100644 src/format/assets/dark-solarized.scss create mode 100644 src/format/assets/light-github.scss create mode 100644 src/format/assets/light-ocean.scss create mode 100644 src/format/assets/light-solarized.scss delete mode 100644 src/format/html/assets/style.css create mode 100644 src/format/style.rs diff --git a/Cargo.lock b/Cargo.lock index 5787b37..354c7de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,12 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + [[package]] name = "bytemuck" version = "1.4.1" @@ -1364,6 +1370,17 @@ dependencies = [ "version_check 0.9.2", ] +[[package]] +name = "nom_locate" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67484adf5711f94f2f28b653bf231bff8e438be33bf5b0f35935a0db4f618a2" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + [[package]] name = "notify" version = "4.0.15" @@ -1382,6 +1399,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num-bigint" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits 0.2.14", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -2008,6 +2036,22 @@ dependencies = [ "winreg 0.7.0", ] +[[package]] +name = "rsass" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1144f0d9fd5df1f8fbac387e91b73a2d315e013ee6beb48353a71d8ec2c5ab" +dependencies = [ + "lazy_static", + "nom", + "nom_locate", + "num-bigint", + "num-integer", + "num-rational", + "num-traits 0.2.14", + "rand 0.7.3", +] + [[package]] name = "rust-argon2" version = "0.8.3" @@ -2249,6 +2293,7 @@ dependencies = [ "rayon", "regex", "reqwest", + "rsass", "serde 1.0.118", "sha2", "structopt", diff --git a/Cargo.toml b/Cargo.toml index d057dff..e4f34aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ image = "0.23.12" parking_lot = "0.11.1" sha2 = "0.9.2" config = "0.10.1" +rsass = "0.16.0" headless_chrome = { version = "0.9.0", optional = true } failure = { version = "0.1.8", optional = true } diff --git a/src/format/assets/base.scss b/src/format/assets/base.scss new file mode 100644 index 0000000..d058082 --- /dev/null +++ b/src/format/assets/base.scss @@ -0,0 +1,159 @@ +body { + background-color: $background-color-variant-2; + overflow-x: hidden; + color: $primary-color; + word-break: break-word; +} + +.content { + font-family: "Fira Sans", "Noto Sans", SansSerif, sans-serif; + width: 100vh; + max-width: calc(100% - 4rem); + padding: 2rem; + margin: auto; + background-color: $background-color; +} + +h1 { + font-size: 2.2rem; +} + +h2 { + font-size: 1.8rem; +} + +h3 { + font-size: 1.4rem; +} + +h4 { + font-size: 1rem; +} + +h5 { + font-size: 0.8rem; +} + +h6 { + font-size: 0.4rem; +} + +img { + max-width: 100%; + max-height: 100vh; + height: auto; +} + +code { + color: $primary-color; + + pre { + font-family: "Fira Code", "Mono", monospace; + padding: 0.8em 0.2em; + background-color: $background-color-variant-1 !important; + border-radius: 0.25em; + overflow: auto; + } + + &.inlineCode { + font-family: "Fira Code", monospace; + border-radius: 0.1em; + background-color: $background-color-variant-1; + padding: 0 0.1em; + } +} + +.tableWrapper { + overflow-x: auto; + width: 100%; + + & > table { + margin: auto; + } +} + +table { + border-collapse: collapse; + + tr { + &:nth-child(odd) { + background-color: $background-color-variant-2; + } + + &:nth-child(1) { + background-color: $background-color-variant-2; + font-weight: bold; + border-bottom: 1px solid invert($background-color) + } + } +} + +table td, table th { + border-left: 1px solid invert($background-color); + padding: 0.2em 0.5em; +} + +table tr td:first-child, table tr th:first-child { + border-left: none; +} + +blockquote { + margin-left: 0; + background-color: rgba(0, 0, 0, 0); +} + +a { + color: $secondary-color; +} + +.quote { + border-left: 0.3em solid $background-color-variant-3; + border-radius: 0.2em; + padding-left: 1em; + margin-left: 0; + background-color: $background-color-variant-1; + + .metadata { + font-style: italic; + padding-left: 0.5em; + color: $primary-variant-1; + } +} + + +.figure { + width: 100%; + display: block; + text-align: center; + + .imageDescription { + display: block; + color: $primary-variant-1; + font-style: italic; + } +} + +.centered { + text-align: center; +} + +.glossaryReference { + text-decoration: none; + color: inherit; + border-bottom: 1px dotted $primary-color; +} + +.arrow { + font-family: "Fira Code", "Mono", monospace; +} + +@media print { + + .content > section > section, .content > section > section { + page-break-inside: avoid; + } + + body { + background-color: $background-color !important; + } +} \ No newline at end of file diff --git a/src/format/assets/dark-ocean.scss b/src/format/assets/dark-ocean.scss new file mode 100644 index 0000000..0411d68 --- /dev/null +++ b/src/format/assets/dark-ocean.scss @@ -0,0 +1,7 @@ +$background-color: darken(#2b303b, 8%); +$background-color-variant-1: lighten($background-color, 7%); +$background-color-variant-2: lighten($background-color, 14%); +$background-color-variant-3: lighten($background-color, 21%); +$primary-color: #EEE; +$primary-variant-1: darken($primary-color, 14%); +$secondary-color: #3aa7df; \ No newline at end of file diff --git a/src/format/assets/dark-solarized.scss b/src/format/assets/dark-solarized.scss new file mode 100644 index 0000000..8e0784f --- /dev/null +++ b/src/format/assets/dark-solarized.scss @@ -0,0 +1,7 @@ +$background-color: darken(#002b36, 5%); +$background-color-variant-1: lighten($background-color, 7%); +$background-color-variant-2: lighten($background-color, 14%); +$background-color-variant-3: lighten($background-color, 21%); +$primary-color: #EEE; +$primary-variant-1: darken($primary-color, 14%); +$secondary-color: #0096c9; diff --git a/src/format/assets/light-github.scss b/src/format/assets/light-github.scss new file mode 100644 index 0000000..2d53d91 --- /dev/null +++ b/src/format/assets/light-github.scss @@ -0,0 +1,7 @@ +$background-color: #FFF; +$background-color-variant-1: darken($background-color, 7%); +$background-color-variant-2: darken($background-color, 14%); +$background-color-variant-3: darken($background-color, 21%); +$primary-color: #000; +$primary-variant-1: lighten($primary-color, 14%); +$secondary-color: #00286a; \ No newline at end of file diff --git a/src/format/assets/light-ocean.scss b/src/format/assets/light-ocean.scss new file mode 100644 index 0000000..c1bf8b9 --- /dev/null +++ b/src/format/assets/light-ocean.scss @@ -0,0 +1,7 @@ +$background-color: #FFF; +$background-color-variant-1: darken($background-color, 7%); +$background-color-variant-2: darken($background-color, 14%); +$background-color-variant-3: darken($background-color, 21%); +$primary-color: #112; +$primary-variant-1: lighten($primary-color, 14%); +$secondary-color: #00348e; diff --git a/src/format/assets/light-solarized.scss b/src/format/assets/light-solarized.scss new file mode 100644 index 0000000..eee3eb6 --- /dev/null +++ b/src/format/assets/light-solarized.scss @@ -0,0 +1,7 @@ +$background-color: #fff8f0; +$background-color-variant-1: darken($background-color, 4%); +$background-color-variant-2: darken($background-color, 8%); +$background-color-variant-3: darken($background-color, 12%); +$primary-color: #112; +$primary-variant-1: lighten($primary-color, 14%); +$secondary-color: #2b61be; diff --git a/src/format/html/assets/style.css b/src/format/html/assets/style.css deleted file mode 100644 index 5df7317..0000000 --- a/src/format/html/assets/style.css +++ /dev/null @@ -1,149 +0,0 @@ -body { - background-color: #DDD; - overflow-x: hidden; - color: #000; - word-break: break-word; -} - -.content { - font-family: "Fira Sans", "Noto Sans", SansSerif, sans-serif; - width: 100vh; - max-width: calc(100% - 4rem); - padding: 2rem; - margin: auto; - background-color: #FFF; -} - -h1 { - font-size: 2.2rem; -} - -h2 { - font-size: 1.8rem; -} - -h3 { - font-size: 1.4rem; -} - -h4 { - font-size: 1rem; -} - -h5 { - font-size: 0.8rem; -} - -h6 { - font-size: 0.4rem; -} - -img { - max-width: 100%; - max-height: 100vh; - height: auto; -} - -code { - color: #000; -} - -code pre { - font-family: "Fira Code", "Mono", monospace; - padding: 0.8em 0.2em; - background-color: #EEE !important; - border-radius: 0.25em; -} - -code.inlineCode { - font-family: "Fira Code", monospace; - border-radius: 0.1em; - background-color: #EEE; - padding: 0 0.1em -} - -.tableWrapper { - overflow-x: auto; - width: 100%; -} - -.tableWrapper > table { - margin: auto; -} - -table { - border-collapse: collapse; -} - -table tr:nth-child(odd) { - background-color: #DDD; -} - -table tr:nth-child(1) { - background-color: white; - font-weight: bold; - border-bottom: 1px solid black; -} - -table td, table th { - border-left: 1px solid black; - padding: 0.2em 0.5em -} - -table tr td:first-child, table tr th:first-child { - border-left: none; -} - -blockquote { - margin-left: 0; - background-color: rgba(0, 0, 0, 0); -} - -.quote { - border-left: 0.3em solid gray; - border-radius: 0.2em; - padding-left: 1em; - margin-left: 0; - background-color: #EEE; -} - -.quote .metadata { - font-style: italic; - padding-left: 0.5em; - color: #444 -} - -.figure { - width: 100%; - display: block; - text-align: center; -} - -.figure .imageDescription { - display: block; - color: #444; - font-style: italic; -} - -.centered { - text-align: center; -} - -.glossaryReference { - text-decoration: none; - color: inherit; - border-bottom: 1px dotted #000; -} - -.arrow { - font-family: "Fira Code", "Mono", monospace; -} - -@media print { - .content > section > section, .content > section > section { - page-break-inside: avoid; - } - body { - background-color: white !important; - } -} \ No newline at end of file diff --git a/src/format/html/html_writer.rs b/src/format/html/html_writer.rs index e5ba6e6..50c28ef 100644 --- a/src/format/html/html_writer.rs +++ b/src/format/html/html_writer.rs @@ -1,14 +1,16 @@ +use crate::settings::format_settings::Theme; use std::io; use std::io::Write; pub struct HTMLWriter { inner: Box, + theme: Theme, } impl HTMLWriter { /// Creates a new writer - pub fn new(inner: Box) -> Self { - Self { inner } + pub fn new(inner: Box, theme: Theme) -> Self { + Self { inner, theme } } /// Writes a raw string @@ -30,4 +32,9 @@ impl HTMLWriter { pub fn flush(&mut self) -> io::Result<()> { self.inner.flush() } + + /// Return the theme of the html writer + pub fn get_theme(&mut self) -> Theme { + self.theme.clone() + } } diff --git a/src/format/html/to_html.rs b/src/format/html/to_html.rs index 22d8e15..464a590 100644 --- a/src/format/html/to_html.rs +++ b/src/format/html/to_html.rs @@ -1,5 +1,6 @@ use crate::elements::*; use crate::format::html::html_writer::HTMLWriter; +use crate::format::style::{get_code_theme_for_theme, get_css_for_theme}; use crate::format::PlaceholderTemplate; use crate::references::glossary::{GlossaryDisplay, GlossaryReference}; use crate::references::templates::{Template, TemplateVariable}; @@ -7,9 +8,7 @@ use asciimath_rs::format::mathml::ToMathML; use htmlescape::encode_attribute; use minify::html::minify; use std::io; -use syntect::highlighting::ThemeSet; use syntect::html::highlighted_html_for_string; -use syntect::parsing::SyntaxSet; const MATHJAX_URL: &str = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"; @@ -108,7 +107,7 @@ impl ToHtml for Document { if self.is_root { let language = self.config.lock().metadata.language.clone(); - let style = minify(std::include_str!("assets/style.css")); + let style = minify(get_css_for_theme(writer.get_theme()).as_str()); writer.write("".to_string())?; writer.write(" io::Result<()> { writer.write("
0 { writer.write(" lang=\"".to_string())?; writer.write_attribute(self.language.clone())?; writer.write("\">".to_string())?; - lazy_static::lazy_static! { static ref PS: SyntaxSet = SyntaxSet::load_defaults_nonewlines(); } - lazy_static::lazy_static! { static ref TS: ThemeSet = ThemeSet::load_defaults(); } + let (theme, syntax_set) = get_code_theme_for_theme(writer.get_theme()); - if let Some(syntax) = PS.find_syntax_by_token(self.language.as_str()) { + if let Some(syntax) = syntax_set.find_syntax_by_token(self.language.as_str()) { writer.write(highlighted_html_for_string( self.code.as_str(), - &PS, + &syntax_set, syntax, - &TS.themes["InspiredGitHub"], + &theme, ))?; } else { writer.write("
".to_string())?;
diff --git a/src/format/mod.rs b/src/format/mod.rs
index 168963b..6fc2726 100644
--- a/src/format/mod.rs
+++ b/src/format/mod.rs
@@ -4,6 +4,7 @@ use std::collections::HashMap;
 #[cfg(feature = "pdf")]
 pub mod chromium_pdf;
 pub mod html;
+pub mod style;
 
 pub struct PlaceholderTemplate {
     value: String,
diff --git a/src/format/style.rs b/src/format/style.rs
new file mode 100644
index 0000000..ef6f03d
--- /dev/null
+++ b/src/format/style.rs
@@ -0,0 +1,53 @@
+use crate::settings::format_settings::Theme;
+use std::time::Instant;
+use syntect::highlighting::ThemeSet;
+use syntect::parsing::SyntaxSet;
+
+/// Returns the css of a theme compiled from sass
+pub fn get_css_for_theme(theme: Theme) -> String {
+    let start = Instant::now();
+    let vars = match theme {
+        Theme::GitHub => include_str!("assets/light-github.scss"),
+        Theme::SolarizedDark => include_str!("assets/dark-solarized.scss"),
+        Theme::SolarizedLight => include_str!("assets/light-solarized.scss"),
+        Theme::OceanDark => include_str!("assets/dark-ocean.scss"),
+        Theme::OceanLight => include_str!("assets/light-ocean.scss"),
+    };
+    let style = format!("{}\n{}", vars, include_str!("assets/base.scss"));
+
+    let css = compile_sass(&*style);
+
+    log::debug!("Compiled style in {} ms", start.elapsed().as_millis());
+
+    css
+}
+
+/// Returns the syntax theme for a given theme
+pub fn get_code_theme_for_theme(theme: Theme) -> (syntect::highlighting::Theme, SyntaxSet) {
+    lazy_static::lazy_static! { static ref PS: SyntaxSet = SyntaxSet::load_defaults_nonewlines(); }
+    lazy_static::lazy_static! { static ref TS: ThemeSet = ThemeSet::load_defaults(); }
+
+    let theme = match theme {
+        Theme::GitHub => "InspiredGitHub",
+        Theme::SolarizedDark => "Solarized (dark)",
+        Theme::SolarizedLight => "Solarized (light)",
+        Theme::OceanDark => "base16-ocean.dark",
+        Theme::OceanLight => "base16-ocean.light",
+    };
+
+    return (TS.themes[theme].clone(), PS.clone());
+}
+
+fn compile_sass(sass: &str) -> String {
+    String::from_utf8(
+        rsass::compile_scss(
+            sass.as_bytes(),
+            rsass::output::Format {
+                style: rsass::output::Style::Compressed,
+                precision: 5,
+            },
+        )
+        .unwrap(),
+    )
+    .unwrap()
+}
diff --git a/src/main.rs b/src/main.rs
index d50159d..3bd88b6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -207,7 +207,10 @@ fn render_format(opt: &RenderOptions, document: Document, writer: BufWriter) {
-    let mut writer = HTMLWriter::new(Box::new(writer));
+    let mut writer = HTMLWriter::new(
+        Box::new(writer),
+        document.config.lock().formatting.theme.clone(),
+    );
     document.to_html(&mut writer).unwrap();
     writer.flush().unwrap();
 }
diff --git a/src/settings/format_settings.rs b/src/settings/format_settings.rs
index 873bcf1..07b7748 100644
--- a/src/settings/format_settings.rs
+++ b/src/settings/format_settings.rs
@@ -3,12 +3,23 @@ use serde::{Deserialize, Serialize};
 #[derive(Serialize, Deserialize, Clone, Debug)]
 pub struct FormatSettings {
     pub bib_ref_display: String,
+    pub theme: Theme,
 }
 
 impl Default for FormatSettings {
     fn default() -> Self {
         Self {
             bib_ref_display: "{{number}}".to_string(),
+            theme: Theme::GitHub,
         }
     }
 }
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub enum Theme {
+    GitHub,
+    SolarizedDark,
+    SolarizedLight,
+    OceanDark,
+    OceanLight,
+}