AnnoyinC
AnnoyinC

Reputation: 496

How to make custom svg font glyphs?

I'm trying to make an svg font for the unicode braille range. I want the black dots to be filled dots in (#fontcolor), and the non-dots to be circular lines.

"⠛" becomes the following svg:

<svg width="40" height="80">
    <g
        style="fill-opacity:1;fill:#000000;stroke:#000000;stroke-width:2;stroke-opacity:1"
    >
        <circle id="c0"
            cx="10" cy="10" r="9" />
        <circle id="c1"
            cx="10" cy="30" r="9" />
        <circle id="c2"
            style="fill:none;"
            cx="10" cy="50" r="9" />
        <circle id="c3"
            cx="30" cy="10" r="9" />
        <circle id="c4"
            cx="30" cy="30" r="9" />
        <circle id="c5"
            style="fill:none;"
            cx="30" cy="50" r="9" />
        <circle id="c6"
            style="fill:none;"
            cx="10" cy="70" r="9" />
        <circle id="c7"
            style="fill:none;"
            cx="30" cy="70" r="9" />
    </g>
</svg>

Here's an image of what I'm trying to copy: braille image

As you can tell, the relation between braille glyphs is highly patern-ic. A brailly glyph has 8 possible dots, and each dot is like a bit: from 2800 to 28ff, or 00 to ff, the braille glpyhs appear to visibly map to an 8-bit binary number.

Making 256 glyphs is also a lot of work. So I wrote some C to generate svg glyphs automatically. However, the resulting svg font doesn't seem correct, because when using it, every glyph gets rendered as a single filled dot, but I don't know what the issue is. Is it a programming bug? Failure to follow the svg font spec?

One error is that I manually set the style to be "black". I want this to be the font's css color, but I don't know how to "access" that "variable" from within an svg font.

Compile and run without special flags, pipe the output into "font.svg".

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

typedef unsigned char u8;

void generateSVG(){

    /* unicode braille

    8 "bits" per glyph.

    pattern where each bit gets placed in the glyph:

    0 3
    1 4
    2 5
    6 7

    */
    const char* prelude = 
    "<svg width=\"100%\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">"
    "<defs>"
    "<font id=\"Braille-font\" horiz-adv-x=\"40\">"
    "<font-face"
    "   units-per-em=\"40\""
    "   cap-height=\"80\""
    "   x-height=\"80\""
    "   ascent=\"80\""
    "   descent=\"0\""
    "   bbox=\"0, 0, 40, 80\""
    "   unicode-range=\"U+2800-28ff\""
    "   font-variant = \"normal\""
    ">"
    "<font-face-src>"
    "   <font-face-name name=\"Braille Font\" />"
    "</font-face-src>"
    "</font-face>";

    const char* postlude = "</font></defs></svg>";

    printf("%s\n", prelude);

    const int poslut[8][2] = {
        {10,10},
        {10,30},
        {10,50},
        {30,10},
        {30,30},
        {30,50},
        {10,70},
        {30,70}
    }; // {x,y}[]

    const char* cfmt = "<circle cx=\"%d\" cy=\"%d\" r=\"9\" %s/>";
    const char* gfmt = "<glyph glyph-name=\"uni28%02x\" unicode=\"&#x28%02x;\" horiz-adv-x=\"40\">";

    // [0] = override the fill to "none", [1] = don't override
    const char* fills[2] = {"style=\"fill:none;\" ",""};

    u8 i = 0;
    do {

        char buffer[1024] = {'\0'};
        int pos = sprintf(buffer, gfmt, i,i);

        u8 bits[8] = {
            [0] = i & 0b00000001,
            [1] = i & 0b00000010,
            [2] = i & 0b00000100,
            [3] = i & 0b00001000,
            [4] = i & 0b00010000,
            [5] = i & 0b00100000,
            [6] = i & 0b01000000,
            [7] = i & 0b10000000
        };
        // for some reason the bit shift method (i&(1<<bit)) does not work, so an ugly lut is required

        for (u8 bit = 0; bit < 8; ++bit)
        {
            int x = poslut[bit][0];
            int y = poslut[bit][1];

            //const char* fill = i&(1<<bit) ? fills[1] : fills[0] ;
            //const char* fill = fills[ i&(1<<bit) != 0 ];
            const char* fill = fills[bits[bit] != 0];


            pos += sprintf(&buffer[pos], cfmt,
                                         x, y, fill
            );
        }
        pos += sprintf(&buffer[pos], "</glyph>");

        printf("%s\n", buffer);

        i++;
    } while (i != 0); // 256 mod 256 = 0

    printf("%s\n", postlude);
}

Upvotes: 0

Views: 727

Answers (1)

Michael Rovinsky
Michael Rovinsky

Reputation: 7210

Here is an example of 'filled' and 'hollow' circle paths:

Outer circle (Clockwise): 'M cx-or,cy A or,or 1 1 1 cx+or,cy A or,or 1 1 1 cx-or,cy'

Inner circle (Counter-clockwise): 'M cx+ir,cy A ir ir 1 1 0 cx-ir,cy A ir,ir 1 1 0 cx+ir,cy'

where cx,cy - center point coordinates, ir - inner radius, or - outer radius

Filled circle is outer only, hollow is a combination of both inner and outer.

<svg width="32" height="32">
  <path d="M 6,12 A 6,6 1 1 1 18,12 A 6,6 1 1 1 6,12"/>
</svg>
  
<svg width="32" height="32">  
  <path d="M 6,12 A 6,6 1 1 1 18,12 A 6,6 1 1 1 6,12 M 16,12 A 4,4 1 1 0 8,12 A 4,4 1 1 0 16,12"/> 
</svg>

Upvotes: 2

Related Questions