Skip to content

Feature: support a transparent (or configurable) captcha background #52

Description

@McFcologne

Problem

SpecCaptcha (and the other captcha types) always render on an opaque white
background: the background is filled with Color.WHITE on a BufferedImage of type
TYPE_INT_RGB (no alpha channel), inside a private graphicsImage(...) method.

Because that method is private and the background colour/image type are hard-coded, there
is no way to get a captcha PNG with a transparent background through configuration or
subclassing — the only option is to re-implement the whole rendering.

Use case: we embed the image captcha directly on a page and want the page background to
show through instead of a white box.

How we worked around it

We had to subclass SpecCaptcha and fully re-implement out(OutputStream), mirroring the
original glyph + interference-line layout, but drawing onto a TYPE_INT_ARGB image with
no background fill:

public class TransparentSpecCaptcha extends SpecCaptcha {

    public TransparentSpecCaptcha(int width, int height, int len) {
        super(width, height, len);
    }

    @Override
    public boolean out(OutputStream os) {
        char[] strs = textChar();
        int w = getWidth(), h = getHeight();

        BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = (Graphics2D) bi.getGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // No background fill -> stays transparent.

        for (int i = 0; i < 5; i++) {
            drawBesselLine(1, g);
        }

        g.setFont(getFont());
        FontMetrics fm = g.getFontMetrics();
        int charWidth = w / strs.length;
        int leftPadding = (charWidth - (int) fm.getStringBounds("W", g).getWidth()) / 2;
        for (int i = 0; i < strs.length; i++) {
            g.setColor(color());
            String s = String.valueOf(strs[i]);
            int y = h - ((h - (int) fm.getStringBounds(s, g).getHeight()) >> 1);
            g.drawString(s, i * charWidth + leftPadding + 3, y - 3);
        }
        g.dispose();

        try {
            ImageIO.write(bi, "png", os);
            os.flush();
            return true;
        } catch (IOException e) {
            return false;
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions