Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion crates/mdbook-html/src/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())?)?;
Expand Down
50 changes: 48 additions & 2 deletions crates/mdbook-html/src/theme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P: AsRef<Path>>(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<P: AsRef<Path>, O: AsRef<Path>>(
theme_dir: P,
override_theme_dir: Option<O>,
) -> 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...
Expand Down Expand Up @@ -115,10 +125,39 @@ impl Theme {
}
};

let override_binary = |filename: &Path, dest: &mut Vec<u8>| {
if !filename.exists() {
return false;
}
load_file_contents(filename, dest).is_ok()
};

let combine_utf = |filename: &Path, dest: &mut Vec<u8>| {
if !filename.exists() {
return false;
}
let mut tmp: Vec<u8> = 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();
Expand All @@ -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) => {
Expand Down