Sometimes when you are presented with a project you simply don’t know where to begin. This was the case with my latest endeavor. The core of the project, produce a vector based output of text to be able to outline and shadow it. Once those operations are completed move the resulting data into an Adobe Illustrator compatible EPS file. I like a good challenge and this was definitely one of them.
Using .net to draw vector paths
The .net runtime features a vector class in the System.drawing.drawing2D namespace called GraphicsPath. With this class you can perform many common vector operations such as adding lines, polygons, beziers, and more. You can also do transformations, scaling, and warping to name a few. Another benefit is it is extremely easy to draw the resulting class to a form making it quick to see your work in action. Point data is stored in an array and flagged by a mirror array called pathtype, using some references of what the enumeration values mean you can translate the type of each point. Finally, one of the more useful methods is taking the vector data and moving it into a compatible format you can interface with other libraries. Flatten() allows you to create an approximation based on the flatness input. The result is a polygon of connected line segments. For my uses this was perfect.
Union Boolean Operation
So I established my data container class and my search began on how exactly I was going to perform the outlining and union operations. This proved to be the most complicated part of the project. One requirement was the outline had to be round, not pointed this affected the type of algorithm I was looking for. Like most programmers I hate trying to reinvent the wheel as I know all of what I was trying to do had been done many times before. In my days of searching for graphics libraries in what started in just .net moved out to C and C++. I had much more luck when I started looking at C and C++ open source projects. I ran across the General Polygon Clipper Library which already has a wrapper in C#! Perfect! After some test using the code it works perfectly and is quick too. GPC is written in C and output as a WIN32 dll. With GPC in my toolbox I could now accomplish taking two graphics paths and performing a union(join) on them.
My 2nd requirement however was that the vector needed to be able to be outlined at some distance to a rounded corner. On the surface this may seem like a simple problem to tackle, however it isn’t. At least not for me, this isn’t my specialty at all! I was having trouble even finding something to look for. Finally the keyword that landed the most useful information was polygon offset algorithm. I came across CGAL with what looked like a nice algorithm to do offsets using a straight skeleton algorithm, however they weren’t rounded. Luckily there is another section on their site about using Minkowski sums to offset a polygon. After reading this page for a while and reviewing the images I decided I thought I could write this algorithm. Long store short a few days of work I got what I was considered moderately good results, but if something like this doesn’t work perfect it is useless. That was my issue, there were various scenarios where my code just didn’t work right, the logic was sound for simple cases, but for complex shapes (letters) it was falling short. Since my inspiration came from the CGAL page I decided to give the library a shot. I had serious reservations about using it because it was extreme overkill for what I was doing it seemed. After nearly a full day (no kidding) I was able to get CGAL to compile out of visual studio.net 2008. Yes it can be done! It wasn’t that hard, just many steps and files that have to be downloaded and setup correctly before it will work. Hint: make sure you have boost setup in your path correct, and get the CMAKE gui to build the project files for VS.net.
CGAL is written in C++, basically I had not used C++ since college although at the time I was pretty comfortable for with. It didn’t take me long to get back into the swing of writing C++ again, but I’m still a bit rusty. My approach, peel out the pieces of CGAL I need into a WIN32 DLL and let that talk to my C# app. I had never done any of the above before but yet another challenge I took on. After some head banging and many searches on google I was able to expose the C++ methods (make sure you use a .def file) and get to coding. My work with CGAL in one word was… frustrating. I think it is an excellent piece of work, but complicated. I found the documentation hard to shuffle through, but complete enough you could usually figure out what was going on between one of the examples and the documentation. My main source of confusion was the heavily templated structure which often left me wondering how various pieces of code worked. Granted, a lot of these complaints fall on my lack of knowledge and experience with this sort of library (aka newbie!). After much frustration and trail and error I was able to get a approximated_offset call in C++ to CGAL back out of the DLL and into C# onto the screen. Reconstructing the CGAL general_polygon_2 structure proved very challenging since the data was in line segments and conic arcs. .Net addArc method required input that I didn’t have from the CGAL data structure. I have a feeling I was doing it the hard way, but in order to come up with an arc sweep, starting angle, and bounding box I had to use CGAls source circle and bounding box. Interestingly enough CGAL already returned the intersection points to the bounding box, but I discovered there to actually be a bug in this calculation. I plan to report it as a bug, basically the circle passes through the box to form an arc, two intersection points exist on the box, this calculation returned a point that was not bounded inside the box in some cases (the 2nd point in the line circle intersection). At any rate it felt like I had climbed to the top of Mt. Everest at that point as I had an put together the major pieces of the project. Little did I know that I spoke to soon.
There is always a catch
CGAL is written very complete full of assertions, although these can be disabled they are there for a reason. Various fonts I used kept tripping assertions, specifically my polygon was not simple. A complex polygon is one that self intersects, I thought this is impossible since I am using a known working font. I was mistaken, apparently there are fonts out there that infact have characters that are self intersecting polygons. This has proved to throw a major wrench in my work. I see as I have two options either fix the polygon in some way that CGAL will perform operations on it or findanother way to offset complex polygons. This particular issue I have not progressed on (more on that later). I have found that the issue is not really shown in windows system fonts, moreso in fonts that you can download from free font sites. My current plan for phase 2 of the project after completion is evaluate how hard it would be to discover intersections and split the polygon into two (or more) simple polygons, perform the offset, then join the resulting polygon.
Putting it all together
After choosing my battles and regrouping I began a search on how to get a graphicsPath object into Adobe Illustrator 8 file format. After some searches it became clear the EPS is simply a variation on PostScript which there is a wealth of information. After reviewing some sources on postscript I eventually found my way to the Adobe Illustrator file specification pdf. After disecting some simple eps files out of illstrator it came to my attention that in general the file format is pretty bloated (about 400k) of built in postscript for gradient brushes, effects, andmore. Come to find out moving a flattened GraphicsPath to eps format is pretty simple. Things to remember are that the Y-Axis on a windows form and an EPS file are reversed so you need to translate the Y * -1. Illustrator eps files have a few simple commands to assign color along with the type of point you are drawing. One important thing I learned the hard way is the winding order of the polygon. In order for polygon holes to actually show as holes you need to make sure the order of points are reversed from the outer polygon boundary. Although it will work if they are not reversed you won’t see the path correctly in Illustrator.
The entire project is about 80% complete and will hopefully be done this week. Although I won’t consider this project totally done until I figure out the complex polygon outline issue, but for now I’m satisfied. If anyone wants some code snippets I can post up some samples in the article.