Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fa3870b
Add a config variable for a logo path
scattenlaeufer Jun 22, 2021
fb415da
Add a logo to the HTML template
scattenlaeufer Jun 24, 2021
db82eab
Add informations about the logo to the guide
scattenlaeufer Jun 24, 2021
96de687
Fix a typo
scattenlaeufer Oct 24, 2021
f446322
Fix the link to the logo so it worked on every page
scattenlaeufer Oct 24, 2021
a2e36ae
fix tests
Chocorean Sep 3, 2026
3c16f0f
add alt to book logo
Chocorean Sep 4, 2026
eafe563
change logo extension from png to svg in guide documentation
Chocorean Sep 4, 2026
613e08e
fix display issue for logo ; it is now displayed in the center of the…
Chocorean Sep 4, 2026
f6ad728
add logo to guide
Chocorean Sep 4, 2026
375fa9e
fix broken tests
Chocorean Sep 4, 2026
bfa235f
add logo path to complex test example
Chocorean Sep 4, 2026
c907143
add title next to logo to fill the space, rustdoc style
Chocorean Sep 4, 2026
ed6c150
fix gui test
Chocorean Sep 4, 2026
4b35b3f
add missing double quote
Chocorean Sep 4, 2026
9217acc
fix properties order
Chocorean Sep 4, 2026
5c5d81a
add tests: display, position, src, across different nested chapters
Chocorean Sep 5, 2026
e6030ed
Address comments
Chocorean Sep 7, 2026
0aeacff
crash after loading the config if the logo does not exist, add testca…
Chocorean Sep 8, 2026
024fb96
replace logo copy with symlink
Chocorean Sep 9, 2026
258cce2
move test case
Chocorean Sep 10, 2026
f59370d
Rewrite check: revise failure conditions for logo config
Chocorean Sep 15, 2026
9edf1a4
attempt to fix gui tests
Chocorean Sep 15, 2026
59b3fb5
fix swapped logo paths
Chocorean Sep 15, 2026
dcce71b
add logo absolute test variant for windows platforms
Chocorean Sep 15, 2026
8e24b48
fix logo path for windows absolute logo path test
Chocorean Sep 15, 2026
ae508fa
last css adjustments: logo is 20% wide and max 8rem tall, title is 70…
Chocorean Sep 15, 2026
08ef043
fix logo img tag
Chocorean Sep 15, 2026
5caab23
minor tweaks for logo large/tall tests
Chocorean Sep 15, 2026
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
48 changes: 46 additions & 2 deletions crates/mdbook-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@

use crate::static_regex;
use crate::utils::{TomlExt, fs, log_backtrace};
use anyhow::{Context, Error, Result, bail};
use anyhow::{Context, Error, Result, anyhow, bail};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::env;
use std::path::{Path, PathBuf};
use std::path::{Component, Path, PathBuf};
use std::str::FromStr;
use toml::Value;
use toml::value::Table;
Expand Down Expand Up @@ -303,6 +303,31 @@ impl Config {

Ok(())
}

/// The config can load properly with valid, but wrong values, for instance wrong paths.
/// This is the place to check for such config variables.
pub fn check<P: Into<PathBuf>>(&self, book_root: P) -> Result<()> {
if let Some(logo_path) = self.book.get_logo_absolute_path(book_root) {
// Forbid absolute paths and parent directory references in the config file
if let Some(logo_path) = &self.book.logo {
if logo_path.is_absolute()
|| logo_path.components().any(|c| c == Component::ParentDir)
{
return Result::Err(anyhow!(
"invalid value for `logo`: should live under `{}/`",
self.book.src.to_string_lossy()
));
}
}
if !logo_path.exists() {
return Result::Err(anyhow!(
"invalid value for `logo`: {} does not exist",
logo_path.to_str().unwrap_or(&logo_path.to_string_lossy()),
));
}
}
Result::Ok(())
}
}

fn parse_env(key: &str) -> Option<String> {
Expand Down Expand Up @@ -330,6 +355,9 @@ pub struct BookConfig {
/// The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL).
/// When not specified, the text direction is derived from [`BookConfig::language`].
pub text_direction: Option<TextDirection>,
/// A logo to be displayed on top of the navigation bar. The path is relative to the source
/// path
pub logo: Option<PathBuf>,
}

/// Helper for serde serialization.
Expand All @@ -346,6 +374,7 @@ impl Default for BookConfig {
src: PathBuf::from("src"),
language: Some(String::from("en")),
text_direction: None,
logo: None,
}
}
}
Expand All @@ -360,6 +389,19 @@ impl BookConfig {
TextDirection::from_lang_code(self.language.as_deref().unwrap_or_default())
}
}

/// Compute the absolute path of the book's logo, if provided, and canonicalize it
pub fn get_logo_absolute_path<P: Into<PathBuf>>(&self, book_root: P) -> Option<PathBuf> {
if let Some(logo_cfg) = self.logo.clone() {
Some(if logo_cfg.is_absolute() {
logo_cfg
} else {
book_root.into().join(&self.src).join(logo_cfg)
})
} else {
None
}
}
}

/// Text direction to use for HTML output
Expand Down Expand Up @@ -742,6 +784,7 @@ mod tests {
description = "A completely useless book"
src = "source"
language = "ja"
logo = "images/logo.svg"

[build]
build-dir = "outputs"
Expand Down Expand Up @@ -779,6 +822,7 @@ mod tests {
src: PathBuf::from("source"),
language: Some(String::from("ja")),
text_direction: None,
logo: Some(PathBuf::from("images/logo.svg")),
};
let build_should_be = BuildConfig {
build_dir: PathBuf::from("outputs"),
Expand Down
1 change: 1 addition & 0 deletions crates/mdbook-driver/src/mdbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl MDBook {
};

config.update_from_env()?;
config.check(book_root.clone())?;

if tracing::enabled!(tracing::Level::TRACE) {
for line in format!("Config: {config:#?}").lines() {
Expand Down
19 changes: 19 additions & 0 deletions crates/mdbook-html/front-end/css/chrome.css
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,25 @@ html:not(.sidebar-resizing) .sidebar {
right: 0;
padding: 10px 10px;
}
.sidebar .sidebar-scrollbox .sidebar-book-logo {
align-items: center;
column-gap: 20px;
display: flex;
justify-content: center;
margin: 20px 0;
}
.sidebar .sidebar-scrollbox .sidebar-book-logo img {
display: block;
max-height: 8rem;
width: 20%;
}
.sidebar .sidebar-scrollbox .sidebar-book-logo h2 {
margin: 0;
width: 70%;
overflow-wrap: anywhere;
text-wrap: balance;
}

.sidebar .sidebar-resize-handle {
position: absolute;
cursor: col-resize;
Expand Down
7 changes: 7 additions & 0 deletions crates/mdbook-html/front-end/templates/toc.js.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ class MDBookSidebarScrollbox extends HTMLElement {
super();
}
connectedCallback() {
{{#if book_logo }}
this.innerHTML =
'<div id="sidebar-book-logo" class="sidebar-book-logo">' +
'<img src="' + path_to_root + '{{ book_logo }}" alt="{{ book_title }}" />' +
'<h2 class="sidebar-logo-title">{{ book_title }}</h2></div>{{#toc}}{{/toc}}';
{{else}}
this.innerHTML = '{{#toc}}{{/toc}}';
{{/if}}
// Set the current, active page, and reveal it if it's hidden
let current_page = document.location.href.toString().split('#')[0].split('?')[0];
if (current_page.endsWith('/')) {
Expand Down
4 changes: 4 additions & 0 deletions crates/mdbook-html/src/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,10 @@ fn make_data(
"description".to_owned(),
json!(config.book.description.clone().unwrap_or_default()),
);
data.insert(
"book_logo".to_owned(),
json!(config.book.logo.clone().unwrap_or_default()),
);
if theme.favicon_png.is_some() {
data.insert("favicon_png".to_owned(), json!("favicon.png"));
}
Expand Down
1 change: 1 addition & 0 deletions guide/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ title = "mdBook Documentation"
description = "Create book from markdown files. Like Gitbook but implemented in Rust"
authors = ["Mathieu David", "Michael-F-Bryan"]
language = "en"
logo = "images/logo.svg"

[rust]
edition = "2018"
Expand Down
3 changes: 3 additions & 0 deletions guide/src/format/configuration/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ This is general information about your book.
This is also used to derive the direction of text (RTL, LTR) within the book.
- **text-direction**: The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL). Possible values: `ltr`, `rtl`.
When not specified, the text direction is derived from the book's `language` attribute.
- **logo:** Path to a logo to displayed at the top of the navigation bar, relative to `<book_root>/<src>/`.

**book.toml**

```toml
[book]
title = "Example book"
Expand All @@ -59,6 +61,7 @@ description = "The example book covers examples."
src = "my-src" # the source files will be found in `root/my-src` instead of `root/src`
language = "en"
text-direction = "ltr"
logo = "static/logo.svg" # logo lives at `root/my-src/static/logo.svg`
```

### Rust options
Expand Down
1 change: 1 addition & 0 deletions guide/src/images/logo.svg
Comment thread
Chocorean marked this conversation as resolved.
3 changes: 3 additions & 0 deletions tests/gui/books/sidebar-logo-large/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[book]
title = "Large logo"
logo = "logo.svg"
1 change: 1 addition & 0 deletions tests/gui/books/sidebar-logo-large/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- [c1](c1.md)
1 change: 1 addition & 0 deletions tests/gui/books/sidebar-logo-large/src/c1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# c1
4 changes: 4 additions & 0 deletions tests/gui/books/sidebar-logo-large/src/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/gui/books/sidebar-logo-tall/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[book]
title = "Tall logo"
logo = "logo.svg"
1 change: 1 addition & 0 deletions tests/gui/books/sidebar-logo-tall/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- [c1](c1.md)
1 change: 1 addition & 0 deletions tests/gui/books/sidebar-logo-tall/src/c1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# c1
4 changes: 4 additions & 0 deletions tests/gui/books/sidebar-logo-tall/src/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions tests/gui/books/sidebar-logo/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[book]
title = "sidebar-logo test"
language = "en"
logo = "logo.svg"
6 changes: 6 additions & 0 deletions tests/gui/books/sidebar-logo/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Summary

- [Chapter 1](./chapter_1.md)
- [Chapter 2](./chapter_2.md)
- [Nested](./nested/chapter_nested.md)
- [Deeper](./nested/again/chapter_nested.md)
3 changes: 3 additions & 0 deletions tests/gui/books/sidebar-logo/src/chapter_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Chapter 1

This is the first chapter to test logo display at root level.
3 changes: 3 additions & 0 deletions tests/gui/books/sidebar-logo/src/chapter_2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Chapter 2

This is the second chapter.
1 change: 1 addition & 0 deletions tests/gui/books/sidebar-logo/src/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Deeper Chapter

This is a nested chapter to test logo display at different nesting levels.
3 changes: 3 additions & 0 deletions tests/gui/books/sidebar-logo/src/nested/chapter_nested.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Nested Chapter

This is a nested chapter to test logo display at different nesting levels.
87 changes: 87 additions & 0 deletions tests/gui/sidebar-logo.goml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// This GUI test checks that the logo is displayed when configured
// Test cases:
// - test if the logo is displayed when provided
// - test it is displayed above the toc
// - test if works in nested files (level 0, 1 and n > 1)
// - large pictures are properly displayed

define-function: (
"load",
[path],
block {
go-to: |DOC_PATH| + |path|
set-window-size: (1100, 600)
reload:
// Wait for the TOC to be populated by JavaScript
wait-for: "mdbook-sidebar-scrollbox"
wait-for: "ol.chapter"
wait-for: "#sidebar-book-logo"
},
)

define-function: (
"assert-logo",
[],
block {
assert-css: ("#sidebar-book-logo", {"display": "flex"})
assert-css: ("#sidebar-book-logo img", {"display": "block"})
},
)

define-function: (
"assert-logo-src",
[prefix],
block {
assert-attribute: (".sidebar-book-logo img", {"src": |prefix| + "logo.svg"})
assert-find-text-false: "sidebar-logo test logo"
},
)

define-function: (
"assert-logo-position",
[],
block {
store-position: ("#sidebar-book-logo img", {"y": logo_y})
store-position: ("ol.chapter", {"y": chapter_y})
assert: |logo_y| < |chapter_y|
},
)

call-function: ("load", {"path": "sidebar-logo/index.html"})

// The sidebar-book-logo should exist when logo is provided
call-function: ("assert-logo", {})

// Logo image should have the correct src path and loaded
call-function: ("assert-logo-src", {"prefix": ""})

// Logo should be above the TOC
call-function: ("assert-logo-position", {})

// Test on chapter_2, nested, and deeper
click: ".chapter a[href='chapter_2.html']"
wait-for-text: (".header", "Chapter 2")
call-function: ("assert-logo", {})
call-function: ("assert-logo-src", {"prefix": ""})
call-function: ("assert-logo-position", {})

Comment thread
Chocorean marked this conversation as resolved.
click: ".chapter a[href='nested/chapter_nested.html']"
wait-for-text: (".header", "Nested Chapter")
call-function: ("assert-logo", {})
call-function: ("assert-logo-src", {"prefix": "../"})
call-function: ("assert-logo-position", {})

click: ".chapter a[href='../nested/again/chapter_nested.html']"
wait-for-text: (".header", "Deeper Chapter")
call-function: ("assert-logo", {})
call-function: ("assert-logo-src", {"prefix": "../../"})
call-function: ("assert-logo-position", {})

// Large images checks
call-function: ("load", {"path": "sidebar-logo-large/index.html"})
store-property : (".sidebar-book-logo img", {"width": logo_width})
assert: |logo_width| <= 200

call-function: ("load", {"path": "sidebar-logo-tall/index.html"})
store-property : (".sidebar-book-logo img", {"height": logo_height})
assert: |logo_height| <= 200
3 changes: 3 additions & 0 deletions tests/gui/sidebar.goml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ assert-local-storage: {|sidebar_storage_value|: |sidebar_storage_displayed_value
// But the sidebar should be hidden anyway.
assert-css: ("#mdbook-sidebar", {"display": "none"})
assert-position: ("#mdbook-page-wrapper", {"x": 0})

// The logo should not be displayed in the sidebar.
assert-false: "#sidebar-book-logo"
4 changes: 2 additions & 2 deletions tests/testsuite/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ ERROR Invalid configuration file
|
3 | foo = 123
| ^^^
unknown field `foo`, expected one of `title`, `authors`, `description`, `src`, `language`, `text-direction`
unknown field `foo`, expected one of `title`, `authors`, `description`, `src`, `language`, `text-direction`, `logo`


"#]]);
Expand Down Expand Up @@ -232,7 +232,7 @@ fn env_invalid_value() {
.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
ERROR unknown field `titlez`, expected one of `title`, `authors`, `description`, `src`, `language`, `text-direction`
ERROR unknown field `titlez`, expected one of `title`, `authors`, `description`, `src`, `language`, `text-direction`, `logo`


"#]]);
Expand Down
1 change: 1 addition & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod renderer;
mod rendering;
#[cfg(feature = "search")]
mod search;
mod sidebar_logo;
mod test;
mod theme;
mod toc;
Expand Down
1 change: 1 addition & 0 deletions tests/testsuite/preprocessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ fn extension_compatibility() {
"authors": [],
"description": null,
"language": "en",
"logo": null,
"text-direction": null,
"title": "extension_compatibility"
},
Expand Down
1 change: 1 addition & 0 deletions tests/testsuite/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ fn backends_receive_render_context_via_stdin() {
"authors": [],
"description": null,
"language": "en",
"logo": null,
"text-direction": null,
"title": null
},
Expand Down
5 changes: 5 additions & 0 deletions tests/testsuite/sidebar/logo/absolute_unix/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[book]
title = "outside"
authors = ["Chocorean"]
language = "en"
logo = "/absolute/path/logo.svg"
3 changes: 3 additions & 0 deletions tests/testsuite/sidebar/logo/absolute_unix/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Summary

- [Chapter 1](./chapter_1.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Chapter 1
Loading
Loading