Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R + ggplot + pdf device + LaTeX: is it possible to embed fonts one time

I'm having an r-scipt that is generaing a PDF in the following way...

  1. 100+ graphs are beeing made with baseplot and ggplot. The graphs are generated with pdf device. The reason I'm using pdf is that I need to embed fonts (to use custom font in tex labels).
  2. After graphs are generated I call Sweave that generates .tex to gather all that graphs together.
  3. After that I call MikTeX to generate PDF.

It turns out that 75-95% of time needed to generate a graph is taken by embed_fonts call. Is there a way to do less embed_fonts (that seems to be a wrapper for ghostscript) calls? The ideal vatiant is to embed call only one time. Is that possible?

like image 547
Eugeny89 Avatar asked Feb 12 '18 12:02

Eugeny89


2 Answers

Maybe you could use the cairo_pdf device that should embed the fonts.
cairo_pdf seems slower than pdf but faster than pdf + embedFonts.

Of course embedding the fonts in the final pdf document only would be a better solution...

library(microbenchmark)

res_pdf <- microbenchmark({
    f = tempfile(fileext = '.pdf')
    pdf(f); pairs(iris); dev.off()
})

res_embed <- microbenchmark({
    f = tempfile(fileext = '.pdf')
    pdf(f); pairs(iris); dev.off()
    embedFonts(f)
})

res_cairo <- microbenchmark({
    f = tempfile(fileext = '.pdf')
    cairo_pdf(f); pairs(iris); dev.off()
})

res_pdf
#> Unit: milliseconds
#>       min      lq     mean   median       uq     max neval
#>  16.67764 17.0388 18.05949 17.32904 18.18776 60.2542   100

res_embed
#> Unit: milliseconds
#>                                                                                               
#>      min       lq     mean   median       uq      max neval
#>  250.046 252.7647 257.4749 255.2785 259.4858 303.0072   100

res_cairo
#> Unit: milliseconds
#>                                                                                   
#>       min       lq     mean   median      uq      max neval
#>  84.25745 86.60512 88.42902 88.36698 89.5705 111.5881   100
like image 124
Gilles Avatar answered Nov 15 '22 04:11

Gilles


An alternative solution would be to output your plots as TikZ using the excellent tikzDevice package.

From an rmarkdown document we can include a knitr chunk with the device set to tikz.

example.Rmd

---
output:
  pdf_document:
    latex_engine: xelatex
header-includes:
  - \renewcommand{\familydefault}{\sfdefault}
  - \usepackage{tikz}
---

```{r setup, include=FALSE}
library(tikzDevice)

# setting the package options so that the font metrics are calculated correctly
options(
  tikzLatexPackages = c(
    getOption("tikzLatexPackages"),
    "\\renewcommand{\\familydefault}{\\sfdefault}"
  )
)
```

# My plot

This text will be set in the default *sans serif* and so will the text found
within the chart below.

```{r, echo=FALSE, dev='tikz'}
pairs(iris)
```

You can replace my \renewcommand{\familydefault}{\sfdefault} with whatever latex command you require to set your selected font, such as \usepackage{charter}. Just note that you must escape the \ as in my example above.

example.pdf

enter image description here

Alternative

You may want to generate all your plots in advance with another script given you have so many, instead of knitting inline at the time of document generation. I do this for a mass run of reports (1,000+) each with about 20 plots.

# a more simple example due to output size
library(tikzDevice)
mpg <- mtcars$mpg

tikz(file = "example.tex"); hist(mpg); dev.off()

example.tex

% Created by tikzDevice version 0.10.1.2 on 2018-02-21 23:32:57
% !TEX encoding = UTF-8 Unicode
\begin{tikzpicture}[x=1pt,y=1pt]
\definecolor{fillColor}{RGB}{255,255,255}
\path[use as bounding box,fill=fillColor,fill opacity=0.00] (0,0) rectangle (216.81,216.81);
\begin{scope}
\path[clip] (  0.00,  0.00) rectangle (216.81,216.81);
\definecolor{drawColor}{RGB}{0,0,0}

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.20] at (120.41,188.07) {\bfseries Histogram of mpg};

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at (120.41, 15.60) {mpg};

\node[text=drawColor,rotate= 90.00,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 10.80,114.41) {Frequency};
\end{scope}
\begin{scope}
\path[clip] (  0.00,  0.00) rectangle (216.81,216.81);
\definecolor{drawColor}{RGB}{0,0,0}

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 54.47, 61.20) -- (186.34, 61.20);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 54.47, 61.20) -- ( 54.47, 55.20);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 80.85, 61.20) -- ( 80.85, 55.20);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] (107.22, 61.20) -- (107.22, 55.20);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] (133.59, 61.20) -- (133.59, 55.20);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] (159.96, 61.20) -- (159.96, 55.20);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] (186.34, 61.20) -- (186.34, 55.20);

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 54.47, 39.60) {10};

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 80.85, 39.60) {15};

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at (107.22, 39.60) {20};

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at (133.59, 39.60) {25};

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at (159.96, 39.60) {30};

\node[text=drawColor,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at (186.34, 39.60) {35};

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20, 65.14) -- ( 49.20,163.67);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20, 65.14) -- ( 43.20, 65.14);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20, 81.56) -- ( 43.20, 81.56);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20, 97.98) -- ( 43.20, 97.98);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20,114.40) -- ( 43.20,114.40);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20,130.83) -- ( 43.20,130.83);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20,147.25) -- ( 43.20,147.25);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 49.20,163.67) -- ( 43.20,163.67);

\node[text=drawColor,rotate= 90.00,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 34.80, 65.14) {0};

\node[text=drawColor,rotate= 90.00,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 34.80, 81.56) {2};

\node[text=drawColor,rotate= 90.00,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 34.80, 97.98) {4};

\node[text=drawColor,rotate= 90.00,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 34.80,114.40) {6};

\node[text=drawColor,rotate= 90.00,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 34.80,130.83) {8};

\node[text=drawColor,rotate= 90.00,anchor=base,inner sep=0pt, outer sep=0pt, scale=  1.00] at ( 34.80,147.25) {10};
\end{scope}
\begin{scope}
\path[clip] ( 49.20, 61.20) rectangle (191.61,167.61);
\definecolor{drawColor}{RGB}{0,0,0}

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 54.47, 65.14) rectangle ( 80.85,114.40);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] ( 80.85, 65.14) rectangle (107.22,163.67);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] (107.22, 65.14) rectangle (133.59,130.83);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] (133.59, 65.14) rectangle (159.96, 81.56);

\path[draw=drawColor,line width= 0.4pt,line join=round,line cap=round] (159.96, 65.14) rectangle (186.34, 97.98);
\end{scope}
\end{tikzpicture}

N.B. This is not a complete .tex file and is rather designed to be brought in using \input{example.tex} or otherwise embedded into a complete document. Also, be mindful of the font metric calculations, the saved .tex file may render strangely with different fonts if the metrics are significantly out.

like image 37
Kevin Arseneau Avatar answered Nov 15 '22 05:11

Kevin Arseneau