/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2005 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */
package org.freedesktop.cairo;

import org.gnu.glib.Struct;
import org.gnu.glib.Handle;

public class Context extends CairoObject {

    /**
     * Creates a new Context with all graphics state parameters set to default
     * values and with target as a target surface. The target surface should be
     * constructed with a backend-specific function such as ImageSurface.create.
     * 
     * @param target
     *            target surface for the context.
     */
    public Context(Surface target) {
        super(cairo_create(target.getHandle()));
    }

    protected Context(Handle hndl) {
        super(hndl);
    }

    /**
     * Disposes all the native resources used by the object.
     */
    protected void finalize() throws Throwable {
        cairo_destroy(getHandle());
        super.finalize();
    }

    /**
     * Makes a copy of the current state and saves it on an internal stack of
     * saved states. When {@link #restore()} is called, the saved state will be
     * restored. Multiple calls <tt>save()</tt> and <tt>restore()</tt> can
     * be nested; each call to <tt>restore()</tt> restores the state from the
     * matching paired <tt>save()</tt>.
     */
    public void save() {
        cairo_save(getHandle());
    }

    /**
     * Restores the state saved by a preceding call to {@link #save()} and
     * removes that state from the stack of saved states.
     */
    public void restore() {
        cairo_restore(getHandle());
    }

    /**
     * Sets the compositing operator to be used for all drawing operations.
     * 
     * @param op
     */
    public void setOperator(Operator op) {
        cairo_set_operator(getHandle(), op.getValue());
    }

    /**
     * Sets the source pattern within the Cairo object. This pattern will then
     * be used for any subsequent drawing operation until a new source pattern
     * is set.
     * 
     * @param pattern
     */
    public void setSource(Pattern pattern) {
        cairo_set_source(getHandle(), pattern.getHandle());
    }

    /**
     * Sets a constant color for filling and stroking. The color components are
     * floating point numbers in the range 0 to 1. If the values passed in are
     * outside that range, they will be clamped.
     * 
     * @param red
     * @param green
     * @param blue
     */
    public void setSourceRGB(double red, double green, double blue) {
        cairo_set_source_rgb(getHandle(), red, green, blue);
    }

    /**
     * Sets the source pattern within this Cairo object to a translucent color.
     * This color will then be used for any subsequent drawing operation until a
     * new source pattern is set.
     * 
     * The color and alpha components are floating point numbers in the range 0
     * to 1. If the values passed in are outside that range, they will be
     * clamped.
     * 
     * @param red
     * @param green
     * @param blue
     * @param alpha
     */
    public void setSourceRGBA(double red, double green, double blue,
            double alpha) {
        cairo_set_source_rgba(getHandle(), red, green, blue, alpha);
    }

    /**
     * 
     * @param surface
     * @param x
     * @param y
     */
    public void setSource(Surface surface, double x, double y) {
        cairo_set_source_surface(getHandle(), surface.getHandle(), x, y);
    }

    /**
     * Sets the tolerance used when converting paths into trapezoids. Curved
     * segments of the path will be subdivided until the maximum deviation
     * between the original path and the polygonal approximation is less than
     * tolerance. The default value is 0.1. A larger value will give better
     * performance, a smaller value, better appearance. (Reducing the value from
     * the default value of 0.1 is unlikely to improve appearance
     * significantly).
     * 
     * @param tolerance
     */
    public void setTolerance(double tolerance) {
        cairo_set_tolerance(getHandle(), tolerance);
    }

    /**
     * Set the current fill rule within the Context. The fill rule is used to
     * determine which regions are inside or outside a complex (potentially
     * self-intersecting) path. The current fill rule affects both <tt>fill</tt>
     * and <tt>clip</tt>.
     * 
     * @param fillrule
     */
    public void setFillRule(FillRule fillrule) {
        cairo_set_fill_rule(getHandle(), fillrule.getValue());
    }

    /**
     * Sets the current line width within the cairo context. The line width
     * specifies the diameter of a pen that is circular in user-space.
     * 
     * As with the other stroke parameters, the current line cap style is
     * examined by <tt>stroke()</tt>, <tt>strokeExtents()</tt>, and
     * <tt>strokeToPath(), but does not have any effect during path
     * construction.
     * @param width
     */
    public void setLineWidth(double width) {
        cairo_set_line_width(getHandle(), width);
    }

    /**
     * Sets the current line cap style within the cairo context. See
     * <tt>LineCap</tt> for details about how the available line cap styles
     * are drawn.
     * 
     * @param linecap
     */
    public void setLineCap(LineCap linecap) {
        cairo_set_line_cap(getHandle(), linecap.getValue());
    }

    /**
     * Sets the current line join style within the cairo context. See
     * <tt>LineJoin</tt> for details about how the available line join styles
     * are drawn.
     * 
     * @param linejoin
     */
    public void setLineJoin(LineJoin linejoin) {
        cairo_set_line_join(getHandle(), linejoin.getValue());
    }

    /**
     * Sets the line dash.
     * 
     * @param dashes
     * @param offset
     */
    public void setDash(double[] dashes, double offset) {
        cairo_set_dash(getHandle(), dashes, offset);
    }

    /**
     * Sets the miter limit.
     * 
     * @param limit
     */
    public void setMiterLimit(double limit) {
        cairo_set_miter_limit(getHandle(), limit);
    }

    /**
     * Modifies the current transformation matrix (CTM) by tanslating the
     * user-space origin by (tx, ty). This offset is interpreted as a user-space
     * coordinate according to the CTM in place before the new call to
     * translate. In other words, the translation of the user-space origin takes
     * place after any existing transformation.
     * 
     * @param tx
     * @param ty
     */
    public void translate(double tx, double ty) {
        cairo_translate(getHandle(), tx, ty);
    }

    /**
     * Modifies the current transformation matrix (CTM) by scaling the X and Y
     * user-space axes by sx and sy respectively. The scaling of the axes takes
     * place after any existing transformation of user space.
     * 
     * @param sx
     * @param sy
     */
    public void scale(double sx, double sy) {
        cairo_scale(getHandle(), sx, sy);
    }

    /**
     * Modifies the current transformation matrix (CTM) by rotating the
     * user-space axes by angle radians. The rotation of the axes takes places
     * after any existing transformation of user space. The rotation direction
     * for positive angles is from the positive X axis toward the positive Y
     * axis.
     * 
     * @param angle
     */
    public void rotate(double angle) {
        cairo_rotate(getHandle(), angle);
    }

    /**
     * Modifies the current transformation matrix (CTM) by applying matrix as an
     * additional transformation. The new transformation of user space takes
     * place after any existing transformation.
     * 
     * @param matrix
     *            the transformation matrix to append
     */
    public void transform(Matrix matrix) {
        cairo_transform(getHandle(), matrix.getHandle());
    }

    /**
     * Modifies the current transformation matrix (CTM) by setting it equal to
     * matrix.
     * 
     * @param matrix
     *            the transformation matrix
     */
    public void setMatrix(Matrix matrix) {
        cairo_set_matrix(getHandle(), matrix.getHandle());
    }

    /**
     * Resets the current transformation matrix (CTM) by setting it equal to the
     * identity matrix. That is, the user-space and device-space axes will be
     * aligned and one user-space unit will transform to one device-space unit.
     */
    public void identityMatrix() {
        cairo_identity_matrix(getHandle());
    }

    /**
     * Transform a coordinate from user space to device space by multiplying the
     * given point by the current transformation matrix (CTM).
     */
    public Point userToDevice(Point point) {
        double[] px = new double[] { point.getX() };
        double[] py = new double[] { point.getY() };
        cairo_user_to_device(getHandle(), px, py);
        return new Point(px[0], py[0]);
    }

    /**
     * Transform a distance vector from user space to device space. This
     * function is similar to userToDevice() except that the translation
     * components of the CTM will be ignored when transforming the Point.
     */
    public Point userToDeviceDistance(Point distance) {
        double[] dx = new double[] { distance.getX() };
        double[] dy = new double[] { distance.getY() };
        cairo_user_to_device_distance(getHandle(), dx, dy);
        return new Point(dx[0], dy[0]);
    }

    /**
     * Transform a coordinate from device space to user space by multiplying the
     * given point by the inverse of the current transformation matrix (CTM).
     */
    public Point deviceToUser(Point point) {
        double[] px = new double[] { point.getX() };
        double[] py = new double[] { point.getY() };
        cairo_device_to_user(getHandle(), px, py);
        return new Point(px[0], py[0]);
    }

    /**
     * Transform a distance vector from device space to user space. This
     * function is similar to deviceToUser() except that the translation
     * components of the inverse CTM will be ignored when transforming the
     * Point.
     */
    public Point deviceToUserDistance(Point distance) {
        double[] dx = new double[] { distance.getX() };
        double[] dy = new double[] { distance.getY() };
        cairo_device_to_user_distance(getHandle(), dx, dy);
        return new Point(dx[0], dy[0]);
    }

    /**
     * Starts a new path. You can add path segments to this path using the path
     * extension methods (xxxxTo()).
     */
    public void newPath() {
        cairo_new_path(getHandle());
    }

    /**
     * Moves the current point in the path to the given co-ordinates.
     * 
     * @param p
     *            the point co-ordinate of the point to move to
     */
    public void moveTo(Point p) {
        cairo_move_to(getHandle(), p.getX(), p.getY());
    }

    public void moveTo(double x, double y) {
        cairo_move_to(getHandle(), x, y);
    }

    /**
     * Draws a line segment as part of the current path. The line is drawn from
     * the current point of the path to the new co-ordinates.
     * 
     * @param p
     *            the point coordinate for the end point for the line segment
     */
    public void lineTo(Point p) {
        cairo_line_to(getHandle(), p.getX(), p.getY());
    }

    public void lineTo(double x, double y) {
        cairo_line_to(getHandle(), x, y);
    }

    /**
     * Draws a cubic bezier curve from the current point to (x3, y3) using 2
     * control points (x1, y1) and (x2, y2).
     * 
     * @param p1
     *            x co-ordinate of the first control point
     * @param p2
     *            x co-ordinate of the second control point
     * @param p3
     *            x co-ordinate of the end point
     */
    public void curveTo(Point p1, Point p2, Point p3) {
        cairo_curve_to(getHandle(), p1.getX(), p1.getY(), p2.getX(), p2.getY(),
                p3.getX(), p3.getY());
    }

    public void curveTo(double x1, double y1, double x2, double y2, double x3,
            double y3) {
        cairo_curve_to(getHandle(), x1, y1, x2, y2, x3, y3);
    }

    /**
     * Adds an arc from angle1 to angle2 to the current path. If there is a
     * current point, that point is connected to the start of the arc by a
     * straight line segment. Angles are measured in radians with an angle of 0
     * along the X axis and an angle of %M_PI radians (90 degrees) along the Y
     * axis, so with the default transformation matrix, positive angles are
     * clockwise. (To convert from degrees to radians, use <literal>degrees *
     * (M_PI / 180.)</literal>.) This function gives the arc in the direction
     * of increasing angle; see arcNegative() to get the arc in the direction of
     * decreasing angle.
     * 
     * A full arc is drawn as a circle. To make an oval arc, you can scale the
     * current transformation matrix by different amounts in the X and Y
     * directions.
     * 
     * @param point
     * @param radius
     * @param angle1
     * @param angle2
     */
    public void arc(Point point, double radius, double angle1, double angle2) {
        cairo_arc(getHandle(), point.getX(), point.getY(), radius, angle1,
                angle2);
    }

    public void arc(double x, double y, double radius, double angle1,
            double angle2) {
        cairo_arc(getHandle(), x, y, radius, angle1, angle2);
    }

    /**
     * Adds an arc from angle1 to angle2 to the current path. The function
     * behaves identically to <tt>arc()</tt> except that instead of giving the
     * arc in the direction of increasing angle, it gives the arc in the
     * direction of decreasing angle.
     * 
     * @param point
     * @param radius
     * @param angle1
     * @param angle2
     */
    public void arcNegative(Point point, double radius, double angle1,
            double angle2) {
        cairo_arc_negative(getHandle(), point.getX(), point.getY(), radius,
                angle1, angle2);
    }

    public void arcNegative(double x, double y, double radius, double angle1,
            double angle2) {
        cairo_arc_negative(getHandle(), x, y, radius, angle1, angle2);
    }

    /**
     * Moves to the current path to a new point. The co-ordinates of the new
     * point are given in relation to the current point of the state.
     * 
     * @param p
     *            relative distance between current point and the new point
     */
    public void relMoveTo(Point p) {
        cairo_rel_move_to(getHandle(), p.getX(), p.getY());
    }

    public void relMoveTo(double x, double y) {
        cairo_rel_move_to(getHandle(), x, y);
    }

    /**
     * Draws a line segment as part of the current path. The line is drawn from
     * the current point of the path to the new co-ordinates. The new
     * co-ordinates are given relative to the current point.
     * 
     * @param p
     *            The relative coordinate for the end point for the line segment
     */
    public void relLineTo(Point p) {
        cairo_rel_line_to(getHandle(), p.getX(), p.getY());
    }

    public void relLineTo(double x, double y) {
        cairo_rel_line_to(getHandle(), x, y);
    }

    /**
     * Draws a cubic bezier curve from the current point to p3 using 2 control
     * points p1 and p2. The co-ordinates are specified relative to current
     * point in the path.
     * 
     * @param p1
     *            relative co-ordinate of the first control point
     * @param p2
     *            relative co-ordinate of the second control point
     * @param p3
     *            relative co-ordinate of the end point
     */
    public void relCurveTo(Point p1, Point p2, Point p3) {
        cairo_rel_curve_to(getHandle(), p1.getX(), p1.getY(), p2.getX(), p2
                .getY(), p3.getX(), p3.getY());
    }

    public void relCurveTo(double x1, double y1, double x2, double y2,
            double x3, double y3) {
        cairo_rel_curve_to(getHandle(), x1, y1, x2, y2, x3, y3);
    }

    /**
     * Draws a rectangle defined by the upper-left point p1 and the
     * lower-right point p2.
     * 
     * @param upperLeft
     *            x and y coordinates of the upper-left point
     * @param lowerRight
     *            x and y coordinates of the lower-right point
     */
    public void rectangle(Point upperLeft, Point lowerRight) {
        Rectangle r = new Rectangle(upperLeft, lowerRight);
        rectangle(r);
    }

    /**
     * Draws a rectangle defined by the Rectangle object rect.
     * 
     * @param rect
     *            Rectangle defining the coordinate of the shape to draw
     */
    public void rectangle(Rectangle rect) {
        cairo_rectangle(getHandle(), rect.getX(), rect.getY(), rect.getWidth(),
                rect.getHeight());
    }

    /**
     * Closes the current path by connecting current point to the starting point
     * with a line segment.
     */
    public void closePath() {
        cairo_close_path(getHandle());
    }

    /**
     * A drawing operator that paints the current source everywhere within the
     * current clip region.
     */
    public void paint() {
        cairo_paint(getHandle());
    }

    /**
     * A drawing operator that paints the current source everywhere within the
     * current clip region using a mask of constant alpha value alpha. The
     * effect is similar to <tt>paint()</tt>, but the drawing is faded out
     * using the alpha value.
     * 
     * @param alpha
     */
    public void paintWithAlpha(double alpha) {
        cairo_paint_with_alpha(getHandle(), alpha);
    }

    /**
     * A drawing operator that paints the current source using the alpha channel
     * of pattern as a mask. (Opaque areas of mask are painted with the source,
     * transparent areas are not painted.)
     * 
     * @param pattern
     */
    public void mask(Pattern pattern) {
        cairo_mask(getHandle(), pattern.getHandle());
    }

    /**
     * A drawing operator that paints the current source using the alpha channel
     * of surface as a mask. (Opaque areas of surface are painted with the
     * source, transparent areas are not painted.)
     * 
     * @param surface
     * @param sx
     * @param sy
     */
    public void mask(Surface surface, double sx, double sy) {
        cairo_mask_surface(getHandle(), surface.getHandle(), sx, sy);
    }

    /**
     * A drawing operator that strokes the current path according to the current
     * line width, line join, line cap, and dash settings. After stroke, the
     * current path will be cleared from the cairo context.
     */
    public void stroke() {
        cairo_stroke(getHandle());
    }

    /**
     * A drawing operator that strokes the current path according to the current
     * line width, line join, line cap, and dash settings. Unlike stroke(),
     * strokePreserve preserves the path within the cairo context.
     */
    public void strokePreserve() {
        cairo_stroke_preserve(getHandle());
    }

    /**
     * A drawing operator that fills the current path according to the current
     * fill rule. After fill, the current path will be cleared from the cairo
     * context.
     */
    public void fill() {
        cairo_fill(getHandle());
    }

    /**
     * A drawing operator that fills the current path according to the current
     * fill rule. Unlike fill(), fillPreserve preserves the path within the
     * cairo context.
     */
    public void fillPreserve() {
        cairo_fill_preserve(getHandle());
    }

    public void copyPage() {
        cairo_copy_page(getHandle());
    }

    public void showPage() {
        cairo_show_page(getHandle());
    }

    /**
     * Determines whether the point specified by (x, y) lies inside
     * the area bounded by the current path, including the path's
     * stroke area.
     *
     * @param x
     *            x coordinate of the point to test
     * @param y
     *            y coordinate of the point to test
     */
    public boolean inStroke(double x, double y) {
        return cairo_in_stroke(getHandle(), x, y);
    }

    public boolean inStroke(Point p) {
        return cairo_in_stroke(getHandle(), p.getX(), p.getY());
    }

    /**
     * Determines whether the point specified by (x, y) lies inside
     * the area bounded by the current path, excluding the path's
     * stroke area.
     *
     * @param x
     *            x coordinate of the point to test
     * @param y
     *            y coordinate of the point to test
     */
    public boolean inFill(double x, double y) {
        return cairo_in_fill(getHandle(), x, y);
    }

    public boolean inFill(Point p) {
        return cairo_in_fill(getHandle(), p.getX(), p.getY());
    }

    public Rectangle strokeExtents() {
        double[] x1 = new double[1];
        double[] y1 = new double[1];
        double[] x2 = new double[1];
        double[] y2 = new double[1];
        cairo_stroke_extents(getHandle(), x1, y1, x2, y2);
        return new Rectangle(x1[0], y1[0], x2[0], y2[0]);
    }

    public Rectangle fillExtents() {
        double[] x1 = new double[1];
        double[] y1 = new double[1];
        double[] x2 = new double[1];
        double[] y2 = new double[1];
        cairo_fill_extents(getHandle(), x1, y1, x2, y2);
        return new Rectangle(x1[0], y1[0], x2[0], y2[0]);
    }

    /**
     * Reset the current clip region to its original, unrestricted state. That
     * is, set the clip region to an infinitely large shape containing the
     * target surface. Equivalently, if infinity is too hard to grasp, one can
     * imagine the clip region being reset to the exact bounds of the target
     * surface.
     * 
     * Note that code meant to be reusable should not call resetClip() as it
     * will cause results unexpected by higher-level code which calls clip().
     * Consider using save() and restore() around clip() as a more robust means
     * of temporarily restricting the clip region.
     */
    public void resetClip() {
        cairo_reset_clip(getHandle());
    }

    /**
     * Establishes a new clip region by intersecting the current clip region
     * with the current path as it would be filled by fill() and according to
     * the current fill rule (see setFillRule()).
     * 
     * After clip, the current path will be cleared from the cairo context.
     * 
     * The current clip region affects all drawing operations by effectively
     * masking out any changes to the surface that are outside the current clip
     * region.
     * 
     * Calling clip() can only make the clip region smaller, never larger. But
     * the current clip is part of the graphics state, so a tempoarary
     * restriction of the clip region can be achieved by calling clip() within a
     * save()/restore() pair. The only other means of increasing the size of the
     * clip region is resetClip().
     */
    public void clip() {
        cairo_clip(getHandle());
    }

    /**
     * Establishes a new clip region by intersecting the current clip region
     * with the current path as it would be filled by fill() and according to
     * the current fill rule (see setFillRule()).
     * 
     * Unlike clip(), clipPreserve preserves the path within the cairo context.
     * 
     * The current clip region affects all drawing operations by effectively
     * masking out any changes to the surface that are outside the current clip
     * region.
     * 
     * Calling clip() can only make the clip region smaller, never larger. But
     * the current clip is part of the graphics state, so a tempoarary
     * restriction of the clip region can be achieved by calling clip() within a
     * save()/restore() pair. The only other means of increasing the size of the
     * clip region is resetClip().
     */
    public void clipPreserve() {
        cairo_clip_preserve(getHandle());
    }

    /**
     * Selects a family and style of font from a simplified description as a
     * family name, slant and weight. This method is meant to be used only for
     * applications with simple font needs: Cairo doesn't provide for operations
     * such as listing all available fonts on the system, and it is expected
     * that most applications will need to use a more comprehensive font
     * handling and text layout library in addition to Cairo.
     * 
     * @param family
     *            font family name
     * @param slant
     *            font slant
     * @param weight
     *            font weight
     */
    public void selectFontFace(String family, FontSlant slant, FontWeight weight) {
        cairo_select_font_face(getHandle(), family, slant.getValue(), weight
                .getValue());
    }

    /**
     * Sets the current font matrix to a scale by a factor of size, replacing
     * any font matrix previously set with cairo_setFontSize() or
     * setFontMatrix(). This results in a font size of size user space units.
     * (More precisely, this matrix will result in the font's em-square being a
     * size by size square in user space.)
     * 
     * @param scale
     *            the scaling factor.
     */
    public void setFontSize(double scale) {
        cairo_set_font_size(getHandle(), scale);
    }

    /**
     * Sets the current font matrix to matrix. The font matrix gives a
     * transformation from the design space of the font (in this space, the
     * em-square is 1 unit by 1 unit) to user space. Normally, a simple scale is
     * used (see setFontSize()), but a more complex font matrix can be used to
     * shear the font or stretch it unequally along the two axes
     * 
     * @param matrix
     *            transformation matrix.
     */
    public void setFontMatrix(Matrix matrix) {
        cairo_set_font_matrix(getHandle(), matrix.getHandle());
    }

    /**
     * Gets the current font matrix. See setFontMatrix()
     * 
     * @return the current font matrix
     */
    public Matrix getFontMatrix() {
        return new Matrix(cairo_get_font_matrix(getHandle()));
    }

    /**
     * Draws the given text on the screen.
     * 
     * @param text
     *            String to draw on the screen.
     */
    public void showText(String text) {
        cairo_show_text(getHandle(), text);
    }

    public void showGlyphs(Glyph[] glyphs) {
        if (null == glyphs || glyphs.length < 1)
            return;
        Handle[] hndls = new Handle[glyphs.length];
        for (int i = 0; i < glyphs.length; i++)
            hndls[i] = glyphs[i].getHandle();
        cairo_show_glyphs(getHandle(), hndls);
    }

    /**
     * Gets the current font face.
     */
    public FontFace getFontFace() {
        return new FontFace(cairo_get_font_face(getHandle()));
    }

    /**
     * Gets the font extents for the currently selected font.
     */
    public FontExtents fontExtents() {
        Handle hndl = Struct.getNullHandle();
        cairo_font_extents(getHandle(), hndl);
        return new FontExtents(hndl);
    }

    /**
     * Replaces the current FontFace object in the context with fontFace. The
     * replaced font face in the context will be destroyed if there are no other
     * references to it.
     * 
     * @param fontFace
     */
    public void setFontFace(FontFace fontFace) {
        cairo_set_font_face(getHandle(), fontFace.getHandle());
    }

    /**
     * Gets the extents for a string of text. The extents describe a user-space
     * rectangle that encloses the "inked" portion of the text, (as it would be
     * drawn by showText). Additionally, the xAdvance and yAdvance values
     * indicate the amount by which the current point would be advanced by
     * showText.
     * 
     * Note that whitespace characters do not directly contribute to the size of
     * the rectangle (extents.width and extents.height). They do contribute
     * indirectly by changing the position of non-whitespace characters. In
     * particular, trailing whitespace characters are likely to not affect the
     * size of the rectangle, though they will affect the xAdvance and yAdvance
     * values.
     */
    public TextExtents textExtents(String text) {
        Handle hndl = Struct.getNullHandle();
        cairo_text_extents(getHandle(), text, hndl);
        return new TextExtents(hndl);
    }

    /**
     * Gets the extents for an array of glyphs. The extents describe a
     * user-space rectangle that encloses the "inked" portion of the glyphs, (as
     * they would be drawn by showGlyphs). Additionally, the xAdvance and
     * yAdvance values indicate the amount by which the current point would be
     * advanced by cairo_show_glyphs.
     * 
     * Note that whitespace glyphs do not contribute to the size of the
     * rectangle (extents.width and extents.height).
     */
    public TextExtents glyphExtents(Glyph[] glyphs) {
        Handle hndl = Struct.getNullHandle();
        Handle[] gHndls = new Handle[glyphs.length];
        for (int i = 0; i < glyphs.length; i++)
            gHndls[i] = glyphs[i].getHandle();
        cairo_glyph_extents(getHandle(), gHndls, hndl);
        return new TextExtents(hndl);
    }

    public void textPath(String text) {
        cairo_text_path(getHandle(), text);
    }

    public void glyphPath(Glyph[] glyphs) {
        Handle[] gHndls = new Handle[glyphs.length];
        for (int i = 0; i < glyphs.length; i++)
            gHndls[i] = glyphs[i].getHandle();
        cairo_glyph_path(getHandle(), gHndls);
    }

    /**
     * Returns the current surface operator
     * 
     * @return The surface operator.
     */
    public Operator getOperator() {
        return Operator.intern(cairo_get_operator(getHandle()));
    }

    /**
     * Gets the current source pattern for this object.
     */
    public Pattern getSource() {
        Handle hndl = cairo_get_source(getHandle());
        return new Pattern(hndl);
    }

    /**
     * Returns the tesselation tolerance of the current state.
     * 
     * @return tesselation tolerance
     */
    public double getTolerance() {
        return cairo_get_tolerance(getHandle());
    }

    /**
     * Returns the current point of the surface.
     * 
     * The current point is returned in the user-space coordinate system. If
     * there is no defined current point then Point will be set to (0,0)
     * 
     * @return Current point for drawing
     */
    public Point getCurrentPoint() {
        double[] x = new double[1];
        double[] y = new double[1];
        cairo_get_current_point(getHandle(), x, y);
        return new Point(x[0], y[0]);
    }

    /**
     * Gets the current fill rule, as set by setFillRule().
     */
    public FillRule getFillRule() {
        return FillRule.intern(cairo_get_fill_rule(getHandle()));
    }

    /**
     * Returns the stroke line width.
     * 
     * @return The stroke line width
     */
    public double getLineWidth() {
        return cairo_get_line_width(getHandle());
    }

    /**
     * Returns current linecap style.
     * 
     * @return The line cap style
     */
    public LineCap getLineCap() {
        return LineCap.intern(cairo_get_line_cap(getHandle()));
    }

    /**
     * Return current line join style.
     * 
     * @return Line join style
     */
    public LineJoin getLineJoin() {
        return LineJoin.intern(cairo_get_line_join(getHandle()));
    }

    /**
     * Returns the miter limit for miter style line joins
     * 
     * @return The miter limit
     */
    public double getMiterLimit() {
        return cairo_get_miter_limit(getHandle());
    }

    /**
     * Returns the current transformation matrix
     * 
     * @return the current transformation matrix
     */
    public Matrix getMatrix() {
        Handle handle = cairo_get_matrix(getHandle());
        return new Matrix(handle);
    }

    /**
     * Gets the target surface for the cairo context as passed to the
     * constructor.
     * 
     * @return the target surface
     */
    public Surface getTarget() {
        return new Surface(cairo_get_target(getHandle()));
    }

    public Status status() {
        return Status.intern(cairo_status(getHandle()));
    }

    public void setAntialias(Antialias antialias) {
        cairo_set_antialias(getHandle(), antialias.getValue());
    }

    public Antialias getAntialias() {
        return Antialias.intern(cairo_get_antialias(getHandle()));
    }

    public void setFontOptions(FontOptions fontOptions) {
        cairo_set_font_options(getHandle(), fontOptions.getHandle());
    }

    public FontOptions getFontOptions() {
        return new FontOptions(cairo_get_font_options(getHandle()));
    }

    /** Constant use for drawing ellipse */
    private static final double SVG_ARC_MAGIC = 0.5522847498;

    /**
     * Creates an ellipse path.
     * 
     * @param cx
     *            X co-ordinate of the center of ellipse
     * @param cy
     *            Y co-ordinate of the center of ellipse
     * @param rx
     *            X radius of the ellipse
     * @param ry
     *            Y radius of the ellipse
     */
    public void ellipse(double cx, double cy, double rx, double ry) {

        /* approximate an ellipse using 4 bezier curves */
        cairo_new_path(getHandle());
        cairo_move_to(getHandle(), cx + rx, cy);
        cairo_curve_to(getHandle(), cx + rx, cy + ry * SVG_ARC_MAGIC, cx + rx
                * SVG_ARC_MAGIC, cy + ry, cx, cy + ry);
        cairo_curve_to(getHandle(), cx - rx * SVG_ARC_MAGIC, cy + ry, cx - rx,
                cy + ry * SVG_ARC_MAGIC, cx - rx, cy);
        cairo_curve_to(getHandle(), cx - rx, cy - ry * SVG_ARC_MAGIC, cx - rx
                * SVG_ARC_MAGIC, cy - ry, cx, cy - ry);
        cairo_curve_to(getHandle(), cx + rx * SVG_ARC_MAGIC, cy - ry, cx + rx,
                cy - ry * SVG_ARC_MAGIC, cx + rx, cy);
        cairo_close_path(getHandle());
    }

    /*
     * Native calls
     */
    native static final private Handle cairo_create(Handle surface);

    native static final private void cairo_destroy(Handle obj);

    native static final private void cairo_save(Handle obj);

    native static final private void cairo_restore(Handle obj);

    native static final private void cairo_set_operator(Handle obj, int operator);

    native static final private void cairo_set_source(Handle obj, Handle pattern);

    native static final private void cairo_set_source_rgb(Handle obj,
            double red, double green, double blue);

    native static final private void cairo_set_source_rgba(Handle object,
            double red, double green, double glue, double alpha);

    native static final private void cairo_set_source_surface(Handle object,
            Handle surface, double x, double y);

    native static final private void cairo_set_tolerance(Handle obj,
            double tolerance);

    native static final private void cairo_set_fill_rule(Handle obj,
            int fillRule);

    native static final private void cairo_set_line_width(Handle obj,
            double width);

    native static final private void cairo_set_line_cap(Handle obj, int lineCap);

    native static final private void cairo_set_line_join(Handle obj,
            int lineJoin);

    native static final private void cairo_set_dash(Handle obj,
            double[] dashes, double offset);

    native static final private void cairo_set_miter_limit(Handle obj,
            double limit);

    native static final private void cairo_translate(Handle obj, double tx,
            double ty);

    native static final private void cairo_scale(Handle obj, double sx,
            double sy);

    native static final private void cairo_rotate(Handle obj, double angle);

    native static final private void cairo_transform(Handle obj, Handle matrix);

    native static final private void cairo_set_matrix(Handle obj, Handle matrix);

    native static final private void cairo_identity_matrix(Handle obj);

    native static final private void cairo_user_to_device(Handle obj,
            double[] x, double[] y);

    native static final private void cairo_user_to_device_distance(Handle obj,
            double[] dx, double[] dy);

    native static final private void cairo_device_to_user(Handle obj,
            double[] x, double[] y);

    native static final private void cairo_device_to_user_distance(Handle obj,
            double[] dx, double[] dy);

    native static final private void cairo_new_path(Handle obj);

    native static final private void cairo_move_to(Handle obj, double x,
            double y);

    native static final private void cairo_line_to(Handle obj, double x,
            double y);

    native static final private void cairo_curve_to(Handle obj, double x1,
            double y1, double x2, double y2, double x3, double y3);

    native static final private void cairo_arc(Handle obj, double xc,
            double yc, double radius, double angle1, double angle2);

    native static final private void cairo_arc_negative(Handle obj, double xc,
            double yc, double radius, double angle1, double angle2);

    native static final private void cairo_rel_move_to(Handle obj, double dx,
            double dy);

    native static final private void cairo_rel_line_to(Handle obj, double dx,
            double dy);

    native static final private void cairo_rel_curve_to(Handle obj, double dx1,
            double dy1, double dx2, double dy2, double dx3, double dy3);

    native static final private void cairo_rectangle(Handle obj, double x,
            double y, double width, double height);

    native static final private void cairo_close_path(Handle obj);

    native static final private void cairo_paint(Handle obj);

    native static final private void cairo_paint_with_alpha(Handle obj,
            double alpha);

    native static final private void cairo_mask(Handle obj, Handle pattern);

    native static final private void cairo_mask_surface(Handle obj,
            Handle surface, double sx, double sy);

    native static final private void cairo_stroke(Handle obj);

    native static final private void cairo_stroke_preserve(Handle obj);

    native static final private void cairo_fill(Handle obj);

    native static final private void cairo_fill_preserve(Handle obj);

    native static final private void cairo_copy_page(Handle obj);

    native static final private void cairo_show_page(Handle obj);

    native static final private boolean cairo_in_stroke(Handle obj, double x,
            double y);

    native static final private boolean cairo_in_fill(Handle obj, double x,
            double y);

    native static final private void cairo_stroke_extents(Handle obj,
            double[] x1, double[] y1, double[] x2, double[] y2);

    native static final private void cairo_fill_extents(Handle obj,
            double[] x1, double[] y1, double[] x2, double[] y2);

    native static final private void cairo_reset_clip(Handle obj);

    native static final private void cairo_clip(Handle ojb);

    native static final private void cairo_clip_preserve(Handle obj);

    native static final private void cairo_select_font_face(Handle obj,
            String family, int slant, int weight);

    native static final private void cairo_set_font_size(Handle obj,
            double scale);

    native static final private void cairo_set_font_matrix(Handle obj,
            Handle matrix);

    native static final private Handle cairo_get_font_matrix(Handle obj);

    native static final private void cairo_show_text(Handle obj, String utf8);

    native static final private void cairo_show_glyphs(Handle obj,
            Handle[] glyphs);

    native static final private Handle cairo_get_font_face(Handle obj);

    native static final private void cairo_font_extents(Handle obj,
            Handle extents);

    native static final private void cairo_set_font_face(Handle obj, Handle font);

    native static final private void cairo_text_extents(Handle obj,
            String utf8, Handle extents);

    native static final private void cairo_glyph_extents(Handle obj,
            Handle[] glyphs, Handle extents);

    native static final private void cairo_text_path(Handle obj, String utf8);

    native static final private void cairo_glyph_path(Handle obj,
            Handle[] glyphs);

    native static final private int cairo_get_operator(Handle obj);

    native static final private Handle cairo_get_source(Handle obj);

    native static final private double cairo_get_tolerance(Handle obj);

    native static final private void cairo_get_current_point(Handle obj,
            double[] x, double[] y);

    native static final private int cairo_get_fill_rule(Handle obj);

    native static final private double cairo_get_line_width(Handle obj);

    native static final private int cairo_get_line_cap(Handle obj);

    native static final private int cairo_get_line_join(Handle obj);

    native static final private double cairo_get_miter_limit(Handle obj);

    native static final private Handle cairo_get_matrix(Handle obj);

    native static final private Handle cairo_get_target(Handle obj);

    native static final private int cairo_status(Handle obj);

    native static final private void cairo_set_antialias(Handle obj,
            int antialias);

    native static final private int cairo_get_antialias(Handle obj);

    native static final private void cairo_set_font_options(Handle obj,
            Handle fo);

    native static final private Handle cairo_get_font_options(Handle obj);

    // TODO: add the following
    // cairo_path_t *
    // cairo_copy_path (cairo_t *cr);
    //
    // cairo_path_t *
    // cairo_copy_path_flat (cairo_t *cr);
    //
    // void
    // cairo_append_path (cairo_t *cr,
    // cairo_path_t *path);
    //
    // void
    // cairo_path_destroy (cairo_path_t *path);
}
