diff --git a/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs
index 8edac3cace..42917b89b6 100644
--- a/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs
+++ b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs
@@ -332,7 +332,35 @@ impl Renderer for HtmlHandlebars {
None => ctx.root.join("theme"),
};
- let theme = Theme::new(theme_dir);
+ // the user-specified theme directory may still be overridden
+ // by an additional "theme/" directory.
+ // this is useful when one needs to integrate an external
+ // theme project, but still modify certain parts of it.
+ use std::str::FromStr;
+ let override_theme_dir = ctx.root.join("theme");
+ let mut canon_theme_dir = theme_dir.canonicalize().unwrap_or(theme_dir.clone());
+ let mut canon_override_theme_dir = override_theme_dir
+ .canonicalize()
+ .unwrap_or(override_theme_dir.clone());
+ let re = regex::Regex::new(r###"[\\/]$"###).unwrap();
+ canon_theme_dir = PathBuf::from_str(
+ &re.replace(canon_theme_dir.to_str().unwrap(), "")
+ .into_owned(),
+ )
+ .unwrap();
+ canon_override_theme_dir = PathBuf::from_str(
+ &re.replace(canon_override_theme_dir.to_str().unwrap(), "")
+ .into_owned(),
+ )
+ .unwrap();
+ let mut override_theme_dir = Some(override_theme_dir);
+ if canon_theme_dir == canon_override_theme_dir
+ || override_theme_dir.as_ref().map_or(true, |d| !d.is_dir())
+ {
+ override_theme_dir = None;
+ }
+
+ let theme = Theme::new_with_override(theme_dir, override_theme_dir);
debug!("Register the index handlebars template");
handlebars.register_template_string("index", String::from_utf8(theme.index.clone())?)?;
diff --git a/crates/mdbook-html/src/theme/mod.rs b/crates/mdbook-html/src/theme/mod.rs
index c55a30eff3..4d9f3f6ac9 100644
--- a/crates/mdbook-html/src/theme/mod.rs
+++ b/crates/mdbook-html/src/theme/mod.rs
@@ -64,7 +64,17 @@ impl Theme {
/// Creates a `Theme` from the given `theme_dir`.
/// If a file is found in the theme dir, it will override the default version.
pub fn new>(theme_dir: P) -> Self {
+ Self::new_with_override::<_, &str>(theme_dir, None)
+ }
+
+ /// Creates a `Theme` from the given `theme_dir` and, optionally,
+ /// a semi-overriding `override_theme_dir`.
+ pub fn new_with_override, O: AsRef>(
+ theme_dir: P,
+ override_theme_dir: Option,
+ ) -> Self {
let theme_dir = theme_dir.as_ref();
+ let override_theme_dir = override_theme_dir.as_ref().map(|p| p.as_ref());
let mut theme = Theme::default();
// If the theme directory doesn't exist there's no point continuing...
@@ -115,10 +125,39 @@ impl Theme {
}
};
+ let override_binary = |filename: &Path, dest: &mut Vec| {
+ if !filename.exists() {
+ return false;
+ }
+ load_file_contents(filename, dest).is_ok()
+ };
+
+ let combine_utf = |filename: &Path, dest: &mut Vec| {
+ if !filename.exists() {
+ return false;
+ }
+ let mut tmp: Vec = Vec::new();
+ if load_file_contents(filename, &mut tmp).is_err() {
+ false
+ } else {
+ let mut new_str = String::from_utf8(dest.clone()).unwrap();
+ new_str.push_str("\n\n");
+ new_str.push_str(&String::from_utf8(tmp).unwrap());
+ *dest = new_str.as_bytes().iter().cloned().collect::<_>();
+ true
+ }
+ };
+
for (filename, dest) in files {
load_with_warn(&filename, dest);
}
+ // extra theme/ overrides
+ if let Some(dir) = override_theme_dir {
+ combine_utf(&dir.join("head.hbs"), &mut theme.head);
+ override_binary(&dir.join("highlight.js"), &mut theme.highlight_js);
+ }
+
let fonts_dir = theme_dir.join("fonts");
if fonts_dir.exists() {
let mut fonts_css = Vec::new();
@@ -145,9 +184,16 @@ impl Theme {
// If the user overrides one favicon, but not the other, do not
// copy the default for the other.
let favicon_png = &mut theme.favicon_png.as_mut().unwrap();
- let png = load_with_warn(&theme_dir.join("favicon.png"), favicon_png);
+ let mut png = load_with_warn(&theme_dir.join("favicon.png"), favicon_png);
let favicon_svg = &mut theme.favicon_svg.as_mut().unwrap();
- let svg = load_with_warn(&theme_dir.join("favicon.svg"), favicon_svg);
+ let mut svg = load_with_warn(&theme_dir.join("favicon.svg"), favicon_svg);
+
+ // one more overrider for the favicon
+ if let Some(dir) = override_theme_dir {
+ png = override_binary(&dir.join("favicon.png"), favicon_png) || png;
+ svg = override_binary(&dir.join("favicon.svg"), favicon_svg) || svg;
+ }
+
match (png, svg) {
(true, true) | (false, false) => {}
(true, false) => {