NanoSVG is a simple stupid single-header-file SVG parse

Overview

This project is not actively maintained.

Nano SVG

Parser

screenshot of some splines rendered with the sample program

NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.

The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.

NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!

The shapes in the SVG images are transformed by the viewBox and converted to specified units. That is, you should get the same looking data as your designed in your favorite app.

NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.

The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. DPI (dots-per-inch) controls how the unit conversion is done.

If you don't know or care about the units stuff, "px" and 96 should get you going.

Rasterizer

screenshot of tiger.svg rendered with NanoSVG rasterizer

The parser library is accompanied with really simpler SVG rasterizer. Currently it only renders flat filled shapes.

The intended usage for the rasterizer is to for example bake icons of different size into a texture. The rasterizer is not particular fast or accurate, but it's small and packed in one header file.

Example Usage

width, image->height); // Use... for (shape = image->shapes; shape != NULL; shape = shape->next) { for (path = shape->paths; path != NULL; path = path->next) { for (i = 0; i < path->npts-1; i += 3) { float* p = &path->pts[i*2]; drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); } } } // Delete nsvgDelete(image);">
// Load
struct NSVGimage* image;
image = nsvgParseFromFile("test.svg", "px", 96);
printf("size: %f x %f\n", image->width, image->height);
// Use...
for (shape = image->shapes; shape != NULL; shape = shape->next) {
	for (path = shape->paths; path != NULL; path = path->next) {
		for (i = 0; i < path->npts-1; i += 3) {
			float* p = &path->pts[i*2];
			drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
		}
	}
}
// Delete
nsvgDelete(image);

Using NanoSVG in your project

In order to use NanoSVG in your own project, just copy nanosvg.h to your project. In one C/C++ define NANOSVG_IMPLEMENTATION before including the library to expand the NanoSVG implementation in that file. NanoSVG depends on stdio.h ,string.h and math.h, they should be included where the implementation is expanded before including NanoSVG.

#include <stdio.h>
#include <string.h>
#include <math.h>
#define NANOSVG_IMPLEMENTATION	// Expands implementation
#include "nanosvg.h"

By default, NanoSVG parses only the most common colors. In order to get support for full list of SVG color keywords, define NANOSVG_ALL_COLOR_KEYWORDS before expanding the implementation.

#include <stdio.h>
#include <string.h>
#include <math.h>
#define NANOSVG_ALL_COLOR_KEYWORDS	// Include full list of color keywords.
#define NANOSVG_IMPLEMENTATION		// Expands implementation
#include "nanosvg.h"

Compiling Example Project

In order to compile the demo project, your will need to install GLFW to compile.

NanoSVG demo project uses premake4 to build platform specific projects, now is good time to install it if you don't have it already. To build the example, navigate into the root folder in your favorite terminal, then:

  • OS X: premake4 xcode4
  • Windows: premake4 vs2010
  • Linux: premake4 gmake

See premake4 documentation for full list of supported build file types. The projects will be created in build folder. An example of building and running the example on OS X:

$ premake4 gmake
$ cd build/
$ make
$ ./example

License

The library is licensed under zlib license

Comments
  • Issue on computers with some languages

    Issue on computers with some languages

    The function atof use the system locale to define the decimal delimiter character (. or ,). The side effect is that the decimales are discarded. For example atof("4.5"); returns 4.0when the locale define , as the delimiter.

    One correction could be :

    static NSVG_INLINE float nsvg__atof(const char* s)
    {
    	char o[32] ;
    	strcpy(o, setlocale(LC_NUMERIC, NULL)) ;
    
    	setlocale(LC_NUMERIC,"C");
    	float f = atof(s);
    	setlocale(LC_NUMERIC, o);
    	
    	return f;
    }
    
    opened by Meakk 46
  • Array path->pts is out of bounds

    Array path->pts is out of bounds

    path->pts is an array of float reallocated at nanosvg.h:line 694 in nsvg__addPoint path->cpts is a number of allocated pairs, can be 0,8,16,32... Propose path->cpts=8. path->npts is a number of used pairs, increasing 0,1,2... Propose path->npts=7 Condition 'if (p->npts+1 > p->cpts)' { will be false and no new realloc. There is 16 floats allocated. p->npts++ will be 8. Then look into nanosvgrast.h:line 375, for (i = 0; i < path->npts-1; i += 3) { float* p = &path->pts[i*2]; nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); } Last i==6 will be addressed to [6*2+7] which is out of bound. What I missed?

    opened by SergeySlice 14
  • endless loop (DOS) when parsing crafted input via nsvgParseFromFile()

    endless loop (DOS) when parsing crafted input via nsvgParseFromFile()

    Mid-June, I discovered and privately reported an endless loop issue that happens in the following usage scenario with a small crafted input file:

    #include <stdint.h>
    #include <stdio.h>
    
    #define NANOSVG_IMPLEMENTATION
    #include "nanosvg.h"
    
    int main(int argc, char *argv[]) {
      // this will loop for some inputs
      NSVGimage* g_image = nsvgParseFromFile("nanosvg_loop_examplefile1.svg", "px", 96);
      return 0;
    }
    

    The expected security impact is a denial of service.

    So far, I have not received a reply from @memononen. Given that nanosvg is not actively maintained (see README.md), this is somewhat expected, but I want to report the issue anyway because there might still be active users of this library that are affected by this.

    @memononen : can you give some quick feedback on whether you want the details to be disclosed publicly here in the bugtracker or prefer them to stay nonpublic until the 15.9.2020 (90 days after initial disclosure)?

    opened by invd 13
  • CVE-2019-1000032 - Memory corruption bug in nsvg__parseColorRGB

    CVE-2019-1000032 - Memory corruption bug in nsvg__parseColorRGB

    This simple Proof of Concept

    <stop style="stop-color:rgb(0%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%)"/>
    
    

    crashes nanosvg. In this snippet it is clear why this happens:

    #include <stdio.h>
    #include <string.h>
    
    static unsigned int nsvg__parseColorRGB(const char* str)
    {
    	int r = -1, g = -1, b = -1;
    	char s1[32]="", s2[32]="";
    	sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
    	if (strchr(s1, '%')) {
    		return 1;
    	} else {
    		return 0;
    	}
    }
    
    int main(int argc, char const *argv[])
    {
    	printf("%d\n", nsvg__parseColorRGB("rgb(0%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%)"));
    	return 0;
    }
    

    sscanf tries to parse the string, and writes arbitrary number of '%' or '\t' into the s1 or s2 buffer. The buffer overflows and triggers a segfault. This could lead to memory corruption and/or denial of service.

    Regards bitwave

    CC: @gehaxelt for the fuzzing

    opened by bitwave 13
  • Firefox logo renders incorrectly

    Firefox logo renders incorrectly

    When attempting to render the firefox logo using the example2.c file from this repository I get the following output:

    svg

    This is the SVG: firefox

    I first noticed this problem when using Fuzzel (this launcher uses nanosvg for rendering SVGs).

    Please let me know if you need more information, thanks in advance.

    opened by diegoviola 11
  • add basic support for clip paths

    add basic support for clip paths

    Currently, the NanoSVG parser and rasterizer don't handle clip-paths at all (and won't even ignore them). This PR adds some support for them (via stencil bit buffers, including support for multiple intersecting clip paths).

    Here's a demo file exported from Affinity Designer using folded clip paths:

    clip-path-ball.zip

    With the current master NanoSVG, this is generated:

    without-patches

    With the PR, the image is rendered correctly:

    with-patches
    opened by poke1024 11
  • Does all types of .svg files work?

    Does all types of .svg files work?

    Hello! I am trying to use your library to parse .svg files, and then use libtess2 for tesselation in a game engine.

    When I get an .svg file from the web, it works just fine. When I save it from Illustrator 2017, it does not (most times). Have they updated the format or something?

    Looking at the xml, it might seem that version 1.0 is working and 1.1 is not, but I am just guessing randomly at this point.

    Any suggestions :) ?

    opened by duel1000 11
  • Incorrect colors

    Incorrect colors

    Library doesn't render proper colors. For example pure white #FFFFFF is rendered as #FCFCFC, you can check whites on tiger from library description. #000000 is rendered as #010101. Gradients seems to be affected to. Do you have any idea what could be source of this problem?

    Edit: Maybe this? (an other >> 8 operations) int a = ((int)cover[0] * ca) >> 8; vs int a = ((int)cover[0] * ca) / 255;

    opened by jry2 9
  • bug nsvg__pathArcTo()

    bug nsvg__pathArcTo()

    Hi,

    Try the following path and you will get a parse error due to a division by 0 in nsvg__pathArcTo()

    <?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" width="10000mm" height="10000mm"><path d="M40.7595, -20.5388 A38.200000000000074,38.200000000000074,0,0,1,32.0609, -22.2933"/></svg>
    

    The included patch fixes the bug and does not seem to create regression, as observed by the folowing screen shots 4cornerbig

    4cornersmall

    patch:

    Index: trunk/src/nanosvg.h
    ===================================================================
    --- trunk/src/nanosvg.h (revision 50)
    +++ trunk/src/nanosvg.h (working copy)
    @@ -1887,7 +1887,7 @@
        t[4] = cx; t[5] = cy;
    
        // Split arc into max 90 degree segments.
    -   ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 0.5f);
    +   ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f);
        hda = (da / (float)ndivs) / 2.0f;
        kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
        if (da < 0.0f)
    
    opened by maxlem 9
  • rasterization broken

    rasterization broken

    it seems rasterization is broken on my builds. any idea?

    here is the relevant code:

    // Load SVG, parse and rasterize
    char *str = new char[ size + 1 ];
    memcpy( str, ptr, size );
    str[size] = '\0';
    
    NSVGimage *image = nsvgParse( str, "px" /*units*/, 96.f /* dpi */ );
    if( image ) {
        // Create rasterizer (can be used to render multiple images).
        static struct install {
            NSVGrasterizer *rasterizer;
             install() { rasterizer = nsvgCreateRasterizer(); }
            ~install() { if( rasterizer ) nsvgDeleteRasterizer( rasterizer ); rasterizer = 0; }
        } local;
        // Allocate memory for image
        int w = image->width;
        int h = image->height;
    
        double scale = 1.0;
        imageWidth = image->width * scale;
        imageHeight = image->height * scale;
        imageBpp = 4;
    
        imageuc = (stbi_uc *)malloc(w*h*4);
        deleter = FREE_DELETER;
        if( imageuc ) {
            // Rasterizes SVG image, returns RGBA image (non-premultiplied alpha)
            //   r - pointer to rasterizer context
            //   image - pointer to image to rasterize
            //   tx,ty - image offset (applied after scaling)
            //   scale - image scale
            //   dst - pointer to destination image data, 4 bytes per pixel (RGBA)
            //   w - width of the image to render
            //   h - height of the image to render
            //   stride - number of bytes per scaleline in the destination buffer
            nsvgRasterize( local.rasterizer, image, 0,0,scale, imageuc, w, h, w*scale*4 );
        }
        nsvgDelete(image);
    }
    
    delete [] str;
    

    image

    ps: thanks for the lib btw! :grin:

    opened by r-lyeh-archived 9
  • nanosvg doesnt render beautyline icons

    nanosvg doesnt render beautyline icons

    Hi,

    Hi I use Arch Linux and standard package for fuzzel from official repository doesn't ship with librsvg and only nanosvg. As result I doesn't see many icons from beautyline icon set.

    Official fuzzel Arch package: ps_20221127160141

    And fuzzel build from git with librsvg: ps_20221127160103

    Is there a chance that nanosvg would work with beautyline?

    opened by roland-rollo 7
  • HTML &#; entities not supported

    HTML &#; entities not supported

    nanosvg.h does not appear to currently support HTML entity codes, such as &#59; for ". This is troublesome because some special characters like "<> can only appear in some positions as entities. A tool I am using ("Figma") is encoding a > characters in a shape id as &#62;, which is causing problems for my program when it tires to read that id using nanosvg (and it sees a raw &#62; instead of the < it expects).

    I wrote a small &# code decoder and would like to upstream it as a PR. However, I'm not sure exactly where to put it. I think the place to put the entity decode is

    	} else if (strcmp(name, "id") == 0) {
    		strncpy(attr->id, value, 63);
    

    in nsvg__parseAttr, either right after the strncpy or possibly replacing it. However, are there other places it is appropriate to do HTML entity decoding in such a patch? For example are <text> tags currently supported?

    opened by mcclure 0
  • Textured shapes get incorrectly set as PAINT_NONE fill

    Textured shapes get incorrectly set as PAINT_NONE fill

    If a shape's "fill" property is an url(), then attr->hasFill gets set to 2, but then at this point NanoSVG when checking for hasFill 2 checks to see if there's a gradient and if there's no gradient it just sets the shape fill type to NSVG_PAINT_NONE. This is causing my app to misbehave because it handles PAINT_NONE in a particular way and even if it can't currently access the fill url() through nanoSVG currently, it's important to know it is filled.

    I have locally patched this with:

    diff --git a/src/nanosvg.h b/src/nanosvg.h
    index 79a9878..0c55b8b 100644
    --- a/src/nanosvg.h
    +++ b/src/nanosvg.h
    @@ -75,7 +75,8 @@ enum NSVGpaintType {
            NSVG_PAINT_NONE = 0,
            NSVG_PAINT_COLOR = 1,
            NSVG_PAINT_LINEAR_GRADIENT = 2,
    -       NSVG_PAINT_RADIAL_GRADIENT = 3
    +       NSVG_PAINT_RADIAL_GRADIENT = 3,
    +       NSVG_PAINT_SPECIAL = 4
     };
    
     enum NSVGspreadType {
    @@ -995,7 +996,7 @@ static void nsvg__addShape(NSVGparser* p)
                    nsvg__getLocalBounds(localBounds, shape, inv);
                    shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type);
                    if (shape->fill.gradient == NULL) {
    -                       shape->fill.type = NSVG_PAINT_NONE;
    +                       shape->fill.type = NSVG_PAINT_SPECIAL;
                    }
            }
    

    I could create a PR, but I don't know if this is how you want it done upstream because nanosvgrast won't know how to deal with the new NSVG_PAINT_SPECIAL type.

    opened by mcclure 0
  • Unitless quantities are entirely unaffected by DPI

    Unitless quantities are entirely unaffected by DPI

    Say I have this basic SVG file:

    <svg width="356px" height="335px" viewBox="0px 0px 356px 335px" fill="none" xmlns="http://www.w3.org/2000/svg">
    <rect width="356px" height="335px" fill="white"/>
    <rect x="40px" y="70px" width="53px" height="58px" fill="#D9D9D9"/>
    </svg>
    

    ...and I open it by calling nsvgParse(svgString, "px", 0.25);. Nanosvg converts the units of the numbers in the files to pixels (a null conversion) and then divides them by four. Everything is as I expect when I read back the numbers.

    However say I then try to load this other file, which is the same but instead of "px" all values are unitless:

    <svg width="356" height="335" viewBox="0 0 356 335" fill="none" xmlns="http://www.w3.org/2000/svg">
    <rect width="356" height="335" fill="white"/>
    <rect x="40" y="70" width="53" height="58" fill="#D9D9D9"/>
    </svg>
    

    The art program I am currently using emits SVGs with this format. In this case, opening with nsvgParse(svgString, "px", 0.25); does not produce any rescaling. The DPI value appears to get totally ignored.

    Is this intentional?

    Expected behavior:

    1. I find many sources online suggesting that if units are not provided on a value in an SVG file, the units are to be taken as px. Looking at section 8 of the SVG spec I am a little confused as to whether this is true, but spec section 8.3 seems to say this is the case for at least the viewport (IE, the width and height of the file itself):

      If the width or height presentation attributes on the outermost svg element are in user units (i.e., no unit identifier has been provided), then the value is assumed to be equivalent to the same number of "px" units (see Units).

      So I would expect a SVG file with no units to behave exactly the same as the px units.

    2. If the behavior is intentional, and DPI does not control the scaling of user units, then I would hope for some way to manually invoke scaling in place of using DPI, as scaling a document as it is loaded is useful.

    3. Finally, whichever way nanoSvg behaves, I think the comment in the header file explaining DPI should be a little clearer. Currently all it says is:

      // The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. // DPI (dots-per-inch) controls how the unit conversion is done.

      This description does not mention user units (unitless values) as an option, and the description of what the DPI value means is vague. What does DPI mean when the value is of type "cm", or "px"?

    Analysis:

    I tried running NanoSVG in a debugger and breaking on nsvg__convertToPixels. What I found is that with the "unitless" sample file the switch statement was always NSVG_UNITS_USER (expected). However when I tested with the sample file where every value is px, the switch statement went to NSVG_UNITS_CM (very surprising). In the code, DPI is applied to values of type pt, pc, mm, cm, in but is not applied to values of type user-unit or pixel.

    In other words, from reading the code, the original intention of the code appears to be that the DPI should be ignored in both of my two sample files above; I assumed the bug was in the unitless case, but maybe it is the px case that has the bug. I do not understand where nanosvg is getting cm from because it is not either in the file nor the unit given to my nsvgParse() call.

    Configuration

    I am using nanosvg from the github repo, version 3bcdf2f3cdc

    opened by mcclure 5
  •  stroke-dashoffset error

    stroke-dashoffset error

    error svg sample:

        <linearGradient id="Gradient-3" x1="-58.064" y1="-3.352" x2="49.041" y2="-3.352" >
            <stop offset="0" stop-color="#f0a532"/>
            <stop offset="0.5" stop-color="#f8b645"/>
            <stop offset="1" stop-color="#ffc758"/>
        </linearGradient>
    
    
    </defs>
    
    
    
            <g id="Comp-3" transform="translate(190,190)">
                <g id="Group-1-3" opacity="1">
                    <path id="Path-1-3" fill="none" d="M49.302,-49.302C-5.156,-49.302,-49.302,-5.156,-49.302,49.302" stroke="url(#Gradient-3)" stroke-opacity="1" stroke-width="17" stroke-miterlimit="10"  stroke-dasharray="70" stroke-dashoffset="70.001" />
                </g>
            </g>
    
    opened by jiongt 3
Owner
Mikko Mononen
Mikko Mononen
C++ SVG library

SVG++ library can be thought of as a framework, containing parsers for various SVG syntaxes, adapters that simplify handling of parsed data and a lot of other utilities and helpers for the most common tasks. Take a look at Tutorial to get the idea about what it is.

Oleg Maximenko 452 Dec 24, 2022
NanoPM, single header only PatchMatch

NanoPM, single header only PatchMatch NanoPM is a single header-only implementation of PatchMatch algorithm written in C++. Could be used for variety

null 67 Sep 27, 2022
Utility to combine color channels from different textures into a single output.

unity-texture-packer ?? Utility to merge different texture channels into a final texture output. Install Using Git Make sure the Git client is install

Andy Duboc 587 Dec 28, 2022
A GIF art engine that will allow you to generate multi-layer GIFs from single GIFs as layers.

A GIF art engine that will allow you to generate multi-layer GIFs from single GIFs as layers. All the code in this repository has been written by me in c++, inspired by the generative art engine of HashLips that does not support GIFs as layers. The problem arose from my and my teamleader's need to generate animated images and then GIFs, in the same way as HashLips generated static images.

Andre 63 Jan 2, 2023
Reading, writing, and processing images in a wide variety of file formats, using a format-agnostic API, aimed at VFX applications.

README for OpenImageIO Introduction The primary target audience for OIIO is VFX studios and developers of tools such as renderers, compositors, viewer

OpenImageIO 1.6k Jan 2, 2023
A file dialog library for Dear ImGui

ImFileDialog A simple file dialog library for Dear ImGui. This library supports favorites, actual Windows icons, image previews, zooming in, etc... DI

dfranx 398 Jan 3, 2023
An AV1 Image (AVIF) file format plug-in for Adobe® Photoshop®

avif-format An AV1 Image (AVIF) file format plug-in for Adobe® Photoshop®. Single images can be loaded and saved using 8, 10 or 12 bits-per-channel, i

Nicholas Hayes 100 Dec 20, 2022
Image File Execution Options Injection

Image File Execution Options Injection Description from ATT&CK Adversaries may establish persistence and/or elevate privileges by executing malicious

anas 15 Dec 26, 2022
ImGuiFileDialog is a file selection dialog built for (and using only) Dear ImGui

ImGuiFileDialog Purpose ImGuiFileDialog is a file selection dialog built for (and using only) Dear ImGui. My primary goal was to have a custom pane wi

Aiekick 790 Jan 4, 2023
A header-only C++11 library for colors; color space converters for RGB, HSL, XYZ, Lab, etc. and perceptual color difference calculators such as CIEDE2000

color-util A header-only C++11 library for handling colors, including color space converters between RGB, XYZ, Lab, etc. and color difference calculat

Yuki Koyama 79 Oct 6, 2022
A slim, fast and header-only GIF loader written in C

gif_load This is an ANSI C compatible animated GIF loader in a single header file of less than 300 lines of code (less than 200 without empty lines an

Andrey Guskov 68 Dec 10, 2022
Small header-only C library to decompress any BC compressed image

Small header-only C library to decompress any BC compressed image

null 92 Jan 1, 2023
Flameshot - Powerful yet simple to use screenshot software.

Flameshot - Powerful yet simple to use screenshot software.

Flameshot 19.8k Dec 31, 2022
A (very) simple notification wrapper for Dear ImGui

imgui-notify Is a header-only wrapper made to create notifications with Dear ImGui. As I couldn't find any library for this I just decided to create m

Patrick 212 Jan 2, 2023
HDRView is a simple research-oriented image viewer with an emphasis on examining and comparing high-dynamic range (HDR) images

HDRView is a simple research-oriented high-dynamic range image viewer with an emphasis on examining and comparing images, and including minimalistic tonemapping capabilities. HDRView currently supports reading EXR, PNG, TGA, BMP, HDR, JPG, GIF, PNM, PFM, and PSD images and writing EXR, HDR, PNG, TGA, PPM, PFM, and BMP images.

Wojciech Jarosz 177 Jan 5, 2023
A simple API wrapper that generates images & facts of any animal

animality.h A simple API wrapper that generates images & facts of any animal Required dependencies: libcurl for sending HTTPS requests. pthreads for t

animality 5 Nov 10, 2022
A simple pixel-wise image comparator

imgcmp A simple pixel-wise image comparator. This tool compares between two images pixel by pixel. The features of this tool are minimal since it is d

Yetman 11 Nov 15, 2022
Simple, generally spec-compliant, hacky PNG Decoder written in C99.

Simple, generally spec-compliant, hacky PNG Decoder written in C99.

cristei 2 Nov 2, 2021
Simple image to ASCII art converter

ascii-art Simple image to ASCII art converter for Windows. Does not support gifs (for now). Usage Unzip the .zip folder from the releases and put your

Mattias Aksli 2 Jul 13, 2022