protected internal virtual TextString convertToCharacters(string text, Graphics2D g, FontFactory fontFactory, ColorFactory colorFactory)
        {
            TextString        characters = new TextString();
            FontRenderContext frc        = g.getFontRenderContext();
            double            lastx      = 0;

            for (int i = 0; i < text.Length; i++)
            {
                Font          font   = fontFactory.getFont(i);
                char          c      = text[i];
                FontMetrics   fm     = g.getFontMetrics(font);
                Rectangle2D   bounds = font.getStringBounds(Convert.ToString(c), frc);
                TextCharacter tc     = new TextCharacter();
                tc.Character = c;
                tc.Font      = font;
                tc.Width     = fm.charWidth(c);
                tc.Height    = fm.getAscent() + fm.getDescent();
                tc.Ascent    = fm.getAscent();
                tc.Descent   = fm.getDescent();
                tc.X         = lastx;
                tc.Y         = 0;
                tc.Font      = font;
                tc.Color     = colorFactory.getColor(i);
                lastx       += bounds.getWidth();
                characters.addCharacter(tc);
            }
            return(characters);
        }
        public override BufferedImage filter(BufferedImage src, BufferedImage dest)
        {
            if (dest == null)
            {
                dest = createCompatibleDestImage(src, null);
            }
            double     width  = dest.getWidth();
            double     height = dest.getHeight();
            Graphics2D g2     = (Graphics2D)src.getGraphics();

            g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
            Random r  = new Random();
            int    cp = 4 + r.Next(3);

            int[] xPoints = new int[cp];
            int[] yPoints = new int[cp];
            width -= 10;
            for (int i = 0; i < cp; i++)
            {
                xPoints[i] = (int)((int)5 + (i * width) / (cp - 1));
                yPoints[i] = (int)(height * (r.NextDouble() * 0.5 + 0.2));
            }
            int subsections = 6;

            int[] xPointsSpline = new int[(cp - 1) * subsections];
            int[] yPointsSpline = new int[(cp - 1) * subsections];
            for (int i = 0; i < cp - 1; i++)
            {
                double x0 = i > 0 ? xPoints[i - 1] : 2 * xPoints[i] - xPoints[i + 1];
                double x1 = xPoints[i];
                double x2 = xPoints[i + 1];
                double x3 = (i + 2 < cp) ? xPoints[i + 2] : 2 * xPoints[i + 1] - xPoints[i];
                double y0 = i > 0 ? yPoints[i - 1] : 2 * yPoints[i] - yPoints[i + 1];
                double y1 = yPoints[i];
                double y2 = yPoints[i + 1];
                double y3 = (i + 2 < cp) ? yPoints[i + 2] : 2 * yPoints[i + 1] - yPoints[i];
                for (int j = 0; j < subsections; j++)
                {
                    xPointsSpline[i * subsections + j] = (int)catmullRomSpline(x0, x1, x2, x3, 1.0 / subsections * j);
                    yPointsSpline[i * subsections + j] = (int)catmullRomSpline(y0, y1, y2, y3, 1.0 / subsections * j);
                }
            }
            for (int i = 0; i < xPointsSpline.Length - 1; i++)
            {
                g2.setColor(colorFactory.getColor(i));
                g2.setStroke(new BasicStroke(2 + 2 * r.nextFloat()));
                g2.drawLine(xPointsSpline[i], yPointsSpline[i], xPointsSpline[i + 1], yPointsSpline[i + 1]);
            }
            return(src);
        }