---
title: "flextable layout"
output: 
  rmarkdown::html_vignette:
    toc: true 
    toc_depth: 3
vignette: >
  %\VignetteIndexEntry{Table layout}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r echo = FALSE, message=FALSE}
knitr::opts_chunk$set(
  message = FALSE,
  collapse = TRUE,
  comment = "#>", 
  eval = TRUE
)
library(htmltools)
library(magrittr)
library(data.table)

as_ul <- function( names ) {
  do.call( tags$ul, lapply(names, tags$li) )
}

```


`flextable` layout can be easily managed. A set of functions will let you 
merge cells, add title rows, add footer notes, change the withs or heights.

```{r}
library(flextable)
library(officer)
```


## flextable col_keys

Parameter `col_keys` of function `flextable` define the variables to display 
and their order. 

```{r}
data <- iris[c(1:3, 51:53, 101:104),]
myft <- flextable(data, col_keys = c("Species", "Sepal.Length", "Petal.Length") )
myft
```

If parameter `col_keys` has variables that are not existing in the dataset, they 
will be considered as blank columns and can be used as separators (in fact they 
can be use as you want, there is only no mapping of data associated).

```{r}
myft <- flextable(
  data = data, 
  col_keys = c("Species", "col_1", "Sepal.Length", "Petal.Length") )
myft <- theme_vanilla(myft)
myft <- autofit(myft)
myft <- empty_blanks(myft)
myft
```

**`col_keys` default values are the names of the data.frame used to fill the flextable.**

## Change labels

Use `set_header_labels()` to replace labels of the bottom row of header. When the flextable 
is created, their values are the column names of the data.frame. 


```{r}
ft <- flextable( head( iris ) ) 
ft <- set_header_labels(ft, Sepal.Length = "Sepal length", 
    Sepal.Width = "Sepal width", Petal.Length = "Petal length",
    Petal.Width = "Petal width" )
ft
```

New header rows can be added at the top or bottom of the header. 
This part in documented in part 
[Manage headers and footers](#manage-headers-and-footers).

Under the hood, the names are in a single row data.frame 
associated with the header part of the flextable. You can 
add new rows later, they will be binded to that data.frame. 

## Cell merging 

To illustrate functions, we will use a basic flextable example: 

```{r}

dat <- data.frame(
  letters1 = c("a", "b", "b", "c"), 
  letters2 = c("d", "e", "b", "b"), 
  number = 1:4, stringsAsFactors = FALSE )

myft <- flextable(dat)
myft <- theme_box(myft)
myft
```

<div class="row">

<div class="col-12 col-sm-8 col-md-6">

### vertical merging of similar values

`merge_v()` will merge adjacent duplicated cells for each column of the selection.

```{r}
merge_v(myft, j = ~ letters1 + letters2 )
```

</div>

<div class="col-12 col-sm-8 col-md-6">

### horizontal merging of similar values

`merge_h()` will merge adjacent duplicated cells for each row of the selection.

```{r}
merge_h(myft)
```

</div>

</div>
<div class="row">

<div class="col-12 col-sm-8 col-md-6">


### horizontal merging of columns

Function `merge_h_range` is close to the previous one but merge 
all colums between a range of columns.

```{r}
merge_h_range(myft, 
  i =  ~ number < 3, 
  j1 = "letters1", j2 = "letters2")
```


</div>

<div class="col-12 col-sm-8 col-md-6">


### general merging function

`merge_at()` will merge cells for a given continuous selection of rows and cells. 
The result is a single cell.

```{r}
myft %>% 
  merge_at(
    i = 1:2, j = 1:2)
```

Note that the content that is rendered is not the result of the concatenation of each paragraphs 
contained into the merge cells. Only one paragraph will be kept, the top left one of all merge cells.

</div>

</div>



### delete merging informations

If you want to get rid of all merging (i.e. for development purposes), use `merge_none()`:

```{r}
merge_none(myft)
```


### Borders and merging

When cells are merged, the rendered borders will be those of the first cell. If a 
column is made of three merged cells, the bottom border that will be seen will be 
the bottom border of the first cell in the column. From a user point of view, this 
is wrong, the bottom should be the one defined for cell 3. Function `fix_border_issues` 
is trying to fix that issue. 

```{r}
ft <- data.frame(a = 1:5, b = 6:10) %>%
  flextable() %>% theme_box() %>%
  merge_at(i = 4:5, j = 1, part = "body") %>%
  hline(i = 5, part = "body",
        border = fp_border(color = "orange", width = 3) )
ft
fix_border_issues(ft)
```


## Manage headers and footers

Use one of the following functions to add an header row or a footer row:

* **Most needs will be statisfied with functions `add_header_row` and `add_footer_row`. **
These are row oriented functions to let you add a single row of labels (that can be 
displayed along one or more columns in the new row).
* Functions `add_header` and `add_footer` are columns oriented functions that will let 
you add several values (one for each new row) for a given column. If labels are stored in 
a data.frame, use functions `set_header_df` and `set_footer_df`.
* Functions `add_footer_lines` and `add_header_lines` are sugar functions and should be 
used to add labels on one single row where all columns are merged together.


We will mainly demonstrate headers but same can be apply with footers.

The operation below is changing labels that will be displayed instead of 
the original values (the names of dataset).

```{r}
ft <- flextable( head( iris ) ) 
ft <- set_header_labels(ft, Sepal.Length = "Sepal", 
    Sepal.Width = "Sepal", Petal.Length = "Petal",
    Petal.Width = "Petal" )
# merge them 
ft <- merge_at(ft, i = 1, j = 1:2, part = "header")
ft <- merge_at(ft, i = 1, j = 3:4, part = "header")
ft
```

Now let's add new row of labels. 

### Add a row of labels

<div class="row">

<div class="col-12 col-sm-8 col-md-6">

```{r}
ft <- add_header_row(ft, 
  values = c("", "length", "width", "length", "width"), top = FALSE )
ft <- theme_box(ft)
```

</div>

<div class="col-12 col-sm-8 col-md-6">

```{r echo=FALSE}
ft
```

</div>

</div>


### Add lines of text

<div class="row">

<div class="col-12 col-sm-8 col-md-6">

```{r}
ft <- add_header_lines(ft, 
  values = c("this is a first line", 
     "this is a second line") ) 
ft <- theme_box(ft)
```

</div>

<div class="col-12 col-sm-8 col-md-6">

```{r echo=FALSE}
ft
```

</div>

</div>




### Define headers with a reference table

Use `set_header_df()` with a data.frame as parameter. Columns of 
the dataset will be transposed and joined using a key column. 

1. The reference table 

Variable `col_keys` define key values to match with flextable 
column keys (defined by argument `col_keys` of `flextable()` function).

This key column will not be displayed. Other variables will added as 
rows. Note that variables names are not displayed. 

<div class="row">

<div class="col-12 col-sm-8 col-md-6">

```{r}
typology <- data.frame(
  col_keys = c( "Sepal.Length", 
    "Sepal.Width", "Petal.Length",
    "Petal.Width", "Species" ),
  type = c("double", "double", "double", 
    "double", "factor"),
  what = c("Sepal", "Sepal", "Petal", 
    "Petal", "Species"),
  measure = c("Length", "Width", "Length", 
    "Width", "Species"),
  stringsAsFactors = FALSE )
```

</div>

<div class="col-12 col-sm-8 col-md-6">

```{r echo=FALSE}
autofit( theme_vanilla(flextable(typology)) )
```

</div>

</div>


2. Use it as header rows

Then use `set_header_df()` with parameter `key`.  `key` is the name of the column used 
to permform the join operation. 

> Order of columns matters, first column will be first row, second one will 
  be the second row, etc.

<div class="row">

<div class="col-12 col-sm-8 col-md-6">

```{r}
ft <- flextable( head( iris ) )
ft <- set_header_df( ft, mapping = typology, key = "col_keys" )

ft <- merge_h(ft, part = "header")
ft <- merge_v(ft, part = "header")

ft <- theme_booktabs(ft)
ft <- autofit(ft)
ft <- fix_border_issues(ft)
```


</div>

<div class="col-12 col-sm-8 col-md-6">

```{r echo=FALSE}
ft
```

</div>

</div>



## Table width

By default, table width is fixed. This setting allows to have the same rendering 
with Word, HTML and PowerPoint formats. 

You can use another setting with function `set_table_properties()`. Alternative is 
'autofit', an algorithm implemented by HTML and Word. It allows to size the table 
along an available width. 

```{r eval=FALSE}
library(flextable)
ft <- qflextable(head(cars))
set_table_properties(ft, width = .5, layout = "autofit")
set_table_properties(ft, width = 1, layout = "autofit")
```



## Cell widths and heights

> This oonly applies when layout is "fixed"

The default sizes of flextable columns and rows are set by default values. 
This will drive to inadequate rows heights and columns widths in some cases. 
You can use function `dim` to get flextable dimensions. 

```{r}
ft_base <- flextable(head(mtcars))
ft_base <- theme_vader(ft_base, fontsize = 13)
ft_base
dim(ft_base)
```

### Pretty dimensions

Function `dim_pretty()` is computing optimized widths and heights. 

```{r}
dim_pretty(ft_base)
```

### Adjusts automatically cell widths and heights

Function `autofit()` optimises widths and heights of the flextable. 
This function is almost always to be called once when using 
flextable objects, it makes compact tables.

> `autofit` was not my biggest naming idea as users are thinking it is the 
'Microsoft Word feature' (see [set_table_properties](../reference/set_table_properties.html))

```{r}
ft <- autofit(ft_base, add_w = 0, add_h = 0)

dim(ft)
ft
```

> Soft returns (*a line break in a paragraph*) support : function `autofit` and `dim_pretty` do not
support soft returns and may return wrong results (\n will be considered as "").
  
### Adjusts manually cell widths and heights

Function `width()` and `height()` let you control dimensions of a flextable. `height_all()` is an helper function to set the same height to each part of the table.

```{r}
ft <- autofit(ft_base)
ft <- width(ft, j = ~ mpg + cyl + disp, width = 2)
ft <- height_all( ft, height = .4 )
ft <- height( ft, i = 3, height = 1 )
ft
```


