SVG-Fonts-0.4 released on Hackage
June 7, 2011 11 Comments
This library has been laying around in an unusable state on my harddrive for a long time, but now it is finally good enough to be used. In contrast to a lot of graphics libraries on Hackage it is not a binding. That means there were a lot of algorithms that had to be implemented. Although Haskell is often propagated as a language to write correct programs, algorithmic errors are as common as in other languages, because they cannot be catched by the type system. Debug.Trace, debugging with ghci is no alternative because they have no visual output and a list of hundred of floats is not helpful. In the future I plan to have a visual debugger for this.
But it works now. The interface should be easy to integrate into other libraries. Take a look at the following example:
main = do
args <- getArgs
let str = if null args then "Haskell" else head args
resolution = (300, 300)
-- mode: INSIDE_V1_V2: the string is inside v1 v2 boundaries (height/length-relation not kept)
-- mode: INSIDE_V1: stay inside v1 boundary, size of v2 adjusted to height/length-relation
-- mode: INSIDE_V2: stay inside v2 boundary, size of v1 adjusted to height/length-relation
mode = INSIDE_V2
-- spacing: MONO: Use mono spacing between glyphs
-- HADV: Every glyph has a unique constant horiz. advance
-- KERN: Same as HADV but sometimes overridden by kerning: i.e. the horizontal advance
-- in "VV" is bigger than in "VA"
spacing = HADV
gaw = makeOutlMap "../../../src/Test/GirlsareWeird.svg" resolution
lin = makeOutlMap "../../../src/Test/LinLibertine.svg" resolution
o = (0,0,1.1) -- origin
v1 = (5,0,0) -- direction of char-advance
v2 = (0,0,-1) -- height direction
v3 = (0,0.2,0) -- extrusion
f :: String -> [String] -- assigning a property to every character by a unique string
f str = replicate (length str) "p"
-- data CharProp = Prop (FontData, OutlineMap) String Textured
prop :: Map.Map String CharProp
prop = Map.fromList [("p", Prop lin "to3d" False)]
-- transformation of a Geoemtry Node (i.e. triangulation)
transf :: Map.Map String (Geometry -> Geometry)
transf = Map.fromList [("to3d", to3d)]
-- to3d: not the perfect solution since some points are generated twice
to3d geom = ( ((extrude (0,0.1,0)).deleteHoles) geom ) `atop`
( tri (translate (0,0.1,0) geom) )
tri = (triangulate ketTri).deleteHoles
tex = makeTexMap resolution prop transf
node = displayString str "node" resolution prop transf mode spacing o v1 v2 f tex
genCollada (lightedScene node) emptyAnim
putStrLn "Collada File generated"
Line 30 shows that a integration into a drawing combinator library seems to be reasonable. I am a little bit proud that this library is lot smaller than freetype2 (something around 100KB against 4000KB)
and the only things that it cannot do (yet!) is Rastered Images, Autohinting, Unicode, and the big amount of file formats supported by freetype2.
Another difference is that the library consists of several small parts that can be used in many other places while a lot of C-libraries tend to be monolithic (like freetype2).
I have to admit that parsing ttf and the other formats is maybe more work then the what has been done so far.
Thanks to the developers of Blender who fixed a bug I described it is possible to load this file into Blender to generate a raytraced picture:

If you want a somewhat more efficient triangulation algorithm, you can check out my implementation in Sloth2D:
https://github.com/cobbpg/sloth2d/blob/master/Physics/Sloth2D/Geometry2D.hs
The algorithm can actually deal with polygons that contain holes, but there is currently no way to specify such input. Nevertheless, this could be easily changed.
That is interesting. I have looked quite intensively into triangulation algorithms and chose this one because it works. I deal with holes by cutting them open. I can use your library only if it is on Hackage. It would be also nice to know the names of the inventors of the triangulation algorithm. the fastest practical one is maybe from Seidel.
I’m not sure if my algorithm has a name, it’s just a simple sweep-based method, and that’s also why it can deal with holes directly. My implementation doesn’t guarantee O(nlogn) execution time (it could by changing the data structure that keeps track of the open cross sections), but it should be pretty much linear in the typical case like font outlines.
The example is b0rked =(.
Thanks, I am glad I can fix typos in blog posts.
oh wow that is nice…i’m evaluating haskell and would like to know how is the state of svg library in general in haskell
There are some libraries that can generate svg output like cairo. I have written a library that parses the path command: . What is missing is maybe a general way to parse the whole svg-format (not only the special svg-font format).
Is the do-block for main correct? Should it be a) getting the args from somewhere and b) doing “length args > 0″ rather than just “args > 0″ (and even then, “null args” makes more sense) ?
Maybe there’s some weird definition of ‘args’ somewhere? Normally I’d expect ‘args <- getArgs', but… The code in the blog post differs from what I see in Google reader, where both the 'if' and the '<' are missing (but perhaps it was modified?)
you are right… corrected
You may wish to check the line-wrapping of your comments as well.