I wanted to do the same, and also got an error from a Cartesian error. In addition, I wanted to have censorship numbers in my code and the number of events. So I wrote this little snippet. Some more raw materials, but perhaps useful for some.
ggsurvplot<-function( time, event, event.marker=1, marker, tabletitle="tabletitle", xlab="Time(months)", ylab="Disease Specific Survival", ystratalabs=c("High", "Low"), pv=TRUE, legend=TRUE, n.risk=TRUE, n.event=TRUE, n.cens=TRUE, timeby=24, xmax=120, panel="A") { require(ggplot2) require(survival) require(gridExtra) s.fit=survfit(Surv(time, event==event.marker)~marker) s.diff=survdiff(Surv(time, event=event.marker)~marker) #Build a data frame with all the data sdata<-data.frame(time=s.fit$time, surv=s.fit$surv, lower=s.fit$lower, upper=s.fit$upper, n.censor=s.fit$n.censor, n.event=s.fit$n.event, n.risk=s.fit$n.risk) sdata$strata<-rep(names(s.fit$strata), s.fit$strata) m <- max(nchar(ystratalabs)) if(xmax<=max(sdata$time)){ xlims=c(0, round(xmax/timeby, digits=0)*timeby) }else{ xlims=c(0, round((max(sdata$time))/timeby, digits=0)*timeby) } times <- seq(0, max(xlims), by = timeby) subs <- 1:length(summary(s.fit,times=times,extend = TRUE)$strata) strata = factor(summary(s.fit,times = times,extend = TRUE)$strata[subs]) time = summary(s.fit, time = times, extend = TRUE)$time #Buidling the plot basics p<-ggplot(data = sdata, aes(colour = strata, group = strata, shape=strata)) + theme_classic()+ geom_step(aes(x = time, y = surv), direction = "hv")+ scale_x_continuous(breaks=times)+ scale_y_continuous(breaks=seq(0,1,by=0.1)) + geom_ribbon(aes(x = time, ymax = upper, ymin = lower, fill = strata), directions = "hv", linetype = 0,alpha = 0.10) + geom_point(data = subset(sdata, n.censor == 1), aes(x = time, y = surv), shape = 3) + labs(title=tabletitle)+ theme( plot.margin=unit(c(1,0.5,(2.5+length(levels(factor(marker)))*2),2), "lines"), legend.title=element_blank(), legend.background=element_blank(), legend.position=c(0.2,0.2))+ scale_colour_discrete( breaks=c(levels(factor(sdata$strata))), labels=ystratalabs) + scale_shape_discrete( breaks=c(levels(factor(sdata$strata))), labels=ystratalabs) + scale_fill_discrete( breaks=c(levels(factor(sdata$strata))), labels=ystratalabs) + xlab(xlab)+ ylab(ylab)+ coord_cartesian(xlim = xlims, ylim=c(0,1)) #addping the p-value if (pv==TRUE){ pval <- 1 - pchisq(s.diff$chisq, length(s.diff$n) - 1) pvaltxt<-if(pval>=0.001){ paste0("P = ", round(pval, digits=3)) }else{ "P < 0.001" } p <- p + annotate("text", x = 0.85 * max(xlims), y = 0.1, label = pvaltxt) } #adding information for tables times <- seq(0, max(xlims), by = timeby) subs <- 1:length(summary(s.fit,times=times,extend = TRUE)$strata) risk.data<-data.frame(strata = factor(summary(s.fit,times = times,extend = TRUE)$strata[subs]), time = summary(s.fit, time = times, extend = TRUE)$time[subs], n.risk = summary(s.fit,times = times,extend = TRUE)$n.risk[subs], n.cens = summary(s.fit, times=times, extend=TRUE)$n.cens[subs], n.event=summary(s.fit, times=times, extend=TRUE)$n.event[subs]) #adding the risk table if(n.risk==TRUE){ p<- p + annotate("text", cex=3, x=0.5*max(xlims), y=-0.15, label="Numbers at risk") for (q in 1:length(levels(factor(marker)))){ p<- p + annotate("text", cex=3, x=-0.15*max(xlims),y=(-0.15+(-0.05*q)), label=paste0(ystratalabs[q])) for(i in ((q-1)*length(times)+1):(q*length(times))){ p <- p + annotate("text", cex=3, x=risk.data$time[i], y=(-0.15+(-0.05*q)), label=paste0(risk.data$n.risk[i])) } } } #adding the event table if(n.event==TRUE){ p<- p + annotate("text", cex=3, x=0.5*max(xlims), y=(-0.20+(-0.05*length(levels(factor(marker))))), label="Number of events") for (q in 1:length(levels(factor(marker)))){ p<- p + annotate("text", cex=3, x=-0.15*max(xlims),y=(-0.20+(-0.05*length(levels(factor(marker))))+(-0.05*q)), label=paste0(ystratalabs[q])) for(i in ((q-1)*length(times)+1):(q*length(times))){ p <- p + annotate("text", cex=3, x=risk.data$time[i], y=(-0.20+(-0.05*length(levels(factor(marker))))+(-0.05*q)), label=paste0(risk.data$n.event[i])) } } } #adding the cens table if(n.event==TRUE){ p<- p + annotate("text", cex=3, x=0.5*max(xlims), y=(-0.25+(-0.05*length(levels(factor(marker)))*2)), label="Number of censored") for (q in 1:length(levels(factor(marker)))){ p<- p + annotate("text", cex=3, x=-0.15*max(xlims),y=(-0.25+(-0.05*length(levels(factor(marker)))*2)+(-0.05*q)), label=paste0(ystratalabs[q])) for(i in ((q-1)*length(times)+1):(q*length(times))){ p <- p + annotate("text", cex=3, x=risk.data$time[i], y=(-0.25+(-0.05*length(levels(factor(marker)))*2)+(-0.05*q)), label=paste0(risk.data$n.cens[i])) } } } #adding panel marker p <- p + annotate("text", cex=10, x= -0.2*max(xlims), y=1.1, label=panel) #drawing the plot with the tables outside the margins gt <- ggplot_gtable(ggplot_build(p)) gt$layout$clip[gt$layout$name=="panel"] <- "off" grid.draw(gt) }