class CustomGraphicsStreamEngine extends PDFGraphicsStreamEngine
{
    /**
     * Constructor.
     *
     * @param page PDF Page
     */
    protected CustomGraphicsStreamEngine(PDPage page)
    {
        super(page);
    }

    public static void main(String[] args) throws IOException
    {
        File file = new File("src/main/resources/org/apache/pdfbox/examples/rendering/",
                             "custom-render-demo.pdf");

        try (PDDocument doc = Loader.loadPDF(file))
        {
            PDPage page = doc.getPage(0);
            CustomGraphicsStreamEngine engine = new CustomGraphicsStreamEngine(page);
            engine.run();
        }
    }
    
    /**
     * Runs the engine on the current page.
     *
     * @throws IOException If there is an IO error while drawing the page.
     */
    public void run() throws IOException
    {
        processPage(getPage());

        for (PDAnnotation annotation : getPage().getAnnotations())
        {
            showAnnotation(annotation);
        }
    }
    
    @Override
    public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException
    {
        System.out.printf("appendRectangle %.2f %.2f, %.2f %.2f, %.2f %.2f, %.2f %.2f%n",
                p0.getX(), p0.getY(), p1.getX(), p1.getY(),
                p2.getX(), p2.getY(), p3.getX(), p3.getY());
    }

    @Override
    public void drawImage(PDImage pdImage) throws IOException
    {
        System.out.println("drawImage");
    }

    @Override
    public void clip(int windingRule) throws IOException
    {
        System.out.println("clip");
    }

    @Override
    public void moveTo(float x, float y) throws IOException
    {
        System.out.printf("moveTo %.2f %.2f%n", x, y);
    }

    @Override
    public void lineTo(float x, float y) throws IOException
    {
        System.out.printf("lineTo %.2f %.2f%n", x, y);
    }

    @Override
    public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException
    {<FILL_FUNCTION_BODY>}

    @Override
    public Point2D getCurrentPoint() throws IOException
    {
        // if you want to build paths, you'll need to keep track of this like PageDrawer does
        return new Point2D.Float(0, 0);
    }

    @Override
    public void closePath() throws IOException
    {
        System.out.println("closePath");
    }

    @Override
    public void endPath() throws IOException
    {
        System.out.println("endPath");
    }

    @Override
    public void strokePath() throws IOException
    {
        System.out.println("strokePath");
    }

    @Override
    public void fillPath(int windingRule) throws IOException
    {
        System.out.println("fillPath");
    }

    @Override
    public void fillAndStrokePath(int windingRule) throws IOException
    {
        System.out.println("fillAndStrokePath");
    }

    @Override
    public void shadingFill(COSName shadingName) throws IOException
    {
        System.out.println("shadingFill " + shadingName.toString());
    }

    /**
     * Overridden from PDFStreamEngine.
     */
    @Override
    public void showTextString(byte[] string) throws IOException
    {
        System.out.print("showTextString \"");
        super.showTextString(string);
        System.out.println("\"");
    }

    /**
     * Overridden from PDFStreamEngine.
     */
    @Override
    public void showTextStrings(COSArray array) throws IOException
    {
        System.out.print("showTextStrings \"");
        super.showTextStrings(array);
        System.out.println("\"");
    }

    /**
     * Overridden from PDFStreamEngine.
     */
    @Override
    protected void showGlyph(Matrix textRenderingMatrix, PDFont font, int code, Vector displacement)
            throws IOException
    {
        System.out.print("showGlyph " + code);
        super.showGlyph(textRenderingMatrix, font, code, displacement);
    }
    
    // NOTE: there are may more methods in PDFStreamEngine which can be overridden here too.
}

        System.out.printf("curveTo %.2f %.2f, %.2f %.2f, %.2f %.2f%n", x1, y1, x2, y2, x3, y3);
    
<methods>public abstract void appendRectangle(java.awt.geom.Point2D, java.awt.geom.Point2D, java.awt.geom.Point2D, java.awt.geom.Point2D) throws java.io.IOException,public abstract void clip(int) throws java.io.IOException,public abstract void closePath() throws java.io.IOException,public abstract void curveTo(float, float, float, float, float, float) throws java.io.IOException,public abstract void drawImage(org.apache.pdfbox.pdmodel.graphics.image.PDImage) throws java.io.IOException,public abstract void endPath() throws java.io.IOException,public abstract void fillAndStrokePath(int) throws java.io.IOException,public abstract void fillPath(int) throws java.io.IOException,public abstract java.awt.geom.Point2D getCurrentPoint() throws java.io.IOException,public abstract void lineTo(float, float) throws java.io.IOException,public abstract void moveTo(float, float) throws java.io.IOException,public abstract void shadingFill(org.apache.pdfbox.cos.COSName) throws java.io.IOException,public abstract void strokePath() throws java.io.IOException<variables>private final non-sealed org.apache.pdfbox.pdmodel.PDPage page