Skip to content

Commit 4cb13c6

Browse files
committed
Use embedded SVG instead of fonts for icons
The [downsides of icon fonts] are well-documented, and also, why ship all of the icons when it only uses 14? [downsides of icon fonts]: https://speakerdeck.com/ninjanails/death-to-icon-fonts
1 parent 94b922d commit 4cb13c6

21 files changed

+215
-2766
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ clap = { version = "4.3.12", features = ["cargo", "wrap_help"] }
2323
clap_complete = "4.3.2"
2424
once_cell = "1.17.1"
2525
env_logger = "0.11.1"
26+
font-awesome-as-a-crate = "0.3.0"
2627
handlebars = "5.0"
2728
log = "0.4.17"
2829
memchr = "2.5.0"

guide/src/format/configuration/renderers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ additional-css = ["custom.css", "custom2.css"]
104104
additional-js = ["custom.js"]
105105
no-section-label = false
106106
git-repository-url = "https://github.com/rust-lang/mdBook"
107-
git-repository-icon = "fa-github"
107+
git-repository-icon = "fab-github"
108108
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
109109
site-url = "/example-book/"
110110
cname = "myproject.rs"
@@ -147,7 +147,7 @@ The following configuration options are available:
147147
- **git-repository-url:** A url to the git repository for the book. If provided
148148
an icon link will be output in the menu bar of the book.
149149
- **git-repository-icon:** The FontAwesome icon class to use for the git
150-
repository link. Defaults to `fa-github` which looks like <i class="fa fa-github"></i>.
150+
repository link. Defaults to `fab-github` which looks like <i class="fa fab-github"></i>.
151151
If you are not using GitHub, another option to consider is `fa-code-fork` which looks like <i class="fa fa-code-fork"></i>.
152152
- **edit-url-template:** Edit url template, when provided shows a
153153
"Suggest an edit" button (which looks like <i class="fa fa-edit"></i>) for directly jumping to editing the currently

guide/src/format/mdbook.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,3 +362,18 @@ fatigue," where people are trained to ignore them because they usually don't
362362
matter for what they're doing.
363363

364364
</div>
365+
366+
## Font-Awesome icons
367+
368+
mdBook includes a copy of [Font Awesome Free's](https://fontawesome.com)
369+
MIT-licensed SVG files. It emulates the `<i>` syntax, but converts the results
370+
to inline SVG. Only the regular, solid, and brands icons are included; paid
371+
features like the light icons are not.
372+
373+
For example, given this HTML syntax:
374+
375+
```hbs
376+
The result looks like this: <i class="fas fa-print"></i>
377+
```
378+
379+
The result looks like this: <i class="fas fa-print"></i>

guide/src/format/theme/index-hbs.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ They are used like this
8787
```handlebars
8888
{{#previous}}
8989
<a href="{{link}}" class="nav-chapters previous">
90-
<i class="fa fa-angle-left"></i> {{title}}
90+
{{fa "solid" "angle-left"}} {{title}}
9191
</a>
9292
{{/previous}}
9393
```
@@ -99,3 +99,25 @@ Of course the inner html can be changed to your liking.
9999

100100
*If you would like other properties or helpers exposed, please [create a new
101101
issue](https://github.com/rust-lang/mdBook/issues)*
102+
103+
### 3. fa
104+
105+
mdBook includes a copy of [Font Awesome Free's](https://fontawesome.com)
106+
MIT-licensed SVG files. It accepts three positional arguments:
107+
108+
1. Type: one of "solid", "regular", and "brands" (light and duotone are not
109+
currently supported)
110+
2. Icon: anything chosen from the
111+
[free icon set](https://fontawesome.com/icons?d=gallery&m=free)
112+
3. ID (optional): if included, an HTML ID attribute will be added to the
113+
icon's wrapping `<span>` tag
114+
115+
For example, this handlebars syntax will become this HTML:
116+
117+
```handlebars
118+
{{fa "solid" "print" "print-button"}}
119+
```
120+
121+
```html
122+
<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>
123+
```

guide/src/guide/reading.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ The icons displayed will depend on the settings of how the book was generated.
3030

3131
| Icon | Description |
3232
|------|-------------|
33-
| <i class="fa fa-bars"></i> | Opens and closes the chapter listing sidebar. |
34-
| <i class="fa fa-paint-brush"></i> | Opens a picker to choose a different color theme. |
35-
| <i class="fa fa-search"></i> | Opens a search bar for searching within the book. |
36-
| <i class="fa fa-print"></i> | Instructs the web browser to print the entire book. |
37-
| <i class="fa fa-github"></i> | Opens a link to the website that hosts the source code of the book. |
38-
| <i class="fa fa-edit"></i> | Opens a page to directly edit the source of the page you are currently reading. |
33+
| <i class="fas fa-bars"></i> | Opens and closes the chapter listing sidebar. |
34+
| <i class="fas fa-paintbrush"></i> | Opens a picker to choose a different color theme. |
35+
| <i class="fas fa-magnifying-glass"></i> | Opens a search bar for searching within the book. |
36+
| <i class="fas fa-print"></i> | Instructs the web browser to print the entire book. |
37+
| <i class="fab fa-github"></i> | Opens a link to the website that hosts the source code of the book. |
38+
| <i class="fas fa-pencil"></i> | Opens a page to directly edit the source of the page you are currently reading. |
3939

4040
Tapping the menu bar will scroll the page to the top.
4141

@@ -59,9 +59,9 @@ Code blocks may contain several different icons for interacting with them:
5959
| Icon | Description |
6060
|------|-------------|
6161
| <i class="fa fa-copy"></i> | Copies the code block into your local clipboard, to allow pasting into another application. |
62-
| <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]). |
62+
| <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]). |
6363
| <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]). |
64-
| <i class="fa fa-history"></i> | For [editable code examples][editor], this will undo any changes you have made. |
64+
| <i class="fas fa-clock-rotate-left"></i> | For [editable code examples][editor], this will undo any changes you have made. |
6565

6666
Here's an example:
6767

src/renderer/html_handlebars/hbs_renderer.rs

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ impl HtmlHandlebars {
218218
let rendered = fix_code_blocks(&rendered);
219219
let rendered = add_playground_pre(&rendered, playground_config, edition);
220220
let rendered = hide_lines(&rendered, code_config);
221+
let rendered = convert_fontawesome(&rendered);
221222

222223
rendered
223224
}
@@ -258,41 +259,6 @@ impl HtmlHandlebars {
258259
write_file(destination, "ayu-highlight.css", &theme.ayu_highlight_css)?;
259260
write_file(destination, "highlight.js", &theme.highlight_js)?;
260261
write_file(destination, "clipboard.min.js", &theme.clipboard_js)?;
261-
write_file(
262-
destination,
263-
"FontAwesome/css/font-awesome.css",
264-
theme::FONT_AWESOME,
265-
)?;
266-
write_file(
267-
destination,
268-
"FontAwesome/fonts/fontawesome-webfont.eot",
269-
theme::FONT_AWESOME_EOT,
270-
)?;
271-
write_file(
272-
destination,
273-
"FontAwesome/fonts/fontawesome-webfont.svg",
274-
theme::FONT_AWESOME_SVG,
275-
)?;
276-
write_file(
277-
destination,
278-
"FontAwesome/fonts/fontawesome-webfont.ttf",
279-
theme::FONT_AWESOME_TTF,
280-
)?;
281-
write_file(
282-
destination,
283-
"FontAwesome/fonts/fontawesome-webfont.woff",
284-
theme::FONT_AWESOME_WOFF,
285-
)?;
286-
write_file(
287-
destination,
288-
"FontAwesome/fonts/fontawesome-webfont.woff2",
289-
theme::FONT_AWESOME_WOFF2,
290-
)?;
291-
write_file(
292-
destination,
293-
"FontAwesome/fonts/FontAwesome.ttf",
294-
theme::FONT_AWESOME_TTF,
295-
)?;
296262
// Don't copy the stock fonts if the user has specified their own fonts to use.
297263
if html_config.copy_fonts && theme.fonts_css.is_none() {
298264
write_file(destination, "fonts/fonts.css", theme::fonts::CSS)?;
@@ -379,6 +345,7 @@ impl HtmlHandlebars {
379345
handlebars.register_helper("next", Box::new(helpers::navigation::next));
380346
// TODO: remove theme_option in 0.5, it is not needed.
381347
handlebars.register_helper("theme_option", Box::new(helpers::theme::theme_option));
348+
handlebars.register_helper("fa", Box::new(helpers::fontawesome::fa_helper));
382349
}
383350

384351
/// Copy across any additional CSS and JavaScript files which the book
@@ -737,9 +704,19 @@ fn make_data(
737704

738705
let git_repository_icon = match html_config.git_repository_icon {
739706
Some(ref git_repository_icon) => git_repository_icon,
740-
None => "fa-github",
707+
None => "fab-github",
708+
};
709+
let git_repository_icon_class = match git_repository_icon.split('-').next() {
710+
Some("fa") => "regular",
711+
Some("fas") => "solid",
712+
Some("fab") => "brands",
713+
_ => "regular",
741714
};
742715
data.insert("git_repository_icon".to_owned(), json!(git_repository_icon));
716+
data.insert(
717+
"git_repository_icon_class".to_owned(),
718+
json!(git_repository_icon_class),
719+
);
743720

744721
let mut chapters = vec![];
745722

@@ -842,6 +819,54 @@ fn insert_link_into_header(
842819
)
843820
}
844821

822+
// Convert fontawesome `<i>` tags to inline SVG
823+
fn convert_fontawesome(html: &str) -> String {
824+
use font_awesome_as_a_crate as fa;
825+
826+
let regex = Regex::new(r##"<i([^>]+)class="([^"]+)"([^>]*)></i>"##).unwrap();
827+
regex
828+
.replace_all(html, |caps: &Captures<'_>| {
829+
let text = &caps[0];
830+
let before = &caps[1];
831+
let classes = &caps[2];
832+
let after = &caps[3];
833+
834+
let mut icon = String::new();
835+
let mut type_ = fa::Type::Regular;
836+
let mut other_classes = String::new();
837+
838+
for class in classes.split(" ") {
839+
if let Some(class) = class.strip_prefix("fa-") {
840+
icon = class.to_owned();
841+
} else if class == "fa" {
842+
type_ = fa::Type::Regular;
843+
} else if class == "fas" {
844+
type_ = fa::Type::Solid;
845+
} else if class == "fab" {
846+
type_ = fa::Type::Brands;
847+
} else {
848+
other_classes += " ";
849+
other_classes += class;
850+
}
851+
}
852+
853+
if icon.is_empty() {
854+
text.to_owned()
855+
} else if let Ok(svg) = fa::svg(type_, &icon) {
856+
format!(
857+
r#"<span{before}class="fa-svg{other_classes}"{after}>{svg}</span>"#,
858+
before = before,
859+
other_classes = other_classes,
860+
after = after,
861+
svg = svg
862+
)
863+
} else {
864+
text.to_owned()
865+
}
866+
})
867+
.into_owned()
868+
}
869+
845870
// The rust book uses annotations for rustdoc to test code snippets,
846871
// like the following:
847872
// ```rust,should_panic
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use font_awesome_as_a_crate as fa;
2+
use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError, RenderErrorReason};
3+
use log::trace;
4+
use std::str::FromStr;
5+
6+
pub fn fa_helper(
7+
h: &Helper<'_>,
8+
_r: &Handlebars<'_>,
9+
_ctx: &Context,
10+
_rc: &mut RenderContext<'_, '_>,
11+
out: &mut dyn Output,
12+
) -> Result<(), RenderError> {
13+
trace!("fa_helper (handlebars helper)");
14+
15+
let type_ = h
16+
.param(0)
17+
.and_then(|v| v.value().as_str())
18+
.and_then(|v| fa::Type::from_str(v).ok())
19+
.ok_or_else(|| {
20+
RenderErrorReason::Other("Param 0 with String type is required for fontawesome helper.".to_owned())
21+
})?;
22+
23+
let name = h.param(1).and_then(|v| v.value().as_str()).ok_or_else(|| {
24+
RenderErrorReason::Other("Param 1 with String type is required for fontawesome helper.".to_owned())
25+
})?;
26+
27+
trace!("fa_helper: {} {}", type_, name);
28+
29+
let name = name.strip_prefix("fa-")
30+
.or_else(|| name.strip_prefix("fab-"))
31+
.or_else(|| name.strip_prefix("fas-"))
32+
.unwrap_or(name);
33+
34+
if let Some(id) = h.param(2).and_then(|v| v.value().as_str()) {
35+
out.write(&format!("<span class=fa-svg id=\"{}\">", id))?;
36+
} else {
37+
out.write("<span class=fa-svg>")?;
38+
}
39+
out.write(
40+
fa::svg(type_, name).map_err(|_| RenderErrorReason::Other(format!("Missing font {}", name)))?,
41+
)?;
42+
out.write("</span>")?;
43+
44+
Ok(())
45+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod fontawesome;
12
pub mod navigation;
23
pub mod theme;
34
pub mod toc;

src/theme/FontAwesome/css/font-awesome.min.css

Lines changed: 0 additions & 4 deletions
This file was deleted.
-132 KB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)