Reading SVG paths using Boost

Here’s a bit of code to read SVG paths – it’s from my code to convert vector graphics to OpenVG commands and 6809 assembler for the Vectrex (among other things). However, as that’s progressing at around the speed of continental drift I may as well put it up here in the hope that someone else may get some use out of it. It uses Boost.Spirit, which is an excellent method of turning EBNF grammars into something that looks like Martian hieroglyphics, should you be so minded :

// Literals.</pre>
<pre>
nsParser::rule<sIter> flag;
nsParser::rule<sIter> comma;
nsParser::rule<sIter> sign;
nsParser::rule<sIter> wsp;
nsParser::rule<sIter> digit;
nsParser::rule<sIter> dot;
nsParser::rule<sIter> comma_wsp;

nsParser::rule<sIter> exponent;
nsParser::rule<sIter> digit_sequence;
nsParser::rule<sIter> integer_constant;
nsParser::rule<sIter> floating_point_constant;
nsParser::rule<sIter> fractional_constant;

nsParser::rule<sIter> number;
nsParser::rule<sIter> nonnegative_number;
nsParser::rule<sIter> coordinate;
nsParser::rule<sIter> coordinate_pair;

// Path commands.

nsParser::rule<sIter> closepath;

nsParser::rule<sIter> lineto_argument_sequence;
nsParser::rule<sIter> lineto;
nsParser::rule<sIter> moveto_argument_sequence;
nsParser::rule<sIter> moveto;

nsParser::rule<sIter> horizontal_lineto_argument_sequence;
nsParser::rule<sIter> horizontal_lineto;
nsParser::rule<sIter> vertical_lineto_argument_sequence;
nsParser::rule<sIter> vertical_lineto;

nsParser::rule<sIter> curveto;
nsParser::rule<sIter> curveto_argument;
nsParser::rule<sIter> curveto_argument_sequence;

nsParser::rule<sIter> smooth_curveto;
nsParser::rule<sIter> smooth_curveto_argument;
nsParser::rule<sIter> smooth_curveto_argument_sequence;

nsParser::rule<sIter> quadratic_bezier_curveto;
nsParser::rule<sIter> quadratic_bezier_curveto_argument;
nsParser::rule<sIter> quadratic_bezier_curveto_argument_sequence;

nsParser::rule<sIter> smooth_quadratic_bezier_curveto;
nsParser::rule<sIter> smooth_quadratic_bezier_curveto_argument;
nsParser::rule<sIter> smooth_quadratic_bezier_curveto_argument_sequence;

nsParser::rule<sIter> elliptical_arc;
nsParser::rule<sIter> elliptical_arc_argument;
nsParser::rule<sIter> elliptical_arc_argument_sequence;

nsParser::rule<sIter> drawto_command;
nsParser::rule<sIter> drawto_commands;
nsParser::rule<sIter> moveto_drawto_command_group;
nsParser::rule<sIter> moveto_drawto_command_groups;
nsParser::rule<sIter> svg_path;

CParsePath::CParsePath ()
{
    InputString = "";
    itStart = InputString.begin ();
    itEnd = InputString.end ();

    // Define parser elements.

    flag = (nsParser::lit ('0') | nsParser::lit ('1'));
    comma = (nsParser::lit (','));
    sign = (nsParser::lit ('+') | nsParser::lit ('-'));

    wsp = (
        nsParser::lit (' ') |
        nsParser::lit ('\t') |
        nsParser::lit ('\r') |
        nsParser::lit ('\n')
    );

    digit = (
        nsParser::lit ('0') |
        nsParser::lit ('1') |
        nsParser::lit ('2') |
        nsParser::lit ('3') |
        nsParser::lit ('4') |
        nsParser::lit ('5') |
        nsParser::lit ('6') |
        nsParser::lit ('7') |
        nsParser::lit ('8') |
        nsParser::lit ('9')
    );

    dot = nsParser::lit ('.');



// 0 or 1 : EBNF ? Qi -
// 0 or more : EBNF * Qi *
// 1 or more : EBNF + Qi +

    comma_wsp = (+wsp >> -comma >> *wsp) | (comma >> *wsp);
/*
digit-sequence:
    digit
    | digit digit-sequence

    NOTE : Don't understand - isn't this just "one or more"?
*/

    digit_sequence = (+digit);
    exponent = ((nsParser::lit ('E') | nsParser::lit ('e')) >> -sign >> digit_sequence);
    integer_constant = digit_sequence;
    fractional_constant = ((-digit_sequence >> dot >> digit_sequence) | (digit_sequence >> dot));
    floating_point_constant = ((fractional_constant >> -digit_sequence) | (digit_sequence >> exponent));
    number = ((-sign >> integer_constant) | (-sign >> floating_point_constant));
    nonnegative_number = (integer_constant | floating_point_constant);
    coordinate = number;
    coordinate_pair = (coordinate >> -comma-wsp >> coordinate);

    closepath = (nsParser::lit (ABSOLUTE_CLOSEPATH) | nsParser::lit (RELATIVE_CLOSEPATH));
    lineto_argument_sequence = ((coordinate_pair) | (coordinate_pair >> -comma_wsp >> lineto_argument_sequence));
    lineto = (nsParser::lit (ABSOLUTE_LINETO) | nsParser::lit (RELATIVE_LINETO)) >> *wsp >> lineto_argument_sequence;
    moveto_argument_sequence = ((coordinate_pair) | (coordinate_pair >> -comma_wsp >> lineto_argument_sequence));
    moveto = (nsParser::lit (ABSOLUTE_MOVETO) | nsParser::lit (RELATIVE_MOVETO)) >> *wsp >> moveto_argument_sequence;
    horizontal_lineto_argument_sequence = ((coordinate) | (coordinate >> -comma_wsp >> horizontal_lineto_argument_sequence));
    horizontal_lineto = (nsParser::lit (ABSOLUTE_HLINETO) | nsParser::lit (RELATIVE_HLINETO)) >> *wsp >> horizontal_lineto_argument_sequence;
    vertical_lineto_argument_sequence = ((coordinate) | (coordinate >> -comma_wsp >> vertical_lineto_argument_sequence));
    vertical_lineto = (nsParser::lit (ABSOLUTE_VLINETO) | nsParser::lit (RELATIVE_VLINETO)) >> *wsp >> vertical_lineto_argument_sequence;
    curveto_argument = (coordinate_pair >> -comma_wsp >> coordinate_pair >> -comma_wsp >> coordinate_pair);
    curveto_argument_sequence = ((curveto_argument) | (curveto_argument >> -comma-wsp >> curveto_argument_sequence));
    curveto = ((nsParser::lit (ABSOLUTE_CURVETO) | nsParser::lit (RELATIVE_CURVETO)) >> *wsp >> curveto_argument_sequence);
    smooth_curveto_argument = (coordinate_pair >> -comma_wsp >> coordinate_pair >> -comma_wsp >> coordinate_pair);
    smooth_curveto_argument_sequence = ((smooth_curveto_argument) | (smooth_curveto_argument >> -comma-wsp >> smooth_curveto_argument_sequence));
    smooth_curveto = ((nsParser::lit (ABSOLUTE_SCURVETO) | nsParser::lit (RELATIVE_CURVETO)) >> *wsp >> smooth_curveto_argument_sequence);
    quadratic_bezier_curveto_argument = (coordinate_pair >> -comma_wsp >> coordinate_pair);
    quadratic_bezier_curveto_argument_sequence = ((quadratic_bezier_curveto_argument) | (quadratic_bezier_curveto_argument >> -comma-wsp >> quadratic_bezier_curveto_argument_sequence));
    quadratic_bezier_curveto = ((nsParser::lit (ABSOLUTE_QCURVETO) | nsParser::lit (RELATIVE_QCURVETO)) >> *wsp >> quadratic_bezier_curveto_argument_sequence);
    smooth_quadratic_bezier_curveto_argument_sequence = ((smooth_quadratic_bezier_curveto_argument) | (smooth_quadratic_bezier_curveto_argument >> -comma-wsp >> smooth_quadratic_bezier_curveto_argument_sequence));
    smooth_quadratic_bezier_curveto_argument = (coordinate_pair >> -comma_wsp >> smooth_quadratic_bezier_curveto_argument_sequence);
    smooth_quadratic_bezier_curveto = ((nsParser::lit (ABSOLUTE_SQCURVETO) | nsParser::lit (RELATIVE_SQCURVETO)) >> *wsp >> smooth_quadratic_bezier_curveto_argument_sequence);
    elliptical_arc_argument = nonnegative_number >> -comma_wsp >> nonnegative_number -comma_wsp >> number >> comma_wsp >> flag >> comma_wsp >> coordinate_pair;
    elliptical_arc_argument_sequence = (elliptical_arc_argument | (elliptical_arc_argument >> -comma_wsp >> elliptical_arc_argument_sequence));
    elliptical_arc = ((nsParser::lit (ABSOLUTE_ARCTO) | nsParser::lit (RELATIVE_ARCTO)) >> *wsp >> elliptical_arc_argument_sequence);

    drawto_command =
        closepath |
        lineto |
        horizontal_lineto |
        vertical_lineto |
        curveto |
        smooth_curveto |
        quadratic_bezier_curveto |
        smooth_quadratic_bezier_curveto |
        elliptical_arc;

    drawto_commands = (drawto_command | (drawto_command >> *wsp >> drawto_commands));
    moveto_drawto_command_group = (moveto >> *wsp >> -drawto_commands);
    moveto_drawto_command_groups = moveto_drawto_command_group | (moveto_drawto_command_group >> *wsp >> moveto_drawto_command_groups);

    svg_path = *wsp >> -moveto_drawto_command_groups >> *wsp;
}

</pre>
<pre>

NOTE : Obviously this is C++ rather than C, however I can’t seem to get WordPress to accept “C++” as a category name at present …

Leave a comment