LaTeX Font-consistent EPS R Plots Using tikzexternalize

We are about to submit a final camera-ready version of a manuscript and they sent us this email the other day reminding us, among other things, to “adhere to the prescribed [ACM] format as set out in paragraph 2.3.10 [1]”… In a nutshell, they want all figures in EPS format. Now I am very particular about font-consistency when integrating plots within TeX documents, and so I spent most of last night trying to figure this out. I eventually decided to leverage a somewhat widely used technique–‘tikzexternalize’. I found this [2] thread on StackExchange really helpful.

The Problem

I have ten ggplot2 plots that I would like to output to EPS format and at the same time ensure that the fonts used on the plots are consistent with the LaTeX document I am integrating them into.

My normal LaTeX/R plot integration workflow is simple: I run Sweave to generate native TeX code and subsequently integrate resulting ‘.tex’ within the main LaTeX document. This makes it possible for plot fonts to be consistent with whatever font I am using within the LaTeX document. However, this workflow breaks if I use a different output format. The problem is that if I generate EPS files using the traditional R way using postscript function [3], there is NO guarantee that the resulting plot fonts will be consistent with the main LaTeX document.

The Solution

  • Run Sweave files to generate TeX equilavalent plots (R CMD Sweave [FILENAME].Rnw)
  • Create dumy/fake LaTeX document with class corresponding and integrate Sweave generated TeX using ‘\input’
    • Ensure preamble is set up for externalization
    • Run LaTeX document using ‘latex –shell-escape FILENAME’ command
    • EPS outputs will be generated with incremental names relative to their position within dummy/fake document ([FAKEDOCNAME]-figure0.eps … [FAKEDOCNAME]-figure[N].eps)
  • Integrate EPS output in manuscript using ‘\epsfig’ or ‘\includegraphics’
  • Run latex or pdflatex on main LaTeX manuscript

A Minimal Example

Sweave file

# Import required packages
require(tikzDevice)
require(ggplot2)
require(reshape2)
require(grid)
require(scales)
require(ggthemes)

# Generate native TikZ TeX ouput
tikz('minimal_example_plot.tex')

dataset <- read.csv(
text="Age_Range,Total,Male,Female,Rural_Total,Rural_Male,Rural_Female,Urban_Total,Urban_Male,Urban_Female
<1,14698,7293,7405,11536,5736,5800,3162,1557,1605
1-4,63824,31588,32236,49110,24299,24811,14714,7289,7425
5-9,67542,33702,33840,51919,26059,25860,15623,7643,7980
10-14,63241,31573,31668,47725,24319,23406,15516,7254,8262
15-19,52788,26518,26270,38025,19360,18665,14763,7158,7605
20-24,40587,19013,21574,28419,13494,14925,12168,5519,6649
25-29,35558,16909,18649,24678,11865,12813,10880,5044,5836
30-34,28170,14392,13778,19360,9843,9517,8810,4549,4261
35-39,23123,12149,10974,16375,8406,7969,6748,3743,3005
40-44,15747,8285,7462,11578,5951,5627,4169,2334,1835
45-49,12885,6371,6514,9709,4718,4991,3176,1653,1523
50-54,9815,4776,5039,7487,3585,3902,2328,1191,1137
55-59,6722,3270,3452,5299,2536,2763,1423,734,689
60-64,6319,2775,3544,5137,2194,2943,1182,581,601
65-69,4834,2016,2818,4076,1658,2418,758,358,400
70-74,3942,1703,2239,3421,1441,1980,521,262,259
75-79,2819,1276,1543,2479,1132,1347,340,144,196
80-84,1449,655,794,1298,584,714,151,71,80
85+,1720,670,1050,1525,601,924,195,69,126", header=TRUE
)

# re-order requests
dataset$Age_Range <- factor(dataset$Age_Range, levels=c("<1","1-4","5-9","10-14","15-19","20-24","25-29","30-34","35-39","40-44", "45-49", "50-54", "55-59", "60-64", "65-69", "70-74", "75-79", "80-84", "85+"), labels=c("< 1","1 - 4","5 - 9","10 - 14","15 - 19","20 - 24","25 - 29","30 - 34","35 - 39","40 - 44", "45 - 49", "50 - 54", "55 - 59", "60 - 64", "65 - 69", "70 - 74", "75 - 79", "80 - 84", "85+"))

# Convert dataset to molten form
experimentdataset <- melt(dataset[c(1, 3:4)], id.vars=c(1))

# Dummy column for fancy faceting
experimentdataset["yaba"] <- "Population in Chipata, Zambia (2010 Census of Population)"

# Rename levels
levels(experimentdataset$variable)[levels(experimentdataset$variable)=="Male"] <- paste("Male Individuals","")
levels(experimentdataset$variable)[levels(experimentdataset$variable)=="Female"] <- paste("Female Individuals","")

# Final plot
ggplot(data=experimentdataset,
       aes(x=as.factor(Age_Range),
           y=value,
           colour=variable,
           group=variable)) +
             geom_line(aes(linetype=variable), size=1.5) +
             geom_point(aes(shape=variable), fill="white", size=1.5) +
             scale_shape_manual(values=c(1,1)) + scale_linetype_manual(values=c("solid","solid")) +
             facet_wrap(~ yaba, scales="free_x") +
             labs(x="Population age ranges",y="Number of individuals") +
             scale_colour_stata() +
             theme(legend.title=element_blank(),
                   legend.position="bottom",
                   legend.text=element_text(size=14)
                   legend.key.height=unit(0.35,"cm"),
                   plot.margin=unit(c(0.1,0,-0.5,0.8),"cm"),
                   strip.text = element_text(size=14),
                   axis.title.x = element_text(vjust=-0.8,size=14),
                   axis.title.y = element_text(vjust=-0.2,size=14),
                   axis.text = element_text(size=14),
                   axis.text.x = element_text(angle=90, vjust=0.5)) +
             guides(linetype=guide_legend(nrow=1,byrow=TRUE),shape=guide_legend(nrow=1,byrow=TRUE))
@
  • Run the file above using command below
phiri@phiri-ThinkPad-T540p:~/Dropbox$ R CMD Sweave minimal_example_plot.Rnw
Loading required package: tikzDevice
:
        /tmp/RtmpzWjQDz/tikzMetricsDictionary
Output file:  minimal_example_plot.tex
phiri@phiri-ThinkPad-T540p:~/Dropbox$

Dummy/fake host LaTeX document


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
\documentclass[a4paper,10pt]{article}

\usepackage{charter}
\usepackage{tikz}

\usetikzlibrary{external}
\tikzset{external/system call={latex \tikzexternalcheckshellescape -halt-on-error
-interaction=batchmode -jobname "\image" "\texsource";
dvips -o "\image".ps "\image".dvi;
ps2eps "\image.ps"}}
\tikzexternalize

\begin{document}
  \input{minimal_example_plot.tex}
\end{document
}
  • Fake document MUST have lines 4 to 11
  • The fake host LaTeX document MUST be run using latex NOT pdflatex (see command below)
phiri@phiri-ThinkPad-T540p:~/Dropbox$ latex --shell-escape minimal_example_fake.tex
This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013)
:
:

 

 

Actual LaTeX document


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
\documentclass[a4paper,12pt]{article}

\usepackage{charter}
\usepackage{graphicx}

\begin{document}

This is a sample output file. Notice how the text font is consistent with the font used in Figure~\ref{fig:minimal_example_plot}

\begin{figure*}[htbp]
  \includegraphics[width=\textwidth]{minimal_example_fake-figure0.eps}
  \caption{This plot an integrated into the current document using EPS format}
  \label{fig:minimal_example_plot}
\end{figure*}

\end{document
}
Run the main LaTeX document normally, e.g. using pdflatex
phiri@phiri-ThinkPad-T540p:~/Dropbox$ pdflatex minimal_example.tex
This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013)
:
:

Final Output

LaTeX font-consistent EPS using tikzexternalize
LaTeX font-consistent EPS using tikzexternalize

Some Useful Notes

  • I found that I did not have to provide explicit width and height parameters to tikz function within my Sweave (.Rnw) files
  • I had to painstakingly figure out the appropriate font size for EPS output. Original TeX output use 8-point and I eventually settled for 14-point for EPS output
  • EPS output quality is too inferior–see comparison in figure below

 

Quality implication for font-consistent EPS Using tikzexternalize
Quality implication for font-consistent EPS Using tikzexternalize

Bibliography

[1] http://www.acm.org/sigs/publications/sigguide-v2.2sp
[2] http://tex.stackexchange.com/a/8646/17923
[3] http://stat.ethz.ch/R-manual/R-patched/library/grDevices/html/postscript.html