This is part 2 on this subject. If you haven’t already read it, please read part 1 to understand the winding problem.
So to recap quickly: HTML5 only supports the Non-Zero winding rule, however PDF supports both Non-Zero and Even-Odd rules. This means that we need to do something with shapes filled using the Even-Odd rule, otherwise they may not display correctly.
To demonstrate in this blog article, I will be using this example:
Just a red circle? The first time I saw it, that was my thought too. It’s only after changing the fill to a stroke that you realise what it actually is.
To help understand what’s going on, here it is with arrows showing the directions of the paths shown.
So our first thought was that we need to make some kind of change to the way that we draw the shape. The obvious thing was to change the direction of some of the paths drawn in order to “convert” it from Even-Odd to Non-Zero.
Would alternating between clockwise and anti-clockwise work? Well, it was worth a try just to see what the affect was. And besides, it will only take a minute anyway.
Well, it turns out that it’s actually a bit of a headache. It’s fine if you are doing something simple like just using lineTo to draw a box, but with bezier curves it’s not quite so easy.
If you were to just reverse the order of the draw commands, you get something like this:
The order of the two control points makes a difference to the way that the curve is drawn. So what about reversing the order of the control points for the lines too?
Oh, that doesn’t quite seem right either. The type of bezier curve used is a bezierCurveTo, which means that it takes our current position (where we ended up from the previous command), and draws a curve to a position that we specify using two control points. What that means is that we need to step the to position backwards so that each command uses the to position of the last command, so that our control points draw our curve correctly.
Good. So we can reverse the direction of the path so that our image draws correctly:
So are we done? No. While doing something like this may be OK for our no entry sign, where we can control the order and direction of the shapes that get drawn, this approach is no good for a general case. In the real world, we can’t just program for specific cases, we need to program for a general case, so that our code works for any PDF file, not just the ones we’ve seen before.
And this means that doing something like alternating the direction between paths is not going to work. Here’s how it would look for our no entry sign:
My next thought was that we could just fill the overlapping shapes with the background colour. But this wouldn’t work either because again, the order is important. Even if we were to fill our two D’s with white, when we draw and fill the circle around them, it’s just going to cover up all of our hard work. It would also be no good for shapes that only partially overlap each other, because only the intersection of the two would need to be unfilled. And it’s also important to remember that the unfilled pieces are just that – unfilled. By filling them with a background colour, you lose transparency, which means we wouldn’t be able to put anything behind our no entry sign to say you’re not allowed to do it.
So what now? If we can’t draw the shapes in a different order, and we can’t change the direction that some of the shapes are drawn, and we can’t fill in different pieces to create the illusion that we have fixed the problem, how else can we alter our shape so that it magically works using the Non-Zero winding rule?
We could get really clever and do some really complicated stuff and somehow cut about our combined shape into lots of smaller shapes so that we can detect how it should be drawn, and then fill the smaller shapes accordingly.
A bit like this:
_____ | __|__ |__|__| | |_____| _____ | __| |__| __ |__| __ __| | |_____|
But that’s a little overkill for what we want. After all, it might not even matter that the shape we are drawing is drawn using Non-Zero rather than Even-Odd. And that’s a lot of computation time that will just be wasted, resulting in poor performance for our convertor. And besides, I am no where near clever enough to be able to code that up.
Since I started playing with these shapes there has been a niggling thought at the back of my mind. What if we were just to output these shapes as images and be done with it?
This would work just fine, but it is not optimal. Very rarely is there a shape drawn where the output from using either the Non-Zero or Even-Odd rule is actually different. So outputting as image every time a shape is drawn using the Even-Odd rule is going to output lots of images that are not actually required.
And this is no fun because shapes are nice. They scale beautifully, take no time at all to convert from PDF to HTML5, and take up barely any space at all compared to an image.
But what other option is there? Unfortunately we are left with a compromise. The best we can do is create some criteria that the shape must pass before we output it as an image.
What should these criteria be?
To find out, part 3 of this blog article can be found here.