packages feed

SVGFonts 1.0 → 1.1

raw patch · 9 files changed

+308/−835 lines, 9 filesdep +data-defaultdep +diagrams-libdep +vector-spacedep −GLUTdep −OpenGLdep −SVGPathdep ~parsecdep ~splitPVP ok

version bump matches the API change (PVP)

Dependencies added: data-default, diagrams-lib, vector-space

Dependencies removed: GLUT, OpenGL, SVGPath, array, bytestring, collada-output, collada-types, haskell98, tga, triangulation

Dependency ranges changed: parsec, split

API changes (from Hackage documentation)

- Graphics.SVGFonts.RasterFont: B :: Bitmask -> AA
- Graphics.SVGFonts.RasterFont: NB :: AA
- Graphics.SVGFonts.RasterFont: bitSum :: Bitmask -> Word16
- Graphics.SVGFonts.RasterFont: bitmask :: Int -> Int -> UArray (Int, Int) Word16 -> Bitmask
- Graphics.SVGFonts.RasterFont: bitmasks :: (X, Y) -> [F2] -> [F2P]
- Graphics.SVGFonts.RasterFont: bits :: Word16 -> Word16
- Graphics.SVGFonts.RasterFont: borderToTex :: (Int, Int) -> [(Int, Int, Bitmask)] -> (V4, V4) -> TGAData
- Graphics.SVGFonts.RasterFont: commandsToRasterPoints :: [PathCommand] -> F2 -> F2 -> [[F2P]]
- Graphics.SVGFonts.RasterFont: createTexture :: (Int, Int) -> TGAData -> String -> IO (TextureObject, String)
- Graphics.SVGFonts.RasterFont: data AA
- Graphics.SVGFonts.RasterFont: endBits :: Bitmask -> Word16
- Graphics.SVGFonts.RasterFont: fillBitmask :: Bitmask -> Word16 -> Bitmask
- Graphics.SVGFonts.RasterFont: fillSpans :: (Int, Int) -> [(Int, Int, Bitmask)] -> ByteString
- Graphics.SVGFonts.RasterFont: ftrace :: String -> a -> a
- Graphics.SVGFonts.RasterFont: instance Show AA
- Graphics.SVGFonts.RasterFont: line :: Int -> Int -> (Int, Int) -> (Int, Int) -> UArray (Int, Int) Word16
- Graphics.SVGFonts.RasterFont: orBm :: Bitmask -> Bitmask -> Bitmask
- Graphics.SVGFonts.RasterFont: overlaps :: [(Int, Int, Bitmask)] -> [(Int, Int, Bitmask)]
- Graphics.SVGFonts.RasterFont: showBits :: Word16 -> String
- Graphics.SVGFonts.RasterFont: sortSpans :: [(Int, Int, Bitmask)] -> [(Int, Int, Bitmask)]
- Graphics.SVGFonts.RasterFont: type Bitmask = UArray Int Word16
- Graphics.SVGFonts.RasterFont: type F2P = (X, Y, AA)
- Graphics.SVGFonts.ReadFont: ConstDx :: Rx
- Graphics.SVGFonts.ReadFont: Exactly :: Int -> Rx
- Graphics.SVGFonts.ReadFont: INSIDE_V1 :: Mode
- Graphics.SVGFonts.ReadFont: INSIDE_V1_V2 :: Mode
- Graphics.SVGFonts.ReadFont: INSIDE_V2 :: Mode
- Graphics.SVGFonts.ReadFont: MONO :: Spacing
- Graphics.SVGFonts.ReadFont: OneTexture :: Rx
- Graphics.SVGFonts.ReadFont: Outl :: (FontData, OutlineMap, OutlineTexMap) -> String -> CharProp
- Graphics.SVGFonts.ReadFont: Tex :: (FontData, OutlineMap, OutlineTexMap) -> String -> CharProp
- Graphics.SVGFonts.ReadFont: XPowerOfTwo :: Rx
- Graphics.SVGFonts.ReadFont: data CharProp
- Graphics.SVGFonts.ReadFont: data Rx
- Graphics.SVGFonts.ReadFont: displayString :: String -> String -> (Rx, Int) -> Mode -> Spacing -> (V3, V3, V3) -> P -> Props -> Transf -> TexMap -> Scene
- Graphics.SVGFonts.ReadFont: makeOutlMaps :: String -> (Rx, Int) -> (FontData, OutlineMap, OutlineTexMap)
- Graphics.SVGFonts.ReadFont: makeTexMap :: (Rx, Int) -> Props -> Transf -> TexMap
- Graphics.SVGFonts.ReadFont: type OutlineTexMap = Map String [[F2P]]
- Graphics.SVGFonts.ReadFont: type P = [Char] -> [String]
- Graphics.SVGFonts.ReadFont: type Props = Map String CharProp
- Graphics.SVGFonts.ReadFont: type TexMap = Map (String, String, String) (TextureObject, String)
- Graphics.SVGFonts.ReadFont: type Transf = Map String (Geometry -> Geometry)
- Graphics.SVGFonts.ReadFont: type X = Float
- Graphics.SVGFonts.ReadFont: type Y = Float
+ Graphics.SVGFonts.ReadFont: INSIDE_H :: Mode
+ Graphics.SVGFonts.ReadFont: INSIDE_W :: Mode
+ Graphics.SVGFonts.ReadFont: INSIDE_WH :: Mode
+ Graphics.SVGFonts.ReadFont: TextOpts :: String -> (FontData, OutlineMap) -> Mode -> Spacing -> Double -> Double -> TextOpts
+ Graphics.SVGFonts.ReadFont: commands :: String -> SvgGlyph -> [PathCommand]
+ Graphics.SVGFonts.ReadFont: commandsToTrails :: [PathCommand] -> [Segment R2] -> R2 -> R2 -> R2 -> [Path R2]
+ Graphics.SVGFonts.ReadFont: data TextOpts
+ Graphics.SVGFonts.ReadFont: fdo :: TextOpts -> (FontData, OutlineMap)
+ Graphics.SVGFonts.ReadFont: horizontalAdvances :: [String] -> FontData -> Bool -> [Double]
+ Graphics.SVGFonts.ReadFont: instance Default TextOpts
+ Graphics.SVGFonts.ReadFont: kernAdvance :: String -> String -> Kern -> Bool -> Double
+ Graphics.SVGFonts.ReadFont: mode :: TextOpts -> Mode
+ Graphics.SVGFonts.ReadFont: outlMap :: String -> (FontData, OutlineMap)
+ Graphics.SVGFonts.ReadFont: spacing :: TextOpts -> Spacing
+ Graphics.SVGFonts.ReadFont: textHeight :: TextOpts -> Double
+ Graphics.SVGFonts.ReadFont: textSVG :: String -> Double -> Path R2
+ Graphics.SVGFonts.ReadFont: textSVG_ :: TextOpts -> Path R2
+ Graphics.SVGFonts.ReadFont: textWidth :: TextOpts -> Double
+ Graphics.SVGFonts.ReadFont: txt :: TextOpts -> String
+ Graphics.SVGFonts.ReadFont: type FileName = String
+ Graphics.SVGFonts.ReadPath: A_abs :: PathCommand
+ Graphics.SVGFonts.ReadPath: A_rel :: PathCommand
+ Graphics.SVGFonts.ReadPath: C_abs :: (X1, Y1, X2, Y2, X, Y) -> PathCommand
+ Graphics.SVGFonts.ReadPath: C_rel :: (X1, Y1, X2, Y2, X, Y) -> PathCommand
+ Graphics.SVGFonts.ReadPath: H_abs :: X -> PathCommand
+ Graphics.SVGFonts.ReadPath: H_rel :: X -> PathCommand
+ Graphics.SVGFonts.ReadPath: L_abs :: Tup -> PathCommand
+ Graphics.SVGFonts.ReadPath: L_rel :: Tup -> PathCommand
+ Graphics.SVGFonts.ReadPath: M_abs :: Tup -> PathCommand
+ Graphics.SVGFonts.ReadPath: M_rel :: Tup -> PathCommand
+ Graphics.SVGFonts.ReadPath: Q_abs :: (X1, Y1, X, Y) -> PathCommand
+ Graphics.SVGFonts.ReadPath: Q_rel :: (X1, Y1, X, Y) -> PathCommand
+ Graphics.SVGFonts.ReadPath: S_abs :: (X2, Y2, X, Y) -> PathCommand
+ Graphics.SVGFonts.ReadPath: S_rel :: (X2, Y2, X, Y) -> PathCommand
+ Graphics.SVGFonts.ReadPath: T_abs :: Tup -> PathCommand
+ Graphics.SVGFonts.ReadPath: T_rel :: Tup -> PathCommand
+ Graphics.SVGFonts.ReadPath: V_abs :: Y -> PathCommand
+ Graphics.SVGFonts.ReadPath: V_rel :: Y -> PathCommand
+ Graphics.SVGFonts.ReadPath: Z :: PathCommand
+ Graphics.SVGFonts.ReadPath: data PathCommand
+ Graphics.SVGFonts.ReadPath: instance Show PathCommand
+ Graphics.SVGFonts.ReadPath: pathFromString :: String -> IO [PathCommand]
- Graphics.SVGFonts.CharReference: characterStrings :: Map String [String] -> [(Char, String)] -> [Text]
+ Graphics.SVGFonts.CharReference: characterStrings :: String -> [String] -> [Text]
- Graphics.SVGFonts.ReadFont: type FontData = (SvgGlyph, Kern, [Float], String)
+ Graphics.SVGFonts.ReadFont: type FontData = (SvgGlyph, Kern, [Double], String)
- Graphics.SVGFonts.ReadFont: type Kern = (Map String [Int], Map String [Int], Map String [Int], Map String [Int], Vector X)
+ Graphics.SVGFonts.ReadFont: type Kern = (Map String [Int], Map String [Int], Map String [Int], Map String [Int], Vector Double)
- Graphics.SVGFonts.ReadFont: type OutlineMap = Map String [[(X, Y)]]
+ Graphics.SVGFonts.ReadFont: type OutlineMap = Map String (Path R2)
- Graphics.SVGFonts.ReadFont: type SvgGlyph = Map String (String, X, String)
+ Graphics.SVGFonts.ReadFont: type SvgGlyph = Map String (String, Double, String)

Files

LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2010, Tillmann Vogt
+Copyright (c) 2011, Tillmann Vogt
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
README view
@@ -5,11 +5,3 @@   "Save All"). If a font converted on your own doesn't work, try the repair options, and if this still
   doens't work edit the file by hand or tell me.
   Remember that a lot of fonts are not allowed to be distributed freely.
-
-Example:
-  src/Test/Font.hs generates a Collada file that can be read with tools like blender.
-  Textures currently don't work with blenders new native collada import.
-  But one can see them in Scenixviewer from nvidia.
-  The data that is passed to genCollada is represented with a data type that I use for all my libraries
-  to allow easy combination.
-
SVGFonts.cabal view
@@ -1,30 +1,39 @@ Name:             SVGFonts
-Version:          1.0
+Version:          1.1
 Synopsis:         Fonts from the SVG-Font format
-Description:      This format is easy to parse and was therefore chosen for a font library
-                  completely written in Haskell
+Description:      Native font support for the Diagrams library. The SVG-Font format is easy to parse
+                  and was therefore chosen for a font library completely written in Haskell
                   .
                   You can convert your own font to SVG with <http://fontforge.sourceforge.net/> or use the included LinLibertine, Bitstream
                   .
                   Features
                   .
-                  * A powerful functional interface, demonstrated with an example (Fonts.hs)
-                  .
-                  * The generated outlines can be transformed in various ways, i.e.
-                    elimination of holes for triangulation, representation with triangles, extrusion
-                  .
-                  * Color textures that are stored as files (font cache). Foreground and background color can be freely chosen.
-                  .
-                  * Everything is stored in a simple SceneGraph: <http://hackage.haskell.org/package/collada-types>
-                  .
                   * Complete implementation of the features that fontforge produces (but not the complete SVG format):
                   .
                   * Kerning (i.e. the two characters in \"VA\" have a shorter distance than in \"VV\")
                   .
-                  * Unicode, Ligatures
+                  * Unicode
                   .
+                  * Ligatures
+                  .
                   XML speed issues can be solved by trimming the svg file to only those characters that are used (or maybe binary xml one day)
+                  . 
+                  Version 1.0 of this library supports texturing which would only make sense in a Diagrams Backend that does rasterization in Haskell.
                   .
+                  Example:
+                  .
+                  >  # LANGUAGE NoMonomorphismRestriction #
+                  >
+                  > import Diagrams.Prelude
+                  > import Diagrams.Backend.Cairo.CmdLine
+                  > import Graphics.SVGFonts.ReadFont (textSVG)
+                  >
+                  >
+                  > main = defaultMain ( (text' "Hello World") <> (rect 8 1) # alignBL )
+                  >
+                  > text'  t = stroke (textSVG t 1) # fc purple # fillRule EvenOdd
+                  > text'' t = stroke (textSVG_ $ TextOpts t lin INSIDE_H KERN 1 1 ) # fc purple # fillRule EvenOdd
+                  .
 category:         Graphics
 License:          BSD3
 License-file:     LICENSE
@@ -39,30 +48,21 @@     hs-source-dirs: src
     other-modules: Paths_SVGFonts
     build-depends:
-        array,
         attoparsec-text,
         base == 4.*,
-        bytestring == 0.9.1.*,
         containers == 0.4.*,
-        collada-types >= 0.3,
-        collada-output >= 0.6,
+        data-default,
+        diagrams-lib,
         directory >= 1.0 && <1.2,
-        GLUT >= 2.1.2.1,
-        haskell98,
-        OpenGL >= 2.2.3.0,
-        parsec >= 2.1 && <= 3.2,
-        split == 0.1.*,
-        SVGPath == 1.1,
+        parsec,
+        split,
         text,
-        tga >= 0.2,
-        triangulation >= 0.3,
         tuple,
         vector,
+        vector-space,
         xml == 1.3.*
     exposed-modules:
         Graphics.SVGFonts.ReadFont
-        Graphics.SVGFonts.RasterFont
-Executable Fonts
-    hs-source-dirs: src
-    main-is:        Test/Fonts.hs
-    other-modules:  Graphics.SVGFonts.ReadFont, Paths_SVGFonts
+        Graphics.SVGFonts.CharReference
+        Graphics.SVGFonts.ReadPath
+
TODO view
@@ -1,11 +1,5 @@ Sorted after importance
 
-- correct errors in rasterization (negative x or higher than hadv: "fjJ" in LinLibertine, wrong tga size, ...)
-- correct errors in triangulation
-- syntax highlighter example
-
-- Integration in other libraries (support other scengraphs?)
 - speed
 - Binary XML (i.e. fast infoset), or a temp file, because xml-parsing has a speed problem)
-- Auto Hinting
-- Mono spacing of non-mono-spaced fonts
+- Auto Hinting (this depends on how the rasterization in the backend works)
src/Graphics/SVGFonts/CharReference.hs view
@@ -4,7 +4,7 @@ import qualified Data.Map as Map
 import qualified Data.Text as T
 import Data.Maybe (fromJust)
-import List (sortBy)
+import Data.List (sortBy)
 
 charRef :: Parser Int
 charRef
@@ -43,15 +43,13 @@ charsFromFullName (Just str) = map toEnum ( fromCharRefs (T.pack str) )
 
 
--- | a string defines a glyph, i.e. the ligature \"ffi\" is a string that defines the ligature glyph ffi
-characterStrings :: Map.Map String [String] -> [(Char,String)] -> [T.Text]
-characterStrings ligmap fstr | null ligs = map ((T.pack).(\x->[x]).fst) fstr
-                             | otherwise = case parseOnly myParser str
+-- | A string defines a glyph, i.e. the ligature \"ffi\" is a string that defines the ligature glyph ffi
+characterStrings :: String -> [String] -> [T.Text]
+characterStrings str ligs | null ligs = map ((T.pack).(\x->[x])) str
+                          | otherwise = case parseOnly myParser (T.pack str)
                                            of Right x -> x
                                               Left  x -> []
-  where str = T.pack $ map fst fstr
-        ligs = fromJust $ Map.lookup (snd (head fstr)) ligmap
-        myParser = many (try ligatures <|> charToText)
+  where myParser = many (try ligatures <|> charToText)
         ligatures = buildChain $ sortBy -- sort so that the longest ligatures come first, i.e. "ffi", "ff", ..
                                  (\x y -> compare (length y) (length x)) $ ligs
         buildChain [x]    = parseLigature x -- try to parse with the first parsers in the chain first
− src/Graphics/SVGFonts/RasterFont.hs
@@ -1,370 +0,0 @@-{-# LANGUAGE CPP, FlexibleContexts #-}-module Graphics.SVGFonts.RasterFont-(- commandsToRasterPoints, bitmasks, line, bitmask, borderToTex, sortSpans, overlaps, orBm, fillSpans, fillBitmask,- bitSum, endBits, bits, showBits, createTexture, F2P(..), AA(..), Bitmask, ftrace-)-where--import Data.Array.MArray-import Data.Array.ST (STArray,STUArray)-import Data.Array.Unboxed (elems, amap, UArray, IArray, listArray)-import Data.Bits ( (.&.), (.|.), testBit, setBit, clearBit, shiftL, bit, shiftR, complement)-import Data.Maybe (isJust)-import Data.Word (Word8, Word16)-import Data.Tuple.Select-import Graphics.Formats.Collada.Vector2D3D (V4(..))-import Graphics.Formats.TGA.TGA -- (TGAData, Picture (..))-import Graphics.SVG.ReadPath (PathCommand, ctp)-import Graphics.Rendering.OpenGL-import List (sortBy)-import qualified Data.STRef-import Control.Monad (when, forM_)-import Control.Monad.ST-import Foreign (withArray)-import Debug.Trace-import System.IO.Unsafe-import qualified Data.ByteString.Lazy as B--var = Data.STRef.newSTRef-gett = Data.STRef.readSTRef-mutate = Data.STRef.modifySTRef--type X = Float-type Y = Float-type F2 = (X,Y)-type F2P = (X, Y, AA)-data AA = NB | -- ^ No Bitmask-          B Bitmask-type Bitmask = UArray Int Word16 -- ^ A Bitmask contains 256 values: An Array of 16 Word16---- | Convert path-commands to outline points, which consist of bitmaps (resolution 16x16) of lines that go through---   the pixel (subpixel rasterization)-commandsToRasterPoints :: [PathCommand] -> F2 -> F2 -> [[F2P]]-commandsToRasterPoints commands (dx, dy) (offsetX, offsetY)-    | null points = []-    | otherwise = map (bitmasks (dx, dy)) (map (\r -> r ++ [head r]) points)-  where points = ctp commands [(0,0)] (0,0) True (dx,dy) (offsetX, offsetY)---------------------------------------------------------------------------------------------------------------------- generating bitmasks for rasterization---- | Every tuple of consecutive outline points builds a line that is rasterized and cut into little bitmasks-bitmasks :: (X,Y) -> [F2] -> [F2P]-bitmasks _ [a] = []-bitmasks (dx,dy) ((p0x,p0y):((p1x,p1y):bs))-     | p0x_int == p1x_int = -- ftrace ((show p0x) ++ " " ++ (show p0y) ++ " " ++ " vert\n" ++ (concat (map showF2P (rasterStraightLine True)) ) ++ "\n\n\n" ) $-                            (rasterStraightLine True) ++-                            rest -- vertical line-     | p0y_int == p1y_int = -- ftrace ((show p0x) ++ " " ++ (show p0y) ++ " " ++ " hor\n" ++ (concat (map showF2P (rasterStraightLine False)) ) ++ "\n\n\n" ) $-                            (rasterStraightLine False) ++-                            rest -- horizontal line-     | ( (abs (p1x_int-p0x_int)) == 1 && (abs (p1y_int-p0y_int)) == 1 ) || (abs (p1x-p0x)) < dx && (abs (p1y-p0y)) < dy = - -- ftrace ((show p0x) ++ " " ++ (show p0y) ++ " " ++ " div22\n" ++ (concat (map showF2P div22) ) ++ "\n\n\n" ) $-                                                      div22 ++-                                                      rest -- line that is at most one pixel long-     | otherwise = rest -- trace (show (p1x_int-p0x_int) ++ " " ++ show (p1y_int-p0y_int) ++ " error in RasterFont.hs") rest -- should not happen-  where  rest = bitmasks (dx,dy) ((p1x, p1y):bs)-         (p0x_int, p0y_int) | p0y < p1y = (truncate $ p0x/dx, truncate $ p0y/dy)-                            | otherwise = (truncate $ p1x/dx, truncate $ p1y/dy)-         (p1x_int, p1y_int) | p0y < p1y = (truncate $ p1x/dx, truncate $ p1y/dy)-                            | otherwise = (truncate $ p0x/dx, truncate $ p0y/dy)--         rasterStraightLine up | up        = (divide 1 len_y smallerX p0y_int (line 1 len_y pixP0 pixP1)) -- up-                               | otherwise = (divide len_x 1 smallerX p0y_int (line len_x 1 pixP2 pixP3)) -- right-         divide :: Int -> Int -> Int -> Int -> UArray (Int,Int) Word16 -> [F2P]-         divide nx ny x y a = [ (fromIntegral (x+ex), fromIntegral (y+ey), B (bitmask ex ey a)) |-		                                                         ey <- [0..(ny-1)], ex <- [0..(nx-1)] ]-         div22 = divide 2 2 smallerX p0y_int (line 2 2 pixP4 pixP5)-         len_x = abs (p1x_int - p0x_int) + 1-         len_y = p1y_int - p0y_int + 1-         smallerX = truncate $ if (p0x < p1x) then p0x/dx else p1x/dx--         pixP0 | p0y < p1y =  (fracX p0x, fracY p0y)-               | otherwise =  (fracX p1x, fracY p1y)-         pixP1 | p0y < p1y =  (fracX p1x,(fracY p1y) + (p1y_int-p0y_int) * 16)-               | otherwise =  (fracX p0x,(fracY p0y) + (p1y_int-p0y_int) * 16)-         pixP2 | p0x < p1x =  (fracX p0x, fracY p0y)-               | otherwise =  (fracX p1x, fracY p1y)-         pixP3 | p0x < p1x = ((fracX p1x) + (abs (p1x_int-p0x_int)) * 16, fracY p1y)-               | otherwise = ((fracX p0x) + (abs (p1x_int-p0x_int)) * 16, fracY p0y)-         pixP4 | p0y < p1y = ((fracX p0x) + if p0x<p1x then 0 else 16, fracY p0y)-               | otherwise = ((fracX p1x) + if p0x<p1x then 16 else 0, fracY p1y)-         pixP5 | p0y < p1y = ((fracX p1x) + if p0x<p1x then 16 else 0, (fracY p1y) + 16)-               | otherwise = ((fracX p0x) + if p0x<p1x then 0 else 16, (fracY p0y) + 16)--         fracX = truncate.(*16).snd.properFraction.(/dx)-         fracY = truncate.(*16).snd.properFraction.(/dy)---thawSTU :: (IArray UArray e, MArray (STUArray s) e (ST s)) => UArray (Int,Int) e -> ST s (STUArray s (Int,Int) e)-thawSTU = thaw---- | Bresenham line algorithm, adjusted to produce only one y value per row--- The data structure for the line is a 2d array of bits, in x-direction a sequence of word16s--- nx,ny are the size as multiples of 16,  draw line from (xa, ya) to (xb, yb)-line :: Int -> Int -> (Int,Int) -> (Int,Int) -> UArray (Int,Int) Word16-line nx ny (xa, ya) (xb, yb) = runST $ do-    a <- newArray ((0,0),(nx-1,ny*16-1)) (0 :: Word16) :: ST s (STUArray s (Int,Int) Word16)-    yV <- var y1-    errorV <- var $ deltax `div` 2-    forM_ [x1 .. x2] (\x -> do-        y <- gett yV-        mutate errorV $ subtract deltay-        error <- gett errorV-        when steep (setPix (a, y, x))-        when (not steep && (error < 0)) (setPix (a, x, y))-        when (error < 0) (do-            mutate yV (+ ystep)-            mutate errorV (+ deltax)))-    a' <- unsafeFreeze a-    return (a' :: UArray (Int,Int) Word16)-  where steep = abs (yb - ya) > abs (xb - xa)-        (xa', ya', xb', yb') = if steep-          then (ya, xa, yb, xb)-          else (xa, ya, xb, yb)-        (x1, y1, x2, y2) = if xa' > xb'-          then (xb', yb', xa', ya')-          else (xa', ya', xb', yb')-        deltax = x2 - x1-        deltay = abs $ y2 - y1-        ystep = if y1 < y2 then 1 else -1-        setPix (a, x, y) =-          when (x<nx*16 && y<ny*16 && x>=0 && y>=0)-          (writeArray a ((x `div` 16), y) (setBit (0::Word16) (x `mod` 16)))---- | Slice out a single bitmask from an array of bitmasks--- (this should be an unboxed array of unboxed arrays,---  but since this is not so easy in Haskell, one 2d-array with a supersampled y-coordinate (*16) is used)--- the x-supersampling are the bits in the Word16-bitmask :: Int -> Int -> UArray (Int,Int) Word16 -> Bitmask-bitmask ix iy mask = runST $ do-  ar <- thawSTU mask-  m <- newArray (0,15) (0::Word16) :: ST s (STUArray s Int Word16)-  forM_ [iy*16..iy*16+15] $ \y -> do-    e <- readArray ar (ix,y)-    writeArray m (y-iy*16) e-  m' <- unsafeFreeze m-  return (m' :: Bitmask)----------------------------------------------------------------------------------------------------------- rasterization--alpha (V4 _ _ _ a) = a---- | Border points to texture-borderToTex :: (Int,Int) -> [(Int, Int, Bitmask)] -> (V4,V4) -> TGAData-borderToTex (rx,ry) borderPoints (col, bgCol) | (alpha col) == 1 && (alpha bgCol) == 1-                         = TGAData (B.empty) (RGB24 (tgaRGB24 (col,bgCol) tgaData)) 0 0 rx ry-       | otherwise       = TGAData (B.empty) (RGB32 (tgaRGB32  col        tgaData)) 0 0 rx ry-  where-  tgaData = lines fs -- traceBitmask (overlaps $ sortSpans borderPoints) -- Caution! only small resolutions, i.e. 27x50, or you wait forever-  lines bs | B.null bs = B.empty-           | otherwise = B.append (B.reverse (B.take (fromIntegral rx) bs)) (lines (B.drop (fromIntegral rx) bs))--  fs = fillSpans (rx,ry) $ overlaps $ sortSpans borderPoints--tgaRGB32 col@(V4 r g b a) bs-    | isJust (B.uncons bs) = B.cons (m b) $-                             B.cons (m g) $-                             B.cons (m r) $-                             B.cons x (tgaRGB32 col (B.tail bs))-    | otherwise = B.empty-  where x = B.head bs-        m c = if (truncate (c*256)) == 0 then 0 else (truncate (c*256)) - 1--tgaRGB24 ( col@(V4 r g b _), bgCol@(V4 br bg bb _)) bs-    | isJust (B.uncons bs) = B.cons (m (sel3 color)) $-                             B.cons (m (sel2 color)) $-                             B.cons (m (sel1 color)) (tgaRGB24 (col,bgCol) (B.tail bs))-    | otherwise = B.empty-  where color = ( r*x + br*(256-x), g*x + bg*(256-x), b*x + bb*(256-x) )-        x = fromIntegral $ B.head bs-        m c = if (truncate c) == 0 then 0 else (truncate c) - 1----- | Sorting so that the lowest y comes first, equal ys then the lowest x comes first-sortSpans :: [(Int,Int,Bitmask)] -> [(Int,Int,Bitmask)]-sortSpans pixels = sortBy sxy pixels -  where sxy (x0,y0,b0) (x1,y1,b1) | y0 < y1 || (y0 == y1 && x0 < x1) = LT-                                  | y0 == y1 && x0 == x1 = EQ-                                  | otherwise = GT---- | Assuming the list of bitmasks is sorted, two consecutive bitmasks can be on the same position---   this happens if a line ends in pixel and a new line starts in the same pixel-overlaps :: [(Int,Int,Bitmask)] -> [(Int,Int,Bitmask)]-overlaps [] = []-overlaps [(x0,y0,b0)] = [(x0,y0,b0)]-overlaps ((x0,y0,b0):(x1,y1,b1):cs) | x0 == x1 && y0 == y1 = overlaps ( (x0,y0,b0 `orBm` b1) : cs )-                                    | otherwise            = (x0,y0,b0) : (overlaps ((x1,y1,b1):cs))---- | Bitwise oring two Bitmasks-orBm :: Bitmask -> Bitmask -> Bitmask-orBm b0 b1 = listArray (0,15) $ zipWith (.|.) (elems b0) (elems b1)---- | Fill a bitmask with 1s if the bit/subpixel is inside a polygon, (the bitmask was filled before with--- subpixels of outline drawing of the polygon). The bitmask is an array of 16 word16 where the--- x-direction are the bit position in the word16s. Horizontal(x-direction) lines are drawn if the--- line is inside the polygon, called a span (see Jordan curve theorem).--- A subpixel marks the beginnig or the end of a span depending on the state of the line.--- At the beginning all lines are white, if one intersects a subpixel it becomes black until it--- hits a subpixel again. The states at the end of the lines are stored for the next pixel in fillSpans.--- If these state bits are all one or all zero and the next bitmask is completely zero then fewer--- calculations need to be done.-fillSpans :: (Int, Int) -> [(Int,Int,Bitmask)] -> B.ByteString-fillSpans (rx,ry) [] = B.replicate (fromIntegral (rx*ry)) 0-fillSpans (rx,ry) ((x0,y0,b0):cs) = B.reverse $ B.append (B.replicate (fromIntegral (rx*y0+x0)) 0)-                                                         ( fillSp (rx,ry) 0 ((x0,y0,b0):cs) )--fillSp :: (Int, Int) -> Word16 -> [(Int,Int,Bitmask)] -> B.ByteString-fillSp (rx,ry) endbs ((x0,y0,b0):(x1,y1,b1):cs)-    | y0 /= y1 = B.append-    -- filling the end of a line (rx-x0) and the room between two shapes and the beginning of a new one (x1)-                 (B.cons (toC (bitSum fbm)) (B.replicate (fromIntegral (rx-x0-1+x1+linesBetween)) 0))-                 ( fillSp (rx,ry) 0 ((x1,y1,b1):cs) )--    | otherwise =    -- between two border points-        ( B.append (B.cons (toC (bitSum fbm)) (B.replicate (fromIntegral (x1-x0-1)) c))-                   (fillSp (rx,ry) eb ((x1,y1,b1):cs))-        )-  where fbm = fillBitmask b0 endbs-        eb  = endBits fbm-        beb = bits eb-        c = toC beb*16-        toC c | c /= 0 = fromIntegral $ c-1   -- because 256 is not a Byte-              | otherwise = fromIntegral c-        newl x y = x ++ "\n" ++ y-        linesBetween = (y1-y0-1)*rx -- the room between two shapes----ftrace (if (y0==36) then (show x0 ++ " " ++ show y0 ++ " " ++ show endbs ++ " " ++ show (x1-x0) ++---                  " " ++ show eb ++ " " ++ "\n" ++ (foldr newl "" $ map showBits $ elems b0) ++ (foldr newl "" $---                  map showBits $ elems fbm) ++ "$\n" ) else [])--fillSp (rx,ry) _ [(x0,y0,b0)] =-    B.replicate (fromIntegral (rx-x0+(ry-y0-1)*rx)) 0 -- filling after the last border point----- | Assuming a single line passes through a Bitmask, set all points right to this line to 1-fillBitmask :: Bitmask -> Word16 -> Bitmask-fillBitmask mask endbs =  listArray (0,15) $-                          zipWith (\x y -> if testBit endbs x then fillBefore y else fillAfter y)-                                 [0..15]-                                 (elems mask)--fillWord w16 i b | i == 15 = if tb then (if b then clearBit w16 i else setBit w16 i)-                                   else (if b then setBit w16 i   else clearBit w16 i)-                 | b && not tb = fillWord (setBit w16 i) (i+1) True  --  filling-                 | b &&     tb = fillWord  w16           (i+1) False -- no filling-                 | not b && not tb = fillWord  w16       (i+1) False -- no filling-                 | not b &&     tb = fillWord  w16       (i+1) True  --  filling-  where tb = testBit w16 i---- | Before the first occurrance of a 1 set all bits to 1 example: 00001000 ~> 11110000-fillBefore :: Word16 -> Word16-fillBefore w16 = fillWord w16 0 True---- | After the first occurrance of a 1 set all bits to 1 example: 00001000 ~> 00001111-fillAfter :: Word16 -> Word16-fillAfter w16 = fillWord w16 0 False---- fillBefore w16 | w16 /= 0  = w16 + w16 - 1---               | otherwise = -1  -- 00xFF--- not always correct, but fast--- fillAfter w16 | w16 /= 0  = complement (w16 + w16 - 1)---               | otherwise = 0---- | The sum of all bits in a Bitmask, 16 rows with 16 bits each => between 0 and 255-bitSum :: Bitmask -> Word16-bitSum mask = foldr (+) 0 $ elems $ amap bits mask---- | Return the the rightmost column of a bitmask-endBits :: Bitmask -> Word16-endBits mask = sum $ zipWith shiftR (map lastBit (elems mask)) (reverse [0..15])-lastBit x = x .&. bit 15---- | Count the number of 1-bits with divide and conquer--- it can be done a little bit faster, but for the beginning it should just be correct--- see "Hacker's Delight by Henry S. Warren, Addison Wesley" for bit counting--- fst line: starting to count the 1's in 2-tuples: 0x55 = 01010101--- 2nd line: 4 tuples:  0x33 = 0011001100-bits :: Word16 -> Word16-bits w16 = d8-  where d  = w16 .&. 0x5555 + (shiftR w16 1) .&. 0x5555-        d2 = d   .&. 0x3333 + (shiftR d  2)  .&. 0x3333-        d4 = d2  .&. 0x0F0F + (shiftR d2 4)  .&. 0x0F0F-        d8 = d4  .&. 0x00FF + (shiftR d4 8)--------------------------------------------------------------------------------------------------------------------- | OpenGL specific texture-createTexture :: (Int,Int) -> TGAData -> String -> IO (TextureObject, String)-createTexture (rx,ry) (TGAData _ (RGB24 picture) _ _ w h) fileName = tex fileName (rx,ry) (B.unpack picture)-createTexture (rx,ry) (TGAData _ (RGB32 picture) _ _ w h) fileName = tex fileName (rx,ry) (B.unpack picture)--tex :: String -> (Int,Int) -> [Word8] -> IO (TextureObject,String)-tex fileName (rx,ry) xs = do-  [texName] <- genObjectNames 1-  textureBinding Texture2D $= Just texName-  textureFilter Texture2D $= ((Nearest, Nothing), Nearest)-  let imageSize = TextureSize2D (fromIntegral rx) (fromIntegral ry)-  withCheckImage xs imageSize f $-    texImage2D Nothing NoProxy 0  RGBA' imageSize 0-  return (texName, fileName)- where-  f (a:(b:(c:(d:ds)))) = (Color4 (fromIntegral a) (fromIntegral b) (fromIntegral c) 255) : (f ds)-  f _ = []--withCheckImage :: [Word8] -> TextureSize2D ->-                  ([Word8] -> [(Color4 GLubyte)]) ->-                  (PixelData (Color4 GLubyte) -> IO ()) ->-                  IO ()-withCheckImage xs (TextureSize2D w h) f act =-  withArray (f xs)-  $ act. PixelData RGBA UnsignedByte------------------------------------------------------------------------------------------------------------------- show / debugging functions--instance Show AA-  where show (NB) = "NB"-        show (B bits) = "B " ++ show (elems bits)---- | Show a binary representation of a Word16-showBits :: Word16 -> String-showBits w16 = map ((\x -> if x then '1' else '0').(testBit w16)) (reverse [0..15])--showBitmask :: Bitmask -> String-showBitmask b = foldr newl "" (map showBits (elems b))-  where newl x y = x ++ "\n" ++ y--showF2P :: F2P -> String-showF2P (x,y,B b)  = show (truncate x) ++ " " ++ show (truncate y) ++ "\n" ++ (showBitmask b)---- | Like trace from Debug.Trace but into a file-ftrace :: String -> a -> a-ftrace string expr = unsafePerformIO $ do-    appendFile "debug.txt" string-    return expr--traceBitmask :: [(Int,Int,Bitmask)] -> a -> a-traceBitmask bms expr = unsafePerformIO $ do-    writeFile "bitmasks.txt" (unlines (mergeBitmasks bms []))-    return expr---- | Insert every Bitmask in a x,y-position to generate an ASCII picture in a text file-mergeBitmasks :: [(Int,Int,Bitmask)] -> [String] -> [String]-mergeBitmasks [] res = res-mergeBitmasks ((x,y,b):bs) ls = mergeBitmasks bs newPic-  where newPic = (take (y*16) ls) ++  -- copy lines until y-position-                 -- if an y-position is accessed that is bigger than ever before, write empty lines-                 (replicate (y*16-(length ls)) []) ++ -                 insertedBitmasks ++ -- 16 lines where the bitmask is located-                 (drop ((y+1)*16) ls)-        insertLines = take 16 $ (drop (y*16) ls) ++ (replicate ((y+1)*16-(length ls)) [])-        insertedBitmasks = zipWith (\line bmLine-> (take (x*16) line) ++ (replicate (x*16- (length line)) '.' ) ++-                                                    bmLine ++ (drop ((x+1)*16) line))-                                   insertLines-                                   (map (reverse.showBits) (elems b))
src/Graphics/SVGFonts/ReadFont.hs view
@@ -1,42 +1,37 @@ module Graphics.SVGFonts.ReadFont
-(openFont, makeOutlMaps, makeTexMap, Mode(..), Spacing(..), Rx(..), displayString,
-Kern, SvgGlyph, FontData, OutlineMap, OutlineTexMap,
-P, CharProp(..), Props, Transf, TexMap, X ,Y
-)
+-- (openFont, outlMap, textSVG, textSVG_, Mode(..), Spacing(..), Kern, SvgGlyph, FontData, OutlineMap, TextOpts(..))
 where
 
 import Data.Char (isSpace)
-import Data.List (zip5)
+import Data.Default
+import Data.List (intersect,sortBy)
 import Data.List.Split (splitOn, splitWhen)
-import Data.Maybe (fromMaybe, fromJust, isJust, maybeToList, catMaybes)
+import Data.Maybe (fromMaybe, fromJust, isJust, isNothing, maybeToList)
 import qualified Data.Map as Map
 import qualified Data.Text as T
+import Data.Monoid (mconcat)
 import Data.Tuple.Select (sel1, sel2, sel3, sel4, sel5)
 import Data.Vector (Vector)
+import Data.VectorSpace
+import Diagrams.Path
+import Diagrams.Segment
+import Diagrams.TwoD.Types
+import Diagrams.Prelude
 import qualified Data.Vector as V
-import Graphics.Formats.Collada.ColladaTypes
-import Graphics.Formats.Collada.GenerateObjects (cube, blue, obj, makeScene, get_name, getDiffuseColor, getAmbientColor)
-import Graphics.Formats.Collada.Vector2D3D (V3(..), V4(..), mul, divide, v_len, set_len, cross3)
-import Graphics.Formats.TGA.TGA (readTGA, writeTGA)
-import Graphics.Rendering.OpenGL (TextureObject)
 import Graphics.SVGFonts.CharReference (charsFromFullName, characterStrings)
-import Graphics.SVGFonts.RasterFont (createTexture, borderToTex, commandsToRasterPoints, AA(..), F2P(..), ftrace)
-import Graphics.SVG.ReadPath (commandsToPoints, pathFromString)
-import List (intersect,sortBy)
+import Graphics.SVGFonts.ReadPath (pathFromString, PathCommand(..))
+import Paths_SVGFonts(getDataFileName)
 import System.Directory
 import System.IO.Unsafe (unsafePerformIO)
 import Text.XML.Light
-import Debug.Trace
 
 -- http://www.w3.org/TR/SVG/fonts.html#KernElements
 type Kern = ( Map.Map String [Int],
               Map.Map String [Int],
               Map.Map String [Int],
-              Map.Map String [Int], Vector X ) -- ^ u1s, u2s, g1s, g2s, k
-type SvgGlyph = Map.Map String (String, X, String) -- ^ \[ (unicode, (glyph_name, horiz_advance, ds)) \]
-type FontData = (SvgGlyph, Kern, [Float], String) -- ^ (SvgGlyph, Kern, bbox-string, filename)
-type X = Float
-type Y = Float
+              Map.Map String [Int], Vector Double ) -- ^ u1s, u2s, g1s, g2s, k
+type SvgGlyph = Map.Map String (String, Double, String) -- ^ \[ (unicode, (glyph_name, horiz_advance, ds)) \]
+type FontData = (SvgGlyph, Kern, [Double], String) -- ^ (SvgGlyph, Kern, bbox-string, filename)
 
 -- | Open an SVG-Font File and extract the data
 --
@@ -51,7 +46,8 @@ -- ie all the (\"name1\",0) (\"name1\",1) to (\"name1\",[0,1]).
 -- Now the g2s are converted in the same way as the g1s.
 -- Whenever two consecutive chars are being printed try to find an
--- intersection of the list assigned to the first char and second char 
+-- intersection of the list assigned to the first char and second char
+
 openFont :: FilePath -> FontData
 openFont file = ( Map.fromList (myZip4 (unicodes, glyphNames, horiz, ds)),  -- Map with unicode keys
                   (transform u1s, transform u2s, transform g1s, transform g2s, kAr), -- kerning data
@@ -60,11 +56,11 @@                 )
   where
     -- monospaced fonts sometimes don't have a "horiz-adv-x="-value , replace with bbox value
-    myZip4 (a:as, b:bs, c:cs, d:ds) | c == []   = (a, (b, (parsedBBox!!2) - (parsedBBox!!0), d)) : (myZip4 (as,bs,cs,ds))
+    myZip4 (a:as, b:bs, c:cs, d:ds) | c == []   = (a, (b, (parsedBBox!!2) - (parsedBBox!!0), d)) :
+                                                  (myZip4 (as,bs,cs,ds))
                                     | otherwise = (a, (b, read c, d)) : (myZip4 (as,bs,cs,ds))
     myZip4 _ = []
-
-    xml = onlyElems $ parseXML (unsafePerformIO (readFile file))
+    xml = onlyElems $ parseXML $ unsafePerformIO $ readFile $ file
     selectFontface = concat $ map (findElements (unqual "font-face")) xml
     selectGlyphs   = concat $ map (findElements (unqual "glyph"))     xml
     selectKerns    = concat $ map (findElements (unqual "hkern"))     xml
@@ -99,22 +95,22 @@ 
     fname f = last $ init $ concat (map (splitOn "/") (splitOn "." f))
 
+
 -- | horizontal advances of characters inside a string
 -- a character is stored with a string (originally because of ligatures)
-horizontalAdvances :: [(String,FontData)] -> Bool -> [X]
-horizontalAdvances []                      _       = []
-horizontalAdvances [(ch,fd)]               _       = [hadv ch fd]
-horizontalAdvances ((ch0,fd0):(ch1,fd1):s) kerning = ((hadv ch0 fd0) - (ka (sel2 fd0)))
-                                                      : (horizontalAdvances ((ch1,fd1):s) kerning)
-  where ka kern |(sel4 fd0) == (sel4 fd1) && kerning = (kernAdvance ch0 ch1 kern True) +
-                                                       (kernAdvance ch0 ch1 kern False) -- no kerning when different fonts
+horizontalAdvances :: [String] -> FontData -> Bool -> [Double]
+horizontalAdvances []          _  _       = []
+horizontalAdvances [ch]        fd _       = [hadv ch fd]
+horizontalAdvances (ch0:ch1:s) fd kerning = ((hadv ch0 fd) - (ka (sel2 fd))) :
+                                            (horizontalAdvances (ch1:s) fd kerning)
+  where ka kern | kerning = (kernAdvance ch0 ch1 kern True) + (kernAdvance ch0 ch1 kern False)
                 | otherwise = 0
 
 hadv ch fontD | isJust lookup = sel2 (fromJust (Map.lookup ch (sel1 fontD)))
               | otherwise = 0
   where lookup = Map.lookup ch (sel1 fontD)
 
-kernAdvance :: String -> String -> Kern -> Bool -> X
+kernAdvance :: String -> String -> Kern -> Bool -> Double
 kernAdvance ch0 ch1 kern u |     u && not (null s0) = (sel5 kern) V.! (head s0)
                            | not u && not (null s1) = (sel5 kern) V.! (head s1)
                            | otherwise = 0
@@ -122,355 +118,153 @@         s1 = intersect (s sel3 ch0) (s sel4 ch1)
         s sel ch = concat (maybeToList (Map.lookup ch (sel kern)))
 
-type TexMap =  Map.Map (String,String,String) (TextureObject, String)
-type OutlineMap =  Map.Map String [[(X,Y)]]
-type OutlineTexMap =  Map.Map String [[F2P]]
-data Mode = INSIDE_V1_V2-- ^ INSIDE_V1_V2: The string is inside v1 v2 boundaries (height/length-relation not kept)
-          | INSIDE_V1 -- ^ INSIDE_V1: Stay inside v1 boundary, size of v2 adjusted to height/length-relation
-          | INSIDE_V2 -- ^ INSIDE_V2: Stay inside v2 boundary, size of v1 adjusted to height/length-relation
+type OutlineMap =  Map.Map String (Path R2)
+data Mode = INSIDE_WH-- ^ INSIDE_WH: The string is stretched inside Width and Height boundaries
+          | INSIDE_W -- ^ INSIDE_W:  The string fills the complete width, heigth adjusted
+          | INSIDE_H -- ^ INSIDE_H:  The string fills the complete height, width adjusted
 
-mV1V2V3 INSIDE_V1_V2 = True
-mV1V2V3 _ = False
-mV1 INSIDE_V1 = True
-mV1 _ = False
-mV2 INSIDE_V2 = True
-mV2 _ = False
+mWH INSIDE_WH = True
+mWH _ = False
+mW INSIDE_W = True
+mW _ = False
+mH INSIDE_H = True
+mH _ = False
 
 -- | See <http://en.wikipedia.org/wiki/Kerning>
 data Spacing = KERN -- ^ Recommended, same as HADV but sometimes overridden by kerning:
                     -- i.e. the horizontal advance in "VV" is bigger than in "VA"
              | HADV -- ^ Every glyph has a unique constant horiz. advance
-             | MONO -- ^ Mono spacing between glyphs. Experimental.
-                    -- Better use a monospaced font from the beginning.
-                    -- The longest glyph influences the bbox that is used.
-isMono MONO = True
-isMono _    = False
+
 isKern KERN = True
 isKern _    = False
 
--- | The y resolution is constant. The x resolution of a glyph changes in non-mono-spaced fonts.
-data Rx = Exactly Int
-        | ConstDx -- ^ The x-resolution of a single glvph is chosen so that all pixels
-                  -- from several glyphs have the same size
-        | XPowerOfTwo -- ^ With this option the resolution nearest of a power of two is chosen
-                      -- i.e. "l" would have (256,512), while "w" would have (512,512)
-        | OneTexture -- ^ The whole string as one texture (not implemented yet)
+type FileName = String
 
-isExactly (Exactly _) = True
-isExactly _           = False
-isConstDx ConstDx = True
-isConstDx _       = False
-isXPowerOfTwo XPowerOfTwo = True
-isXPowerOfTwo _           = False
-getRx (Exactly rx) = Just rx
-getRx _ = Nothing
 
-type FileName = String
-type P = [Char] -> [String]
-data CharProp = Outl (FontData, OutlineMap, OutlineTexMap) String |
-                Tex (FontData, OutlineMap, OutlineTexMap) String
+textSVG :: String -> Double -> Path R2
+textSVG t h = textSVG_ with { txt = t, textHeight = h }
 
-isTex :: CharProp -> Bool
-isTex (Tex _ _) = True
-isTex (Outl _ _) = False
+data TextOpts = TextOpts
+                { txt :: String
+                , fdo :: (FontData, OutlineMap)
+                , mode :: Mode
+                , spacing :: Spacing
+                , textWidth :: Double
+                , textHeight :: Double
+                }
 
-getFont :: CharProp -> (FontData, OutlineMap, OutlineTexMap)
-getFont (Outl font _) = font
-getFont (Tex font _) = font
-getTr (Outl _ tr) = tr
-getTr (Tex _ tr) = tr
+ro = unsafePerformIO . getDataFileName -- read only of static data (safe)
 
-type Transf = Map.Map String (Geometry -> Geometry)
-type Props = Map.Map String CharProp
--- | Main library function, explained with an example (that is also in Fonts.hs):
---
--- @
--- main = do
---   args <- getArgs
---   let str = if null args then \"Haskell\"
---                          else head args
---       resolution = (400, 400)
--- @
---
--- The resolution is used for textures but also for outlines.
--- Every outline point is placed in one grid position
---
--- @
---       mode = INSIDE_V2
---       spacing = KERN
---       tex = ConstDx
---       bit = makeOutlMaps \"../../../src/Test/Bitstream.svg\" resolution
---       lin = makeOutlMap \"..\/..\/../src\/Test\/LinLibertine.svg\" resolution
--- @
---
--- Several different fonts can be used. They are stored in Data.Map structures to avoid recalculation.
--- Lazy Evaluation ensures that outlines are only calculated if needed.
---
--- @
---      o  = V3 0 0 0 -- origin
---      v1 = V3 (-5) 0 0 -- direction of char-advance
---      v2 = V3 0 0 1  -- height direction
---      v3 = V3 0 0.1 0 -- extrusion
--- @
---
--- The position and size of the string
---
--- @
---       f :: String -> [String]
---       f str = take (length str) (concat (repeat [\"p\",\"q\"]))
--- @
---
--- Assigning a property to every character by a string. Here an alternation of 3d and textured characters
---
--- @
---      props :: Props
---      props = Map.fromList [("p", Outl bit "to3d"), ("q", Outl bit "to3d2"),
---                            ("r", Tex bit "red"),   ("s", Tex bit "blue") ]
--- @
---
--- Finite data structures are assigned to every property string.
--- This is needed to lazily make a Data.Map with every possible representation of a character.
--- A textured character needs an unchanged (maybe colored) outline. Thats why \"q\" uses the id function.
--- If several fonts are used, kerning is disabled between every two characters that are from different fonts.
---
--- @
---       transf :: Transf
---       transf = Map.fromList [("to3d",to3d), ("to3d2",to3d2), ("red", red.bgWhite), ("blue",blue.bgWhite), ("id",id)]
--- @
---
--- Although there might be finitely many functions that make sense it is still to much and we need
--- a small finite list for Data.Map. The number of combinations is ((Number of chars in font0) +
--- (Number of chars in font1) + ...) * (number of transformation functions) and every of these
--- combinations is built (but only evaluated if needed because of lazy evaluation)
--- Example: (font1: 40 chars + font2: 1000 chars) X (5 colors (transformation functions))
---          5200 key-value-pairs (assuming all colors are used in both fonts).
+bit = outlMap (ro "src/Test/Bitstream.svg")
+lin = outlMap (ro "src/Test/LinLibertine.svg")
+
+instance Default TextOpts where
+    def = TextOpts "text" lin INSIDE_H KERN 1 1
+
+-- | Main library functions, usage:
 --
 -- @
---      to3d geom = red $ ( ((extrude v3).deleteHoles) geom ) \`atop\` ( tri ((translate v3) geom) )
---      to3d2 geom = blue $ ( ((extrude (0,0.2,0)).deleteHoles) geom ) `atop` ( tri ((translate (0,0.2,0)) geom) )
---      tri = (triangulate ketTri).deleteHoles -- openglTriangulation
---      red =  changeDiffuseColor "red" (1,0,0,1) -- if used with textures diffuse is interpreted as foreground color
---      blue = changeDiffuseColor "blue" (0,0,1,1)
---      bgWhite = changeAmbientColor "white" (1,1,1,1) -- if used with textures interpreted as background color
--- @
--- 
--- The functions can be anything like extrusion, triangulation, color
+-- \{-\# LANGUAGE NoMonomorphismRestriction \#-\}
 --
--- @
---       texmap = makeTexMap resolution props transf
--- @
+--import Diagrams.Prelude
+--import Diagrams.Backend.Cairo.CmdLine
+--import Graphics.SVGFonts.ReadFont
 --
--- Again a Data.Map-structure to avoid recalculation of textures.
--- This has been separated from the outlineMap because it may one day also store
--- the transformations applied to the outlines and maybe not every char is a texture
+--main = defaultMain ( (text' \"Hello World\") \<\> (rect 8 1) \# alignBL )
 --
--- @
---      node = displayString str "node" resolution mode spacing (o,v1,v2) f props transf texmap
---   genCollada (lightedScene node) emptyAnim
---   putStrLn \"Collada File generated\"
+--text' t = stroke (textSVG_ $ TextOpts t lin INSIDE_H KERN 1 1 ) \# fc purple \# fillRule EvenOdd
 -- @
 --
--- node is a Node in a Scenegraph, that is inserted into a lighted scene 
--- and written into file that can be viewed in Blender
---
-displayString :: String -> String -> (Rx,Int) ->Mode->Spacing-> (V3,V3,V3) ->P->Props->Transf-> TexMap -> Scene
-displayString txt sid (rx,ry) mode spacing (o,v1,v2) f props transf texmap | mV1V2V3 mode = makeString v1 v2
-                                                                           | mV1 mode     = makeString v1 newV2
-                                                                           | mV2 mode     = makeString newV1 v2
+textSVG_ :: TextOpts -> Path R2
+textSVG_ to | mWH (mode to) = makeString (textWidth to) (textHeight to)
+            | mW  (mode to) = makeString (textWidth to) ((textWidth to) * maxY / sumh)
+            | mH  (mode to) = makeString ((textHeight to) * sumh / maxY) (textHeight to)
   where
-    makeString u1 u2 = makeScene sid $ map (\(g,transl) -> obj (get_name (head g)) g transl) (withoutSpaces u1 u2)
-    withoutSpaces u1 u2 = filter ((not.null).fst) (geometrieVs u1 u2)
-    geometrieVs u1 u2 = map (getC u1 u2 sumh (rx,ry) texmap) (zip5 str horPos hs newProps newTrList)
-    sumh | isMono spacing = maxX * (fromIntegral (length str)) -- a hack, not meant to be monospaced
-         | otherwise      = sum hs                              -- maybe a very long glyph can mess up a font
-    horPos | isMono spacing = reverse $ added (o: (replicate (length str) (v1Advance * (V3 maxX maxX maxX))))
-           | otherwise      = reverse $ added (o: stretch hs)
-    hs = horizontalAdvances (zip str newFontList) (isKern spacing)
-    stretch = map (v1Advance `mul`)
-    added = snd.(foldl (\(h,l) (b,_) -> (h + b, (h + b):l))
-                       ((V3 0 0 0),[])).  (map (\x->(x,[]))) -- [o,o+h0,o+h0+h1,..]
-    newV1 = set_len v1 ( (v_len v2) * (sumh/maxY) ) -- in case there are several fonts in a string
-    newV2 = set_len v2 ( (v_len v1) * (maxY/sumh) ) -- maxY is the average of max heights
-    maxX = (sum (map maximum_x fontList)) /
-           (fromIntegral (length fontList)) -- difficult to treat different fonts in one string
-
-    maxY = (sum (map bbox_dy fontList)) / (fromIntegral (length fontList)) -- max height of glyph
-    v1Advance | mV1V2V3 mode || mV1 mode = v1    `divide` sumh
-              | mV2 mode                 = newV1 `divide` sumh
-
-    properties = map (fromJust.((\x y -> Map.lookup y x) props)) (f txt)
-    fontList = map (sel1.getFont) properties
-    trList = map ((\x -> (x, fromJust (Map.lookup x transf))).getTr) properties
-
-	-- because of ligatures, str may contain fewer glyphs, properties, fontList and trList have to be adjsuted
-    newProps = dropSome (map length str) properties
-    newFontList = dropSome (map length str) fontList
-    newTrList = dropSome (map length str) trList
-    dropSome [] _ = []
-    dropSome _ [] = []
-    dropSome (l:ls) ps = (head ps) : (dropSome ls (drop l ps))
+    makeString w h = -- translate (-w/2, - h/2) $ -- origin in the middle
+                     scaleY (h/maxY) $ scaleX (w/sumh) $
+                     translate (0, - bbox_ly fontD) $
+                     mconcat $
+                     zipWith translate horPos
+                     (map (polygonChar outl) str)
+    (fontD,outl) = (fdo to)
+    polygonChar outl ch = fromJust (Map.lookup ch outl)
+    horPos = reverse $ added ( (0,0) : (map ((1,0) ^*) hs) )
+    hs = horizontalAdvances str fontD (isKern (spacing to))
+    sumh = sum hs
+    added = snd.(foldl (\(h,l) (b,_) -> (h ^+^ b, (h ^+^ b):l))
+                       ((0,0),[])).  (map (\x->(x,[]))) -- [o,o+h0,o+h0+h1,..]
+    maxY = bbox_dy fontD -- max height of glyph
 
-    ligaturesOfFontName = Map.fromList $ zip (map sel4 fontList)
-                                             (map ((filter ((>1).length)).(Map.keys).sel1) fontList)
-    constFontStrings :: String -> [String] -> [(Char,String)] -> [[(Char,String)]]
-    constFontStrings [c] [f] cc = [((c,f):cc)] -- group characters that have consecutively the same font
-    constFontStrings (c0:c1:cs) (f0:f1:fs) cc | f0 == f1  =  constFontStrings (c1:cs) (f1:fs) ((c0,f0):cc)
-                                              | otherwise = ((c0,f0):cc) : constFontStrings (c1:cs) (f1:fs) []
-    str = map T.unpack $ concat $ map (characterStrings ligaturesOfFontName)
-                                      (map reverse (constFontStrings txt (map sel4 fontList) []))
+    ligatures = ((filter ((>1).length)).(Map.keys).sel1) fontD
+    str = map T.unpack $ characterStrings (txt to) ligatures
 
 
-type Glyphdata = (String,V3,Float,CharProp,(String,Geometry->Geometry))
-
-getC :: V3 -> V3 -> Float -> (Rx,Int) -> TexMap -> Glyphdata -> ([Geometry],V3)
-getC u1 u2 sh (rx,ry) tex (ch,h,h_ad,pr,tr) | isTex pr  = texChar tex sfd u1 u2 sh (ch,h,h_ad,fst tr) (rx,ry)
-                                            | otherwise = (map (snd tr) (fst pc), snd pc)
-  where pc = polygonChar om sfd u1 u2 sh (rx,ry) (fst tr) (ch,h)
-        sfd = sel1 fd
-        fd = getFont pr
-        om = sel2 fd
-
 bbox_dy fontData = (bbox!!3) - (bbox!!1)
   where bbox = sel3 fontData -- bbox = [lower left x, lower left y, upper right x, upper right y]
 
 bbox_lx fontData   = (sel3 fontData) !! 0
 bbox_ly fontData   = (sel3 fontData) !! 1
-maximum_x fontData = (sel3 fontData) !! 2
 
-polygonChar :: OutlineMap -> FontData -> V3->V3->Float -> (Rx,Int) -> String -> (String,V3) -> ([Geometry],V3)
-polygonChar outl fontD v1 v2 sum_of_hs (rx,ry) tr (ch,h) = (geometry,h)
-  where
-  resize (x,y) = (v1 `mul` (x / sum_of_hs)) + (v2 `mul` (y / (bbox_dy fontD)  ))
-  out = fromMaybe [] (Map.lookup ch outl)
-  l = map (map resize) out
-  geometry | ch /= " " = [outline_geometry ch l fontD ("outline_" ++ ch ++ "_" ++ (sel4 fontD) ++ "_" ++ tr)]
-           | otherwise = []
 
-
-outline_geometry ch l fontD name = Geometry name
-                          [ LP (LinePrimitive indices indices V.empty [blue]) ]
-                           (Vertices "outline_vertices" (V.fromList (concat l))  -- vertices
-                             (V.replicate (length (concat l)) (V3 0 0 1))) -- normals
-  where
-    indices = parts 0 lengths
-    parts n (p:ps) = V.cons (V.fromList [n..(n+p-1)]) (parts (n+p) ps)
-    parts _ []     = V.empty
-    lengths = map length l
-
-
-texChar :: TexMap -> FontData -> V3 -> V3 -> Float -> (String,V3,Float,String) -> (Rx,Int) -> ([Geometry],V3)
-texChar texmap fontD v1 v2 sum_of_hs (ch,h,h_ad,tr) (rx,ry) = (geometry,h)
-  where
-  fontName = sel4 fontD
-  cn | isJust $ Map.lookup ch (sel1 fontD) = sel1 $ fromJust $ Map.lookup ch (sel1 fontD) -- description of the char (with a word)
-     | otherwise = ch
-  charName ch | length ch == 1 && (head ch) >= 'A' && (head ch) <= 'Z' = ch ++ "BIG" -- for Windows
-              | otherwise = ch
-  name = "tex_" ++ (charName cn) ++ "_" ++ fontName ++ "_" ++ tr
-  tex = Texture (name ++ "_im") fileName $! texObj -- force texObj to be evaluated because it produces a file
-  fileName = (charName cn) ++"_"++ fontName ++"_"++ tr ++ (show (getx ch fontD (rx,ry))) ++ "x" ++ (show ry) ++ ".tga"
-  geometry =
-    [Geometry name [PL (LinePrimitive (V.fromList [V.fromList [0,1,2,3]]) -- indices to vertices
-                                      (V.fromList [V.fromList [0,0,0,0]]) -- indices to normals
-                                      (V.fromList [V.fromList [0,1,2,3]]) -- indices to texture coordinates
-         [("phong_" ++ name, COMMON "" NoParam
-           (PhongTex [(TDiffuse tex)]
-             [[0,0,1,0,1,1,0,1]] -- [u0,v0,u1,v1,..] coordinates (Floats betw. 0 and 1)
-                                                        -- that point into the texture
-           ) ""
-         )]
-                  )]
-       (Vertices (name ++ "_vertices")
-        (V.fromList [res (n,n), res (v1,n), res (v1,v2), res (n,v2)]) -- vertices
-        (V.fromList [ cross3 v1 v2 ]) -- normals
-       )
-    ]
-
-  res (v,w) = (v `mul` (h_ad / sum_of_hs)) + w
-  texObj = case Map.lookup (ch,fontName,tr) texmap of
-           Just x -> Just $! (fst $! x)
-           Nothing -> Nothing
-
-n = (V3 0 0 0)
-
--- | Generate a map of lists of outlines
--- i.e. the letter 'i' consists of two lists.
---
--- Two different kinds of outlines are produced because:
---
--- * 'OutlineMap': There are non-equally-spaced points possible (for outlines)
+-- | Generate Paths of outlines
 --
--- * 'OutlineTexMap': There are only equally spaced points (for spans in rasterization)
-makeOutlMaps :: String -> (Rx,Int) -> (FontData, OutlineMap, OutlineTexMap)
-makeOutlMaps str (rx,ry) = ( fontD, Map.fromList [ (ch, outlines ch)     | ch <- allUnicodes ],
-                                    Map.fromList [ (ch, outlinesTex ch)  | ch <- allUnicodes ] )
+outlMap :: String -> (FontData, OutlineMap)
+outlMap str = ( fontD, Map.fromList [ (ch, outlines ch) | ch <- allUnicodes ] )
   where
   allUnicodes = Map.keys (sel1 fontD)
-  outlines ch    = commandsToPoints (cs ch) (ds ch) (0, -(bbox_ly fontD))
-  -- TO DO: screws up rasterization in LinLibertine
-  outlinesTex ch = map positiveX ( commandsToRasterPoints (cs ch) (stretch ch (ds ch)) (0, -(bbox_ly fontD)) )
-  positiveX = map ( \(x,y,b) -> (if x>0 then x else 0, y, b) )
-  stretch ch (x,y) = (x * (hadv ch fontD) / max_x, y)
-  max_x = maximum_x fontD
-  cs ch = commands ch (sel1 fontD)
-  ds ch = deltas ch fontD (rx, ry)
+  outlines ch = mconcat $ commandsToTrails (commands ch (sel1 fontD)) [] (0,0) (0,0) (0,0)
   fontD = openFont str
 
-deltas :: String -> FontData -> (Rx,Int) -> (Float,Float)
-deltas ch fontD (rx, ry) = (max_x/(fromIntegral $ getx ch fontD (rx,ry) ), (bbox_dy fontD) / (fromIntegral ry))
-  where max_x = maximum_x fontD
-
-getx :: String -> FontData -> (Rx,Int) -> Int
-getx ch fontD (rx, ry) | isExactly rx = fromJust $ getRx rx
-                       | isConstDx rx = round nrx
-                       | isXPowerOfTwo rx = roundP2 nrx
-
-  where nrx = ((fromIntegral ry)*((hadv ch fontD)/ (bbox_dy fontD) )) -- ry/rx * (h_ad/max_y) * rx
-        -- | Round to the Power of two, i.e. roundP2 110 == 128, roundP2 130 == 256
-        roundP2 :: Float -> Int
-        roundP2 a = round $ head $ filter (>a) (take 100 (power2s 1))
-        power2s a = a : (power2s (a*2))
-
+commandsToTrails :: [PathCommand] -> [Segment R2] -> R2 -> R2 -> R2 -> [Path R2]
+commandsToTrails [] _ _ _ _ = []
+commandsToTrails (c:cs) segments (lx,ly) lastContr beginPoint -- (lx,ly) is the endpoint of the last segment
+      | isNothing nextSegment = (translate beginPoint (pathFromTrail $ fromSegments segments)) :
+                  ( commandsToTrails cs [] (lx+x0, ly+y0) (contr c) (beginP c) ) -- one outline completed
+      | otherwise = commandsToTrails cs (segments ++ [fromJust nextSegment])
+                                           (lx+x0, ly+y0) (contr c) (beginP c)   -- work on outline
+  where nextSegment = go c
+        (x0,y0) | isJust nextSegment = segOffset (fromJust nextSegment)
+                | otherwise = (0,0)
+        (cx,cy) = lastContr -- last control point is always in absolute coordinates
+        beginP ( M_abs (x,y) ) = (x,y)
+        beginP ( M_rel (x,y) ) = (lx+x, ly+y)
+        beginP _ = beginPoint
+        contr ( C_abs (x1,y1,x2,y2,x,y) ) = (x0+x-x2, y0+y-y2 ) -- control point of bezier curve
+        contr ( C_rel (x1,y1,x2,y2,x,y) ) = (   x-x2,    y-y2 )
+        contr ( S_abs (x2,y2,x,y) )       = (x0+x-x2, y0+y-y2 )
+        contr ( S_rel (x2,y2,x,y) )       = (   x-x2,    y-y2 )
+        contr ( Q_abs (x1,y1,x,y) ) = (x0+x-x1, y0+y-y1 )
+        contr ( Q_rel (x1,y1,x,y) ) = (   x-x1,    y-y1 )
+        contr ( T_abs (x,y) )       = (2*x0-cx, 2*y0-cy )
+        contr ( T_rel (x,y) )       = (   x-cx,    y-cy )
+        contr ( L_abs (x,y) ) = (x0, y0)
+        contr ( L_rel (x,y) ) = ( 0,  0)
+        contr ( M_abs (x,y) ) = (x0, y0)
+        contr ( M_rel (x,y) ) = ( 0,  0)
+        contr ( H_abs x ) = (x0, y0)
+        contr ( H_rel x ) = ( 0, y0)
+        contr ( V_abs y ) = (x0, y0)
+        contr ( V_rel y ) = (x0,  0)
+        go ( M_abs (x,y) ) = Nothing
+        go ( M_rel (x,y) ) = Nothing
+        go ( L_abs (x,y) ) = Just $ straight (x0+x, y0+y)
+        go ( L_rel (x,y) ) = Just $ straight (x, y)
+        go ( H_abs x) = Just $ straight (x0 + x, y0)
+        go ( H_rel x) = Just $ straight (x, 0)
+        go ( V_abs y) = Just $ straight (x0, y0 + y)
+        go ( V_rel y) = Just $ straight (0, y)
+        go ( C_abs (x1,y1,x2,y2,x,y) ) = Just $ bezier3 (x0+x1, y0+y1) (x0+x2,y0+y2) (x0+x,y0+y)
+        go ( C_rel (x1,y1,x2,y2,x,y) ) = Just $ bezier3 (x1, y1) (x2, y2) (x, y)
+        go ( S_abs (      x2,y2,x,y) ) = Just $ bezier3 (cx, cy) (x0+x2, y0+y2) (x0+x, y0+y)
+        go ( S_rel (      x2,y2,x,y) ) = Just $ bezier3 (cx, cy) (x2, y2) (x, y)
+        go ( Q_abs (x1,y1,x,y) ) = Just $ bezier3 (x0 + x1, y0 + y1) (x0 + x1, y0 + y1) (x0 + x, y0 + y)
+        go ( Q_rel (x1,y1,x,y) ) = Just $ bezier3 (x1, y1) (x1, y1) (x, y)
+        go ( T_abs (x,y) ) = Just $ bezier3 (cx, cy) (cx, cy) (x0 + x, y0 + y)
+        go ( T_rel (x,y) ) = Just $ bezier3 (cx, cy) (cx, cy) (x, y)
+        go ( Z ) = Nothing
 
+commands :: String -> SvgGlyph -> [PathCommand]
 commands ch glyph | isJust element = unsafePerformIO $ pathFromString $ sel3 $ fromJust element
                   | otherwise      = []
   where element = Map.lookup ch glyph
 
--- | Texture images for all combinations of characters, fonts and transformations
--- for combinations of fonts and transformations only those are used that exist in this combination in the
--- property list while every character is used that exists in the svg-font file.
-makeTexMap :: (Rx,Int) -> Props -> Transf -> TexMap
-makeTexMap (rx,ry) ps trs = Map.fromList [ (getID ch p, (glyph ch p)) | p <- (Map.elems ps), isTex p,
-                                                                        ch <- (allUnicodes p) ]
-  where
-   allUnicodes (Tex (fontD,_,_) _) = Map.keys (sel1 fontD)
-   glyph ch (Tex (fontD,_,outl) tr) = trace ch $ unsafePerformIO $
-                             getGlyphTexture ch fontD outl (tr,trs) (getx ch fontD (rx,ry), fromIntegral ry)
-   getID :: String -> CharProp -> (String, String, String)
-   getID ch (Tex (fontD, _, _) transformation) = (ch, sel4 fontD, transformation)
-
-
-getGlyphTexture :: String -> FontData -> OutlineTexMap -> (String,Transf) -> (Int,Int) ->
-                   IO (TextureObject,String)
-getGlyphTexture ch fontD om (tr,trs) (rx,ry) =
-    (do fileExists <- doesFileExist fileName
-        if fileExists then do tga <- (readTGA fileName)
-                              createTexture (rx,ry) tga fileName
-                      else do (writeTGA fileName (borderToTex (rx,ry) borderPoints (color coloredDummyObj) ))
-                              tga <- (readTGA fileName)
-                              createTexture (rx,ry) tga fileName
-    )
-  where cn | isJust $ Map.lookup ch (sel1 fontD) = sel1 $ fromJust $ Map.lookup ch (sel1 fontD) -- description of the char (with a word)
-           | otherwise = ch
-        charName ch | length ch == 1 && (head ch) >= 'A' && (head ch) <= 'Z' = ch ++ "BIG" -- for Windows
-                    | otherwise = ch
-
-        fileName = (charName cn) ++ "_" ++ (sel4 fontD) ++ "_" ++ tr ++ (show rx) ++ "x" ++ (show ry) ++ ".tga"
-        f :: Geometry -> Geometry
-        f = fromJust $ Map.lookup tr trs
-        coloredDummyObj = f  $ Geometry "" [ LP (LinePrimitive e e e [blue]) ] (Vertices "" e e)
-        e = V.empty
-        color (Geometry "" [ LP (LinePrimitive _ _ _ [ (_, COMMON "" NoParam (PhongCol cs) _) ]) 
-                           ] _) = ( head $ catMaybes (map getDiffuseColor cs), 
-                                    head $ catMaybes (map getAmbientColor cs) )
-        borderPoints = map (\(x,y,B b) -> (truncate x, truncate y, b)) $ concat $ fromJust ( Map.lookup ch om )
+ src/Graphics/SVGFonts/ReadPath.hs view
@@ -0,0 +1,134 @@+--------------------------------------------------------------------
+-- |
+-- Module    : Graphics.SVG.ReadPath
+-- Copyright : (c) 2011 Tillmann Vogt
+-- License   : BSD3
+--
+-- Maintainer: Tillmann Vogt <tillk.vogt@googlemail.com>
+-- Stability : stable
+-- Portability: portable
+--
+-- parsing the SVG path command, see <http://www.w3.org/TR/SVG/paths.html#PathData> :
+
+module Graphics.SVGFonts.ReadPath
+ ( pathFromString,
+   PathCommand(..),
+ )
+ where
+
+import Text.ParserCombinators.Parsec hiding (spaces)
+import Text.ParserCombinators.Parsec.Expr
+import Text.ParserCombinators.Parsec.Prim
+import qualified Text.ParserCombinators.Parsec.Token as P
+import Text.ParserCombinators.Parsec.Language(emptyDef)
+import System.IO.Unsafe (unsafePerformIO)
+import Debug.Trace
+
+type X = Double
+type Y = Double
+type F2 = (X,Y)
+type Tup = (X,Y)
+type X1 = X
+type Y1 = Y
+type X2 = X
+type Y2 = Y
+data PathCommand =
+  M_abs Tup | -- ^Establish a new current point (with absolute coords)
+  M_rel Tup | -- ^Establish a new current point (with coords relative to the current point)
+  Z | -- ^Close current subpath by drawing a straight line from current point to current subpath's initial point
+  L_abs Tup | -- ^A line from the current point to Tup which becomes the new current point
+  L_rel Tup |
+  H_abs X | -- ^A horizontal line from the current point (cpx, cpy) to (x, cpy)
+  H_rel X |
+  V_abs Y | -- ^A vertical line from the current point (cpx, cpy) to (cpx, y)
+  V_rel Y |
+  C_abs (X1,Y1,X2,Y2,X,Y) | -- ^Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the
+  -- ^control point at the beginning of the curve and (x2,y2) as the control point at the end of the curve.
+  C_rel (X1,Y1,X2,Y2,X,Y) |
+  S_abs (X2,Y2,X,Y) | -- ^Draws a cubic Bézier curve from the current point to (x,y). The first control point is
+-- assumed to be the reflection of the second control point on the previous command relative to the current point.
+-- (If there is no previous command or if the previous command was not an C, c, S or s, assume the first control
+-- point is coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at
+-- the end of the curve).
+  S_rel (X2,Y2,X,Y) |
+  Q_abs (X1,Y1,X,Y) | -- ^A quadr. Bézier curve from the curr. point to (x,y) using (x1,y1) as the control point
+  Q_rel (X1,Y1,X,Y) | -- ^Nearly the same as cubic, but with one point less
+  T_abs Tup | -- ^T_Abs = Shorthand/smooth quadratic Bezier curveto
+  T_rel Tup |
+  A_abs | -- ^A = Elliptic arc (not used)
+  A_rel
+  deriving Show
+
+-- | convert a SVG path string into a list of commands
+pathFromString :: String -> IO [PathCommand]
+pathFromString str
+  = do{ case (parse path "" str) of
+           Left err -> do{ putStr "parse error at "
+                         ; print err
+                         ; return []
+                         }
+           Right x  -> return x
+      }
+
+spaces = skipMany space
+
+path :: Parser [PathCommand]
+path = do{ l <- many pathElement
+         ; eof
+         ; return (concat l)
+         }
+
+pathElement :: Parser [PathCommand]
+pathElement = do{ whiteSpace;
+              do{ symbol "M";  l <- many1 tupel2; return (map (\x-> M_abs x) l) } <|>
+              do{ symbol "m";  l <- many1 tupel2; return (map (\x-> M_rel x) l) } <|>
+              do{ symbol "z"; return [Z]; } <|>
+              do{ symbol "Z"; return [Z]; } <|>
+              do{ symbol "L";  l <- many1 tupel2; return (map (\x-> L_abs x) l) } <|>
+              do{ symbol "l";  l <- many1 tupel2; return (map (\x-> L_rel x) l) } <|>
+              do{ symbol "H";  l <- many1 myfloat; return (map (\x-> H_abs (realToFrac x)) l) } <|>
+              do{ symbol "h";  l <- many1 myfloat; return (map (\x-> H_rel (realToFrac x)) l) } <|>
+              do{ symbol "V";  l <- many1 myfloat; return (map (\x-> V_abs (realToFrac x)) l) } <|>
+              do{ symbol "v";  l <- many1 myfloat; return (map (\x-> V_rel (realToFrac x)) l) } <|>
+              do{ symbol "C";  l <- many1 tupel6; return (map (\x-> C_abs x) l) } <|>
+              do{ symbol "c";  l <- many1 tupel6; return (map (\x-> C_rel x) l) } <|>
+              do{ symbol "S";  l <- many1 tupel4; return (map (\x-> S_abs x) l) } <|>
+              do{ symbol "s";  l <- many1 tupel4; return (map (\x-> S_rel x) l) } <|>
+              do{ symbol "Q";  l <- many1 tupel4; return (map (\x-> Q_abs x) l) } <|>
+              do{ symbol "q";  l <- many1 tupel4; return (map (\x-> Q_rel x) l) } <|>
+              do{ symbol "T";  l <- many1 tupel2; return (map (\x-> T_abs x) l) } <|>
+              do{ symbol "t";  l <- many1 tupel2; return (map (\x-> T_rel x) l) } <|>
+              do{ symbol "A";  l <- many1 tupel2; return (map (\x-> A_abs) l) } <|> -- not used
+              do{ symbol "a";  l <- many1 tupel2; return (map (\x-> A_rel) l) }     -- not used
+            }
+
+comma = do{ spaces; try (do { (char ','); return () }) <|> spaces }
+
+tupel2 :: Parser (X,Y)
+tupel2 = do{ x <- myfloat; comma; y <- myfloat; spaces;
+             return (realToFrac x, realToFrac y)
+           }
+
+tupel4 :: Parser (X,Y,X,Y)
+tupel4 = do{ x1 <- myfloat; comma; y1 <- myfloat; spaces;
+              x <- myfloat; comma;  y <- myfloat; spaces;
+             return (realToFrac x1, realToFrac y1, realToFrac x, realToFrac y)
+           }
+
+tupel6 :: Parser (X,Y,X,Y,X,Y)
+tupel6 = do{ x1 <- myfloat; comma; y1 <- myfloat; spaces;
+             x2 <- myfloat; comma; y2 <- myfloat; spaces;
+              x <- myfloat; comma;  y <- myfloat; spaces;
+             return (realToFrac x1, realToFrac y1, realToFrac x2, realToFrac y2, realToFrac x, realToFrac y)
+           }
+
+myfloat = try (do{ symbol "-"; n <- float; return (negate n) }) <|>
+          try float <|> -- 0 is not recognized as a float, so recognize it as an integer and then convert to float
+              do { i<-integer; return(fromIntegral i) } 
+
+lexer = P.makeTokenParser emptyDef
+
+whiteSpace      = P.whiteSpace lexer    
+symbol          = P.symbol lexer    
+integer         = P.integer lexer    
+float           = P.float lexer
− src/Test/Fonts.hs
@@ -1,69 +0,0 @@-module Main where
-
-import Data.Tree
-import Graphics.SVGFonts.ReadFont
-import Graphics.Formats.Collada.ColladaTypes (Geometry, SceneNode(..))
-import Graphics.Formats.Collada.GenerateObjects (lightedScene, lightedSceneNode, emptyAnim)
-import Graphics.Formats.Collada.GenerateCollada (genCollada)
-import Graphics.Formats.Collada.Transformations (extrude, atop, translate, changeDiffuseColor, changeAmbientColor)
-import Graphics.Formats.Collada.Vector2D3D (V3(..),V4(..))
-import Graphics.Triangulation.Triangulation (triangulate, deleteHoles, gjpTri)
-import Graphics.Triangulation.KETTriangulation (ketTri)
-import qualified Data.Map as Map
-import System.Environment
-
-main = do
-  args <- getArgs
-  let str = if null args then "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVXYZ0123456789" -- UW
--- "The fluffiest bunny VVAV \x2192\x2193 x2200\x2203\x21d2\x2192\x2190 ss"
-                         else head args
-      resolution = (ConstDx, 100) -- (Exactly 512, 512) (ConstDx, 512) (XPowerOfTwo, 512) (OneTexture, 512)
--- The y resolution is constant. The x resolution of a glyph changes in non-mono-spaced fonts.
--- Rx: Exactly: Use this resolution
---     ConstDx: The x-resolution of a glvph is chosen so that the pixels of all glyphs have the same size
---     XPowerOfTwo: With this option the resolution nearest of a power of two is chosen,
---                  i.e. "l" would have (256,512), while "w" would have (512,512)
---     OneTexture: The whole string as one texture (not implemented yet)
-      mode = INSIDE_V2
--- 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
-      spacing = HADV -- KERN
--- spacing: MONO: Use mono spacing between glyphs. The longest glyph influences the bbox that is used
---          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"
-      bit = makeOutlMaps "../../../src/Test/Bitstream.svg" resolution
-      lin = makeOutlMaps "../../../src/Test/LinLibertine.svg" resolution
-      o  = V3 0 0 0 -- origin
-      v1 = V3 (-5) 0 0 -- direction of char-advance
-      v2 = V3 0 0 1  -- height direction
-      v3 = V3 0 0.1 0 -- extrusion
-
-      f :: String -> [String]  -- assigning a property to every character by a unique string
-      f str = take (length str) (concat (repeat ["r","s"]))
-
-      props :: Props
-      props = Map.fromList [("p", Outl lin "to3d"), ("q", Outl lin "to3d2"),
-                            ("r", Tex bit "red"),   ("s", Tex bit "blue") ]
-
-      -- transformation of a Geoemtry Node (i.e. triangulation) has to be done with a string assignment,
-      -- because there has to be a finite number of functions for Data.Map
-      transf :: Transf
-      transf = Map.fromList [("to3d",to3d), ("to3d2",to3d2), ("red", red.bgWhite), ("blue",blue.bgWhite), ("id",id)]
-      -- not the perfect solution since some points are generated twice
-      to3d geom  = red  $ ( ((extrude v3).deleteHoles) geom ) `atop`
-                          ( tri ((translate v3) geom) )
-      to3d2 geom = blue $ ( ((extrude (V3 0 0.05 0)).deleteHoles) geom ) `atop`
-                          ( tri ((translate (V3 0 0.05 0)) geom) )
-      tri = (triangulate ketTri).deleteHoles -- gjpTri
-      red =  changeDiffuseColor "red" (V4 1 0 0 1) -- if used with textures diffuse is interpreted as foreground color
-      blue = changeDiffuseColor "blue" (V4 0 0 1 1)
-      bgWhite = changeAmbientColor "white" (V4 1 1 1 1) -- if used with textures interpreted as background color
-
-      texmap = makeTexMap resolution props transf
-      n0 = displayString str "line0" resolution mode spacing (o,v1,v2) f props transf texmap
---      n1 = displayString "test" "line1" resolution mode spacing (V3 0 0 (-1),v1,v2) f props transf texmap
-      node = Node EmptyRoot [n0]
-  genCollada (lightedScene node) emptyAnim
-  putStrLn "Collada File generated"