1

Note: This is a question about vector techniques

I originally failed to make that clear enough. (I included "using CSS and SVG" in the title but hardly mentioned it again.)

Also: I am a long-time graphics programmer, making SVGs and Javascript as it happens. I am primarily a mathematician; offsetting paths and intersecting awesomely complex things is an amusement to me. I suppose mentioning that earlier might have been useful; we live and we learn...


Here is a good example of what I mean by the 'empty' parts of a font:

SVG rendered chess board

The pieces are Unicode characters -- so that they can be copied from the SVG to a text editor -- and as you can see, the square colours interfere with their appearance. Altering the 'fill' is definitely wrong; whole characters change colour, and still leave those same empty windows onto the square colours below.

They would of course look better if I could achieve the effect of backing each one with a plain white silhouette of the character. If only I had the outermost outline of each, as a closed path, that would become easy. (But is this possible? Is there even such a path? Surely it wouldn't be closed; can it be closed? Easily?)

Of course, I could define my own graphics for the chess pieces, but that would defeat the advantages of using Unicode characters, since it would require extra work by me and a larger file size for the user. I don't want to repeat the efforts of others, to design nice glyphs, and my eventual applications might involve many imported fonts etc.

So it would be really nice to find a robust solution that gets both the character, and an appropriate backing for that character, from the font.

This is not just for chess, although of course it makes a great example.

Thanks in advance,

Tom

UPDATE

Here is a fudge, suitable for Chess only, which almost works. It relies on the fact that in most fonts with chess pieces, the interiors of the black and white pieces are almost complements of each other.

<?xml version="1.0" standalone="no"?><svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="440" height="440" style="font-size: 34px;"><defs><style type="text/css">.large{fill:#700;}.small{fill:#eee;}</style></defs><rect x="0" y="0" width="440" height="440" fill="white"/><rect x="0" y="20" width="20" height="20" class="small"/><rect x="20" y="0" width="20" height="20" class="small"/><rect x="20" y="40" width="20" height="20" class="small"/><rect x="40" y="20" width="20" height="20" class="small"/><rect x="60" y="0" width="20" height="20" class="small"/><rect x="60" y="40" width="20" height="20" class="small"/><rect x="80" y="20" width="20" height="20" class="small"/><rect x="100" y="0" width="20" height="20" class="small"/><rect x="100" y="40" width="20" height="20" class="small"/><rect x="120" y="20" width="20" height="20" class="small"/><rect x="140" y="0" width="20" height="20" class="small"/><rect x="140" y="40" width="20" height="20" class="small"/><rect x="160" y="20" width="20" height="20" class="small"/><rect x="180" y="0" width="20" height="20" class="small"/><rect x="180" y="40" width="20" height="20" class="small"/><rect x="200" y="20" width="20" height="20" class="small"/><rect x="220" y="0" width="20" height="20" class="small"/><rect x="220" y="40" width="20" height="20" class="small"/><rect x="240" y="20" width="20" height="20" class="small"/><rect x="260" y="0" width="20" height="20" class="small"/><rect x="260" y="40" width="20" height="20" class="small"/><rect x="280" y="20" width="20" height="20" class="small"/><rect x="300" y="0" width="20" height="20" class="small"/><rect x="300" y="40" width="20" height="20" class="small"/><rect x="320" y="20" width="20" height="20" class="small"/><rect x="340" y="0" width="20" height="20" class="small"/><rect x="340" y="40" width="20" height="20" class="small"/><rect x="360" y="20" width="20" height="20" class="small"/><rect x="380" y="0" width="20" height="20" class="small"/><rect x="380" y="40" width="20" height="20" class="small"/><rect x="400" y="20" width="20" height="20" class="small"/><rect x="420" y="0" width="20" height="20" class="small"/><rect x="420" y="40" width="20" height="20" class="small"/><rect x="0" y="20" width="20" height="20" class="small"/><rect x="0" y="60" width="20" height="20" class="small"/><rect x="0" y="100" width="20" height="20" class="small"/><rect x="0" y="140" width="20" height="20" class="small"/><rect x="0" y="180" width="20" height="20" class="small"/><rect x="0" y="220" width="20" height="20" class="small"/><rect x="0" y="260" width="20" height="20" class="small"/><rect x="0" y="300" width="20" height="20" class="small"/><rect x="0" y="340" width="20" height="20" class="small"/><rect x="20" y="0" width="20" height="20" class="small"/><rect x="20" y="40" width="20" height="20" class="small"/><rect x="20" y="80" width="20" height="20" class="small"/><rect x="20" y="120" width="20" height="20" class="small"/><rect x="20" y="160" width="20" height="20" class="small"/><rect x="20" y="200" width="20" height="20" class="small"/><rect x="20" y="240" width="20" height="20" class="small"/><rect x="20" y="280" width="20" height="20" class="small"/><rect x="20" y="320" width="20" height="20" class="small"/><rect x="20" y="360" width="20" height="20" class="small"/><rect x="40" y="20" width="20" height="20" class="small"/><rect x="40" y="60" width="20" height="20" class="small"/><rect x="40" y="100" width="20" height="20" class="small"/><rect x="40" y="140" width="20" height="20" class="small"/><rect x="40" y="180" width="20" height="20" class="small"/><rect x="40" y="220" width="20" height="20" class="small"/><rect x="40" y="260" width="20" height="20" class="small"/><rect x="40" y="300" width="20" height="20" class="small"/><rect x="40" y="340" width="20" height="20" class="small"/><rect x="380" y="0" width="20" height="20" class="small"/><rect x="380" y="40" width="20" height="20" class="small"/><rect x="380" y="80" width="20" height="20" class="small"/><rect x="380" y="120" width="20" height="20" class="small"/><rect x="380" y="160" width="20" height="20" class="small"/><rect x="380" y="200" width="20" height="20" class="small"/><rect x="380" y="240" width="20" height="20" class="small"/><rect x="380" y="280" width="20" height="20" class="small"/><rect x="380" y="320" width="20" height="20" class="small"/><rect x="380" y="360" width="20" height="20" class="small"/><rect x="400" y="20" width="20" height="20" class="small"/><rect x="400" y="60" width="20" height="20" class="small"/><rect x="400" y="100" width="20" height="20" class="small"/><rect x="400" y="140" width="20" height="20" class="small"/><rect x="400" y="180" width="20" height="20" class="small"/><rect x="400" y="220" width="20" height="20" class="small"/><rect x="400" y="260" width="20" height="20" class="small"/><rect x="400" y="300" width="20" height="20" class="small"/><rect x="400" y="340" width="20" height="20" class="small"/><rect x="420" y="0" width="20" height="20" class="small"/><rect x="420" y="40" width="20" height="20" class="small"/><rect x="420" y="80" width="20" height="20" class="small"/><rect x="420" y="120" width="20" height="20" class="small"/><rect x="420" y="160" width="20" height="20" class="small"/><rect x="420" y="200" width="20" height="20" class="small"/><rect x="420" y="240" width="20" height="20" class="small"/><rect x="420" y="280" width="20" height="20" class="small"/><rect x="420" y="320" width="20" height="20" class="small"/><rect x="420" y="360" width="20" height="20" class="small"/><rect x="0" y="380" width="20" height="20" class="small"/><rect x="0" y="420" width="20" height="20" class="small"/><rect x="20" y="400" width="20" height="20" class="small"/><rect x="40" y="380" width="20" height="20" class="small"/><rect x="40" y="420" width="20" height="20" class="small"/><rect x="60" y="400" width="20" height="20" class="small"/><rect x="80" y="380" width="20" height="20" class="small"/><rect x="80" y="420" width="20" height="20" class="small"/><rect x="100" y="400" width="20" height="20" class="small"/><rect x="120" y="380" width="20" height="20" class="small"/><rect x="120" y="420" width="20" height="20" class="small"/><rect x="140" y="400" width="20" height="20" class="small"/><rect x="160" y="380" width="20" height="20" class="small"/><rect x="160" y="420" width="20" height="20" class="small"/><rect x="180" y="400" width="20" height="20" class="small"/><rect x="200" y="380" width="20" height="20" class="small"/><rect x="200" y="420" width="20" height="20" class="small"/><rect x="220" y="400" width="20" height="20" class="small"/><rect x="240" y="380" width="20" height="20" class="small"/><rect x="240" y="420" width="20" height="20" class="small"/><rect x="260" y="400" width="20" height="20" class="small"/><rect x="280" y="380" width="20" height="20" class="small"/><rect x="280" y="420" width="20" height="20" class="small"/><rect x="300" y="400" width="20" height="20" class="small"/><rect x="320" y="380" width="20" height="20" class="small"/><rect x="320" y="420" width="20" height="20" class="small"/><rect x="340" y="400" width="20" height="20" class="small"/><rect x="360" y="380" width="20" height="20" class="small"/><rect x="360" y="420" width="20" height="20" class="small"/><rect x="380" y="400" width="20" height="20" class="small"/><rect x="400" y="380" width="20" height="20" class="small"/><rect x="400" y="420" width="20" height="20" class="small"/><rect x="420" y="400" width="20" height="20" class="small"/><rect x="60" y="100" width="40" height="40" class="large"/><rect x="60" y="180" width="40" height="40" class="large"/><rect x="60" y="260" width="40" height="40" class="large"/><rect x="60" y="340" width="40" height="40" class="large"/><rect x="100" y="60" width="40" height="40" class="large"/><rect x="100" y="140" width="40" height="40" class="large"/><rect x="100" y="220" width="40" height="40" class="large"/><rect x="100" y="300" width="40" height="40" class="large"/><rect x="140" y="100" width="40" height="40" class="large"/><rect x="140" y="180" width="40" height="40" class="large"/><rect x="140" y="260" width="40" height="40" class="large"/><rect x="140" y="340" width="40" height="40" class="large"/><rect x="180" y="60" width="40" height="40" class="large"/><rect x="180" y="140" width="40" height="40" class="large"/><rect x="180" y="220" width="40" height="40" class="large"/><rect x="180" y="300" width="40" height="40" class="large"/><rect x="220" y="100" width="40" height="40" class="large"/><rect x="220" y="180" width="40" height="40" class="large"/><rect x="220" y="260" width="40" height="40" class="large"/><rect x="220" y="340" width="40" height="40" class="large"/><rect x="260" y="60" width="40" height="40" class="large"/><rect x="260" y="140" width="40" height="40" class="large"/><rect x="260" y="220" width="40" height="40" class="large"/><rect x="260" y="300" width="40" height="40" class="large"/><rect x="300" y="100" width="40" height="40" class="large"/><rect x="300" y="180" width="40" height="40" class="large"/><rect x="300" y="260" width="40" height="40" class="large"/><rect x="300" y="340" width="40" height="40" class="large"/><rect x="340" y="60" width="40" height="40" class="large"/><rect x="340" y="140" width="40" height="40" class="large"/><rect x="340" y="220" width="40" height="40" class="large"/><rect x="340" y="300" width="40" height="40" class="large"/><rect x="60" y="60" width="320" height="320" style="fill:none; stroke-width:1; stroke:#000;"/><text x="240" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♔</text><text x="200" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♕</text><text x="80" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♖</text><text x="360" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♖</text><text x="160" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♗</text><text x="280" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♗</text><text x="320" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♘</text><text x="120" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♘</text><text x="80" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="120" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="160" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="200" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="240" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="280" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="320" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="360" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="240" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♚</text><text x="200" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♛</text><text x="80" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♜</text><text x="360" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♜</text><text x="160" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♝</text><text x="280" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♝</text><text x="320" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♞</text><text x="120" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♞</text><text x="80" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="120" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="160" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="200" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="240" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="280" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="320" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="360" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="240" y="374" text-anchor="middle" fill="black">♔</text><text x="200" y="374" text-anchor="middle" fill="black">♕</text><text x="80" y="374" text-anchor="middle" fill="black">♖</text><text x="360" y="374" text-anchor="middle" fill="black">♖</text><text x="160" y="374" text-anchor="middle" fill="black">♗</text><text x="280" y="374" text-anchor="middle" fill="black">♗</text><text x="320" y="374" text-anchor="middle" fill="black">♘</text><text x="120" y="374" text-anchor="middle" fill="black">♘</text><text x="80" y="334" text-anchor="middle" fill="black">♙</text><text x="120" y="334" text-anchor="middle" fill="black">♙</text><text x="160" y="334" text-anchor="middle" fill="black">♙</text><text x="200" y="334" text-anchor="middle" fill="black">♙</text><text x="240" y="334" text-anchor="middle" fill="black">♙</text><text x="280" y="334" text-anchor="middle" fill="black">♙</text><text x="320" y="334" text-anchor="middle" fill="black">♙</text><text x="360" y="334" text-anchor="middle" fill="black">♙</text><text x="240" y="94" text-anchor="middle" fill="black">♚</text><text x="200" y="94" text-anchor="middle" fill="black">♛</text><text x="80" y="94" text-anchor="middle" fill="black">♜</text><text x="360" y="94" text-anchor="middle" fill="black">♜</text><text x="160" y="94" text-anchor="middle" fill="black">♝</text><text x="280" y="94" text-anchor="middle" fill="black">♝</text><text x="320" y="94" text-anchor="middle" fill="black">♞</text><text x="120" y="94" text-anchor="middle" fill="black">♞</text><text x="80" y="134" text-anchor="middle" fill="black">♟</text><text x="120" y="134" text-anchor="middle" fill="black">♟</text><text x="160" y="134" text-anchor="middle" fill="black">♟</text><text x="200" y="134" text-anchor="middle" fill="black">♟</text><text x="240" y="134" text-anchor="middle" fill="black">♟</text><text x="280" y="134" text-anchor="middle" fill="black">♟</text><text x="320" y="134" text-anchor="middle" fill="black">♟</text><text x="360" y="134" text-anchor="middle" fill="black">♟</text></svg>

Almost, that is, but not quite. I for one can still see red, through these pieces, and they only look perfect when seen on black squares.

Thomas Poole
  • 302
  • 4
  • 10
  • I'm going off of memory here, but I seem to remember facing a similar issue and discovering that, essentially, it wasn't possible. I could certainly be misremembering or incorrect, but my hunch is that you're going to have to abandon the font and switch to SVG or images. Hope I'm wrong, though! – Alexander Nied Apr 26 '17 at 04:09
  • Okay. I may be back later to answer my own question then. How fully did you investigate whether you can get the paths from a font, at all? I'm currently looking at this other question for clues on how to do that: http://stackoverflow.com/questions/7742148/how-to-convert-text-to-svg-paths I bet they won't include a closed outer path, but I also bet I can robustly construct one from the many paths that I find. I'll stop this line of investigation if someone can point out a definite show-stopper I'm going to come up against -- or a simpler, more automatic way to get the path I'm after. – Thomas Poole Apr 26 '17 at 04:26
  • Yeah, I think the way I came at it was actually looking at "multicolor svg fonts". A quick google search revealed [this](http://stackoverflow.com/questions/33371715/create-multicolor-icons-icomoon) and [this](https://css-tricks.com/stackicons-icon-fonts/) -- I didn't look at them too closely just now, but they _might_ be relevant to what you're working on. I've starred this-- if this post gets a solid answer on how to make this work, I've got some PNG's to replace! – Alexander Nied Apr 26 '17 at 04:37
  • 1
    I'm seriously considering simply using a rasterisation stage: Rasterise a character on a small white square; flood-fill the square outside the character, with alpha = 0; reposition the rastered graphic on whatever background you like. For me it's a good stop-gap and for some it might be a perfect solution. It retains most of the advantages of SVG and of sourcing the characters from Unicode; it simply doesn't scale well, unless we compromise on performance by adding code that redoes the rasterisation stage, on a resize. – Thomas Poole Apr 26 '17 at 05:22
  • Further to that comment: It's annoying for when I want to machine an image, of course, (and I might, on a laser cutter), because I was hoping not to care about how the machine rasters. I suppose, with this stop-gap, I do now care what resolution I need to target. – Thomas Poole Apr 26 '17 at 05:43
  • Yes floodfilling is a good solution, performance wise, you only need to do it once, here is a good answer showing [how to do flood-filling with canvas](http://stackoverflow.com/a/41306829/3702797), and here is [a canvas demo](https://jsfiddle.net/5j5pw94p/) on how to make the inner part white. You could then extract it has images if you need to set it with HTML+CSS / SVG. – Kaiido Apr 26 '17 at 15:01
  • @anied I came back to reassure you that things are going well, and hopefully my solution will be general enough for the both of us. I was talking about SVGs already by the way. Do indeed watch this space! I want to help your PNGs as well! – Thomas Poole May 04 '17 at 05:30
  • @ThomasPoole -- looks great! Excellent that you're documenting your research and progress so nicely here. Thanks! – Alexander Nied May 04 '17 at 13:41

2 Answers2

2

Doing what you want is very hard/tricky. Perhaps you can consider a simpler approach? How about just outlining all the pieces with white so that they stand out from the background?

div {
  font-family: sans-serif;
  font-size: 80px;
  line-height: 80px;
  text-align: center;
  font-weight: bold;
  display: inline-block;
  width: 80px;
  height: 80px;
  position: relative;
}

div:before
{
  content: attr(title);
  position: absolute;
  left: 0;
  width: 80px;
  height: 80px;
  z-index: -1;
  text-shadow:
     0px -1px 0 white,
     1px -1px 0 white,
     1px  0px 0 white,
     1px  1px 0 white,
     0px  1px 0 white,
    -1px  1px 0 white,
    -1px  0px 0 white,
    -1px -1px 0 white;
}

div:nth-child(2):before
{
  background-color: #700;
}
<div title="K">K</div><div title="Q">Q</div>
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • Creative alternative approach! – Alexander Nied Apr 26 '17 at 12:18
  • anied, I don't know about 'creative' since this is a common fallback when strokes aren't available, and used to be a lot more common for outlined text. But we can compliment it nonetheless; it IS well implemented. – Thomas Poole Apr 26 '17 at 17:43
  • My bad. `This answer was harmed by a badly asked question` I have improved that now. **I am using vector techniques anyway** so a stroke effect like yours is easy, crisp, and not a hack `I was hoping for a shortcut to the outermost path of a vector based font glyph`, without having to implement the harder answer that I am now using. Once I've implemented it, I will post it as an answer, **but I will still accept answers regarding any shortcut** – Thomas Poole May 04 '17 at 05:40
  • +1 because it's good code with a good looking result up to a surprisingly high zoom. I can simply add a stroke effect in a single attribute, however; I'm afraid it does not solve my problem for the infilled areas.. Outlining would leave a visually confusing chessboard; for instance `the bishops of one player will still look different from each other`, and that would make for a less readable board position. – Thomas Poole May 04 '17 at 06:35
1

In Progress...

Please expect evolution here, especially where diagrams and code can improve the answer. The work takes time.

Hopefully it will only involve one more edit, when the solution is complete.

----------

There are several near solutions. My implementation will degrade gracefully, so I'm using more than one, but I'll try to keep the components separate here, in order to help others more; some people may only want one part.

Solution 1 (for me only a stop-gap)

Compromise on vector-only solution, with a raster of just the back

I don't want to rely on this but it is a great stop-gap that looks perfect.

It isn't a final answer because it's hard to predict all the consequences of rasterisation for different display devices. I hate early rasters. I can try a screen, and a laser cutter. Without giving too much irrelevant detail about my applications, vectors almost always feel more correct for the things I find myself doing.

One of the things a friend is learning is the use of a laser cutter for instance, and I am tying it into my work. I have yet to investigate how it rasterises.

< EDITME For an illustration of roughly what this looks like but entirely with the vector graphics that closer to my first language. I will reuse the one I posted earlier. It isn't perfect; you'll still see red because I've not backed them with fully white silhouettes; I've backed white pieces (in black) with black pieces (in white) and I've backed black pieces (top layer in black again) with white pieces (bottom layer in white again). These are almost complements -- but not quite -- in most browsers, depending on your font.

<?xml version="1.0" standalone="no"?><svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="440" height="440" style="font-size: 34px;"><defs><style type="text/css">.large{fill:#700;}.small{fill:#eee;}</style></defs><rect x="0" y="0" width="440" height="440" fill="white"/><rect x="0" y="20" width="20" height="20" class="small"/><rect x="20" y="0" width="20" height="20" class="small"/><rect x="20" y="40" width="20" height="20" class="small"/><rect x="40" y="20" width="20" height="20" class="small"/><rect x="60" y="0" width="20" height="20" class="small"/><rect x="60" y="40" width="20" height="20" class="small"/><rect x="80" y="20" width="20" height="20" class="small"/><rect x="100" y="0" width="20" height="20" class="small"/><rect x="100" y="40" width="20" height="20" class="small"/><rect x="120" y="20" width="20" height="20" class="small"/><rect x="140" y="0" width="20" height="20" class="small"/><rect x="140" y="40" width="20" height="20" class="small"/><rect x="160" y="20" width="20" height="20" class="small"/><rect x="180" y="0" width="20" height="20" class="small"/><rect x="180" y="40" width="20" height="20" class="small"/><rect x="200" y="20" width="20" height="20" class="small"/><rect x="220" y="0" width="20" height="20" class="small"/><rect x="220" y="40" width="20" height="20" class="small"/><rect x="240" y="20" width="20" height="20" class="small"/><rect x="260" y="0" width="20" height="20" class="small"/><rect x="260" y="40" width="20" height="20" class="small"/><rect x="280" y="20" width="20" height="20" class="small"/><rect x="300" y="0" width="20" height="20" class="small"/><rect x="300" y="40" width="20" height="20" class="small"/><rect x="320" y="20" width="20" height="20" class="small"/><rect x="340" y="0" width="20" height="20" class="small"/><rect x="340" y="40" width="20" height="20" class="small"/><rect x="360" y="20" width="20" height="20" class="small"/><rect x="380" y="0" width="20" height="20" class="small"/><rect x="380" y="40" width="20" height="20" class="small"/><rect x="400" y="20" width="20" height="20" class="small"/><rect x="420" y="0" width="20" height="20" class="small"/><rect x="420" y="40" width="20" height="20" class="small"/><rect x="0" y="20" width="20" height="20" class="small"/><rect x="0" y="60" width="20" height="20" class="small"/><rect x="0" y="100" width="20" height="20" class="small"/><rect x="0" y="140" width="20" height="20" class="small"/><rect x="0" y="180" width="20" height="20" class="small"/><rect x="0" y="220" width="20" height="20" class="small"/><rect x="0" y="260" width="20" height="20" class="small"/><rect x="0" y="300" width="20" height="20" class="small"/><rect x="0" y="340" width="20" height="20" class="small"/><rect x="20" y="0" width="20" height="20" class="small"/><rect x="20" y="40" width="20" height="20" class="small"/><rect x="20" y="80" width="20" height="20" class="small"/><rect x="20" y="120" width="20" height="20" class="small"/><rect x="20" y="160" width="20" height="20" class="small"/><rect x="20" y="200" width="20" height="20" class="small"/><rect x="20" y="240" width="20" height="20" class="small"/><rect x="20" y="280" width="20" height="20" class="small"/><rect x="20" y="320" width="20" height="20" class="small"/><rect x="20" y="360" width="20" height="20" class="small"/><rect x="40" y="20" width="20" height="20" class="small"/><rect x="40" y="60" width="20" height="20" class="small"/><rect x="40" y="100" width="20" height="20" class="small"/><rect x="40" y="140" width="20" height="20" class="small"/><rect x="40" y="180" width="20" height="20" class="small"/><rect x="40" y="220" width="20" height="20" class="small"/><rect x="40" y="260" width="20" height="20" class="small"/><rect x="40" y="300" width="20" height="20" class="small"/><rect x="40" y="340" width="20" height="20" class="small"/><rect x="380" y="0" width="20" height="20" class="small"/><rect x="380" y="40" width="20" height="20" class="small"/><rect x="380" y="80" width="20" height="20" class="small"/><rect x="380" y="120" width="20" height="20" class="small"/><rect x="380" y="160" width="20" height="20" class="small"/><rect x="380" y="200" width="20" height="20" class="small"/><rect x="380" y="240" width="20" height="20" class="small"/><rect x="380" y="280" width="20" height="20" class="small"/><rect x="380" y="320" width="20" height="20" class="small"/><rect x="380" y="360" width="20" height="20" class="small"/><rect x="400" y="20" width="20" height="20" class="small"/><rect x="400" y="60" width="20" height="20" class="small"/><rect x="400" y="100" width="20" height="20" class="small"/><rect x="400" y="140" width="20" height="20" class="small"/><rect x="400" y="180" width="20" height="20" class="small"/><rect x="400" y="220" width="20" height="20" class="small"/><rect x="400" y="260" width="20" height="20" class="small"/><rect x="400" y="300" width="20" height="20" class="small"/><rect x="400" y="340" width="20" height="20" class="small"/><rect x="420" y="0" width="20" height="20" class="small"/><rect x="420" y="40" width="20" height="20" class="small"/><rect x="420" y="80" width="20" height="20" class="small"/><rect x="420" y="120" width="20" height="20" class="small"/><rect x="420" y="160" width="20" height="20" class="small"/><rect x="420" y="200" width="20" height="20" class="small"/><rect x="420" y="240" width="20" height="20" class="small"/><rect x="420" y="280" width="20" height="20" class="small"/><rect x="420" y="320" width="20" height="20" class="small"/><rect x="420" y="360" width="20" height="20" class="small"/><rect x="0" y="380" width="20" height="20" class="small"/><rect x="0" y="420" width="20" height="20" class="small"/><rect x="20" y="400" width="20" height="20" class="small"/><rect x="40" y="380" width="20" height="20" class="small"/><rect x="40" y="420" width="20" height="20" class="small"/><rect x="60" y="400" width="20" height="20" class="small"/><rect x="80" y="380" width="20" height="20" class="small"/><rect x="80" y="420" width="20" height="20" class="small"/><rect x="100" y="400" width="20" height="20" class="small"/><rect x="120" y="380" width="20" height="20" class="small"/><rect x="120" y="420" width="20" height="20" class="small"/><rect x="140" y="400" width="20" height="20" class="small"/><rect x="160" y="380" width="20" height="20" class="small"/><rect x="160" y="420" width="20" height="20" class="small"/><rect x="180" y="400" width="20" height="20" class="small"/><rect x="200" y="380" width="20" height="20" class="small"/><rect x="200" y="420" width="20" height="20" class="small"/><rect x="220" y="400" width="20" height="20" class="small"/><rect x="240" y="380" width="20" height="20" class="small"/><rect x="240" y="420" width="20" height="20" class="small"/><rect x="260" y="400" width="20" height="20" class="small"/><rect x="280" y="380" width="20" height="20" class="small"/><rect x="280" y="420" width="20" height="20" class="small"/><rect x="300" y="400" width="20" height="20" class="small"/><rect x="320" y="380" width="20" height="20" class="small"/><rect x="320" y="420" width="20" height="20" class="small"/><rect x="340" y="400" width="20" height="20" class="small"/><rect x="360" y="380" width="20" height="20" class="small"/><rect x="360" y="420" width="20" height="20" class="small"/><rect x="380" y="400" width="20" height="20" class="small"/><rect x="400" y="380" width="20" height="20" class="small"/><rect x="400" y="420" width="20" height="20" class="small"/><rect x="420" y="400" width="20" height="20" class="small"/><rect x="60" y="100" width="40" height="40" class="large"/><rect x="60" y="180" width="40" height="40" class="large"/><rect x="60" y="260" width="40" height="40" class="large"/><rect x="60" y="340" width="40" height="40" class="large"/><rect x="100" y="60" width="40" height="40" class="large"/><rect x="100" y="140" width="40" height="40" class="large"/><rect x="100" y="220" width="40" height="40" class="large"/><rect x="100" y="300" width="40" height="40" class="large"/><rect x="140" y="100" width="40" height="40" class="large"/><rect x="140" y="180" width="40" height="40" class="large"/><rect x="140" y="260" width="40" height="40" class="large"/><rect x="140" y="340" width="40" height="40" class="large"/><rect x="180" y="60" width="40" height="40" class="large"/><rect x="180" y="140" width="40" height="40" class="large"/><rect x="180" y="220" width="40" height="40" class="large"/><rect x="180" y="300" width="40" height="40" class="large"/><rect x="220" y="100" width="40" height="40" class="large"/><rect x="220" y="180" width="40" height="40" class="large"/><rect x="220" y="260" width="40" height="40" class="large"/><rect x="220" y="340" width="40" height="40" class="large"/><rect x="260" y="60" width="40" height="40" class="large"/><rect x="260" y="140" width="40" height="40" class="large"/><rect x="260" y="220" width="40" height="40" class="large"/><rect x="260" y="300" width="40" height="40" class="large"/><rect x="300" y="100" width="40" height="40" class="large"/><rect x="300" y="180" width="40" height="40" class="large"/><rect x="300" y="260" width="40" height="40" class="large"/><rect x="300" y="340" width="40" height="40" class="large"/><rect x="340" y="60" width="40" height="40" class="large"/><rect x="340" y="140" width="40" height="40" class="large"/><rect x="340" y="220" width="40" height="40" class="large"/><rect x="340" y="300" width="40" height="40" class="large"/><rect x="60" y="60" width="320" height="320" style="fill:none; stroke-width:1; stroke:#000;"/><text x="240" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♔</text><text x="200" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♕</text><text x="80" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♖</text><text x="360" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♖</text><text x="160" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♗</text><text x="280" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♗</text><text x="320" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♘</text><text x="120" y="94" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♘</text><text x="80" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="120" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="160" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="200" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="240" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="280" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="320" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="360" y="134" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♙</text><text x="240" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♚</text><text x="200" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♛</text><text x="80" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♜</text><text x="360" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♜</text><text x="160" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♝</text><text x="280" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♝</text><text x="320" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♞</text><text x="120" y="374" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♞</text><text x="80" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="120" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="160" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="200" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="240" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="280" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="320" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="360" y="334" stroke-width="0.3" stroke="white" text-anchor="middle" fill="white">♟</text><text x="240" y="374" text-anchor="middle" fill="black">♔</text><text x="200" y="374" text-anchor="middle" fill="black">♕</text><text x="80" y="374" text-anchor="middle" fill="black">♖</text><text x="360" y="374" text-anchor="middle" fill="black">♖</text><text x="160" y="374" text-anchor="middle" fill="black">♗</text><text x="280" y="374" text-anchor="middle" fill="black">♗</text><text x="320" y="374" text-anchor="middle" fill="black">♘</text><text x="120" y="374" text-anchor="middle" fill="black">♘</text><text x="80" y="334" text-anchor="middle" fill="black">♙</text><text x="120" y="334" text-anchor="middle" fill="black">♙</text><text x="160" y="334" text-anchor="middle" fill="black">♙</text><text x="200" y="334" text-anchor="middle" fill="black">♙</text><text x="240" y="334" text-anchor="middle" fill="black">♙</text><text x="280" y="334" text-anchor="middle" fill="black">♙</text><text x="320" y="334" text-anchor="middle" fill="black">♙</text><text x="360" y="334" text-anchor="middle" fill="black">♙</text><text x="240" y="94" text-anchor="middle" fill="black">♚</text><text x="200" y="94" text-anchor="middle" fill="black">♛</text><text x="80" y="94" text-anchor="middle" fill="black">♜</text><text x="360" y="94" text-anchor="middle" fill="black">♜</text><text x="160" y="94" text-anchor="middle" fill="black">♝</text><text x="280" y="94" text-anchor="middle" fill="black">♝</text><text x="320" y="94" text-anchor="middle" fill="black">♞</text><text x="120" y="94" text-anchor="middle" fill="black">♞</text><text x="80" y="134" text-anchor="middle" fill="black">♟</text><text x="120" y="134" text-anchor="middle" fill="black">♟</text><text x="160" y="134" text-anchor="middle" fill="black">♟</text><text x="200" y="134" text-anchor="middle" fill="black">♟</text><text x="240" y="134" text-anchor="middle" fill="black">♟</text><text x="280" y="134" text-anchor="middle" fill="black">♟</text><text x="320" y="134" text-anchor="middle" fill="black">♟</text><text x="360" y="134" text-anchor="middle" fill="black">♟</text></svg>

EDITME >

Solution 2:

2A. Require download of just one specially designed SVG font that makes a clear board position using fill and stroke effects to distinguish the pieces

Done and my newly home-made font looks good but not yet as good as the built in ones

That allows me to retain the Unicode characters so that a user can copy the chess position to a text editor, and other niceties.

It compromises file size, but not by much, especially if I design a font of my own that only includes chess pieces.

It compromises because I actually quite like the chess characters in most built-in fonts, and I would also like to give the user a choice of all fonts, including mine.

It compromises on cross-browser support; of all my browsers, only Safari understands SVG fonts, surprisingly enough. BUT they excite me and I won't be a part of that heel-dragging problem, so I'm including them in my implementation just to learn.

It's a tiny step from SVG to SVG fonts anyway.

2B. Also make the pieces look distinct when typed into a text editor all in a black font colour. (So Unicode 9812 to 9817 are white pieces while 9818 to 9823 are black.)

Current delay: Offsetting paths -- which seems to be a theme in every job I do! -- is necessary for making a really good looking font of my own, and I'm wrapped up in this fun mathematical puzzle at the moment.

This part is less relevant to my original question, but will be very helpful for questions other people are asking on Stack Exchange; I will be answering them in their own place, but I will still include links here for anyone interested in path offsetting; the subjects are closely related.

Solution 3: Compromise on using no external code

Opentype.js may or may not help me with inspecting the browser's internal fonts, but I'm not sure yet. This part of the answer is where I hope to find a shortcut. I want the outermost path of a font that I did not create or download; hopefully it is somehow available through an API, and this is the part I am seeking help on.

I can implement my own, I want to and am now very far along with doing so. But I would still like to access the browser's ability to provide such data, and then fall back to my own code.

Thomas Poole
  • 302
  • 4
  • 10
  • I won't be able to mark my own answer as accepted unless I discover that I can get the information about system fonts. That would allow for instance a slimline version that does not download a font -- more appropriate for instance on mobile, where texture and font downloads causing download limits to be hit is a serious problem for VR. And yes, I do have VR applications for SVGs. I can render one to a canvas, then capture that as a texture. – Thomas Poole May 04 '17 at 07:43
  • A potential problem is that I can't find SVG fonts online that include glyphs for the chess pieces. So an SVG only solution means that I can only use my own font, or use the raster technique. But then also, Opentype.js might be totally unable to access the system fonts, *let alone* give me the 'shortcut' I'm after for the outermost path. In that case the only way for me to provide font choice to the user would be by falling back to the raster backing -- or spending the rest of my life building fonts. – Thomas Poole May 04 '17 at 08:11