diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
index 3298e3fbb4629cba0cb66187666b332df7406cc4..a83a14c965abd5bcac251a40c413dd2a97285694 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
@@ -22,40 +22,24 @@ import com.oracle.truffle.r.library.fastrGrid.device.GridColor;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError.Message;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
-    private static final double SMALL = 0.25;
-    private static final double RADIUS = 0.375;
-    private static final double SQRC = 0.88622692545275801364; /* sqrt(pi / 4) */
-    @SuppressWarnings("unused") private static final double DMDC = 1.25331413731550025119; /*
-                                                                                            * sqrt(
-                                                                                            * pi /
-                                                                                            * 4) *
-                                                                                            * sqrt(
-                                                                                            * 2)
-                                                                                            */
-    @SuppressWarnings("unused") private static final double TRC0 = 1.55512030155621416073; /*
-                                                                                            * sqrt(4
-                                                                                            * *
-                                                                                            * pi/(3
-                                                                                            * *
-                                                                                            * sqrt(3
-                                                                                            * )))
-                                                                                            */
-    @SuppressWarnings("unused") private static final double TRC1 = 1.34677368708859836060; /*
-                                                                                            * TRC0 *
-                                                                                            * sqrt(
-                                                                                            * 3) / 2
-                                                                                            */
-    @SuppressWarnings("unused") private static final double TRC2 = 0.77756015077810708036; /*
-                                                                                            * TRC0 /
-                                                                                            * 2
-                                                                                            */
+    private static final double TRC0 = 1.55512030155621416073; /* sqrt(4 * pi/(3 * sqrt(3))) */
+    private static final double TRC1 = 1.34677368708859836060; /* TRC0 * sqrt(3) / 2 */
+    private static final double TRC2 = 0.77756015077810708036; /* TRC0 / 2 */
+
+    // empiracally chosen to match GNU R look better
+    private static final double TRIANGLE_SIZE_FACTOR = 1.15;
+
+    // empirically chosen factor to visually approx match GNU R
+    private static final double SIZE_FACTOR = 0.375;
+
+    // we assume at leat 72 points per inch
+    private static final double PIXEL_SIZE = 1. / 72.;
 
     static {
         Casts casts = new Casts(LPoints.class);
@@ -85,120 +69,225 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
 
         // Note: unlike in other drawing primitives, we only consider length of x
         int length = Unit.getLength(xVec);
-        DrawingContext initialDrawingCtx = gpar.getDrawingContext(0);
-        PointDrawingContext pointDrawingCtx = new PointDrawingContext(initialDrawingCtx, initialDrawingCtx.getFillColor(), initialDrawingCtx.getFillColor());
+        ContextCache contextCache = new ContextCache(null);
         for (int i = 0; i < length; i++) {
             Point loc = TransformMatrix.transLocation(Point.fromUnits(xVec, yVec, i, conversionCtx), vpTransform.transform);
             double size = Unit.convertWidth(sizeVec, i, conversionCtx);
             if (loc.isFinite() && Double.isFinite(size)) {
-                pointDrawingCtx = pointDrawingCtx.update(gpar.getDrawingContext(i));
-                pointDrawingCtx = drawSymbol(pointDrawingCtx, dev, cex, pchVec.getDataAt(i % pchVec.getLength()), size, loc.x, loc.y);
+                contextCache = contextCache.from(gpar.getDrawingContext(i));
+                drawSymbol(contextCache, dev, cex, pchVec.getDataAt(i % pchVec.getLength()), size * SIZE_FACTOR, loc.x, loc.y);
             }
         }
         return RNull.instance;
     }
 
-    // transcribed from engine.c function GESymbol
-
-    private static PointDrawingContext drawSymbol(PointDrawingContext drawingCtx, GridDevice dev, double cex, int pch, double size, double x, double y) {
+    private static void drawSymbol(ContextCache ctxCache, GridDevice dev, double cex, int pch, double halfSize, double x, double y) {
         // pch 0 - 25 are interpreted as geometrical shapes, pch from ascii code of ' ' are
         // interpreted as corresponding ascii character, which should be drawn
+        // the coordinates should be interpreted as the center of the symbol
+        double fullSize = halfSize * 2;
+        DrawingContext emptyFill = ctxCache.getTransparentFill();
         switch (pch) {
-            case 46:
-                return drawDot(drawingCtx, dev, cex, x, y);
+            case 0:
+                drawSquare(emptyFill, dev, halfSize, x, y);
+                break;
             case 1:
-                return drawOctahedron(drawingCtx, dev, GridColor.TRANSPARENT, size, x, y);
-            case 19: /* R filled circle */
-                return drawFilledCircle(drawingCtx, dev, RADIUS * size, x, y);
-            case 20: /* R `Dot' (small circle) */
-                return drawFilledCircle(drawingCtx, dev, SMALL * size, x, y);
-            case 21: /* circles */
-                return drawCircle(drawingCtx, dev, size, x, y);
-            case 22: /* squares */
-                return drawSquare(drawingCtx, dev, size, x, y);
-            case 16:
-                return drawOctahedron(drawingCtx, dev, drawingCtx.getWrapped().getColor(), size, x, y);
+                dev.drawCircle(emptyFill, x, y, halfSize);
+                break;
+            case 2: // triangle up
+                triangleUp(emptyFill, dev, halfSize * TRIANGLE_SIZE_FACTOR, x, y);
+                break;
+            case 3: /* S plus */
+                drawPlus(emptyFill, dev, halfSize, x, y);
+                break;
+            case 4: // S times
+                drawTimes(emptyFill, dev, halfSize, x, y);
+                break;
+            case 5: // S diamond
+                drawDiamond(emptyFill, dev, halfSize, fullSize, x, y);
+                break;
+            case 6: // S triangle point down
+                triangleDown(emptyFill, dev, halfSize * TRIANGLE_SIZE_FACTOR, x, y);
+                break;
+            case 7: // S square and times superimposed
+                drawSquare(emptyFill, dev, halfSize, x, y);
+                drawTimes(emptyFill, dev, halfSize, x, y);
+                break;
+            case 8: // S times and plus superimposed
+                drawPlus(emptyFill, dev, halfSize, x, y);
+                drawTimes(emptyFill, dev, halfSize, x, y);
+                break;
+            case 9: // S diamond and plus superimposed
+                drawPlus(emptyFill, dev, halfSize, x, y);
+                drawDiamond(emptyFill, dev, halfSize, fullSize, x, y);
+                break;
+            case 10: // S circle and plus
+                dev.drawCircle(emptyFill, x, y, halfSize);
+                drawPlus(emptyFill, dev, halfSize, x, y);
+                break;
+            case 11: // S superimposed triangles
+                triangleUp(emptyFill, dev, halfSize * TRIANGLE_SIZE_FACTOR, x, y);
+                triangleDown(emptyFill, dev, halfSize * TRIANGLE_SIZE_FACTOR, x, y);
+                break;
+            case 12: // S square and plus superimposed
+                drawSquare(emptyFill, dev, halfSize, x, y);
+                drawPlus(emptyFill, dev, halfSize, x, y);
+                break;
+            case 13: // S circle and times
+                dev.drawCircle(emptyFill, x, y, halfSize);
+                drawTimes(ctxCache.original, dev, halfSize, x, y);
+                break;
+            case 14: // S rectangle with triangle up
+                dev.drawRect(emptyFill, x - halfSize, y - halfSize, fullSize, fullSize, 0);
+                drawConnected(ctxCache.getTransparentFill(), dev, x - halfSize, y - halfSize, x + halfSize, y - halfSize, x, y + halfSize);
+                break;
+            case 15: // S filled square
+            case 22: // S filled (with different color) square
+                dev.drawRect(ctxCache.getFilled(), x - halfSize, y - halfSize, fullSize, fullSize, 0);
+                break;
+            case 16: // S filled circle (should be 'octagon')
+            case 19: // S filled circle
+            case 21: // S filled (with different color) circle
+                dev.drawCircle(ctxCache.getFilled(), x, y, halfSize);
+                break;
+            case 17: // S filled triangle up
+            case 24: // S filled (with different color) triangle up
+                triangleUp(ctxCache.getFilled(), dev, halfSize * TRIANGLE_SIZE_FACTOR, x, y);
+                break;
+            case 18: // S filled diamond
+            case 23: // S filled (with different color) diamond
+                drawDiamond(ctxCache.getFilled(), dev, halfSize, fullSize, x, y);
+                break;
+            case 20: // S smaller filled circle
+                dev.drawCircle(ctxCache.getFilled(), x, y, halfSize * .6);
+                break;
+            case 25: // S triangle down filled
+                triangleDown(ctxCache.getFilled(), dev, halfSize * TRIANGLE_SIZE_FACTOR, x, y);
+                break;
+            case 46: // small dot
+                // we assume at leat 72 points per inch
+                dev.drawRect(ctxCache.getFilled(), x - PIXEL_SIZE / 2, y - PIXEL_SIZE / 2, PIXEL_SIZE, PIXEL_SIZE, 0);
+                break;
             default:
-                throw RInternalError.unimplemented("grid.points unimplemented symbol " + pch);
+                drawTextSymbol(ctxCache, dev, x, y, new String(new char[]{(char) pch}));
         }
     }
 
-    private static PointDrawingContext drawFilledCircle(PointDrawingContext drawingCtxIn, GridDevice dev, double radius, double x, double y) {
-        PointDrawingContext drawingCtx = drawingCtxIn.update(drawingCtxIn.getWrapped().getColor(), drawingCtxIn.getWrapped().getColor());
-        dev.drawCircle(drawingCtx, x, y, radius);
-        return drawingCtx;
+    private static void drawDiamond(DrawingContext ctx, GridDevice dev, double halfSize, double fullSize, double x, double y) {
+        dev.drawRect(ctx, x - halfSize, y - halfSize, fullSize, fullSize, 1.75 * Math.PI);
+    }
+
+    private static void drawSquare(DrawingContext ctx, GridDevice dev, double halfSize, double x, double y) {
+        double fullSize = halfSize * 2.;
+        dev.drawRect(ctx, x - halfSize, y - halfSize, fullSize, fullSize, 0);
+    }
+
+    private static void drawTimes(DrawingContext ctx, GridDevice dev, double halfSize, double x, double y) {
+        drawLine(ctx, dev, x - halfSize, y + halfSize, x + halfSize, y - halfSize);
+        drawLine(ctx, dev, x + halfSize, y + halfSize, x - halfSize, y - halfSize);
+    }
+
+    private static void drawPlus(DrawingContext ctx, GridDevice dev, double halfSize, double x, double y) {
+        drawLine(ctx, dev, x - halfSize, y, x + halfSize, y);
+        drawLine(ctx, dev, x, y + halfSize, x, y - halfSize);
     }
 
-    private static PointDrawingContext drawCircle(PointDrawingContext drawingCtx, GridDevice dev, double size, double x, double y) {
-        double xc = RADIUS * size;
-        dev.drawCircle(drawingCtx, x, y, xc);
-        return drawingCtx;
+    private static void triangleDown(DrawingContext ctx, GridDevice dev, double halfSize, double x, double y) {
+        double yc = halfSize * TRC2;
+        double xc = halfSize * TRC1;
+        drawConnected(ctx, dev, x, y - halfSize * TRC0, x - xc, y + yc, x + xc, y + yc);
     }
 
-    private static PointDrawingContext drawSquare(PointDrawingContext drawingCtx, GridDevice dev, double size, double x, double y) {
-        double xc = RADIUS * SQRC * size;
-        double yc = RADIUS * SQRC * size;
-        dev.drawRect(drawingCtx, x - xc, y - yc, x + xc, y + yc, 0);
-        return drawingCtx;
+    private static void triangleUp(DrawingContext ctx, GridDevice dev, double halfSize, double x, double y) {
+        double yc = halfSize * TRC2;
+        double xc = halfSize * TRC1;
+        drawConnected(ctx, dev, x, y + halfSize * TRC0, x - xc, y - yc, x + xc, y - yc);
     }
 
-    private static PointDrawingContext drawOctahedron(PointDrawingContext drawingCtxIn, GridDevice dev, GridColor fill, double size, double x, double y) {
-        PointDrawingContext drawingCtx = drawingCtxIn.update(drawingCtxIn.getWrapped().getColor(), fill);
-        dev.drawCircle(drawingCtx, x, y, RADIUS * size);
-        return drawingCtx;
+    private static void drawTextSymbol(ContextCache ctxCache, GridDevice dev, double x, double y, String symbols) {
+        double height = dev.getStringHeight(ctxCache.getSymbol(), symbols);
+        double width = dev.getStringWidth(ctxCache.getSymbol(), symbols);
+        dev.drawString(ctxCache.getSymbol(), x - width / 2, y - height / 2, 0, symbols);
     }
 
-    private static PointDrawingContext drawDot(PointDrawingContext drawingCtxIn, GridDevice dev, double cex, double x, double y) {
-        // NOTE: we are *filling* a rect with the current colour (we are not drawing the border AND
-        // we are not using the current fill colour)
-        PointDrawingContext drawingCtx = drawingCtxIn.update(GridColor.TRANSPARENT, drawingCtxIn.getWrapped().getColor());
-        /*
-         * The idea here is to use a 0.01" square, but to be of at least one device unit in each
-         * direction, assuming that corresponds to pixels. That may be odd if pixels are not square,
-         * but only on low resolution devices where we can do nothing better.
-         *
-         * For this symbol only, size is cex (see engine.c).
-         *
-         * Prior to 2.1.0 the offsets were always 0.5.
+    /**
+     * Simpler to use by hand version of drawPolyline. Points are expected to be in format [x1, y1,
+     * x2, y2, ...].
+     */
+    private static void drawConnected(DrawingContext ctx, GridDevice dev, double... points) {
+        assert points.length % 2 == 0 && points.length > 0;
+        double[] x = new double[(points.length / 2) + 1];
+        double[] y = new double[(points.length / 2) + 1];
+        x[x.length - 1] = points[0];
+        y[y.length - 1] = points[1];
+        for (int i = 0; i < x.length - 1; i++) {
+            x[i] = points[i * 2];
+            y[i] = points[(i * 2) + 1];
+        }
+        dev.drawPolygon(ctx, x, y, 0, y.length);
+    }
+
+    private static void drawLine(DrawingContext ctx, GridDevice dev, double x1, double y1, double x2, double y2) {
+        dev.drawPolyLines(ctx, new double[]{x1, x2}, new double[]{y1, y2}, 0, 2);
+    }
+
+    private static final class ContextCache {
+        public final DrawingContext original;
+        private DrawingContext filled;
+        private DrawingContext transprentFill;
+        private DrawingContext symbol;
+
+        private ContextCache(DrawingContext original) {
+            this.original = original;
+        }
+
+        ContextCache from(DrawingContext newOriginal) {
+            if (original == newOriginal) {
+                return this;
+            }
+            return new ContextCache(newOriginal);
+        }
+
+        /**
+         * Context with fill color set to the normal color of the original context.
          */
-        double xc = cex * 0.005;
-        double yc = cex * 0.005;
-        if (cex > 0 && xc < 0.5) {
-            xc = 0.5;
+        DrawingContext getFilled() {
+            if (filled == null) {
+                filled = new PointDrawingContext(original, original.getColor(), original.getColor(), 1);
+            }
+            return filled;
         }
-        if (cex > 0 && yc < 0.5) {
-            yc = 0.5;
+
+        DrawingContext getTransparentFill() {
+            if (transprentFill == null) {
+                transprentFill = new PointDrawingContext(original, original.getColor(), GridColor.TRANSPARENT, 1);
+            }
+            return transprentFill;
+        }
+
+        DrawingContext getSymbol() {
+            if (symbol == null) {
+                symbol = new PointDrawingContext(original, original.getColor(), original.getFillColor(), 1.4);
+            }
+            return symbol;
         }
-        dev.drawRect(drawingCtx, x - xc, y - yc, x + xc, y + yc, 0);
-        return drawingCtx;
     }
 
+    /**
+     * Context that has the same parameters as the given context except for the color and fill color
+     * and multiplication factor for font size, which are given explicitly.
+     */
     private static final class PointDrawingContext implements DrawingContext {
         private final DrawingContext inner;
         private final GridColor color;
         private final GridColor fillColor;
+        private final double fontsizeFactor;
 
-        private PointDrawingContext(DrawingContext inner, GridColor color, GridColor fillColor) {
+        private PointDrawingContext(DrawingContext inner, GridColor color, GridColor fillColor, double fontsizeFactor) {
             this.inner = inner;
             this.color = color;
             this.fillColor = fillColor;
-        }
-
-        // This allows to re-use the existing instance if it would have the same parameters. The
-        // assumption is that the users will actually draw many points in a row with the same
-        // parameters.
-        private PointDrawingContext update(GridColor newColor, GridColor newFillColor) {
-            if (this.color.equals(newColor) && this.fillColor.equals(newFillColor)) {
-                return this;
-            }
-            return new PointDrawingContext(inner, newColor, newFillColor);
-        }
-
-        private PointDrawingContext update(DrawingContext newInner) {
-            if (this.inner == newInner) {
-                return this;
-            }
-            return new PointDrawingContext(newInner, this.color, this.fillColor);
+            this.fontsizeFactor = fontsizeFactor;
         }
 
         @Override
@@ -233,7 +322,7 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
 
         @Override
         public double getFontSize() {
-            return inner.getFontSize();
+            return inner.getFontSize() * fontsizeFactor;
         }
 
         @Override
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java
index 5001af1fd813ade89275b4a5ba1e9156310d3cf5..e8051d5c63c0263639650ca2a42699774dc20f65 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCairo.java
@@ -43,7 +43,7 @@ public class DevCairo extends RExternalBuiltinNode {
 
         String filename = RRuntime.asString(args.getArgument(0));
         int witdh = RRuntime.asInteger(args.getArgument(2));
-        int height = RRuntime.asInteger(args.getArgument(2));
+        int height = RRuntime.asInteger(args.getArgument(3));
         if (RRuntime.isNA(witdh) || RRuntime.isNA(height) || RRuntime.isNA(filename) || filename.isEmpty()) {
             throw error(Message.INVALID_ARG_TYPE);
         }
diff --git a/documentation/graphics.md b/documentation/graphics.md
index e5b13a857ae5abe437e365b04e3f3ef4a2ce4d94..e940d16f39881787e71e3142786bacf1fd341b61 100644
--- a/documentation/graphics.md
+++ b/documentation/graphics.md
@@ -1,7 +1,7 @@
 # Introduction
 
 There are two main built-in R packages that provide basic graphical output 
-support: *graphics* and *lattice*. They both use some parts of even lower level 
+support: *graphics* and *grid*. They both use some parts of even lower level
 *grDevices* package, which allows users to change the "device" to which the 
 graphical output will be drawn. The probably most popular graphical packages 
 *lattice* and *ggplot2* are build on top of the *grid* package.
@@ -84,5 +84,13 @@ method and pass the graphics object to R code using `PolyglotEngine`.
 The R code can do any *grid* based visualization and it will be directly 
 displayed in the UI.
 
+# Limitations
 
+FastR's grid implementation does not yet support:
 
+* expressions in `grid.text`
+* `grid.xspline` function
+* clipping
+
+FastR does not plan to implement the R graphics engine display list
+and related functions. However, the grid display list is implemented.