you can edit gtable to set heights for physical units (like cm) instead of relative ("null")
require(ggplot2) p = qplot(Sepal.Width, Sepal.Length, data=iris) + facet_wrap(~Species, ncol=1) g = ggplotGrob(p) panels = which(sapply(g[["heights"]], "attr", "unit") == "null") g[["heights"]][panels] = list(unit(4, "cm"), unit(8, "cm"), unit(2, "cm")) device.height = convertHeight(sum(g[["heights"]]), "in", valueOnly=TRUE) pdf("test.pdf", height = device.height) grid.draw(g) dev.off()

Change As a subsequent function, the height and width of all panels with a fixed value are set here,
freeze_panels <- function(p, draw=TRUE, width=unit(5,"cm"), height=unit(1,"in")){ require(grid) g <- ggplotGrob(p) vertical_panels <-which(sapply(g[["heights"]], "attr", "unit") == "null") horizontal_panels <-which(sapply(g[["widths"]], "attr", "unit") == "null") g[["heights"]][vertical_panels] <- replicate(length(vertical_panels), height, simplify=FALSE) g[["widths"]][horizontal_panels] <- replicate(length(horizontal_panels), width, simplify=FALSE) device.height <- convertHeight(sum(g[["heights"]]), "in", valueOnly=TRUE) device.width <- convertWidth(sum(g[["widths"]]), "in", valueOnly=TRUE) if(draw){ dev.new(height=device.height, width=device.width) grid.newpage() grid.draw(g) } invisible(g) } require(ggplot2) d1 <- subset(mtcars, carb != 8) d2 <- subset(mtcars, carb %in% c(1,2,3)) p = qplot(vs, am, data=d1) + facet_wrap(~carb) freeze_panels(p) freeze_panels(p %+% d2) freeze_panels(p %+% d2 + facet_wrap(~carb, ncol=1))