I came across SVGCaptcha, and immediately knew it was a bad idea.

I would like you to show just how bad an idea this is by extracting the validation code from the SVG images that code produces.

An example image looks like this:
Here is the source of the example image:

<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
             width="200" height="40"
    > <rect x="0" y="0" width="200" height="40" 
        style="stroke: none; fill: none;" >
        </rect> <text style="fill: #4d9363;" x="5" y="34" font-size="20" transform="translate(5, 34) rotate(-17) translate(-5, -34)">8</text>
<text style="fill: #be8b33;" x="125" y="29" font-size="21" transform="translate(125, 29) rotate(17) translate(-125, -29)">f</text>
<text style="fill: #d561ff;" x="45" y="35" font-size="20" transform="translate(45, 35) rotate(-2) translate(-45, -35)">4</text>
<text style="fill: #3de754;" x="85" y="31" font-size="21" transform="translate(85, 31) rotate(-9) translate(-85, -31)">8</text>
<text style="fill: #5ed4bf;" x="25" y="33" font-size="22" transform="translate(25, 33) rotate(16) translate(-25, -33)">u</text>
<text style="fill: #894aee;" x="105" y="28" font-size="25" transform="translate(105, 28) rotate(9) translate(-105, -28)">1</text>
<text style="fill: #e4c437;" x="65" y="32" font-size="20" transform="translate(65, 32) rotate(17) translate(-65, -32)">x</text>

The input is the SVG image, which is a textual format.

The only real restriction is that your code must produce the values in the correct order.
The input <text> elements are in random order so you have to pay attention to the x attribute in the <text> tag

Score is the number of bytes in the code

Since the code currently does two transforms that cancel each-other out you can ignore them, but if you do take them in consideration, go ahead and take a 30% reduction from your score.

  • 3
    \$\begingroup\$ You haven't actually stated explicitly what the input and output are: I'm presuming the SVG file and the letters contained therein? And it's not clear to me whether answers are required to actually implement the SVG spec or whether they can assume that the SVG is generated by the current version of SVGCaptcha and so the transforms can be ignored. \$\endgroup\$ – Peter Taylor Nov 15 '15 at 22:51
  • \$\begingroup\$ I suggest limiting the output to STDOUT or function return value, and making it code-golf \$\endgroup\$ – TheDoctor Nov 15 '15 at 22:54
  • 1
    \$\begingroup\$ No, questions need an objective, quantifiable winning criterion to be on-topic for this site. \$\endgroup\$ – Alex A. Nov 15 '15 at 23:00
  • 7
    \$\begingroup\$ I'm not sure how relevant image-processing is here. \$\endgroup\$ – SuperJedi224 Nov 16 '15 at 0:42
  • 18
    \$\begingroup\$ This question is now the 4th result when googling 'svgcaptcha' :) \$\endgroup\$ – Blue Nov 16 '15 at 9:33

16 Answers 16


Bash, 63 56 39 bytes

cat<<_|grep -o 'x=.*>'|cut -c4-|sort -n|grep -o '>.</t'|cut -c2

grep -o 'x=.*>'|cut -c4-|sort -n|grep -o '>.</t'|cut -c2

grep -o 'x=.*<'|sort -k1.4n|rev|cut -c2

Note: requires cat, grep, sort, rev, and cut. Takes input from stdin. The output is separated by line breaks on stdout. Make sure to press CTRL+D (not COMMAND+D on Mac) when finished entering the CAPTCHA. Input must be followed by a newline and then '_'.

EDIT: Saved 13 bytes.

EDIT 2: Saved 20 bytes thanks to @manatwork!

  • \$\begingroup\$ GNU coreutils sort supports character position in the keydef: cut -c4-|sort -nsort -k1.4n. \$\endgroup\$ – manatwork Jan 18 '18 at 10:56
  • \$\begingroup\$ @manatwork Thanks, I updated the answer. \$\endgroup\$ – Coder-256 Jan 18 '18 at 21:55

CJam, 26 bytes


Try it online in the CJam interpreter.

How it works

q     e# Read all input from STDIN.
"x="/ e# Split it at occurrences of "x=".
2>    e# Discard the first two chunks (head and container).
{     e# Sort the remaining chunks by the following key:
  '"/ e#   Split at occurrences of '"'.
  1=  e#   Select the second chunk (digits of x="<digits>").
  i   e#   Cast to integer.
}$    e#
{     e# For each of the sorted chunks:
  '>/ e#   Split at occurrences of '>'.
  1=  e#   Select the second chunk.
  c   e#   Cast to character.
}/    e#

JavaScript, 95 93 91 bytes


edit: -2 bytes changing documentRoot to lastChild; -2 bytes changing join('') to join``, thanks Vɪʜᴀɴ

Enter code in the browser console on a page containg the SVG in question, writes to console output.

  • \$\begingroup\$ document.rootElement is retuning undefined. I've tried Firefox and Safari \$\endgroup\$ – Downgoat Nov 16 '15 at 2:31
  • \$\begingroup\$ This was only tested in Chrome, I'll look into what could be changed. \$\endgroup\$ – Nickson Nov 16 '15 at 2:32
  • \$\begingroup\$ It appears to work in Firefox, is SVG the only content of the file? \$\endgroup\$ – Nickson Nov 16 '15 at 2:39
  • \$\begingroup\$ Okay, tried it in Chrome, now it worked. +1. You can also save two bytes by changing the ('') to two backticks: `` \$\endgroup\$ – Downgoat Nov 16 '15 at 2:41
  • \$\begingroup\$ This is 78: t=>(l=[],r=/x="(\d*).*?>(.)/g,eval("while(e=r.exec(t))l[e[1]]=e[2];l.join``")) (takes xml string as parameter, returns captcha text) \$\endgroup\$ – DankMemes Nov 16 '15 at 7:25

Perl, 40 bytes

39 bytes code + 1 for -n

$a[$1]=$2 for/x="(.+)".+(.)</g}{print@a


perl -ne '$a[$1]=$2 for/x="(.+)".+(.)</g}{print@a' <<< '<example from above>'
  • \$\begingroup\$ Man that is just full of warnings if you turn them on. Excellent use of Perl's default lax nature. \$\endgroup\$ – Brad Gilbert b2gills Nov 16 '15 at 15:44
  • \$\begingroup\$ @BradGilbertb2gills Yeah, I try not to test the warnings, I'm so surprised any golfed code even works sometimes! \$\endgroup\$ – Dom Hastings Nov 17 '15 at 8:49

Bash + GNU utilities, 53

grep -Po '(?<=x=").*(?=<)'|sort -n|grep -Po '(?<=>).'

Like this answer, output is one char per line.


Perl 6, 68 bytes

say [~] lines.map({/'x="'(\d+).*(.)\</??(+$0=>$1)!!()}).sort».value

Befunge, 79 bytes

It feels like it should be possible to golf at least one more byte off of this, but I've been working on it for a couple of days now, and this is as good as I could get it.


Try it online!


Source code with execution paths highlighted

* Make the execution direction right-to-left, and wrap around to start the main loop.
* Read a char from stdin, and test for the end-of-file value.
* If it's not end-of-file, check if it's a >.
* If it's not a >, add it to the value on the stack which tracks the last two characters, and check if the current pair matches x=.
* If not, multiply by 126 and mod with 1262 to drop the oldest value from the pair and make space for the next character.
* Wrap around again to repeat the main loop.
* When an x= pair is encountered, skip the next character (the quote), read an integer (the x value), and divide by 20. This becomes the current offset which is saved for later.
* When a > is encountered, read the next character (typically one of the captcha letters), and save that at the current offset in an "array". Reset the offset to 9, so the captcha letter won't be overwritten when later > characters are encountered.
* Finally, when then end-of-file is reached, iterate over the 7 values saved in the array and output them one by one. That should give you all the captcha letters in the correct order.

I'm glossing over some of the details here, since the code paths overlap each other in ways which are a little difficult to explain, but it should give you a general idea of how the algorithm works.


Python2, 129 bytes

import re,sys
print''.join(t[1] for t in sorted(re.findall(r'(\d+), -\d+\)"\>(.)\</t',sys.stdin.read()),key=lambda t:int(t[0])))

Takes the HTML source on stdin, produces code on stdout.

  • \$\begingroup\$ How does this sort the output? The <text> elements are in a random order, and the only real requirement is that you have to put them in the correct order. That means you have to use the x from the <text> and follow any transforms. \$\endgroup\$ – Brad Gilbert b2gills Nov 15 '15 at 22:36
  • \$\begingroup\$ @BradGilbertb2gills I missed that the first time around, fixed now. \$\endgroup\$ – orlp Nov 15 '15 at 22:43

Mathematica, 106 bytes


Note: The input needs to be in exactly the format specified by the example.


V, 28 26 25 24 bytes


Try it online!


d5j              delete first 6 lines
   Í<0x81>x=     In every line, replace everything up to x=" (inclusive) by nothing
ún               Sort numerically
J                Join line (closing </svg>) with next line
 Í<0x81>">       In every line, replace everything up to "> by nothing
l␖H$d            Visual block around closing </text> tags, delete
     Íî          In every line, replace \n by nothing.


00000000: 6435 6acd 8178 3d0a fa6e 0a4a cd81 223e  d5j..x=..n.J..">
00000010: 0a6c 1648 2464 cdee                      .l.H$d..

QuadS, 49 bytes

∊c[⍋⊃x c←↓⍎¨@1⍉(⊢⍴⍨2,⍨.5×≢)3↓⍵]

Try it online!

Finds x values (digit-runs after x=") and "letters" (pinned by closing and opening tags), then executes the following APL (where is the list of found x values and letters, in order of appearance):

3↓⍵ drop the first three elements (spaces around <rect/rect> and the <rect's x value).

() apply the following tacit function on that:

 the number of remaining items

.5× halve that

2,⍨ append a two

⊢⍴⍨ reshape to that shape (i.e. an n×2 matrix)

 transpose (to a 2×n matrix)

⍎¨@1 execute each string in the first row (turning them into numbers)

 split the matrix into two vectors (one per row)

x c← store those two in x (x values) and c (characters) respectively

 pick the first (x)

 grade up (the indices into x which would sort x)

c[] use that to index into c

ϵnlist (flatten) because each letter is a string by itself

The equivalent APL expression of the entire QuadS program is:

∊c[⍋⊃x c←↓⍎¨@1⍉(⊢⍴⍨2,⍨.5×≢)3↓'x="(\d+)"' '>(.)<'⎕S'\1'⊢⎕]

Java 8, 197 173 bytes

import java.util*;s->{String a[]=s.split("x=\""),r="";Map m=new TreeMap();for(int i=2;i<a.length;m.put(new Long(a[i].split("\"")[0]),a[i++].split(">|<")[1]));return m.values();}

Outputs a java.util.Collection of characters.


Try it online.

import java.util*;            // Required import for Map and TreeMap
s->{                          // Method with String as both parameter and return-type
  String a[]=s.split("x=\""), //  Split the input by `x="`, and store it as String-array
         r="";                //  Result-String, starting empty
  Map m=new TreeMap();        //  Create a sorted key-value map
  for(int i=2;                //  Skip the first two items in the array,
      i<a.length;             //  and loop over the rest
    m.put(new Long(a[i].split("\"")[0]),
                              //   Split by `"` and use the first item as number-key
                              //   Split by `>` AND `<`, and use the second item as value
    return m.values();}       //  Return the values of the sorted map as result

Gema, 65 characters


In Gema there is no sorting, but fortunately is not even needed.

Sample run:

bash-4.4$ gema 'x\="<D>*\>?=@set{$1;?};?=;\Z=${5}${25}${45}${65}${85}${105}${125}' < captcha.svg

XMLStarlet, 46 characters

xmlstarlet sel -t -m //_:text -s A:N:U @x -v .

Hopefully this is valid solution as XMLStarlet is transpiler that generates and executes XSLT code, which is a Turing complete language.

Sample run:

bash-4.4$ xmlstarlet sel -t -m //_:text -s A:N:U @x -v . < captcha.svg 

PHP, 96 bytes

Given that $i is the input string

preg_match_all('|x="(\d+).*(.)\<|',$i,$m);$a=array_combine($m[1],$m[2]);ksort($a);echo join($a);
  • 1
    \$\begingroup\$ Instead of array_combine() + ksort() you could use array_multisort() like this: array_multisort($m[1],$m[2]);echo join($m[2]);. But please note that solutions are expected to handle input and output themselves (unless the language does it automatically), instead of expecting to find the input in a variable or just leave the result in a variable. See related meta. \$\endgroup\$ – manatwork Jan 19 '18 at 9:12

Clean, 277 150 bytes

Yay pattern matching!

import StdEnv,StdLib
?s=map snd(sort(zip(map(toInt o toString)[takeWhile isDigit h\\['" x="':h]<-tails s],[c\\[c:t]<-tails s|take 7 t==['</text>']])))

Try it online!

Defines the function ?, taking [Char] and giving [Char].


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.