Skip to content

Add copy-custom-fonts parameter to HtmlConfig to enable custom fonts #1807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
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
553 changes: 496 additions & 57 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ description = "Creates a book from markdown files"
[dependencies]
anyhow = "1.0.28"
chrono = "0.4"
clap = { version = "3.0", features = ["cargo"] }
clap = { version = "3.1.15", features = ["cargo"] }
clap_complete = "3.0"
env_logger = "0.9.0"
handlebars = "4.0"
Expand All @@ -34,6 +34,7 @@ shlex = "1"
tempfile = "3.0"
toml = "0.5.1"
topological-sort = "0.1.0"
lewp-css = "0.2"

# Watch feature
notify = { version = "4.0", optional = true }
Expand Down
1 change: 1 addition & 0 deletions guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
- [index.hbs](format/theme/index-hbs.md)
- [Syntax highlighting](format/theme/syntax-highlighting.md)
- [Editor](format/theme/editor.md)
- [Custom fonts](format/custom-fonts.md)
- [MathJax Support](format/mathjax.md)
- [mdBook-specific features](format/mdbook.md)
- [Markdown](format/markdown.md)
Expand Down
59 changes: 59 additions & 0 deletions guide/src/format/custom-fonts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Custom fonts

It is possible to use custom fonts by using a combination of the parameters
`copy-fonts` and `copy-custom-fonts`. `copy-fonts` is enabled by default.

The fonts are specified by the `fonts/fonts.css` file, relative to the root directory
of your book project.

## The copy-fonts option

If `copy-fonts` is enabled, the build in `fonts/fonts.css` file is being copied
to the output directory. In addition to that, the default fonts are copied to
the output directory.

## The copy-custom-fonts option

If `copy-custom-fonts` is enabled, the `fonts/fonts.css` file that you have created
is copied to the output directory. Also, the file gets parsed and searched for
`@font-face` rules having a `src: url(CUSTOM-FONT)` definition. These `CUSTOM-FONT`
files are copied to the output directory.

## Enabling both options

It is also possible to use both options in parallel. Then, the build in fonts
are copied, as well as the custom font files that have been found. The build in
and the custom definition of `fonts/fonts.css` are combined and copied to the
output directory as well.

## Example with both options enabled

Create a `fonts/` folder in your book root. Then copy the fonts you want to use
into this folder (in this example we are assuming `Lato-Regular.ttf`)

Create a custom fonts file `fonts/fonts.css`

```css
@font-face {
font-family: "Lato";
font-style: normal;
font-weight: normal;
src: url('Lato-Regular.ttf');
}
```

Setup your `book.toml` that it contains the following parameters:

```toml
[output.html]
copy-fonts = true
copy-custom-fonts = true
```

Adjust your `theme/css/general.css` file according to your needs, for example:

```css
html {
font-family: "Lato", sans-serif;
}
```
6 changes: 3 additions & 3 deletions src/cmd/build.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::{get_book_dir, open};
use clap::{arg, App, Arg, ArgMatches};
use clap::{arg, Arg, ArgMatches, Command};
use mdbook::errors::Result;
use mdbook::MDBook;

// Create clap subcommand arguments
pub fn make_subcommand<'help>() -> App<'help> {
App::new("build")
pub fn make_subcommand<'help>() -> Command<'help> {
Command::new("build")
.about("Builds a book from its markdown files")
.arg(
Arg::new("dest-dir")
Expand Down
6 changes: 3 additions & 3 deletions src/cmd/clean.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::get_book_dir;
use anyhow::Context;
use clap::{arg, App, Arg, ArgMatches};
use clap::{arg, Arg, ArgMatches, Command};
use mdbook::MDBook;
use std::fs;

// Create clap subcommand arguments
pub fn make_subcommand<'help>() -> App<'help> {
App::new("clean")
pub fn make_subcommand<'help>() -> Command<'help> {
Command::new("clean")
.about("Deletes a built book")
.arg(
Arg::new("dest-dir")
Expand Down
6 changes: 3 additions & 3 deletions src/cmd/init.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::get_book_dir;
use clap::{arg, App, Arg, ArgMatches};
use clap::{arg, Arg, ArgMatches, Command as ClapCommand};
use mdbook::config;
use mdbook::errors::Result;
use mdbook::MDBook;
Expand All @@ -8,8 +8,8 @@ use std::io::Write;
use std::process::Command;

// Create clap subcommand arguments
pub fn make_subcommand<'help>() -> App<'help> {
App::new("init")
pub fn make_subcommand<'help>() -> ClapCommand<'help> {
ClapCommand::new("init")
.about("Creates the boilerplate structure and files for a new book")
// the {n} denotes a newline which will properly aligned in all help messages
.arg(arg!([dir]
Expand Down
6 changes: 3 additions & 3 deletions src/cmd/serve.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(feature = "watch")]
use super::watch;
use crate::{get_book_dir, open};
use clap::{arg, App, Arg, ArgMatches};
use clap::{arg, Arg, ArgMatches, Command};
use futures_util::sink::SinkExt;
use futures_util::StreamExt;
use mdbook::errors::*;
Expand All @@ -18,8 +18,8 @@ use warp::Filter;
const LIVE_RELOAD_ENDPOINT: &str = "__livereload";

// Create clap subcommand arguments
pub fn make_subcommand<'help>() -> App<'help> {
App::new("serve")
pub fn make_subcommand<'help>() -> Command<'help> {
Command::new("serve")
.about("Serves a book at http://localhost:3000, and rebuilds it on changes")
.arg(
Arg::new("dest-dir")
Expand Down
10 changes: 5 additions & 5 deletions src/cmd/test.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::get_book_dir;
use clap::{arg, App, Arg, ArgMatches};
use clap::{arg, Arg, ArgMatches, Command};
use mdbook::errors::Result;
use mdbook::MDBook;

// Create clap subcommand arguments
pub fn make_subcommand<'help>() -> App<'help> {
App::new("test")
pub fn make_subcommand<'help>() -> Command<'help> {
Command::new("test")
.about("Tests that a book's Rust code samples compile")
.arg(
Arg::new("dest-dir")
Expand Down Expand Up @@ -37,8 +37,8 @@ pub fn make_subcommand<'help>() -> App<'help> {
.long("library-path")
.value_name("dir")
.takes_value(true)
.use_delimiter(true)
.require_delimiter(true)
.use_value_delimiter(true)
.require_value_delimiter(true)
.multiple_values(true)
.multiple_occurrences(true)
.forbid_empty_values(true)
Expand Down
6 changes: 3 additions & 3 deletions src/cmd/watch.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{get_book_dir, open};
use clap::{arg, App, Arg, ArgMatches};
use clap::{arg, Arg, ArgMatches, Command};
use mdbook::errors::Result;
use mdbook::utils;
use mdbook::MDBook;
Expand All @@ -10,8 +10,8 @@ use std::thread::sleep;
use std::time::Duration;

// Create clap subcommand arguments
pub fn make_subcommand<'help>() -> App<'help> {
App::new("watch")
pub fn make_subcommand<'help>() -> Command<'help> {
Command::new("watch")
.about("Watches a book's files and rebuilds it on changes")
.arg(
Arg::new("dest-dir")
Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,9 @@ pub struct HtmlConfig {
pub mathjax_support: bool,
/// Whether to fonts.css and respective font files to the output directory.
pub copy_fonts: bool,
/// If true, find custom font files by parsing the fonts/fonts.css and copy
/// them to the output directory.
pub copy_custom_fonts: bool,
/// An optional google analytics code.
pub google_analytics: Option<String>,
/// Additional CSS stylesheets to include in the rendered page's `<head>`.
Expand Down Expand Up @@ -549,6 +552,7 @@ impl Default for HtmlConfig {
curly_quotes: false,
mathjax_support: false,
copy_fonts: true,
copy_custom_fonts: false,
google_analytics: None,
additional_css: Vec::new(),
additional_js: Vec::new(),
Expand Down
12 changes: 6 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ extern crate log;

use anyhow::anyhow;
use chrono::Local;
use clap::{App, AppSettings, Arg, ArgMatches};
use clap::{Arg, ArgMatches, Command};
use clap_complete::Shell;
use env_logger::Builder;
use log::LevelFilter;
Expand Down Expand Up @@ -61,13 +61,13 @@ fn main() {
}

/// Create a list of valid arguments and sub-commands
fn create_clap_app() -> App<'static> {
let app = App::new(crate_name!())
fn create_clap_app() -> Command<'static> {
let app = Command::new(crate_name!())
.about(crate_description!())
.author("Mathieu David <[email protected]>")
.version(VERSION)
.setting(AppSettings::PropagateVersion)
.setting(AppSettings::ArgRequiredElseHelp)
.propagate_version(true)
.arg_required_else_help(true)
.after_help(
"For more information about a specific command, try `mdbook <command> --help`\n\
The source code for mdBook is available at: https://github.com/rust-lang/mdBook",
Expand All @@ -77,7 +77,7 @@ fn create_clap_app() -> App<'static> {
.subcommand(cmd::test::make_subcommand())
.subcommand(cmd::clean::make_subcommand())
.subcommand(
App::new("completions")
Command::new("completions")
.about("Generate shell completions for your shell to stdout")
.arg(
Arg::new("shell")
Expand Down
22 changes: 22 additions & 0 deletions src/renderer/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,24 @@ impl HtmlHandlebars {
theme::fonts::SOURCE_CODE_PRO.1,
)?;
}
if html_config.copy_custom_fonts {
let mut custom_fonts_css = std::fs::read(&Path::new("fonts/fonts.css"))?;
if html_config.copy_fonts {
// we need to extend the custom fonts file by the build in one
// to have the build in ones still available
custom_fonts_css.extend(theme::fonts::CSS);
}
write_file(destination, "fonts/fonts.css", &custom_fonts_css)?;
let custom_font_file_names =
helpers::custom_fonts::find_custom_font_files(Path::new("fonts/fonts.css"))?;
for font_file in custom_font_file_names {
write_file(
&Path::new(destination).join("fonts"),
&font_file,
&std::fs::read(&Path::new("fonts").join(&font_file))?,
)?;
}
}

let playground_config = &html_config.playground;

Expand Down Expand Up @@ -658,6 +676,10 @@ fn make_data(
data.insert("copy_fonts".to_owned(), json!(true));
}

if html_config.copy_custom_fonts {
data.insert("copy_custom_fonts".to_owned(), json!(true));
}

// Add check to see if there is an additional style
if !html_config.additional_css.is_empty() {
let mut css = Vec::new();
Expand Down
42 changes: 42 additions & 0 deletions src/renderer/html_handlebars/helpers/custom_fonts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::errors::Result;
use lewp_css::{
domain::{
at_rules::font_face::{FontFaceAtRule, FontUrlSource, Source},
CssRule,
},
Stylesheet,
};
use std::path::{Path, PathBuf};

/// Parses the given file_name and extracts the given font file names.
pub fn find_custom_font_files(css_file_name: &Path) -> Result<Vec<PathBuf>> {
let stylesheet = std::fs::read_to_string(css_file_name)?;
let stylesheet = Stylesheet::parse(&stylesheet).unwrap_or_else(|_| {
panic!(
"Stylesheet: \"{}\" could not be parsed!",
css_file_name.display()
)
});
let css_rules = stylesheet.rules.0;
let mut file_names = vec![];
for rule in css_rules {
match rule {
CssRule::FontFace(font) => {
if let Some(f) = extract_font_file_name(font) {
file_names.push(f);
}
}
_ => continue,
}
}
Ok(file_names)
}

/// Extracts the first file name given in an URL statement from the rule.
pub fn extract_font_file_name(at_rule: FontFaceAtRule) -> Option<PathBuf> {
at_rule.sources.as_ref()?;
match at_rule.sources.unwrap().get(0) {
Some(Source::Url(FontUrlSource { url, .. })) => Some(PathBuf::from(url.0.clone())),
_ => None,
}
}
1 change: 1 addition & 0 deletions src/renderer/html_handlebars/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod custom_fonts;
pub mod navigation;
pub mod theme;
pub mod toc;
2 changes: 1 addition & 1 deletion src/theme/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

<!-- Fonts -->
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
{{#if copy_fonts}}
{{#if (or copy_fonts copy_custom_fonts)}}
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
{{/if}}

Expand Down