---
title: "PowerPoint presentations generation"
output: 
  rmarkdown::html_vignette:
    toc: true 
    toc_depth: 2
vignette: >
  %\VignetteIndexEntry{PowerPoint}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, message = FALSE)
```

```{r, echo = FALSE, message=FALSE, warning=FALSE}
dir.create("assets/pptx", recursive = TRUE, showWarnings = FALSE)
office_doc_link <- function(url){
  stopifnot(requireNamespace("htmltools", quietly = TRUE))
  htmltools::tags$p(  htmltools::tags$span("Download file "),
    htmltools::tags$a(basename(url), href = url), 
    htmltools::tags$span(" - view with"),
    htmltools::tags$a("office web viewer", target="_blank", 
      href = paste0("https://view.officeapps.live.com/op/view.aspx?src=", url)
      ), 
    style="text-align:center;font-style:italic;color:gray;"
    )
}
```

```{r}
library(officer)
# Package `magrittr` makes officer usage easier.
library(magrittr)
```

<div style="text-align:center;">
<img src="data:image/png;base64, " width="128" height="128"/>
</div>


## How to start


Use the function `read_pptx()` to create an R object representing a PowerPoint 
document. The initial PowerPoint file can be specified with the `path` argument. 
If none is provided, this file will be an empty document located in the package 
directory. Formats and available slide layouts will be those available in the 
template file. The content of original document is also preserved (but can be 
manipulated, i.e. delete a slide).
 
```{r}
my_pres <- read_pptx() 
```

The main functions are presented below: 

![officer pptx functions](https://www.ardata.fr/img/illustrations/officer_pptx_functions.svg)

> Note that `ph_with` is new and should replace `ph_with_*` and `ph_with_*_at`.


### Add a slide

To add a new slide, use the function `add_slide()`. It requires 3 arguments: 

- an rpptx object
- a slide layout name
- a master layout name

```{r}
my_pres <- my_pres %>% 
  add_slide(layout = "Title and Content", master = "Office Theme")
```

**Note that the `layout` and `master` values must match values from the initial document.** 
Layout names and master layout names are not easily *readable* within
PowerPoint, but these can be read easily with the function `layout_summary()`.

```{r}
layout_summary(my_pres)
```

#### master layouts and slide layouts

`officer` uses a PowerPoint file as the initial document. This is the original
PowerPoint document where all slide layouts, shapes (placeholders) and
styles come from. Major points to be aware of are:

* Slide layouts are relative to a master layout. A document can contain one or 
more master layouts; a master layout can contain one or more slide layouts. 
* A slide layout inherits design properties from its master layout but some 
properties can be overwritten.
* Designs and formatting properties of layouts and shapes (placeholders in a 
layout) are defined within the initial document. There is no R function to 
modify these values - they must be defined in the initial document.


### Add content in a slide

Use the function `ph_with()` to add content into a new shape. The location of the 
shape is defined with argument `location` with a call to one of the `ph_location*`functions. 


```{r}
my_pres <- my_pres %>% 
  ph_with(value = "Hello world", location = ph_location_type(type = "title")) %>%
  ph_with(value = "A footer", location = ph_location_type(type = "ftr")) %>%
  ph_with(value = format(Sys.Date()), location = ph_location_type(type = "dt")) %>%
  ph_with(value = "slide 1", location = ph_location_type(type = "sldNum")) %>%
  ph_with(value = mtcars, location = ph_location_type(type = "body")) 
```

There are several `ph_location*`functions: `ph_location_type()`, `ph_location_fullsize()`, `ph_location_label()`, `ph_location_left()`, `ph_location_right()` and `ph_location()`.


### Write the PowerPoint file

The (updated) Powerpoint file can be generated using the `print()` function 
along with the `target` argument:

```{r}
print(my_pres, target = "assets/pptx/first_example.pptx") 
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/first_example.pptx" ) )
```

## Slide selection and manipulation

There are functions to let you manipulate slides: `add_slide()`, 
`remove_slide()`, `move_slide()`  and `on_slide()`.

A slide can be added with the `add_slide()` function.

```{r}
my_pres <- read_pptx() %>% 
  add_slide(layout = "Two Content", master = "Office Theme") %>% 
  add_slide(layout = "Title and Content", master = "Office Theme") %>% 
  add_slide(layout = "Title Only", master = "Office Theme")
length(my_pres)
```

A slide can be removed with the `remove_slide()` function.

```{r}
my_pres <- my_pres %>% remove_slide(index = 1)
length(my_pres)
```

A slide can be selected with the `on_slide()` function.

```{r}
my_pres <- my_pres %>% on_slide(index = 1)
```


## Add content in a placeholder

### the location

Use the function `ph_with()` to add an element into a new shape. The function is expecting 
an argument `location` that will be used to specify the location of the new shape. This 
location can be defined with a call to one of the `ph_location*`functions. 

The following example will add text "Hello world" in a new shape. That shape will 
inherit its properties from the placeholder that has type `body`. This is defined 
in the slide layout used by the slide, i.e. `Title and Content` in our example.

```{r}
doc <- read_pptx() %>%
  add_slide(layout = "Title and Content", master = "Office Theme") %>%
  ph_with("Hello world", location = ph_location_type(type = "body") ) 

print(doc, target = "assets/pptx/ph_with_location_type.pptx") 
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_with_location_type.pptx" ) )
```

The following example will print few rows of `iris` data.frame in a new shape. That shape will 
inherit its properties from the placeholder that has label `Content Placeholder 2`. This is defined 
in the slide layout used by the slide, i.e. `Title and Content` in our example. This is the 
same location that is used in the previous example.

```{r}
mypres <- read_pptx() %>%
  add_slide(layout = "Title and Content", master = "Office Theme") 
layout_properties ( x = mypres, layout = "Title and Content", master = "Office Theme" )
ph_with(mypres, head(iris), 
        location = ph_location_label(ph_label = "Content Placeholder 2") )

print(mypres, target = "assets/pptx/ph_with_location_label.pptx") 
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_with_location_label.pptx" ) )
```

The following example will print two paragraphs of text in two new shapes, one representing the 
left side and one representing the right side.


```{r}
doc <- read_pptx() %>%
  add_slide(layout = "Two Content", master = "Office Theme") %>%
  ph_with(value = "A first text", location = ph_location_left()) %>%
  ph_with(value = "A second text", location = ph_location_right()) 

print(doc, target = "assets/pptx/ph_with_location_left_right.pptx") 
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_with_location_left_right.pptx" ) )
```


### Supported contents

The following types of content are supported: 

* `data.frame`
* `ggplot` objects
* images, see wrapper function named `external_img()`
* vectors (`character`, `numeric`, `factor`, `logical`)
* multiple paragraphs, see wrapper function named `block_list()`
* single paragraph, see wrapper function named `fpar()` (a `block_list` is 
 made of `fpar` objects)
* unordered lists, see wrapper function named `unordered_list()`

```{r}
doc <- read_pptx()

# add a "Two Content" slide and then content ----
doc <- add_slide(doc, layout = "Two Content", master = "Office Theme")
doc <- ph_with(x = doc, value = pi, location = ph_location_type(type = "title") )
doc <- ph_with(x = doc, value = as.factor(letters[1:2]), location = ph_location_type(type = "ftr") )
doc <- ph_with(x = doc, value = c("Un titre", "Deux titre"), location = ph_location_left() )
doc <- ph_with(x = doc, value = iris[1:4, 3:5], location = ph_location_right() )

# add a "Title and Content" slide and then a ggplot object ----
if( require("ggplot2") ){
  doc <- add_slide(doc, layout = "Title and Content",
                   master = "Office Theme")
  gg_plot <- ggplot(data = iris ) +
    geom_point(mapping = aes(Sepal.Length, Petal.Length),
               size = 3) + theme_minimal()
  doc <- ph_with(x = doc, value = gg_plot, location = ph_location_fullsize() )
}

# add an image ----
doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme")
img.file <- file.path( R.home("doc"), "html", "logo.jpg" )
doc <- ph_with(x = doc, external_img(img.file, 100/72, 76/72), 
  location = ph_location_type(type = "body") )

# add an unordered_list at a specific location ----
ul <- unordered_list(
  level_list = c(1, 2, 2, 3, 3, 1),
  str_list = c("Level1", "Level2", "Level2", "Level3", "Level3", "Level1"),
  style = fp_text(color = "red", font.size = 0) )

doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme")
doc <- ph_with(x = doc, value = ul, 
               location = ph_location_type(type = "body") )

# add an block_list at a specific location ----
bl <- block_list(
  fpar(ftext("hello world", fp_text(bold = TRUE))),
  fpar(
    ftext("hello", fp_text(bold = TRUE, font.size = 30)),
    ftext(" world", prop = fp_text(color = "red", font.size = 30) )
  )
)

doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme")
doc <- ph_with(x = doc, value = bl, 
               location = ph_location(label = "my_name",
                 left = 2, top = 3, width = 4, height = 4, 
                 bg = "wheat", rotation = 90) 
       )

print(doc, target = "assets/pptx/ph_with_demo.pptx")
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_with_demo.pptx" ) )
```


## Remove content from a slide

Use `slide_summary()` to easily identify shapes in the slide that can be removed.

```{r}
slide_summary(doc)
```

In the following example, the shape corresponding to `type "body"` will be 
removed from the current slide:

```{r}
doc <- ph_remove(x = doc, ph_label = "my_name")
```

## Append text sequentially in a shape

### Add to an empty new placeholder

`ph_empty()` will add a new empty placeholder in the current 
slide. 

```{r}
my_pres <- read_pptx() %>% 
  add_slide(layout = "Title and Content", master = "Office Theme") %>% 
  ph_empty(location = ph_location_right())
slide_summary(my_pres)
```

As there is no paragraph in the new shape yet, the function `ph_add_par()` will 
be used to add a new paragraph. Then `ph_add_text()` will be used to add text 
into that new paragraph.

```{r}
text_prop <- fp_text(color = "red", font.size = 40)
my_pres <- my_pres %>% 
  ph_add_par(ph_label = "Content Placeholder 3") %>%
  ph_add_text(str = "This is a red text!", style = text_prop, ph_label = "Content Placeholder 3") %>% 
  ph_add_par(level = 2, ph_label = "Content Placeholder 3") %>%
  ph_add_text(str = "Level 2", ph_label = "Content Placeholder 3") %>% 
  ph_add_par(level = 3, ph_label = "Content Placeholder 3") %>%
  ph_add_text(str = "Level 3", ph_label = "Content Placeholder 3")

print(my_pres, target = "assets/pptx/ph_add_text_1.pptx")
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_add_text_1.pptx" ) )
```

### Add to an existing placeholder of text

The following code produces a presentation comprised of one text shape 
containing the text "A first text".

```{r}
my_pres <- read_pptx() %>% 
  add_slide(layout = "Title and Content", master = "Office Theme") %>% 
  ph_with(value = "A first text", location = ph_location_type(type = "body"))
slide_summary(my_pres)
```

Since there is now a paragraph in the new shape, `ph_add_par()` will be used to 
add another paragraph and `ph_add_text()` then has to be used to add text into 
the last paragraph of the shape.

```{r}
text_blue_prop <- update(text_prop, color = "blue" )
my_pres <- my_pres %>% 
  ph_add_text(str = "A small red text!", style = text_prop, ph_label = "Content Placeholder 2" ) %>% 
  ph_add_text(str = "Blue text first... ", pos = "before", style = text_blue_prop, ph_label = "Content Placeholder 2" ) %>% 
  ph_add_par(level = 2, ph_label = "Content Placeholder 2") %>%
  ph_add_text(str = "additional paragraph", ph_label = "Content Placeholder 2")

print(my_pres, target = "assets/pptx/ph_add_text_2.pptx") 
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_add_text_2.pptx" ) )
```

## Links

### External links

`ph_hyperlink()` adds a hyperlink to an existing placeholder in the current 
slide. The argument `href` should contain a valid URL (i.e. starting with 
`http(s)`).

```{r}
doc <- read_pptx() %>% 
  add_slide(layout = "Title and Content", master = "Office Theme") %>% 
  ph_with("Blah blah blah", location = ph_location_type(type = "body")) %>% 
  ph_hyperlink(ph_label = "Content Placeholder 2", href = "https://cran.r-project.org") 

print(doc, target = "assets/pptx/ph_hyperlink.pptx")
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_hyperlink.pptx" ) )
```


### Internal links

`ph_slidelink()` adds an internal link into an existing placeholder. The 
argument `slide_index` should contain the index of the target slide.

```{r}
doc <- read_pptx() %>% 
  add_slide(layout = "Title and Content", master = "Office Theme") %>% 
  ph_with("Blah blah blah", location = ph_location_type(type = "body")) %>% 
  add_slide(layout = "Title and Content", master = "Office Theme") %>% 
  ph_with("placeholder target", location = ph_location_type(type = "title")) %>% 
  on_slide(index = 1 ) %>% 
  ph_slidelink(ph_label = "Content Placeholder 2", slide_index = 2)

print(doc, target = "assets/pptx/ph_slidelink.pptx")
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_slidelink.pptx" ) )
```

### Add text with links

The function `ph_add_text()` has an optional argument `href`. If used, the chunk 
of text will be added as a hyperlink. If `href` is not used and `slide_index` is, 
the link will point to another slide in the document.

```{r}
my_pres <- read_pptx() %>% 
  add_slide(layout = "Title and Content", master = "Office Theme") %>% 
  ph_with(value = "An ", location = ph_location_type(type = "body")) %>% 
  ph_add_text(str = "hyperlink", href = "https://cran.r-project.org", 
              ph_label = "Content Placeholder 2", )

print(my_pres, target = "assets/pptx/ph_add_text_3.pptx")
```

```{r echo=FALSE}
office_doc_link( url = paste0( "https://davidgohel.github.io/officer/articles/", "assets/pptx/ph_add_text_3.pptx" ) )
```

## Resources

Len Kiefer wrote two very good blog posts about officer, he is providing nice examples with the corresponding R code:

* http://lenkiefer.com/2017/09/23/crafting-a-powerpoint-presentation-with-r/
* http://lenkiefer.com/2017/09/27/use-purrr/

[Eric Nanz](https://r-podcast.org) gave a short presentation about using officer in the Advanced R Markdown workshop held at the 2019 RStudio conference:

* https://rpodcast.github.io/officer-advrmarkdown/
