Fanbase

svg/svg

version 1.0

Core SVG 1.1 elements (shapes, paths, text, gradients, filters, clipping/masking, markers, style/script, animation)

Format
Scalable Vector Graphics (svg)
Authors
The Fandango Fuzzer Team
License
Apache-2.0
Fandango
>=1.3
Extensions
svg
Media type
image/svg+xml
Specification
https://www.w3.org/TR/SVG2/
Its files should decode
mostly
In the registry
specs/svg/svg/svg.fan

Use it

fanbase install svg
fandango fuzz -F svg -n 10

How it does

accepts 85%, covers 80.6%, 53/s. 1000 inputs, seed 1.

ParserAcceptsVersion
imagemagick48%Version: ImageMagick 7.1.1-43 Q16 x86_64 22550
nanosvg-cov85%NanoSVG 239e102

The spec says its files decode mostly: as expected.

nanosvg-cov

lines reached
80.6% of 1785
real files reach
86.1%
only the generated files
72 lines
only the real files
170 lines
38.4% after 1 inputs, 80.6% after 1000real files: 86.1%

Cite it

@software{fanbase_svg_svg_1_0,
  author  = {{The Fandango Fuzzer Team}},
  title   = {Scalable Vector Graphics (svg/svg), a Fandango input specification},
  version = {1.0},
  url     = {https://github.com/fandango-fuzzer/fanbase},
  note    = {Fanbase registry, specs/svg/svg/svg.fan, sha256 1355a18f63a7},
}

Source

Source (394 lines)
# SVG 1.1: document structure (svg/g/defs/symbol/use/switch/a), basic
# shapes, paths, text, gradients/patterns, clipping/masking, filters,
# markers/images, style/script, and SMIL animation. Every element writes
# its own literal open/close tags, so start/end names match by
# construction.

<start> ::= <svg_document>

<svg_document> ::= <xml_decl> <svg_root> "\n"

<xml_decl> ::= '<?xml version="1.0" encoding="UTF-8"?>' "\n"

<svg_root> ::= (
      '<svg xmlns="http://www.w3.org/2000/svg"'
      ' xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"'
      ' width="' <length> '" height="' <length> '"'
      ' viewBox="' <viewbox> '">' "\n"
      <desc_block>
      <element>{1,6}
      "</svg>"
    )

# descriptive block shown once near the top
<desc_block> ::= <title_el> <desc_el>?

<title_el>    ::= "<title>" <text_content> "</title>" "\n"
<desc_el>     ::= "<desc>" <text_content> "</desc>" "\n"
<metadata_el> ::= "<metadata>" <text_content> "</metadata>" "\n"

# Top-level element choice
<element> ::= ( <leaf_element>
              | <container_g>
              | <defs_el>
              | <use_el>
              | <symbol_el>
              | <switch_el>
              | <a_el>
              | <lineargradient_el>
              | <radialgradient_el>
              | <pattern_el>
              | <clippath_el>
              | <mask_el>
              | <filter_el>
              | <marker_el>
              | <style_el>
              | <script_el>
              | <animation_el>
              | <metadata_el> )

<animation_el> ::= ( <animate_el>
                   | <animatetransform_el>
                   | <animatemotion_el>
                   | <set_el> )

# Leaf (non-container) elements -- safe to nest without recursion blow-up.
<leaf_element> ::= ( <rect_el>
                   | <circle_el>
                   | <ellipse_el>
                   | <line_el>
                   | <polyline_el>
                   | <polygon_el>
                   | <path_el>
                   | <text_el>
                   | <image_el>
                   | <use_el> )

# Containers
<container_g> ::= "<g" <common_attrs> ">" "\n" <leaf_element>{1,5} "</g>" "\n"

<defs_el> ::= "<defs>" "\n" <def_item>{1,5} "</defs>" "\n"

<def_item> ::= ( <lineargradient_el>
               | <radialgradient_el>
               | <pattern_el>
               | <clippath_el>
               | <mask_el>
               | <filter_el>
               | <marker_el>
               | <symbol_el>
               | <container_g> )

<symbol_el> ::= (
      '<symbol id="' <name> '" viewBox="' <viewbox> '">' "\n"
      <leaf_element>{1,4}
      "</symbol>" "\n" )

<switch_el> ::= "<switch>" "\n" <leaf_element>{1,3} "</switch>" "\n"

<a_el> ::= (
      '<a xlink:href="' <uri> '">' "\n"
      <leaf_element>{1,3}
      "</a>" "\n" )

<use_el> ::= (
      '<use xlink:href="#' <name> '"'
      ' x="' <coordinate> '" y="' <coordinate> '"'
      <common_attrs> "/>" "\n" )

# Basic shapes
<rect_el> ::= (
      '<rect x="' <coordinate> '" y="' <coordinate> '"'
      ' width="' <length> '" height="' <length> '"'
      <rxry>? <common_attrs> "/>" "\n" )

<rxry> ::= ' rx="' <length> '" ry="' <length> '"'

<circle_el> ::= (
      '<circle cx="' <coordinate> '" cy="' <coordinate> '"'
      ' r="' <length> '"' <common_attrs> "/>" "\n" )

<ellipse_el> ::= (
      '<ellipse cx="' <coordinate> '" cy="' <coordinate> '"'
      ' rx="' <length> '" ry="' <length> '"' <common_attrs> "/>" "\n" )

<line_el> ::= (
      '<line x1="' <coordinate> '" y1="' <coordinate> '"'
      ' x2="' <coordinate> '" y2="' <coordinate> '"'
      <common_attrs> "/>" "\n" )

<polyline_el> ::= '<polyline points="' <points> '"' <common_attrs> "/>" "\n"
<polygon_el>  ::= '<polygon points="' <points> '"' <common_attrs> "/>" "\n"

<path_el> ::= '<path d="' <path_data> '"' <common_attrs> "/>" "\n"

# Text
<text_el> ::= (
      '<text x="' <coordinate> '" y="' <coordinate> '"'
      <text_attrs> <common_attrs> ">"
      <text_run>
      "</text>" "\n" )

<text_run> ::= <text_content> <tspan_el>* <textpath_el>?

<tspan_el> ::= (
      '<tspan x="' <coordinate> '" dy="' <number> '"'
      <text_attrs> ">" <text_content> "</tspan>" )

<textpath_el> ::= (
      '<textPath xlink:href="#' <name> '">'
      <text_content> "</textPath>" )

<text_attrs> ::= <a_fontfamily>? <a_fontsize>? <a_textanchor>?

<a_fontfamily> ::= ' font-family="' <fontname> '"'
<a_fontsize>   ::= ' font-size="' <length> '"'
<a_textanchor> ::= ' text-anchor="' <anchor> '"'
<anchor>       ::= "start" | "middle" | "end"
<fontname>     ::= "sans-serif" | "serif" | "monospace" | "Arial" | "Verdana"

# Paint servers: gradients / patterns
<lineargradient_el> ::= (
      '<linearGradient id="' <name> '"'
      ' x1="' <pct> '%" y1="' <pct> '%" x2="' <pct> '%" y2="' <pct> '%">' "\n"
      <stop_el>{2,4}
      "</linearGradient>" "\n" )

<radialgradient_el> ::= (
      '<radialGradient id="' <name> '"'
      ' cx="' <pct> '%" cy="' <pct> '%" r="' <pct> '%">' "\n"
      <stop_el>{2,4}
      "</radialGradient>" "\n" )

<stop_el> ::= (
      '<stop offset="' <ratio> '" stop-color="' <color> '"'
      <stopopacity>? "/>" "\n" )

<stopopacity> ::= ' stop-opacity="' <ratio> '"'

<pattern_el> ::= (
      '<pattern id="' <name> '" width="' <length> '" height="' <length> '"'
      ' patternUnits="' <units> '">' "\n"
      <leaf_element>{1,3}
      "</pattern>" "\n" )

<units> ::= "userSpaceOnUse" | "objectBoundingBox"

# Clipping / masking
<clippath_el> ::= (
      '<clipPath id="' <name> '">' "\n"
      <leaf_element>{1,3}
      "</clipPath>" "\n" )

<mask_el> ::= (
      '<mask id="' <name> '">' "\n"
      <leaf_element>{1,3}
      "</mask>" "\n" )

# Filters
<filter_el> ::= (
      '<filter id="' <name> '">' "\n"
      <fe_primitive>{1,4}
      "</filter>" "\n" )

<fe_primitive> ::= ( <fe_gaussianblur>
                   | <fe_offset>
                   | <fe_blend>
                   | <fe_flood>
                   | <fe_colormatrix>
                   | <fe_merge>
                   | <fe_composite>
                   | <fe_morphology>
                   | <fe_turbulence>
                   | <fe_dropshadow> )

<fe_gaussianblur> ::= (
      '<feGaussianBlur in="' <fe_in> '" stdDeviation="' <number> '"/>' "\n" )

<fe_offset> ::= (
      '<feOffset in="' <fe_in> '" dx="' <number> '" dy="' <number> '"/>' "\n" )

<fe_blend> ::= (
      '<feBlend in="' <fe_in> '" in2="' <fe_in> '" mode="' <blendmode> '"/>' "\n" )

<fe_flood> ::= (
      '<feFlood flood-color="' <color> '" flood-opacity="' <ratio> '"/>' "\n" )

<fe_colormatrix> ::= (
      '<feColorMatrix type="matrix" values="' <matrix_values> '"/>' "\n" )

<fe_merge> ::= "<feMerge>" "\n" <fe_mergenode>{1,3} "</feMerge>" "\n"

<fe_mergenode> ::= '<feMergeNode in="' <fe_in> '"/>' "\n"

<fe_composite> ::= (
      '<feComposite in="' <fe_in> '" in2="' <fe_in> '" operator="' <compositeop> '"/>' "\n" )

<fe_morphology> ::= (
      '<feMorphology operator="' <morphop> '" radius="' <upos> '"/>' "\n" )

<fe_turbulence> ::= (
      '<feTurbulence type="' <turbtype> '" baseFrequency="' <ratio> '"'
      ' numOctaves="' <smallint> '"/>' "\n" )

<fe_dropshadow> ::= (
      '<feDropShadow dx="' <number> '" dy="' <number> '"'
      ' stdDeviation="' <upos> '" flood-color="' <color> '"/>' "\n" )

<fe_in>      ::= "SourceGraphic" | "SourceAlpha" | "BackgroundImage"
<blendmode>  ::= "normal" | "multiply" | "screen" | "darken" | "lighten"
<compositeop>::= "over" | "in" | "out" | "atop" | "xor" | "arithmetic"
<morphop>    ::= "erode" | "dilate"
<turbtype>   ::= "turbulence" | "fractalNoise"

# Markers / images
<marker_el> ::= (
      '<marker id="' <name> '" markerWidth="' <length> '"'
      ' markerHeight="' <length> '" orient="' <orient> '">' "\n"
      <leaf_element>{1,2}
      "</marker>" "\n" )

<orient> ::= "auto" | "auto-start-reverse" | <number>

<image_el> ::= (
      '<image xlink:href="' <uri> '" x="' <coordinate> '" y="' <coordinate> '"'
      ' width="' <length> '" height="' <length> '"/>' "\n" )

# Styling / scripting
<style_el> ::= '<style type="text/css">' <css_rules> "</style>" "\n"

<css_rules> ::= <css_rule>{1,3}
<css_rule>  ::= <css_selector> " { " <css_decl>{1,3} "}" "\n"
<css_selector> ::= ( "." <name> ) | ( "#" <name> ) | <shapename>
<css_decl>  ::= <css_prop> ": " <css_val> "; "
<css_prop>  ::= "fill" | "stroke" | "opacity" | "stroke-width"
<css_val>   ::= <color> | <ratio> | <number>
<shapename> ::= "rect" | "circle" | "path" | "text" | "g"

<script_el> ::= '<script type="text/javascript"><![CDATA[' <js_body> ']]></script>' "\n"
<js_body>   ::= r'[A-Za-z0-9_ ;=().+*/-]{0,40}'

# Animation
<animate_el> ::= (
      '<animate attributeName="' <animattr> '" from="' <number> '"'
      ' to="' <number> '" dur="' <duration> '"'
      ' repeatCount="' <repeatcount> '"/>' "\n" )

<animatetransform_el> ::= (
      '<animateTransform attributeName="transform" type="' <transformtype> '"'
      ' from="' <number> '" to="' <number> '" dur="' <duration> '"/>' "\n" )

<animatemotion_el> ::= (
      '<animateMotion path="' <path_data> '" dur="' <duration> '"/>' "\n" )

<set_el> ::= (
      '<set attributeName="' <animattr> '" to="' <number> '" dur="' <duration> '"/>' "\n" )

<animattr>      ::= "x" | "y" | "cx" | "cy" | "r" | "opacity" | "width" | "height"
<transformtype> ::= "translate" | "scale" | "rotate" | "skewX" | "skewY"
<duration>      ::= <upos> "s"
<repeatcount>   ::= "indefinite" | <smallint>

# Common presentation attributes (distinct optional slots => no dup attributes)
<common_attrs> ::= ( <a_id>? <a_class>? <a_fill>? <a_stroke>?
                     <a_strokewidth>? <a_opacity>? <a_transform>? <a_style>? )

<a_id>          ::= ' id="' <name> '"'
<a_class>       ::= ' class="' <name> '"'
<a_fill>        ::= ' fill="' <paint> '"'
<a_stroke>      ::= ' stroke="' <paint> '"'
<a_strokewidth> ::= ' stroke-width="' <length> '"'
<a_opacity>     ::= ' opacity="' <ratio> '"'
<a_transform>   ::= ' transform="' <transform> '"'
<a_style>       ::= ' style="' <inline_style> '"'

<inline_style>  ::= <css_decl>{1,3}

# Attribute value primitives
# Paint = a color, "none", "currentColor", or a reference to a paint server.
<paint> ::= <color> | "none" | "currentColor" | ( 'url(#' <name> ')' )

<color> ::= <hexcolor> | <shorthexcolor> | <rgbcolor> | <namedcolor>

<hexcolor>      ::= "#" r'[0-9a-fA-F]{6}'
<shorthexcolor> ::= "#" r'[0-9a-fA-F]{3}'

# rgb() with each channel constrained to the legal 0..255 range.
<rgbcolor> ::= "rgb(" <channel> "," <channel> "," <channel> ")"
<channel>  ::= r'[0-9]{1,3}'
where int(str(<channel>)) <= 255

<namedcolor> ::= ( "black" | "white" | "red" | "green" | "blue" | "yellow"
                 | "orange" | "purple" | "gray" | "cyan" | "magenta"
                 | "navy" | "teal" | "silver" | "maroon" | "olive" )

# A ratio in the closed unit interval [0, 1] (opacity, gradient offset, ...).
<ratio> ::= "0" | "1" | ( "0." r'[0-9]{1,3}' )

# A percentage value 0..100 (bare number; caller appends the '%').
<pct> ::= r'[0-9]{1,3}'
where int(str(<pct>)) <= 100

# Signed numbers and positive lengths.
<number>     ::= r'-?(0|[1-9][0-9]{0,3})(\.[0-9]{1,2})?'
<upos>       ::= r'(0|[1-9][0-9]{0,3})(\.[0-9]{1,2})?'
<coordinate> ::= <number>
<smallint>   ::= r'[1-9]'

# A length is a positive number with an optional CSS unit.
<length> ::= <upos> <unit>?
<unit>   ::= "px" | "pt" | "em" | "ex" | "%" | "cm" | "mm" | "in"

# viewBox = min-x min-y width height ; width/height must be non-negative.
<viewbox> ::= <number> " " <number> " " <upos> " " <upos>

# points list for polyline/polygon
<points> ::= <point> ( " " <point> ){2,8}
<point>  ::= <number> "," <number>

# names / ids and URIs
<name> ::= r'[A-Za-z_][A-Za-z0-9_-]{0,12}'
<uri>  ::= ( ( "https://example.org/" r'[A-Za-z0-9_/.-]{1,16}' )
           | ( "data:image/png;base64," r'[A-Za-z0-9+/]{4,16}' ) )

# text content: printable, no XML-significant chars (< & > " ')
<text_content> ::= r'[A-Za-z0-9 .,!?:;()-]{1,30}'

# transform attribute
<transform> ::= <transform_fn> ( " " <transform_fn> ){0,2}

<transform_fn> ::= ( ( "translate(" <number> "," <number> ")" )
                   | ( "scale(" <upos> ")" )
                   | ( "rotate(" <number> ")" )
                   | ( "rotate(" <number> "," <number> "," <number> ")" )
                   | ( "skewX(" <number> ")" )
                   | ( "skewY(" <number> ")" )
                   | ( "matrix(" <number> "," <number> "," <number> ","
                                 <number> "," <number> "," <number> ")" ) )

<matrix_values> ::= <number> ( " " <number> ){19}

# path-data mini grammar (subset of the SVG path command set)
<path_data> ::= <moveto> <path_command>{1,6}

<moveto> ::= ( "M" | "m" ) " " <coord_pair>

<path_command> ::= ( <lineto>
                   | <hlineto>
                   | <vlineto>
                   | <curveto>
                   | <quadto>
                   | <arcto>
                   | <closepath> )

<lineto>    ::= " " ( "L" | "l" ) " " <coord_pair>
<hlineto>   ::= " " ( "H" | "h" ) " " <number>
<vlineto>   ::= " " ( "V" | "v" ) " " <number>
<curveto>   ::= " " ( "C" | "c" ) " " <coord_pair> " " <coord_pair> " " <coord_pair>
<quadto>    ::= " " ( "Q" | "q" ) " " <coord_pair> " " <coord_pair>
<arcto>     ::= ( " " ( "A" | "a" ) " " <upos> " " <upos> " " <number>
                  " " <flag> " " <flag> " " <coord_pair> )
<closepath> ::= " " ( "Z" | "z" )

<coord_pair> ::= <number> "," <number>
<flag>       ::= "0" | "1"