Xu Wang
Xu Wang

Reputation: 10587

knitr: cannot create a figure with utf-8 character

The following is my .Rnw file:

\documentclass{article}
\begin{document}

<<myChunk>>=
options(warn = 2)
library(ggplot2)
library(directlabels)
data(BodyWeight,package="nlme")
BodyWeight$temp <- as.character(BodyWeight$Rat)
BodyWeight$temp[BodyWeight$temp == "4"] <- "HI₂"
p <- qplot(Time,weight,data=BodyWeight,colour=temp,geom="line")
direct.label(p,"first.qp")
@

\end{document}

The following is how I call knitr from R:

library(knitr)
# I have tryied this but doesn't make difference:
# pdf.options(encoding='ISOLatin2.enc')
knit("mwe_knitr.Rnw")

I get following as output:

> knit("mwe_knitr.Rnw")


processing file: mwe_knitr.Rnw
  |......................                                           |  33%
  ordinary text without R code

  |...........................................                      |  67%
label: myChunk
Quitting from lines 5-13 (mwe_knitr.Rnw) 
Error in grid.Call(L_convert, x, as.integer(whatfrom), as.integer(whatto),  : 
  (converted from warning) conversion failure on 'HI₂' in 'mbcsToSbcs': dot substituted for <e2>

I tried solutions with encoding, such as posted here: Rhtml: Warning: conversion failure on '<var>' in 'mbcsToSbcs': dot substituted for <var>

(I note in comment above exactly where I try the solution to that problem) but it did not seem to change nothing for me.

I am using R 3.3.1 and knitr package 1.13 on Ubuntu.

Upvotes: 4

Views: 892

Answers (1)

eipi10
eipi10

Reputation: 93811

It looks like using the cairo_pdf device resolves this issue. In the setup chunk below, I set the device option to the cairo_pdf device (that's the line that begins option(device = ...) and the global chunk option dev to default to "cairo_pdf" (in the line that begins knitr::opts_chunk$set(...). This approach is discussed in the knitr documentation (see the section Encoding of Multibyte Characters) and in Issue #436.

I've made a few other changes:

  1. Instead of "hard-coding" "HI₂" I've used the Unicode symbol for the subscripted 2, "\U2082".

  2. Changed the plot call to "standard" ggplot rather than qplot.

  3. Changed from calling directlabels after making the plot to calling geom_dl to add direct labels within the "standard" ggplot workflow.

  4. Set the fontfamily within geom_dl. I found that the subscript 2 was rendered with some font families, but not others.

  5. Changed the warn option to zero (the default) so that warnings won't be turned into errors. I just did this while I was testing the code, but it can, of course, be set back to 2 if desired.

The chunk myChunk1a creates the plot. The chunk myChunk1b creates basically the same plot, but in multiple versions, each using a different font family. In these versions, you can see that the subscript 2 is rendered with some font families, but not others. I'm not sure what determines this and the results may be different on your system.

\documentclass{article}
\begin{document}

<<setup, include=FALSE>>=
options(warn = 0)
options(device = function(file, width = 7, height = 7, ...) {
  cairo_pdf(tempfile(), width = width, height = height, ...)
})
knitr::opts_chunk$set(echo = FALSE, message=FALSE, warning=FALSE, dev="cairo_pdf")
@

<<myChunk>>=
library(ggplot2)
library(directlabels)
library(gridExtra)
library(dplyr)

data(BodyWeight,package="nlme")
BodyWeight$temp <- as.character(BodyWeight$Rat)

BodyWeight$temp[BodyWeight$temp=="4"] = "HI\U2082"

# Change first value so that HI2 label is easily visible
BodyWeight$weight[BodyWeight$temp=="HI\U2082" & BodyWeight$Time==1] = 350
@

<<myChunk1a, fig.height=5>>=
ggplot(BodyWeight, aes(Time, weight, colour=temp)) + 
  geom_line() +
  geom_dl(method=list("first.qp", fontfamily="Helvetica", cex=1), aes(label=temp)) + 
  theme_bw() +
  ggtitle("Helvetica") +
  guides(colour=FALSE)
@

<<myChunk1b, fig.height=11>>=
# Create several plots, each demonstrating a different font family for the labels
grid.arrange(grobs=lapply(c("Helvetica","Courier","Palatino","Times","Serif"), function(f) {
  ggplot(BodyWeight, aes(Time, weight, colour=temp)) + 
    geom_line() +
    geom_dl(method=list("first.qp", fontfamily=f, cex=1), aes(label=temp)) + 
    labs(x="") + 
    theme_bw() +
    theme(plot.margin=unit(c(0,0,0,0), "lines"),
          text=element_text(size=9)) +
    ggtitle(f) +
    guides(colour=FALSE)
}), ncol=1)
@

<<myChunk2, fig.height=5>>=
data(BodyWeight,package="nlme")
BodyWeight$temp <- as.character(BodyWeight$Rat)

# Change first value so that HI2 label is easily visible
BodyWeight$weight[BodyWeight$temp=="4" & BodyWeight$Time==1] = 350

# Set temp==4 to desired expression
BodyWeight$temp[BodyWeight$temp == "4"] <- paste(expression(HI[2]))

# Convert temp to factor to set order
BodyWeight$temp = factor(BodyWeight$temp, levels=unique(BodyWeight$temp))

qplot(Time, weight, data=BodyWeight, colour=temp, geom="line") +
  guides(colour=FALSE) +
  geom_text(data=BodyWeight %>% group_by(temp) %>%
              filter(Time == min(Time)), 
            aes(label=temp, x=Time-0.5, y=weight), parse=TRUE, hjust=1) +
  theme_bw()
@

\end{document}

Here's what the plot from myChunk1a looks like:

enter image description here

Upvotes: 5

Related Questions