Skip to content

Commit fcfb34c

Browse files
committed
use hyphens: manual
1 parent e513663 commit fcfb34c

File tree

7 files changed

+159
-10
lines changed

7 files changed

+159
-10
lines changed

Cargo.lock

+74
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ comrak = "0.4"
1515
fs_extra = "1.1.0"
1616
regex = "1.3"
1717
sass-rs = "0.2.1"
18-
time = "0.1.41"
18+
time = "0.1.41"
19+
kl-hyphenate = "0.7.2"

hyphenation-en-us.bincode

148 KB
Binary file not shown.

src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod blogs;
2+
mod markdown;
23
mod posts;
34

45
use crate::blogs::Blog;

src/markdown.rs

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use comrak::{
2+
nodes::{AstNode, NodeValue},
3+
Arena, ComrakOptions,
4+
};
5+
use kl_hyphenate::{Hyphenator, Language, Load, Standard};
6+
use std::error::Error;
7+
8+
const SOFT_HYPHEN: char = '\u{00AD}';
9+
const HYPHENATION_DICTIONARY: &str = "hyphenation-en-us.bincode";
10+
11+
pub(crate) fn render(input: &str) -> Result<String, Box<dyn Error>> {
12+
let options = ComrakOptions {
13+
ext_header_ids: Some(String::new()),
14+
unsafe_: true, // Allow rendering of raw HTML
15+
..ComrakOptions::default()
16+
};
17+
18+
let hyphenator = Standard::from_path(Language::EnglishUS, HYPHENATION_DICTIONARY)?;
19+
20+
let arena = Arena::new();
21+
let ast = comrak::parse_document(&arena, input, &options);
22+
23+
hyphenate(&ast, &hyphenator);
24+
25+
let mut output = Vec::new();
26+
comrak::format_html(&ast, &options, &mut output)?;
27+
Ok(String::from_utf8(output)?)
28+
}
29+
30+
// Pre-compute points inside words where browsers can add hyphens during rendering.
31+
//
32+
// Support for the CSS rule `hyphens: auto`, which tells the browser to split words by adding
33+
// hyphens when there is no space left on the line, is quite low across browsers, preventing us
34+
// from using it on the blog.
35+
//
36+
// A widely supported alternative is the `hyphens: manual` rule, which moves the burden of deciding
37+
// *where* to break the word to the website. To properly use that rule, the website has to insert
38+
// the "soft hyphen" unicode character (U+00AD) in every position the browser is allowed to break
39+
// the word.
40+
//
41+
// The following piece of code walks through the Markdown AST adding those characters in every
42+
// suitable place, thanks to the kl-hyphenate library.
43+
44+
fn hyphenate<'a>(node: &'a AstNode<'a>, hyphenator: &Standard) {
45+
match &mut node.data.borrow_mut().value {
46+
NodeValue::Text(content) => {
47+
if let Ok(string) = std::str::from_utf8(&content) {
48+
let hyphenated = add_soft_hyphens(string, hyphenator);
49+
*content = hyphenated.as_bytes().to_vec();
50+
}
51+
}
52+
_ => {}
53+
}
54+
for child in node.children() {
55+
hyphenate(child, hyphenator);
56+
}
57+
}
58+
59+
fn add_soft_hyphens(content: &str, hyphenator: &Standard) -> String {
60+
let mut output = String::with_capacity(content.len());
61+
for (i, word) in content.split(' ').enumerate() {
62+
if i != 0 {
63+
output.push(' ');
64+
}
65+
let hyphenated = hyphenator.hyphenate(word);
66+
for (j, segment) in hyphenated.into_iter().segments().enumerate() {
67+
if j != 0 {
68+
output.push(SOFT_HYPHEN);
69+
}
70+
output.push_str(&segment);
71+
}
72+
}
73+
output
74+
}

src/posts.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::blogs::Manifest;
2-
use comrak::ComrakOptions;
32
use regex::Regex;
43
use serde_derive::{Deserialize, Serialize};
54
use std::error::Error;
@@ -64,13 +63,7 @@ impl Post {
6463
layout,
6564
} = serde_yaml::from_str(yaml)?;
6665
// next, the contents. we add + to get rid of the final "---\n\n"
67-
let options = ComrakOptions {
68-
ext_header_ids: Some(String::new()),
69-
unsafe_: true, // Allow rendering of raw HTML
70-
..ComrakOptions::default()
71-
};
72-
73-
let contents = comrak::markdown_to_html(&contents[end_of_yaml + 5..], &options);
66+
let contents = crate::markdown::render(&contents[end_of_yaml + 5..])?;
7467

7568
// finally, the url.
7669
let mut url = PathBuf::from(&*filename);

src/styles/app.scss

+7-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ body {
3333
}
3434

3535
p {
36-
text-align: justify;
36+
text-align: justify;
37+
38+
/* Use manual hyphenation, as automatic hyphenation is not widely
39+
* supported (Chrome doesn't implement it on all platforms). */
40+
-webkit-hyphens: manual;
41+
-ms-hyphens: manual;
42+
hyphens: manual;
3743
}
3844

3945
code {

0 commit comments

Comments
 (0)