Skip to content
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

New feature: hyphenation support using Hyphenopoly.js #34

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Authors@R: c(
person("Adam", "Hyde", role = "ctb", comment = "paged.js in resources/js/"),
person("Min-Zhong", "Lu", role = "ctb", comment = "resume.css in resources/css/"),
person("Zulko", role = "ctb", comment = "poster-relaxed.css in resources/css/"),
person("Mathias", "Nater", role = "ctb", comment = "Hyphenopoly.js library in resources/js"),
person()
)
Description: Use the paged media properties in CSS and the JavaScript polyfill
Expand Down
42 changes: 31 additions & 11 deletions R/paged.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,26 @@
#' built-in CSS files, run \code{pagedown:::list_css()}.
#' @param theme The Bootstrap theme. By default, Bootstrap is not used.
#' @param template The path to the Pandoc template to convert Markdown to HTML.
#' @param self_contained Produce a standalone HTML file with no external
#' dependencies. By default, \code{html_paged} document are not self contained.
#' In this case, you need a web server to preview your document for instance,
#' \code{xaringan::inf_mr()}.
#' @param hyphens Use the \code{Hyphenopoly.js} JavaScript library for hyphenations.
#' Note that this option is incompatible with \code{self_contained = TRUE}.
#' @references \url{https://pagedown.rbind.io}
#' @return An R Markdown output format.
#' @import stats utils
#' @export
html_paged = function(
..., css = c('default-fonts', 'default-page', 'default'), theme = NULL,
template = pkg_resource('html', 'paged.html')
template = pkg_resource('html', 'paged.html'), self_contained = FALSE,
hyphens = FALSE
) {
if (isTRUE(hyphens) & isTRUE(self_contained))
stop('The hyphens option is incompatible with self_contained = TRUE. ')
html_format(
..., css = css, theme = theme, template = template, .pagedjs = TRUE,
..., css = css, theme = theme, template = template,
self_contained = self_contained, hyphens = hyphens, .pagedjs = TRUE,
.pandoc_args = lua_filters('uri-to-fn.lua', 'loft.lua', 'footnotes.lua') # uri-to-fn.lua must come before footnotes.lua
)
}
Expand All @@ -40,16 +50,25 @@ html_letter = function(..., css = c('default', 'letter')) {
html_paged(..., css = css, fig_caption = FALSE)
}

pagedown_dependency = function(css = NULL, js = FALSE) {
list(htmltools::htmlDependency(
'paged', packageVersion('pagedown'), src = pkg_resource(),
script = if (js) c('js/config.js', 'js/paged.js', 'js/hooks.js'),
stylesheet = file.path('css', css), all_files = FALSE
))
pagedown_dependency = function(css = NULL, js = FALSE, hyphens = FALSE) {
c(if (isTRUE(hyphens)) list(
htmltools::htmlDependency(
'Hyphenopoly', '2.6.0', src = pkg_resource('js/Hyphenopoly'),
script = if (js) c('configHyphenopoly.js', 'Hyphenopoly_Loader.js'), all_files = TRUE
)
),
list(
htmltools::htmlDependency(
'paged', packageVersion('pagedown'), src = pkg_resource(),
script = if (js) c('js/config.js', 'js/paged.js', 'js/hooks.js'),
stylesheet = file.path('css', css), all_files = FALSE
)
)
)
}

html_format = function(
..., css, template, pandoc_args = NULL, .dependencies = NULL,
..., css, template, pandoc_args = NULL, hyphens = FALSE, .dependencies = NULL,
.pagedjs = FALSE, .pandoc_args = NULL
) {
css2 = grep('[.]css$', css, value = TRUE, invert = TRUE)
Expand All @@ -58,10 +77,11 @@ html_format = function(
html_document2 = function(..., extra_dependencies = list()) {
bookdown::html_document2(..., extra_dependencies = c(
extra_dependencies, .dependencies,
pagedown_dependency(xfun::with_ext(css2, '.css'), .pagedjs)
pagedown_dependency(xfun::with_ext(css2, '.css'), .pagedjs, hyphens = hyphens)
))
}
html_document2(
..., css = css, template = template, pandoc_args = c(.pandoc_args, pandoc_args)
..., css = css, template = template,
pandoc_args = c(.pandoc_args, pandoc_args, if (isTRUE(hyphens)) c('-V', 'hyphenopoly'))
)
}
39 changes: 39 additions & 0 deletions inst/examples/index.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,45 @@ links-to-footnotes: true

The default behavior of pagedown is to render notes as endnotes because Paged.js does not natively support footnotes for now. However, we introduced an experimental support for footnotes. You can test it including `paged-footnotes: true` in the YAML header of your document. If you get any trouble with this experimental feature, please open an issue on GitHub.

## Hyphenation

Since Chromium/Chrome is the only browser offering a minimal support for CSS Paged Media, you need it to preview `html_paged` documents. However, Chrome offers a very limited support for hyphenation^[see <https://developer.mozilla.org/docs/Web/CSS/hyphens>.]: it is only available on Mac for English and Latin languages.

In order to offer a wider support for hyphenation, **pagedown** uses the [Hyphenopoly.js library](https://github.com/mnater/Hyphenopoly). The `hyphens` option activates Hyphenopoly.js:

```yaml
---
title: "A paged HTML document"
output:
pagedown::html_paged:
hyphens: true
self_contained: false
---
```

Because of technical restrictions, the `hyphens: true` option is not compatible with `self_contained: true` option.

If your document is not written in American English, you are recommended to declare the main language of the document with the `lang` variable:

```yaml
---
title: "Un document HTML paginé"
output:
pagedown::html_paged:
hyphens: true
self_contained: false
lang: fr
---
```

For now, **pagedown** offers hyphenation for the following languages: American English (`en-us`), British English (`en-gb`), German (`de`), Spanish (`es`), French (`fr`) and Portuguese (`pt`). More languages will be added in the future.

You can use different languages in the same document. For instance, a Portuguese sentence can be declared using a bracketed span:

```markdown
[De boas intenções está o Inferno cheio]{lang="pt"}
```

## Tests {data-short-title="Some tests"}

### A list {data-short-title="An ordered list"}
Expand Down
88 changes: 84 additions & 4 deletions inst/resources/html/paged.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,94 @@

<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title>

$if(hyphenopoly)$
<!-- Configure Hyphenopoly.js and ensure that Hyphenopoly.js runs before Paged.js -->
<script>
(function() {
var hyphenate = new Promise(resolve => {
// Hyphenopoly configuration global object (required):
window.Hyphenopoly = {
require: {
// see the inst/resources/js/Hyphenopoly/patterns for supported languages
// for each language, a long word (more than 12 characters) has to be provided in the language
"de": "Silbentrennungsalgorithmus",
"en-gb": "Antidisestablishmentarianism",
"en-us": "Supercalifragilisticexpialidocious",
"es": "Superextraordinarisimo",
"fr": "Anticonstitutionnellement",
"pt": "Anticonstitucionalissimamente"
},
setup: {
// Hyphenopoly will detect the lang attribute in the html tag
// If there is no lang defined, we can define a fallback language
defaultLanguage: "en-us",
dontHyphenate: {
video: true,
audio: true,
script: true,
code: true,
pre: true,
img: true,
br: true,
samp: true,
kbd: true,
var: true,
abbr: true,
acronym: true,
sub: true,
sup: true,
button: true,
option: true,
label: true,
textarea: true,
input: true,
math: true,
svg: true,
style: true,
h1: true,
h2: true,
h3: true,
h4: true,
h5: true,
h6: true
},
// Do not hyphenate MathJax elements
dontHyphenateClass: "math"
},
handleEvent: {
// Do not show Hyphenopoly errors in the Chrome console
error: function(e) {
e.preventDefault();
},
hyphenopolyEnd: resolve
}
};
});

// Hyphenopoly must run first
window.PagedConfig = {
before: () => {
return hyphenate;
}
}
})();
</script>
$endif$

$if(math)$
<!-- dynamically load mathjax for compatibility with self-contained -->
<script>
(function() {
// Retrieve PagedConfig global object (only relevant when Hyphenopoly.js is used)
var previousPagedConfig = window.PagedConfig || {before: () => {return Promise.resolve();}};

window.PagedConfig = {
before: () => {
return new Promise((resolve, reject) => {
before: async () => {
await previousPagedConfig.before();
await new Promise((resolve, reject) => {
var script = document.createElement("script");
script.type = "text/javascript";
var src = "true";
var src = "$if(mathjax)$$mathjax$$endif$";
if (src === "" || src === "true") src = "https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-MML-AM_CHTML";
if (location.protocol !== "file:" && /^https?:/.test(src))
src = src.replace(/^https?:/, '');
Expand All @@ -49,6 +128,7 @@
});
}
};
})();
</script>
$endif$

Expand Down Expand Up @@ -121,7 +201,7 @@
$endif$
</head>

<body>
<body$if(hyphenopoly)$ class="hyphenate"$endif$>

<div class="running-h1-title"></div>

Expand Down
Loading