{-# OPTIONS -fglasgow-exts #-} -- |FreeType - binding to FreeType font rendering library -- module FreeType where -- JAS - how do we do error handling -- exceptions vs Either import Control.Exception import Data.Bits import Data.Char import Data.Int import Data.Word import Foreign.C.Types import Foreign.C.String import Foreign.Marshal.Alloc import Foreign.Ptr import Foreign.Storable import Prelude hiding (catch) #include #include FT_FREETYPE_H type FT_Long = #{type FT_Long} type FT_ULong = #{type FT_ULong} type FT_UInt = #{type FT_UInt} type FT_Int32 = #{type FT_Int32} data FT_LibraryRec type FT_Library = Ptr FT_LibraryRec type FT_Error = #{type FT_Error} foreign import ccall unsafe "FT_Init_FreeType" c_FT_Init_FreeType :: Ptr FT_Library -> IO FT_Error foreign import ccall unsafe "FT_Done_FreeType" c_FT_Done_FreeType :: FT_Library -> IO FT_Error -- JAS - does allocaBytes use bracket ? -- JAS - catch done errors withFreeType :: (FT_Library -> IO a) -> IO (Either (FT_Error, Maybe a) a) withFreeType action = allocaBytes #{size FT_Library} $ \ptr -> do ret <- c_FT_Init_FreeType ptr if ret == 0 then do actionResult <- (peek ptr >>= action) `catch` (\e -> peek ptr >>= c_FT_Done_FreeType >> throwIO e) ret <- peek ptr >>= c_FT_Done_FreeType if ret == 0 then return (Right actionResult) else return (Left (ret, Just actionResult)) else return (Left (ret,Nothing)) -- * FT_Face data FT_FaceRec type FT_Face = Ptr FT_FaceRec foreign import ccall unsafe "FT_New_Face" c_FT_New_Face :: FT_Library -> CString -> FT_Long -> Ptr FT_Face -> IO FT_Error foreign import ccall unsafe "FT_Done_Face" c_FT_Done_Face :: FT_Face -> IO FT_Error withNewFace :: FT_Library -> FilePath -> FT_Long -> ((FT_Face -> IO a) -> IO (Either (FT_Error, Maybe a) a)) withNewFace ftLibrary filePath faceIndex action = do allocaBytes #{size FT_Face} $ \ ftFacePtr -> withCString filePath $ \ fp -> do ret <- c_FT_New_Face ftLibrary fp faceIndex ftFacePtr if ret == 0 then do actionResult <- (peek ftFacePtr >>= action) `catch` (\e -> peek ftFacePtr >>= c_FT_Done_Face >> throwIO e) ret <- peek ftFacePtr >>= c_FT_Done_Face if ret == 0 then return (Right actionResult) else return (Left (ret, Just actionResult)) else return (Left (ret,Nothing)) faceFlags :: FT_Face -> IO #{type FT_Long} faceFlags face = #{peek FT_FaceRec, face_flags} face faceFlagKerning :: #{type FT_Long} faceFlagKerning = #{const FT_FACE_FLAG_KERNING } -- * type FT_F26Dot6 = #{type FT_F26Dot6} foreign import ccall unsafe "FT_Set_Char_Size" setCharSize :: FT_Face -> FT_F26Dot6 -> FT_F26Dot6 -> FT_UInt -> FT_UInt -> IO FT_Error foreign import ccall unsafe "FT_Set_Pixel_Sizes" setPixelSizes :: FT_Face -> FT_UInt -> FT_UInt -> IO FT_Error foreign import ccall unsafe "FT_Get_Char_Index" c_FT_Get_Char_Index :: FT_Face -> FT_ULong -> IO FT_UInt getCharIndex :: FT_Face -> Char -> IO FT_UInt getCharIndex ftFace c = c_FT_Get_Char_Index ftFace (fromIntegral (ord c)) data LoadFlag = LoadDefault | LoadNoScale | LoadNoHinting | LoadRender | LoadNoBitmap | LoadVerticalLayout | LoadForceAutohint | LoadCropBitmap | LoadPedantic | LoadIgnoreGlobalAdvanceWidth | LoadNoRecurse | LoadIgnoreTransform | LoadMonochrome | LoadLinearDesign deriving Show instance Enum LoadFlag where toEnum #{const FT_LOAD_DEFAULT} = LoadDefault toEnum #{const FT_LOAD_NO_SCALE} = LoadNoScale toEnum #{const FT_LOAD_NO_HINTING} = LoadNoHinting toEnum #{const FT_LOAD_RENDER} = LoadRender toEnum #{const FT_LOAD_NO_BITMAP} = LoadNoBitmap toEnum #{const FT_LOAD_VERTICAL_LAYOUT} = LoadVerticalLayout toEnum #{const FT_LOAD_FORCE_AUTOHINT} = LoadForceAutohint toEnum #{const FT_LOAD_CROP_BITMAP} = LoadCropBitmap toEnum #{const FT_LOAD_PEDANTIC} = LoadPedantic toEnum #{const FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH} = LoadIgnoreGlobalAdvanceWidth toEnum #{const FT_LOAD_NO_RECURSE} = LoadNoRecurse toEnum #{const FT_LOAD_IGNORE_TRANSFORM} = LoadIgnoreTransform toEnum #{const FT_LOAD_MONOCHROME} = LoadMonochrome toEnum #{const FT_LOAD_LINEAR_DESIGN} = LoadLinearDesign fromEnum LoadDefault = #{const FT_LOAD_DEFAULT} fromEnum LoadNoScale = #{const FT_LOAD_NO_SCALE} fromEnum LoadNoHinting = #{const FT_LOAD_NO_HINTING} fromEnum LoadRender = #{const FT_LOAD_RENDER} fromEnum LoadNoBitmap = #{const FT_LOAD_NO_BITMAP} fromEnum LoadVerticalLayout = #{const FT_LOAD_VERTICAL_LAYOUT} fromEnum LoadForceAutohint = #{const FT_LOAD_FORCE_AUTOHINT} fromEnum LoadCropBitmap = #{const FT_LOAD_CROP_BITMAP} fromEnum LoadPedantic = #{const FT_LOAD_PEDANTIC} fromEnum LoadIgnoreGlobalAdvanceWidth = #{const FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH} fromEnum LoadNoRecurse = #{const FT_LOAD_NO_RECURSE} fromEnum LoadIgnoreTransform = #{const FT_LOAD_IGNORE_TRANSFORM} fromEnum LoadMonochrome = #{const FT_LOAD_MONOCHROME} fromEnum LoadLinearDesign = #{const FT_LOAD_LINEAR_DESIGN} foreign import ccall unsafe "FT_Load_Glyph" c_FT_Load_Glyph :: FT_Face -> FT_UInt -> FT_Int32 -> IO FT_Error loadGlyph :: FT_Face -> FT_UInt -> [LoadFlag] -> IO FT_Error loadGlyph face glyphIndex flags = c_FT_Load_Glyph face glyphIndex (mkFlags flags) mkFlags :: (Enum f, Num a) => [f] -> a mkFlags flags = fromIntegral $ foldl (.|.) 0 (map fromEnum flags) -- * FT_GlyphSlot data FT_GlyphSlotRec type FT_GlyphSlot = Ptr FT_GlyphSlotRec advance :: FT_GlyphSlot -> IO FT_Vector advance = ftVector . #{ptr FT_GlyphSlotRec, advance} glyph :: FT_Face -> IO FT_GlyphSlot glyph = #{peek FT_FaceRec, glyph} -- * FT_Bitmap data FT_Bitmap bitmap :: FT_GlyphSlot -> Ptr FT_Bitmap bitmap = #{ptr FT_GlyphSlotRec, bitmap} bitmap_left :: FT_GlyphSlot -> IO Int bitmap_left = #{peek FT_GlyphSlotRec, bitmap_left} bitmap_top :: FT_GlyphSlot -> IO Int bitmap_top = #{peek FT_GlyphSlotRec, bitmap_top} rows :: Ptr FT_Bitmap -> IO CInt rows = #{peek FT_Bitmap, rows} width :: Ptr FT_Bitmap -> IO CInt width = #{peek FT_Bitmap, width} pitch :: Ptr FT_Bitmap -> IO CInt pitch = #{peek FT_Bitmap, pitch} numGrays :: Ptr FT_Bitmap -> IO #{type short} numGrays = #{peek FT_Bitmap, num_grays} buffer :: Ptr FT_Bitmap -> IO (Ptr Word8) buffer = #{peek FT_Bitmap, buffer} -- * FT_Vector newtype FT_Vector = FT_Vector ((#type FT_Pos), (#type FT_Pos)) ftVector :: Ptr FT_Vector -> IO FT_Vector ftVector pFtVector = do x <- #{peek FT_Vector, x} pFtVector y <- #{peek FT_Vector, y} pFtVector return $ FT_Vector (x, y) -- * FT_Get_Kerning foreign import ccall unsafe "FT_Get_Kerning" c_FT_Get_Kerning :: FT_Face -> #{type FT_UInt} -> #{type FT_UInt} -> #{type FT_UInt} -> Ptr FT_Vector -> IO FT_Error data KerningMode = KerningDefault | KerningUnfitted | KerningUnscaled deriving Show getKerning :: FT_Face -- face -> #{type FT_UInt} -- left_glyph index -> #{type FT_UInt} -- right_glyph index -> KerningMode -> IO (Either FT_Error FT_Vector) getKerning face leftIndex rightIndex kerningMode = allocaBytes #{size FT_Vector} $ \ (akerning :: (Ptr FT_Vector)) -> do ret <- c_FT_Get_Kerning face leftIndex rightIndex (cKerningMode kerningMode) akerning case ret of 0 -> ftVector akerning >>= return . Right n -> return (Left n) where cKerningMode KerningDefault = #{const FT_KERNING_DEFAULT} cKerningMode KerningUnfitted = #{const FT_KERNING_UNFITTED} cKerningMode KerningUnscaled = #{const FT_KERNING_UNSCALED} {- = FT_Bitmap { bmRows :: #{type int} , bmWidth :: #{type int} , bmPitch :: #{type int} , bmBuffer :: Ptr Word8 , bmNumGrays :: #{type short} , bmPixelMode :: #{type char} , bmPaletteMode :: #{type char} , pall } -} {- Error handling: ~ Almost all the freetype functions return an error code ~ a few functions have new/delete type semantics that we want to wrap into a single function ~ If an error is encountered in the inner with, we may also encounter more errors when passing the error back up. ~ The error may not occur until after we have evaluated the action -- so we may actually have a useful result if only the 'delete' functions fail. ~ if the action throws an exception, we still want to do the cleanup (aka, finally) ~ if we fail before the action, we may not have a result to return ~ if we don't get a result from action, then we definately had an error ~ so we have three return cases: + no error + error before action + error after action -} {- data Result a e = NoError a | NoResult e | ErrorWithResult a e deriving Show -} -- foreign import ccall "FT_Get_Char_Index" c_FT_Get_Char_Index ::