Merge pull request #1330 from notriddle/embed-svg

Use embedded SVG instead of fonts for icons, font-awesome 6.2
This commit is contained in:
Eric Huss 2025-08-15 03:26:53 +00:00 committed by GitHub
commit 4bac54883b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 238 additions and 2769 deletions

7
Cargo.lock generated
View file

@ -622,6 +622,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "font-awesome-as-a-crate"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.1"
@ -1352,6 +1358,7 @@ dependencies = [
"ammonia", "ammonia",
"anyhow", "anyhow",
"elasticlunr-rs", "elasticlunr-rs",
"font-awesome-as-a-crate",
"handlebars", "handlebars",
"hex", "hex",
"log", "log",

View file

@ -33,6 +33,7 @@ clap = { version = "4.5.41", features = ["cargo", "wrap_help"] }
clap_complete = "4.5.55" clap_complete = "4.5.55"
elasticlunr-rs = "3.0.2" elasticlunr-rs = "3.0.2"
env_logger = "0.11.8" env_logger = "0.11.8"
font-awesome-as-a-crate = "0.3.0"
futures-util = "0.3.31" futures-util = "0.3.31"
handlebars = "6.3.2" handlebars = "6.3.2"
hex = "0.4.3" hex = "0.4.3"

View file

@ -11,6 +11,7 @@ rust-version.workspace = true
ammonia = { workspace = true, optional = true } ammonia = { workspace = true, optional = true }
anyhow.workspace = true anyhow.workspace = true
elasticlunr-rs = { workspace = true, optional = true } elasticlunr-rs = { workspace = true, optional = true }
font-awesome-as-a-crate.workspace = true
handlebars.workspace = true handlebars.workspace = true
hex.workspace = true hex.workspace = true
log.workspace = true log.workspace = true

View file

@ -56,7 +56,7 @@ html.sidebar-visible #menu-bar {
#menu-bar.bordered { #menu-bar.bordered {
border-block-end-color: var(--table-border-color); border-block-end-color: var(--table-border-color);
} }
#menu-bar i, #menu-bar .icon-button { #menu-bar .fa-svg, #menu-bar .icon-button {
position: relative; position: relative;
padding: 0 8px; padding: 0 8px;
z-index: 10; z-index: 10;
@ -65,7 +65,7 @@ html.sidebar-visible #menu-bar {
transition: color 0.5s; transition: color 0.5s;
} }
@media only screen and (max-width: 420px) { @media only screen and (max-width: 420px) {
#menu-bar i, #menu-bar .icon-button { #menu-bar .fa-svg, #menu-bar .icon-button {
padding: 0 5px; padding: 0 5px;
} }
} }
@ -76,7 +76,7 @@ html.sidebar-visible #menu-bar {
padding: 0; padding: 0;
color: inherit; color: inherit;
} }
.icon-button i { .icon-button .fa-svg {
margin: 0; margin: 0;
} }
@ -118,14 +118,14 @@ html:not(.js) .left-buttons button {
.mobile-nav-chapters, .mobile-nav-chapters,
.mobile-nav-chapters:visited, .mobile-nav-chapters:visited,
.menu-bar .icon-button, .menu-bar .icon-button,
.menu-bar a i { .menu-bar a .fa-svg {
color: var(--icons); color: var(--icons);
} }
.menu-bar i:hover, .menu-bar .fa-svg:hover,
.menu-bar .icon-button:hover, .menu-bar .icon-button:hover,
.nav-chapters:hover, .nav-chapters:hover,
.mobile-nav-chapters i:hover { .mobile-nav-chapters .fa-svg:hover {
color: var(--icons-hover); color: var(--icons-hover);
} }
@ -240,13 +240,10 @@ pre > .buttons :hover {
border-color: var(--icons-hover); border-color: var(--icons-hover);
background-color: var(--theme-hover); background-color: var(--theme-hover);
} }
pre > .buttons i {
margin-inline-start: 8px;
}
pre > .buttons button { pre > .buttons button {
cursor: inherit; cursor: inherit;
margin: 0px 5px; margin: 0px 5px;
padding: 4px 4px 3px 5px; padding: 2px 3px 0px 4px;
font-size: 23px; font-size: 23px;
border-style: solid; border-style: solid;
@ -365,6 +362,20 @@ mark.fade-out {
background-color: var(--bg); background-color: var(--bg);
} }
#fa-spin {
animation: rotating 2s linear infinite;
display: inline-block;
}
@keyframes rotating {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#searchbar { #searchbar {
width: 100%; width: 100%;
margin-block-start: var(--searchbar-margin-block-start); margin-block-start: var(--searchbar-margin-block-start);

File diff suppressed because one or more lines are too long

View file

@ -278,3 +278,10 @@ sup {
.result-no-output { .result-no-output {
font-style: italic; font-style: italic;
} }
.fa-svg svg {
width: 1em;
height: 1em;
fill: currentColor;
margin-bottom: -0.1em;
}

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 434 KiB

View file

@ -97,6 +97,7 @@ function playground_text(playground, hidden = true) {
if (all_available) { if (all_available) {
play_button.classList.remove('hidden'); play_button.classList.remove('hidden');
play_button.hidden = false;
} else { } else {
play_button.classList.add('hidden'); play_button.classList.add('hidden');
} }
@ -207,26 +208,25 @@ function playground_text(playground, hidden = true) {
const buttons = document.createElement('div'); const buttons = document.createElement('div');
buttons.className = 'buttons'; buttons.className = 'buttons';
buttons.innerHTML = '<button class="fa fa-eye" title="Show hidden lines" \ buttons.innerHTML = '<button title="Show hidden lines" \
aria-label="Show hidden lines"></button>'; aria-label="Show hidden lines"></button>';
buttons.firstChild.innerHTML = document.getElementById('fa-eye').innerHTML;
// add expand button // add expand button
const pre_block = block.parentNode; const pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild); pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.querySelector('.buttons').addEventListener('click', function(e) { buttons.firstChild.addEventListener('click', function(e) {
if (e.target.classList.contains('fa-eye')) { if (this.title === 'Show hidden lines') {
e.target.classList.remove('fa-eye'); this.innerHTML = document.getElementById('fa-eye-slash').innerHTML;
e.target.classList.add('fa-eye-slash'); this.title = 'Hide lines';
e.target.title = 'Hide lines'; this.setAttribute('aria-label', e.target.title);
e.target.setAttribute('aria-label', e.target.title);
block.classList.remove('hide-boring'); block.classList.remove('hide-boring');
} else if (e.target.classList.contains('fa-eye-slash')) { } else if (this.title === 'Hide lines') {
e.target.classList.remove('fa-eye-slash'); this.innerHTML = document.getElementById('fa-eye').innerHTML;
e.target.classList.add('fa-eye'); this.title = 'Show hidden lines';
e.target.title = 'Show hidden lines'; this.setAttribute('aria-label', e.target.title);
e.target.setAttribute('aria-label', e.target.title);
block.classList.add('hide-boring'); block.classList.add('hide-boring');
} }
@ -266,10 +266,11 @@ aria-label="Show hidden lines"></button>';
} }
const runCodeButton = document.createElement('button'); const runCodeButton = document.createElement('button');
runCodeButton.className = 'fa fa-play play-button'; runCodeButton.className = 'play-button';
runCodeButton.hidden = true; runCodeButton.hidden = true;
runCodeButton.title = 'Run this code'; runCodeButton.title = 'Run this code';
runCodeButton.setAttribute('aria-label', runCodeButton.title); runCodeButton.setAttribute('aria-label', runCodeButton.title);
runCodeButton.innerHTML = document.getElementById('fa-play').innerHTML;
buttons.insertBefore(runCodeButton, buttons.firstChild); buttons.insertBefore(runCodeButton, buttons.firstChild);
runCodeButton.addEventListener('click', () => { runCodeButton.addEventListener('click', () => {
@ -289,9 +290,11 @@ aria-label="Show hidden lines"></button>';
const code_block = pre_block.querySelector('code'); const code_block = pre_block.querySelector('code');
if (window.ace && code_block.classList.contains('editable')) { if (window.ace && code_block.classList.contains('editable')) {
const undoChangesButton = document.createElement('button'); const undoChangesButton = document.createElement('button');
undoChangesButton.className = 'fa fa-history reset-button'; undoChangesButton.className = 'reset-button';
undoChangesButton.title = 'Undo changes'; undoChangesButton.title = 'Undo changes';
undoChangesButton.setAttribute('aria-label', undoChangesButton.title); undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
undoChangesButton.innerHTML +=
document.getElementById('fa-clock-rotate-left').innerHTML;
buttons.insertBefore(undoChangesButton, buttons.firstChild); buttons.insertBefore(undoChangesButton, buttons.firstChild);

View file

@ -33,7 +33,6 @@
{{/if}} {{/if}}
<!-- Fonts --> <!-- Fonts -->
<link rel="stylesheet" href="{{ resource "FontAwesome/css/font-awesome.css" }}">
<link rel="stylesheet" href="{{ resource "fonts/fonts.css" }}"> <link rel="stylesheet" href="{{ resource "fonts/fonts.css" }}">
<!-- Highlight.js Stylesheets --> <!-- Highlight.js Stylesheets -->
@ -145,10 +144,10 @@
<div id="menu-bar" class="menu-bar sticky"> <div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons"> <div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar"> <label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i> {{fa "solid" "bars"}}
</label> </label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list"> <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i> {{fa "solid" "paintbrush"}}
</button> </button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu"> <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li> <li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
@ -160,7 +159,7 @@
</ul> </ul>
{{#if search_enabled}} {{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar"> <button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i> {{fa "solid" "magnifying-glass"}}
</button> </button>
{{/if}} {{/if}}
</div> </div>
@ -170,17 +169,17 @@
<div class="right-buttons"> <div class="right-buttons">
{{#if print_enable}} {{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book"> <a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i> {{fa "solid" "print" "print-button"}}
</a> </a>
{{/if}} {{/if}}
{{#if git_repository_url}} {{#if git_repository_url}}
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository"> <a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i> {{fa git_repository_icon_class git_repository_icon}}
</a> </a>
{{/if}} {{/if}}
{{#if git_repository_edit_url}} {{#if git_repository_edit_url}}
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit" rel="edit"> <a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit" rel="edit">
<i id="git-edit-button" class="fa fa-edit"></i> {{fa "solid" "pencil" "git-edit-button"}}
</a> </a>
{{/if}} {{/if}}
@ -193,7 +192,7 @@
<div class="search-wrapper"> <div class="search-wrapper">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header"> <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper"> <div class="spinner-wrapper">
<i class="fa fa-spinner fa-spin"></i> {{fa "solid" "spinner" "fa-spin"}}
</div> </div>
</div> </div>
</form> </form>
@ -224,19 +223,18 @@
{{#if previous}} {{#if previous}}
<a rel="prev" href="{{ path_to_root }}{{previous.link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"> <a rel="prev" href="{{ path_to_root }}{{previous.link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
{{#if (eq ../text_direction "rtl")}} {{#if (eq ../text_direction "rtl")}}
<i class="fa fa-angle-right"></i> {{fa "solid" "angle-right"}}
{{else}} {{else}}
<i class="fa fa-angle-left"></i> {{fa "solid" "angle-left"}}
{{/if}} {{/if}}
</a>
{{/if}} {{/if}}
{{#if next}} {{#if next}}
<a rel="next prefetch" href="{{ path_to_root }}{{next.link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right"> <a rel="next prefetch" href="{{ path_to_root }}{{next.link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
{{#if (eq ../text_direction "rtl")}} {{#if (eq ../text_direction "rtl")}}
<i class="fa fa-angle-left"></i> {{fa "solid" "angle-left"}}
{{else}} {{else}}
<i class="fa fa-angle-right"></i> {{fa "solid" "angle-right"}}
{{/if}} {{/if}}
</a> </a>
{{/if}} {{/if}}
@ -250,9 +248,9 @@
{{#if previous}} {{#if previous}}
<a rel="prev" href="{{ path_to_root }}{{previous.link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"> <a rel="prev" href="{{ path_to_root }}{{previous.link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
{{#if (eq ../text_direction "rtl")}} {{#if (eq ../text_direction "rtl")}}
<i class="fa fa-angle-right"></i> {{fa "solid" "angle-right"}}
{{else}} {{else}}
<i class="fa fa-angle-left"></i> {{fa "solid" "angle-left"}}
{{/if}} {{/if}}
</a> </a>
{{/if}} {{/if}}
@ -260,9 +258,9 @@
{{#if next}} {{#if next}}
<a rel="next prefetch" href="{{ path_to_root }}{{next.link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right"> <a rel="next prefetch" href="{{ path_to_root }}{{next.link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
{{#if (eq text_direction "rtl")}} {{#if (eq text_direction "rtl")}}
<i class="fa fa-angle-left"></i> {{fa "solid" "angle-left"}}
{{else}} {{else}}
<i class="fa fa-angle-right"></i> {{fa "solid" "angle-right"}}
{{/if}} {{/if}}
</a> </a>
{{/if}} {{/if}}
@ -270,6 +268,12 @@
</div> </div>
<template id=fa-eye>{{fa "solid" "eye"}}</template>
<template id=fa-eye-slash>{{fa "solid" "eye-slash"}}</template>
<template id=fa-copy>{{fa "regular" "copy"}}</template>
<template id=fa-play>{{fa "solid" "play"}}</template>
<template id=fa-clock-rotate-left>{{fa "solid" "clock-rotate-left"}}</template>
{{#if live_reload_endpoint}} {{#if live_reload_endpoint}}
<!-- Livereload script (if served using the cli tool) --> <!-- Livereload script (if served using the cli tool) -->
<script> <script>

View file

@ -28,7 +28,6 @@
<link rel="stylesheet" href="{{ resource "css/print.css" }}" media="print"> <link rel="stylesheet" href="{{ resource "css/print.css" }}" media="print">
{{/if}} {{/if}}
<!-- Fonts --> <!-- Fonts -->
<link rel="stylesheet" href="{{ resource "FontAwesome/css/font-awesome.css" }}">
<link rel="stylesheet" href="{{ resource "fonts/fonts.css" }}"> <link rel="stylesheet" href="{{ resource "fonts/fonts.css" }}">
<!-- Custom theme stylesheets --> <!-- Custom theme stylesheets -->
{{#each additional_css}} {{#each additional_css}}

View file

@ -242,6 +242,7 @@ impl HtmlHandlebars {
let rendered = fix_code_blocks(&rendered); let rendered = fix_code_blocks(&rendered);
let rendered = add_playground_pre(&rendered, playground_config, edition); let rendered = add_playground_pre(&rendered, playground_config, edition);
let rendered = hide_lines(&rendered, code_config); let rendered = hide_lines(&rendered, code_config);
let rendered = convert_fontawesome(&rendered);
rendered rendered
} }
@ -271,6 +272,7 @@ impl HtmlHandlebars {
no_section_label: html_config.no_section_label, no_section_label: html_config.no_section_label,
}), }),
); );
handlebars.register_helper("fa", Box::new(helpers::fontawesome::fa_helper));
} }
fn emit_redirects( fn emit_redirects(
@ -635,9 +637,19 @@ fn make_data(
let git_repository_icon = match html_config.git_repository_icon { let git_repository_icon = match html_config.git_repository_icon {
Some(ref git_repository_icon) => git_repository_icon, Some(ref git_repository_icon) => git_repository_icon,
None => "fa-github", None => "fab-github",
};
let git_repository_icon_class = match git_repository_icon.split('-').next() {
Some("fa") => "regular",
Some("fas") => "solid",
Some("fab") => "brands",
_ => "regular",
}; };
data.insert("git_repository_icon".to_owned(), json!(git_repository_icon)); data.insert("git_repository_icon".to_owned(), json!(git_repository_icon));
data.insert(
"git_repository_icon_class".to_owned(),
json!(git_repository_icon_class),
);
let mut chapters = vec![]; let mut chapters = vec![];
@ -737,6 +749,54 @@ fn insert_link_into_header(
) )
} }
// Convert fontawesome `<i>` tags to inline SVG
fn convert_fontawesome(html: &str) -> String {
use font_awesome_as_a_crate as fa;
let regex = Regex::new(r##"<i([^>]+)class="([^"]+)"([^>]*)></i>"##).unwrap();
regex
.replace_all(html, |caps: &Captures<'_>| {
let text = &caps[0];
let before = &caps[1];
let classes = &caps[2];
let after = &caps[3];
let mut icon = String::new();
let mut type_ = fa::Type::Regular;
let mut other_classes = String::new();
for class in classes.split(" ") {
if let Some(class) = class.strip_prefix("fa-") {
icon = class.to_owned();
} else if class == "fa" {
type_ = fa::Type::Regular;
} else if class == "fas" {
type_ = fa::Type::Solid;
} else if class == "fab" {
type_ = fa::Type::Brands;
} else {
other_classes += " ";
other_classes += class;
}
}
if icon.is_empty() {
text.to_owned()
} else if let Ok(svg) = fa::svg(type_, &icon) {
format!(
r#"<span{before}class="fa-svg{other_classes}"{after}>{svg}</span>"#,
before = before,
other_classes = other_classes,
after = after,
svg = svg
)
} else {
text.to_owned()
}
})
.into_owned()
}
// The rust book uses annotations for rustdoc to test code snippets, // The rust book uses annotations for rustdoc to test code snippets,
// like the following: // like the following:
// ```rust,should_panic // ```rust,should_panic

View file

@ -0,0 +1,53 @@
use font_awesome_as_a_crate as fa;
use handlebars::{
Context, Handlebars, Helper, Output, RenderContext, RenderError, RenderErrorReason,
};
use log::trace;
use std::str::FromStr;
pub(crate) fn fa_helper(
h: &Helper<'_>,
_r: &Handlebars<'_>,
_ctx: &Context,
_rc: &mut RenderContext<'_, '_>,
out: &mut dyn Output,
) -> Result<(), RenderError> {
trace!("fa_helper (handlebars helper)");
let type_ = h
.param(0)
.and_then(|v| v.value().as_str())
.and_then(|v| fa::Type::from_str(v).ok())
.ok_or_else(|| {
RenderErrorReason::Other(
"Param 0 with String type is required for fontawesome helper.".to_owned(),
)
})?;
let name = h.param(1).and_then(|v| v.value().as_str()).ok_or_else(|| {
RenderErrorReason::Other(
"Param 1 with String type is required for fontawesome helper.".to_owned(),
)
})?;
trace!("fa_helper: {} {}", type_, name);
let name = name
.strip_prefix("fa-")
.or_else(|| name.strip_prefix("fab-"))
.or_else(|| name.strip_prefix("fas-"))
.unwrap_or(name);
if let Some(id) = h.param(2).and_then(|v| v.value().as_str()) {
out.write(&format!("<span class=fa-svg id=\"{}\">", id))?;
} else {
out.write("<span class=fa-svg>")?;
}
out.write(
fa::svg(type_, name)
.map_err(|_| RenderErrorReason::Other(format!("Missing font {}", name)))?,
)?;
out.write("</span>")?;
Ok(())
}

View file

@ -1,2 +1,3 @@
pub(crate) mod fontawesome;
pub(crate) mod resources; pub(crate) mod resources;
pub(crate) mod toc; pub(crate) mod toc;

View file

@ -62,28 +62,6 @@ impl StaticFiles {
this.add_builtin("ayu-highlight.css", &theme.ayu_highlight_css); this.add_builtin("ayu-highlight.css", &theme.ayu_highlight_css);
this.add_builtin("highlight.js", &theme.highlight_js); this.add_builtin("highlight.js", &theme.highlight_js);
this.add_builtin("clipboard.min.js", &theme.clipboard_js); this.add_builtin("clipboard.min.js", &theme.clipboard_js);
this.add_builtin("FontAwesome/css/font-awesome.css", theme::FONT_AWESOME);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.eot",
theme::FONT_AWESOME_EOT,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.svg",
theme::FONT_AWESOME_SVG,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.ttf",
theme::FONT_AWESOME_TTF,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.woff",
theme::FONT_AWESOME_WOFF,
);
this.add_builtin(
"FontAwesome/fonts/fontawesome-webfont.woff2",
theme::FONT_AWESOME_WOFF2,
);
this.add_builtin("FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF);
if theme.fonts_css.is_none() { if theme.fonts_css.is_none() {
this.add_builtin("fonts/fonts.css", theme::fonts::CSS); this.add_builtin("fonts/fonts.css", theme::fonts::CSS);
for (file_name, contents) in theme::fonts::LICENSES.iter() { for (file_name, contents) in theme::fonts::LICENSES.iter() {
@ -170,13 +148,7 @@ impl StaticFiles {
let mut parts = filename.splitn(2, '.'); let mut parts = filename.splitn(2, '.');
let parts = parts.next().and_then(|p| Some((p, parts.next()?))); let parts = parts.next().and_then(|p| Some((p, parts.next()?)));
if let Some((name, suffix)) = parts { if let Some((name, suffix)) = parts {
// FontAwesome already does its own cache busting with the ?v=4.7.0 thing, if name != "" && suffix != "" && suffix != "txt" {
// and I don't want to have to patch its CSS file to use `{{ resource }}`
if name != ""
&& suffix != ""
&& suffix != "txt"
&& !name.starts_with("FontAwesome/fonts/")
{
let hex = hex::encode(&Sha256::digest(data)[..4]); let hex = hex::encode(&Sha256::digest(data)[..4]);
let new_filename = format!("{}-{}.{}", name, hex, suffix); let new_filename = format!("{}-{}.{}", name, hex, suffix);
self.hash_map.insert(filename.clone(), new_filename.clone()); self.hash_map.insert(filename.clone(), new_filename.clone());

View file

@ -29,18 +29,6 @@ pub static TOMORROW_NIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/tomor
pub static HIGHLIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/highlight.css"); pub static HIGHLIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/highlight.css");
pub static AYU_HIGHLIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/ayu-highlight.css"); pub static AYU_HIGHLIGHT_CSS: &[u8] = include_bytes!("../../front-end/css/ayu-highlight.css");
pub static CLIPBOARD_JS: &[u8] = include_bytes!("../../front-end/js/clipboard.min.js"); pub static CLIPBOARD_JS: &[u8] = include_bytes!("../../front-end/js/clipboard.min.js");
pub static FONT_AWESOME: &[u8] = include_bytes!("../../front-end/css/font-awesome.min.css");
pub static FONT_AWESOME_EOT: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.eot");
pub static FONT_AWESOME_SVG: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.svg");
pub static FONT_AWESOME_TTF: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.ttf");
pub static FONT_AWESOME_WOFF: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.woff");
pub static FONT_AWESOME_WOFF2: &[u8] =
include_bytes!("../../front-end/fonts/fontawesome-webfont.woff2");
pub static FONT_AWESOME_OTF: &[u8] = include_bytes!("../../front-end/fonts/FontAwesome.otf");
/// The `Theme` struct should be used instead of the static variables because /// The `Theme` struct should be used instead of the static variables because
/// the `new()` method will look if the user has a theme directory in their /// the `new()` method will look if the user has a theme directory in their

View file

@ -103,7 +103,7 @@ additional-css = ["custom.css", "custom2.css"]
additional-js = ["custom.js"] additional-js = ["custom.js"]
no-section-label = false no-section-label = false
git-repository-url = "https://github.com/rust-lang/mdBook" git-repository-url = "https://github.com/rust-lang/mdBook"
git-repository-icon = "fa-github" git-repository-icon = "fab-github"
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
site-url = "/example-book/" site-url = "/example-book/"
cname = "myproject.rs" cname = "myproject.rs"
@ -139,7 +139,7 @@ The following configuration options are available:
- **git-repository-url:** A url to the git repository for the book. If provided - **git-repository-url:** A url to the git repository for the book. If provided
an icon link will be output in the menu bar of the book. an icon link will be output in the menu bar of the book.
- **git-repository-icon:** The FontAwesome icon class to use for the git - **git-repository-icon:** The FontAwesome icon class to use for the git
repository link. Defaults to `fa-github` which looks like <i class="fa fa-github"></i>. repository link. Defaults to `fab-github` which looks like <i class="fa fab-github"></i>.
If you are not using GitHub, another option to consider is `fa-code-fork` which looks like <i class="fa fa-code-fork"></i>. If you are not using GitHub, another option to consider is `fa-code-fork` which looks like <i class="fa fa-code-fork"></i>.
- **edit-url-template:** Edit url template, when provided shows a - **edit-url-template:** Edit url template, when provided shows a
"Suggest an edit" button (which looks like <i class="fa fa-edit"></i>) for directly jumping to editing the currently "Suggest an edit" button (which looks like <i class="fa fa-edit"></i>) for directly jumping to editing the currently

View file

@ -363,3 +363,18 @@ fatigue," where people are trained to ignore them because they usually don't
matter for what they're doing. matter for what they're doing.
</div> </div>
## Font-Awesome icons
mdBook includes a copy of [Font Awesome Free's](https://fontawesome.com)
MIT-licensed SVG files. It emulates the `<i>` syntax, but converts the results
to inline SVG. Only the regular, solid, and brands icons are included; paid
features like the light icons are not.
For example, given this HTML syntax:
```hbs
The result looks like this: <i class="fas fa-print"></i>
```
The result looks like this: <i class="fas fa-print"></i>

View file

@ -86,3 +86,25 @@ and accounts for files that are renamed with a hash in their filename.
```handlebars ```handlebars
<link rel="stylesheet" href="{{ resource "css/chrome.css" }}"> <link rel="stylesheet" href="{{ resource "css/chrome.css" }}">
``` ```
### fa
mdBook includes a copy of [Font Awesome Free's](https://fontawesome.com)
MIT-licensed SVG files. It accepts three positional arguments:
1. Type: one of "solid", "regular", and "brands" (light and duotone are not
currently supported)
2. Icon: anything chosen from the
[free icon set](https://fontawesome.com/icons?d=gallery&m=free)
3. ID (optional): if included, an HTML ID attribute will be added to the
icon's wrapping `<span>` tag
For example, this handlebars syntax will become this HTML:
```handlebars
{{fa "solid" "print" "print-button"}}
```
```html
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/></svg></span>
```

View file

@ -30,12 +30,12 @@ The icons displayed will depend on the settings of how the book was generated.
| Icon | Description | | Icon | Description |
|------|-------------| |------|-------------|
| <i class="fa fa-bars"></i> | Opens and closes the chapter listing sidebar. | | <i class="fas fa-bars"></i> | Opens and closes the chapter listing sidebar. |
| <i class="fa fa-paint-brush"></i> | Opens a picker to choose a different color theme. | | <i class="fas fa-paintbrush"></i> | Opens a picker to choose a different color theme. |
| <i class="fa fa-search"></i> | Opens a search bar for searching within the book. | | <i class="fas fa-magnifying-glass"></i> | Opens a search bar for searching within the book. |
| <i class="fa fa-print"></i> | Instructs the web browser to print the entire book. | | <i class="fas fa-print"></i> | Instructs the web browser to print the entire book. |
| <i class="fa fa-github"></i> | Opens a link to the website that hosts the source code of the book. | | <i class="fab fa-github"></i> | Opens a link to the website that hosts the source code of the book. |
| <i class="fa fa-edit"></i> | Opens a page to directly edit the source of the page you are currently reading. | | <i class="fas fa-pencil"></i> | Opens a page to directly edit the source of the page you are currently reading. |
Tapping the menu bar will scroll the page to the top. Tapping the menu bar will scroll the page to the top.
@ -59,9 +59,9 @@ Code blocks may contain several different icons for interacting with them:
| Icon | Description | | Icon | Description |
|------|-------------| |------|-------------|
| <i class="fa fa-copy"></i> | Copies the code block into your local clipboard, to allow pasting into another application. | | <i class="fa fa-copy"></i> | Copies the code block into your local clipboard, to allow pasting into another application. |
| <i class="fa fa-play"></i> | For Rust code examples, this will execute the sample code and display the compiler output just below the example (see [playground]). | | <i class="fas fa-play"></i> | For Rust code examples, this will execute the sample code and display the compiler output just below the example (see [playground]). |
| <i class="fa fa-eye"></i> | For Rust code examples, this will toggle visibility of "hidden" lines. Sometimes, larger examples will hide lines which are not particularly relevant to what is being illustrated (see [hiding code lines]). | | <i class="fa fa-eye"></i> | For Rust code examples, this will toggle visibility of "hidden" lines. Sometimes, larger examples will hide lines which are not particularly relevant to what is being illustrated (see [hiding code lines]). |
| <i class="fa fa-history"></i> | For [editable code examples][editor], this will undo any changes you have made. | | <i class="fas fa-clock-rotate-left"></i> | For [editable code examples][editor], this will undo any changes you have made. |
Here's an example: Here's an example: