Skip to content

Commit

Permalink
bunch of vignettes
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasp85 committed May 1, 2024
1 parent 377a81f commit 8bf143a
Show file tree
Hide file tree
Showing 3 changed files with 567 additions and 3 deletions.
225 changes: 225 additions & 0 deletions vignettes/marquee.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
---
title: "marquee"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{marquee}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
dev.args = list(type = "cairo"),
fig.width = 6,
out.width = "98%",
fig.align = "center",
dpi = 300
)
```

```{r setup}
library(marquee)
library(grid)
library(ggplot2)
```

This document will get you up to speed on using marquee for rich text formatting in R graphics. Marquee is collectively a markdown dialect, a parser for the dialect and a grid renderer for text written in the dialect. The package aims to fill the same use cases as [gridtext](https://wilkelab.org/gridtext/) and [ggtext](https://wilkelab.org/ggtext/) by Claus Wilke, but works in a very different and more low-level way.

## An example

```{r}
md_text <-
"# Lorem Ipsum
Lorem ipsum dolor sit amet, *consectetur* adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna **aliqua**. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo _consequat_. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla ~pariatur~. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
grob <- marquee_grob(md_text, classic_style())
grid.draw(grob)
```

From this small example you can see that markdown behaves as you'd expect. We have emphasis, strong, underline, and strikethrough, and stuff like headings etc. In fact, the full markdown (CommonMark) spec is supported and will behave as you'd expect.

Besides the standard grob arguments you'd expect, `marquee_grob()` takes two main arguments as you can see above. The text, which can be a character vector with multiple separate markdown strings, and a style specification, which again can be a vector of styles or a single style to format everything alike. Marquee ships with `classic_style()` which is designed to mimic the look of standard HTML rendered markdown. The function itself allows you to modify the final style, and you could also modify it after construction to create a completely new style:

```{r}
new_style <- classic_style(body_font = "serif", header_font = "mono", hanging = em(1))
new_style <- modify_style(
new_style,
"str",
background = "lightgrey",
border_radius = 3,
padding = trbl(em(0.2))
)
grid.draw(
marquee_grob(md_text, new_style)
)
```

Besides standard markdown, marquee also allows you to create custom span classes. These span classes can be styled to anything, but marquee contains a nifty feature where, if the class matches a recognized color name or hex-code, it will be automatically coloured.

```{r}
md_text_custom <-
"# Lorem Ipsum
Lorem ipsum dolor {.red sit amet, *consectetur* adipiscing elit, sed do} eiusmod tempor
incididunt ut labore et dolore magna **aliqua**. Ut enim ad minim {#2af veniam}, quis nostrud
exercitation ul lamcolaboris nisi ut aliquip ex ea commodo _consequat_. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla ~pariatur~. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum."
grid.draw(
marquee_grob(md_text_custom, classic_style())
)
```

If you provide a styling for the custom class it takes precedence over any matching color name

```{r}
grid.draw(
marquee_grob(md_text_custom, modify_style(classic_style(), "red", tracking = 400))
)
```

## Use in ggplot2

Recognizing that most people doesn't make visualizations directly with grid, marquee also contains the infrastructure to use markdown inside ggplot2 plots. It is expected that these functions will eventually move to ggplot2 proper, but while the package is stress tested they will stay here.

### The marquee geom

One way to use marquee with ggplot2 is with `geom_marquee()`. It supersedes both `geom_text()` and `geom_label()` in its capabilities, and even if you don't need to plot rich text, the styling of `geom_marquee()` is much more capable:

```{r}
fancy_font <- classic_style(
weight = "semibold",
features = systemfonts::font_feature(
ligatures = c("standard", "discretionary"),
letters = c("stylistic", "swash", "historical")
)
)
ggplot(mtcars, aes(disp, mpg, label = rownames(mtcars))) +
geom_marquee(style = fancy_font, size = 2.5, family = "spectral") # You may not have this font
```

However, the main use will probably be to mix in italicized, bold, or colored fonts in labels, which marquee makes very easy

```{r}
cars <- sub("(\\w+)", "{.red ***\\1***}", rownames(mtcars))
cars
ggplot(mtcars) + aes(disp, mpg, label = cars) +
geom_marquee()
```

Another use of the geom, where rich text may come more into play, is in annotations. Let's make a textbox style and add some lorem ipsum to our plot

```{r}
text_box_style <- modify_style(
classic_style(base_size = 2),
"body",
padding = skip_inherit(trbl(0, 10, 10)),
border_radius = 3
)
ggplot(mtcars) + aes(disp, mpg, label = cars) +
geom_marquee(size = 2) +
annotate(GeomMarquee$geom,
label = md_text,
x = 450,
y = 35,
style = text_box_style,
size = 2,
fill = "lightgrey",
width = 0.3,
hjust = "right",
vjust = "top"
)
```

### The marquee element

Rich text in plotted text is probably limited in use, but when you need it you really need it. Different from this is formatting of the text that surrounds the plot. Here you tend to treat the text as rich text and would want full control of the styling. Enter `element_marquee()`, a drop-in substitute for `element_text()` which will format and style any non-plotting text.

```{r}
ggplot(mtcars) + aes(disp, mpg, label = cars) +
geom_marquee(size = 2) +
ggtitle(md_text) +
theme(plot.title = element_marquee(size = 9))
```

As can be seen, the defaults will let the text run for as long as it needs without doing any text wrapping. This is because it makes alignment behave in the same way as `element_text()`. However, if you have a lot of text like we do above, you certainly want to turn on that feature. You do that by setting a width. `1` is equivalent to the width of the area the element occupies so that is a natural value here, but you can set it to any `grid::unit()` value you wish.

```{r, fig.asp=0.8}
ggplot(mtcars) + aes(disp, mpg, label = cars) +
geom_marquee(size = 2) +
ggtitle(md_text) +
theme(plot.title = element_marquee(size = 9, width = 1))
```

## A bit about images

Markdown famously supports images through the `![]()` syntax. So does marquee of course (which means you can put images everywhere in ggplot2 if you wish). Currently, there is support for PNG, JPEG, and SVG, with the latter being responsive to resizing etc. The images can reside online or on your computer — it all should "just work". If an image is placed inline it is sized to fit the height of that line and adjusting the width to keep the correct aspect ratio. If an image is placed by itself, it expands to fill the provided width, width the height being calculated to match the aspect ratio of the image.

```{r}
md_text_fig <-
"# Lorem Ipsum ![](https://cran.r-project.org/Rlogo.svg)
Lorem ipsum dolor {.red sit amet, *consectetur* adipiscing elit, sed do} eiusmod tempor
incididunt ut labore et dolore magna **aliqua**. Ut enim ad minim {#2af veniam}, quis nostrud
exercitation ul lamcolaboris nisi ut aliquip ex ea commodo _consequat_. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla ~pariatur~. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum."
grid.draw(marquee_grob(md_text_fig, classic_style()))
```

Compare that to

```{r, fig.asp=1.3}
md_text_fig2 <-
"# Lorem Ipsum
![](https://cran.r-project.org/Rlogo.svg)
Lorem ipsum dolor {.red sit amet, *consectetur* adipiscing elit, sed do} eiusmod tempor
incididunt ut labore et dolore magna **aliqua**. Ut enim ad minim {#2af veniam}, quis nostrud
exercitation ul lamcolaboris nisi ut aliquip ex ea commodo _consequat_. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla ~pariatur~. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum."
grid.draw(marquee_grob(md_text_fig2, classic_style()))
```

However, marquee has a pretty unique trick up it's sleeve compared to other markdown formats. It doesn't just accept paths to images — it also accepts R graphic objects (currently grid grobs, ggplot2 plots and patchwork plots). It all works as you'd expect:

```{r, fig.asp=1.1}
p <- ggplot(mtcars) +
geom_histogram(aes(x = gear))
md_text_plot <-
"# Lorem Ipsum
![](p)
Lorem ipsum dolor {.red sit amet, *consectetur* adipiscing elit, sed do} eiusmod tempor
incididunt ut labore et dolore magna **aliqua**. Ut enim ad minim {#2af veniam}, quis nostrud
exercitation ul lamcolaboris nisi ut aliquip ex ea commodo _consequat_. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla ~pariatur~. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum."
grid.draw(marquee_grob(md_text_plot, classic_style()))
```

## Wrapping up

This should give you enough of an overview to get started. The Marquee Style and Marquee Syntax vignettes provides a bit more detail on their respective areas if you need to dive deeper.
37 changes: 34 additions & 3 deletions vignettes/marquee_style.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ vignette: >
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
comment = "#>",
dev.args = list(type = "cairo")
)
```

Expand All @@ -20,10 +21,40 @@ library(marquee)

As we discussed in the Marquee Syntax article, most of the syntax in markdown is semantic, meaining that it concerns itself with the nature of what it encodes, rather than the look of it. The other side of the coin, how it looks, will be dealt with in this article. Note that this division between semantics and style is also seen in HTML5 where the looks is fully relegated to CSS and the tags are all semantic.

## A slightly less cascading style
## A slightly less cascading style specification

HTML has CSS to take care of styling for it, and that has over the years morphed into a sprawling and powerful language of it's own. Both too complicated to support for something like marquee, and too complicated to be needed for styling markdown (after all it's main goal is to style full webpages, not documents). Marquee instead defines it's own limited styling system which is simple enough to reason about, and powerful enough to support what is needed for markdown text. Many concepts are borrowed from CSS so if you are used to that you should feel pretty much at home.

### A style
### Styles

In marquee, a style defines the full specification for how an element (block or span) should visually appear. It holds `r length(formals(style))` different settings concerning everything from font rendering, to spacing, coloring, and alignment. Rather than going through all here, I'll point towards the documentation for the `style()` function.

While span and block elements takes the same style specifications, some settings only apply to block elements and are ignored for spans. These are `lineheight`, `align`, `indent`, `hanging`, `margin`, `bullets`, and `img_asp`.

### Style sets

A style set is a collection of styles, each style related to a specific type of element. It does not need to provide a style for all elements in the text, and the elements doesn't need to be complete. The only hard requirement is that it contains a complete style named `base`, from which everything else can be derived. marquee ships with a style set called `classic_style()` which aims to mimic the standard look of rendered markdown. New style sets can either be build from the ground up or based off of `classic_style()`.

### Inheritance

Marquee uses a very simple inheritance model for it's styling, that can be boiled down to a single short sentance:

> An element inherits any unspecified from it's parent element.
Any setting in a style object set to `NULL` will be inherited from the parent all the way down to the base style (which is the reason why that style must be complete). As an example, let's look at the style for the `em` element in the `classic_style()`:

```{r}
classic_style()[[1]]$em
```

The only thing it touches is the `italic` setting, everything else is borrowed from whatever element it is placed into.

There are two other ways to influence style inheritance. The first one is wrapping a value in `skip_inherit()`. This will instruct marquee to inherit the value one level up in the hierarchy rather than from the direct parent (if the parent of the parent is also set to `skip_inherit()` it moves up yet another level, and so on). The second one is wrapping a numeric value in `relative()` which will instruct marquee to use the relative value as a multiplier applied to the parent.

### Sizing

All sizes in a style is in (big)points (the distinction between bigpoints and points are rather small and boring. Bigpoints are 1/72 inches, which is what we use). Instead of setting a style setting directly to a numeric value, you can make it relative to the font size of the style using `em()`. This is beneficial for e.g. padding, margin, indent, etc. where you often want it to scale with the font size. In the case you set the size to an em value, it will be relative to it's parent and effectively be equivalent to using `relative()`. There is a variant of em values called `rem()` (root em). Instead of making the setting relative to the size of the current element it makes it relative to the size in the base element of the document.

### Side values

A style has three settings that relates to the four sides of a box. `margin`, `padding`, and `border_size`. These are set to values constructed with `trbl()`, which works like margin settings in CSS. If you provide it with 1 value it will set all sides to that value. If you provide it with two values and it will set top and bottom to the first and left and right to the second. If you provide it with three values it will set the top to the first, left and right to the second, and bottom to the third. If you provide it with four values it will set the **t**op to the first, **r**ight to the second, **b**ottom to the third, and **l**eft to the fourth (the bold first letters gives a clue to why the constructor is named as it is).
Loading

0 comments on commit 8bf143a

Please sign in to comment.