{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TupleSections #-}
module Typograffiti.Utils (
module FT
, FreeTypeT
, FreeTypeIO
, getAdvance
, getCharIndex
, getLibrary
, getKerning
, glyphFormatString
, hasKerning
, loadChar
, loadGlyph
, newFace
, setCharSize
, setPixelSizes
, withFreeType
, runFreeType
) where
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Except
import Control.Monad.State.Strict
import Control.Monad (unless)
import Graphics.Rendering.FreeType.Internal as FT
import Graphics.Rendering.FreeType.Internal.PrimitiveTypes as FT
import Graphics.Rendering.FreeType.Internal.Library as FT
import Graphics.Rendering.FreeType.Internal.FaceType as FT
import Graphics.Rendering.FreeType.Internal.Face as FT hiding (generic)
import Graphics.Rendering.FreeType.Internal.GlyphSlot as FT
import Graphics.Rendering.FreeType.Internal.Bitmap as FT
import Graphics.Rendering.FreeType.Internal.Vector as FT
import Foreign as FT
import Foreign.C.String as FT
-- TODO: Tease out the correct way to handle errors.
-- They're kinda thrown all willy nilly.
type FreeTypeT m = ExceptT String (StateT FT_Library m)
type FreeTypeIO = FreeTypeT IO
glyphFormatString :: FT_Glyph_Format -> String
glyphFormatString fmt
| fmt == ft_GLYPH_FORMAT_COMPOSITE = "ft_GLYPH_FORMAT_COMPOSITE"
| fmt == ft_GLYPH_FORMAT_OUTLINE = "ft_GLYPH_FORMAT_OUTLINE"
| fmt == ft_GLYPH_FORMAT_PLOTTER = "ft_GLYPH_FORMAT_PLOTTER"
| fmt == ft_GLYPH_FORMAT_BITMAP = "ft_GLYPH_FORMAT_BITMAP"
| otherwise = "ft_GLYPH_FORMAT_NONE"
liftE :: MonadIO m => String -> IO (Either FT_Error a) -> FreeTypeT m a
liftE msg f = liftIO f >>= \case
Left e -> fail $ unwords [msg, show e]
Right a -> return a
runIOErr :: MonadIO m => String -> IO FT_Error -> FreeTypeT m ()
runIOErr msg f = do
e <- liftIO f
unless (e == 0) $ fail $ unwords [msg, show e]
runFreeType :: MonadIO m => FreeTypeT m a -> m (Either String (a, FT_Library))
runFreeType f = do
(e,lib) <- liftIO $ alloca $ \p -> do
e <- ft_Init_FreeType p
lib <- peek p
return (e,lib)
if e /= 0
then do
_ <- liftIO $ ft_Done_FreeType lib
return $ Left $ "Error initializing FreeType2:" ++ show e
else fmap (,lib) <$> evalStateT (runExceptT f) lib
withFreeType :: MonadIO m => Maybe FT_Library -> FreeTypeT m a -> m (Either String a)
withFreeType Nothing f = runFreeType f >>= \case
Left e -> return $ Left e
Right (a,lib) -> do
_ <- liftIO $ ft_Done_FreeType lib
return $ Right a
withFreeType (Just lib) f = evalStateT (runExceptT f) lib
getLibrary :: MonadIO m => FreeTypeT m FT_Library
getLibrary = lift get
newFace :: MonadIO m => FilePath -> FreeTypeT m FT_Face
newFace fp = do
ft <- lift get
liftE "ft_New_Face" $ withCString fp $ \str ->
alloca $ \ptr -> ft_New_Face ft str 0 ptr >>= \case
0 -> Right <$> peek ptr
e -> return $ Left e
setCharSize :: (MonadIO m, Integral i) => FT_Face -> i -> i -> i -> i -> FreeTypeT m ()
setCharSize ff w h dpix dpiy = runIOErr "ft_Set_Char_Size" $
ft_Set_Char_Size ff (fromIntegral w) (fromIntegral h)
(fromIntegral dpix) (fromIntegral dpiy)
setPixelSizes :: (MonadIO m, Integral i) => FT_Face -> i -> i -> FreeTypeT m ()
setPixelSizes ff w h =
runIOErr "ft_Set_Pixel_Sizes" $ ft_Set_Pixel_Sizes ff (fromIntegral w) (fromIntegral h)
getCharIndex :: (MonadIO m, Integral i)
=> FT_Face -> i -> FreeTypeT m FT_UInt
getCharIndex ff ndx = liftIO $ ft_Get_Char_Index ff $ fromIntegral ndx
loadGlyph :: MonadIO m => FT_Face -> FT_UInt -> FT_Int32 -> FreeTypeT m ()
loadGlyph ff fg flags = runIOErr "ft_Load_Glyph" $ ft_Load_Glyph ff fg flags
loadChar :: MonadIO m => FT_Face -> FT_ULong -> FT_Int32 -> FreeTypeT m ()
loadChar ff char flags = runIOErr "ft_Load_Char" $ ft_Load_Char ff char flags
hasKerning :: MonadIO m => FT_Face -> FreeTypeT m Bool
hasKerning = liftIO . ft_HAS_KERNING
getKerning :: MonadIO m => FT_Face -> FT_UInt -> FT_UInt -> FT_Kerning_Mode -> FreeTypeT m (Int,Int)
getKerning ff prevNdx curNdx flags = liftE "ft_Get_Kerning" $ alloca $ \ptr ->
ft_Get_Kerning ff prevNdx curNdx (fromIntegral flags) ptr >>= \case
0 -> do FT_Vector vx vy <- peek ptr
return $ Right (fromIntegral vx, fromIntegral vy)
e -> return $ Left e
getAdvance :: MonadIO m => FT_GlyphSlot -> FreeTypeT m (Int,Int)
getAdvance slot = do
FT_Vector vx vy <- liftIO $ peek $ advance slot
return (fromIntegral vx, fromIntegral vy)