M Data/Text/Glyphize.hs => Data/Text/Glyphize.hs +12 -2
@@ 3,9 3,19 @@ module Data.Text.Glyphize where
import Data.Text.Glyphize.Buffer
import Data.Text.Glyphize.Font
--- Don't think I'll implement shapeplans at this stage...
--- shape :: Font -> Buffer -> [(GlyphInfo, GlyphPos)]
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import System.IO.Unsafe (unsafePerformIO)
+foreign import ccall "hb_shape" hb_shape :: Font_ -> Buffer_ -> Ptr () -> Int -> IO ()
+shape :: Font -> Buffer -> [(GlyphInfo, GlyphPos)]
+shape font buf = unsafePerformIO $ do
+ buf_ <- freeze' buf
+ withForeignPtr font $ \font' -> withForeignPtr buf_ $ \buf' ->
+ hb_shape font' buf' nullPtr 0
+ infos <- glyphInfos' buf_
+ pos <- glyphsPos' buf_
+ return $ zip infos pos
-- Defer implementing font features...
-- version :: (Int, Int, Int)
M Data/Text/Glyphize/Buffer.hs => Data/Text/Glyphize/Buffer.hs +37 -33
@@ 102,14 102,13 @@ data ClusterLevel = ClusterMonotoneGraphemes | ClusterMonotoneChars | ClusterCha
data GlyphInfo = GlyphInfo {
codepoint :: Word32,
cluster :: Word32
-}
+} deriving (Show, Read, Eq)
instance Storable GlyphInfo where
- sizeOf (GlyphInfo a b) = sizeOf a + sizeOf b
- alignment (GlyphInfo a b) = alignment a
+ sizeOf _ = 2 * sizeOf (undefined :: Word32)
+ alignment _ = alignment (undefined :: Word32)
peek p = do
- q <- return $ castPtr p
- codepoint' <- peek q
- cluster' <- peekElemOff q 1
+ codepoint' <- peek $ castPtr p
+ cluster' <- peekElemOff (castPtr p) 1
return $ GlyphInfo codepoint' cluster'
poke p (GlyphInfo a b) = do
q <- return $ castPtr p
@@ 119,10 118,10 @@ instance Storable GlyphInfo where
data GlyphPos = GlyphPos {
x_advance :: Word32, y_advance :: Word32,
x_offset :: Word32, y_offset :: Word32
-}
+} deriving (Show, Read, Eq)
instance Storable GlyphPos where
- sizeOf (GlyphPos a _ _ _) = 4 * sizeOf a
- alignment (GlyphPos a _ _ _) = alignment a
+ sizeOf _ = 4 * sizeOf (undefined :: Word32)
+ alignment _ = alignment (undefined :: Word32)
peek p = do
q <- return $ castPtr p
xa <- peek q
@@ 167,6 166,7 @@ dirVertical dir = dir `Prelude.elem` [DirTTB, DirBTT]
type Buffer' = ForeignPtr Buffer''
data Buffer''
+type Buffer_ = Ptr Buffer''
freeze = unsafePerformIO . freeze'
freeze' buf = do
@@ 213,12 213,16 @@ glyphInfos' :: Buffer' -> IO [GlyphInfo]
glyphInfos' buf' = alloca $ \length' -> do
arr <- withForeignPtr buf' $ \buf'' -> hb_buffer_get_glyph_infos buf'' length'
length <- peek length'
- forM [0..length - 1] $ peekElemOff arr
+ if length == 0
+ then return []
+ else forM [0..length - 1] $ peekElemOff arr
glyphsPos = unsafePerformIO . glyphsPos'
glyphsPos' buf' = alloca $ \length' -> do
arr <- withForeignPtr buf' $ \buf'' -> hb_buffer_get_glyph_positions buf'' length'
length <- peek length'
- forM [0..length-1] $ peekElemOff arr
+ if length == 0
+ then return []
+ else forM [0..length-1] $ peekElemOff arr
thaw :: Buffer' -> Buffer
thaw = unsafePerformIO . thaw'
@@ 255,18 259,18 @@ thaw' buf' = do
invisibleGlyph = w2c invisibleGlyph', replacementCodepoint = w2c replacementCodepoint'
}
-foreign import ccall "hb_buffer_create" hb_buffer_create :: IO (Ptr Buffer'')
-foreign import ccall "&hb_buffer_destroy" hb_buffer_destroy :: FunPtr (Ptr Buffer'' -> IO ())
+foreign import ccall "hb_buffer_create" hb_buffer_create :: IO Buffer_
+foreign import ccall "&hb_buffer_destroy" hb_buffer_destroy :: FunPtr (Buffer_ -> IO ())
foreign import ccall "hb_buffer_add_utf8" hb_buffer_add_utf8
- :: Ptr Buffer'' -> Ptr Word8 -> Int -> Int -> Int -> IO ()
+ :: Buffer_ -> Ptr Word8 -> Int -> Int -> Int -> IO ()
hb_buffer_add_bytestring _ Lazy.Empty = return ()
hb_buffer_add_bytestring buf (Lazy.Chunk (Strict.PS ptr offset length) next) = do
withForeignPtr ptr $ \ptr' -> hb_buffer_add_utf8 buf ptr' length offset (length - offset)
hb_buffer_add_bytestring buf next
foreign import ccall "hb_buffer_set_content_type" hb_buffer_set_content_type
- :: Ptr Buffer'' -> Int -> IO ()
+ :: Buffer_ -> Int -> IO ()
foreign import ccall "hb_buffer_set_direction" hb_buffer_set_direction
- :: Ptr Buffer'' -> Int -> IO ()
+ :: Buffer_ -> Int -> IO ()
hb_tag_from_string :: String -> Word32
hb_tag_from_string str = case str ++ Prelude.repeat '\0' of
c1:c2:c3:c4:_ -> Prelude.foldl (.|.) 0 [
@@ 296,7 300,7 @@ hb_script_from_string str = hb_tag_from_string $ case titlecase str of
'S':'y':'r':'n':_ -> "Syrc"
hb_script_from_txt txt = hb_script_from_string $ Short.toString txt
foreign import ccall "hb_buffer_set_script" hb_buffer_set_script
- :: Ptr Buffer'' -> Word32 -> IO ()
+ :: Buffer_ -> Word32 -> IO ()
foreign import ccall "hb_script_get_horizontal_direction" hb_script_get_horizontal_direction
:: Word32 -> Int
foreign import ccall "hb_language_from_string" hb_language_from_string
@@ 305,22 309,22 @@ hb_language_from_txt txt = let Strict.PS ptr offset size = toByteString txt
in withForeignPtr ptr $ \ptr' -> return $
hb_language_from_string (plusPtr ptr' offset) (size - offset)
foreign import ccall "hb_buffer_set_language" hb_buffer_set_language
- :: Ptr Buffer'' -> Int -> IO ()
-foreign import ccall "hb_buffer_set_flags" hb_buffer_set_flags :: Ptr Buffer'' -> Int -> IO ()
+ :: Buffer_ -> Int -> IO ()
+foreign import ccall "hb_buffer_set_flags" hb_buffer_set_flags :: Buffer_ -> Int -> IO ()
foreign import ccall "hb_buffer_set_cluster_level" hb_buffer_set_cluster_level
- :: Ptr Buffer'' -> Int -> IO ()
+ :: Buffer_ -> Int -> IO ()
foreign import ccall "hb_buffer_set_invisible_glyph" hb_buffer_set_invisible_glyph
- :: Ptr Buffer'' -> Word32 -> IO ()
+ :: Buffer_ -> Word32 -> IO ()
foreign import ccall "hb_buffer_set_replacement_codepoint" hb_buffer_set_replacement_codepoint
- :: Ptr Buffer'' -> Word32 -> IO ()
+ :: Buffer_ -> Word32 -> IO ()
foreign import ccall "hb_buffer_guess_segment_properties" hb_buffer_guess_segment_properties
- :: Ptr Buffer'' -> IO ()
+ :: Buffer_ -> IO ()
foreign import ccall "hb_buffer_get_content_type" hb_buffer_get_content_type
- :: Ptr Buffer'' -> IO Int
-foreign import ccall "hb_buffer_get_direction" hb_buffer_get_direction :: Ptr Buffer'' -> IO Int
-foreign import ccall "hb_buffer_get_script" hb_buffer_get_script :: Ptr Buffer'' -> IO Word32
+ :: Buffer_ -> IO Int
+foreign import ccall "hb_buffer_get_direction" hb_buffer_get_direction :: Buffer_ -> IO Int
+foreign import ccall "hb_buffer_get_script" hb_buffer_get_script :: Buffer_ -> IO Word32
hb_tag_to_string :: Word32 -> String
hb_tag_to_string tag = [
w2c (shiftR tag 24 .&. 0x7),
@@ 328,16 332,16 @@ hb_tag_to_string tag = [
w2c (shiftR tag 8 .&. 0x7),
w2c (shiftR tag 0 .&. 0x7)
]
-foreign import ccall "hb_buffer_get_language" hb_buffer_get_language :: Ptr Buffer'' -> IO (Ptr ())
+foreign import ccall "hb_buffer_get_language" hb_buffer_get_language :: Buffer_ -> IO (Ptr ())
foreign import ccall "hb_language_to_string" hb_language_to_string :: Ptr () -> CString
-foreign import ccall "hb_buffer_get_flags" hb_buffer_get_flags :: Ptr Buffer'' -> IO Int
+foreign import ccall "hb_buffer_get_flags" hb_buffer_get_flags :: Buffer_ -> IO Int
foreign import ccall "hb_buffer_get_cluster_level" hb_buffer_get_cluster_level
- :: Ptr Buffer'' -> IO Int
+ :: Buffer_ -> IO Int
foreign import ccall "hb_buffer_get_glyph_infos" hb_buffer_get_glyph_infos
- :: Ptr Buffer'' -> Ptr Int -> IO (Ptr GlyphInfo)
+ :: Buffer_ -> Ptr Int -> IO (Ptr GlyphInfo)
foreign import ccall "hb_buffer_get_glyph_positions" hb_buffer_get_glyph_positions
- :: Ptr Buffer'' -> Ptr Int -> IO (Ptr GlyphPos)
+ :: Buffer_ -> Ptr Int -> IO (Ptr GlyphPos)
foreign import ccall "hb_buffer_get_invisible_glyph" hb_buffer_get_invisible_glyph
- :: Ptr Buffer'' -> IO Word32
+ :: Buffer_ -> IO Word32
foreign import ccall "hb_buffer_get_replacement_codepoint" hb_buffer_get_replacement_codepoint
- :: Ptr Buffer'' -> IO Word32
+ :: Buffer_ -> IO Word32
M Data/Text/Glyphize/Font.hs => Data/Text/Glyphize/Font.hs +24 -54
@@ 3,13 3,14 @@ module Data.Text.Glyphize.Font where
import Data.ByteString
import Data.Text.Short
---import FreeType.Core.Base
+import FreeType.Core.Base
import Data.Text.Glyphize.Buffer (Direction(..), dir2int)
import System.IO.Unsafe (unsafePerformIO)
import Foreign.Ptr
import Foreign.StablePtr
import Foreign.ForeignPtr
+import qualified Foreign.Concurrent as Conc
import Foreign.Marshal.Alloc
import Foreign.Storable
import Foreign.C.String
@@ 39,11 40,11 @@ createFace bytes index = unsafePerformIO $ do
face <- withForeignPtr blob $ flip hb_face_create index
newForeignPtr hb_face_destroy face
---foreign import ccall "hb_ft_face_create_referenced" hb_ft_face_create_referenced
--- :: FT_Face -> Face_
---ftCreateFace :: FT_Face -> Face
---ftCreateFace =
--- unsafePerformIO . newForeignPtr hb_face_destroy . hb_ft_face_create_referenced
+foreign import ccall "hb_ft_face_create_referenced" hb_ft_face_create_referenced
+ :: FT_Face -> Face_
+ftCreateFace :: FT_Face -> Face
+ftCreateFace =
+ unsafePerformIO . newForeignPtr hb_face_destroy . hb_ft_face_create_referenced
foreign import ccall "hb_face_get_empty" hb_face_get_empty :: Face_
emptyFace :: Face
@@ 77,13 78,13 @@ createFont fce = unsafePerformIO $ do
hb_font_make_immutable font
newForeignPtr hb_font_destroy font
---foreign import ccall "hb_ft_font_create_referenced" hb_ft_font_create_referenced
--- :: FT_Face -> IO Face_
--- ftCreateFont :: FT_Face -> IO Font
--- ftCreateFont fce = unsafePerformIO $ do
--- font <- hb_ft_font_create_referenced
--- hb_font_make_immutable font
--- newForeignPtr hb_font_destroy font
+foreign import ccall "hb_ft_font_create_referenced" hb_ft_font_create_referenced
+ :: FT_Face -> IO Font_
+ftCreateFont :: FT_Face -> IO Font
+ftCreateFont fce = do
+ font <- hb_ft_font_create_referenced fce
+ hb_font_make_immutable font
+ newForeignPtr hb_font_destroy font
foreign import ccall "hb_font_get_empty" hb_font_get_empty :: Font_
emptyFont :: Font
@@ 297,30 298,6 @@ fontScale font = unsafePerformIO $
y <- peek y'
return (x, y)
-foreign import ccall "hb_font_get_synthetic_slant" hb_font_get_synthetic_slant
- :: Font_ -> Float
-fontSynthSlant :: Font -> Float
-fontSynthSlant = fontFunc hb_font_get_synthetic_slant
-
-foreign import ccall "hb_font_get_variance_glyph" hb_font_get_variance_glyph
- :: Font_ -> Word32 -> Word32 -> Ptr Word32 -> IO Bool
-fontVarianceGlyph :: Font -> Word32 -> Word32 -> Maybe Word32
-fontVarianceGlyph font glyph1 glyph2 = unsafePerformIO $
- withForeignPtr font $ \font' -> alloca $ \ret -> do
- success <- hb_font_get_variance_glyph font' glyph1 glyph2 ret
- if success
- then return . Just =<< peek ret
- else return Nothing
-
-foreign import ccall "hb_font_get_var_coords_design" hb_font_get_var_coords_design
- :: Font_ -> Ptr Int -> IO (Ptr Float)
-fontVarCoordsDesign :: Font -> [Float]
-fontVarCoordsDesign font = unsafePerformIO $
- withForeignPtr font $ \font' -> alloca $ \length' -> do
- arr <- hb_font_get_var_coords_design font' length'
- length <- peek length'
- forM [0..length-1] $ peekElemOff arr
-
foreign import ccall "hb_font_get_var_coords_normalized"
hb_font_get_var_coords_normalized :: Font_ -> Ptr Int -> IO (Ptr Int)
fontVarCoordsNormalized :: Font -> [Int]
@@ 355,13 332,11 @@ fontGlyph2Str font glyph length = unsafePerformIO $
data FontOptions = FontOptions {
optionPPEm :: Maybe (Int, Int),
optionPtEm :: Maybe Float,
- optionScale :: Maybe (Int, Int),
- optionSynthSlant :: Maybe Float
+ optionScale :: Maybe (Int, Int)
}
defaultFontOptions = FontOptions {
optionPPEm = Nothing, optionPtEm = Nothing,
- optionScale = Nothing,
- optionSynthSlant = Nothing
+ optionScale = Nothing
}
_setFontOptions font opts = do
@@ 374,9 349,6 @@ _setFontOptions font opts = do
case optionScale opts of
Just (x, y) -> hb_font_set_scale font x y
Nothing -> return ()
- case optionSynthSlant opts of
- Just slant -> hb_font_set_synthetic_slant font slant
- Nothing -> return ()
createFontWithOptions :: FontOptions -> Face -> Font
createFontWithOptions opts fce = unsafePerformIO $ do
@@ 385,18 357,16 @@ createFontWithOptions opts fce = unsafePerformIO $ do
hb_font_make_immutable font
newForeignPtr hb_font_destroy font
---ftCreateFontWithOptiosn :: FontOptions -> FT_Face -> Font
---ftCreateFontWithOptions opts fce = unsafePerformIO $ do
--- font <- hb_ft_font_create_referenced
--- _setFontOptions font opts
--- hb_font_make_immutable font
--- newForeignPtr hb_font_destroy font
+ftCreateFontWithOptions :: FontOptions -> FT_Face -> Font
+ftCreateFontWithOptions opts fce = unsafePerformIO $ do
+ font <- hb_ft_font_create_referenced fce
+ _setFontOptions font opts
+ hb_font_make_immutable font
+ newForeignPtr hb_font_destroy font
foreign import ccall "hb_font_set_ppem" hb_font_set_ppem :: Font_ -> Int -> Int -> IO ()
foreign import ccall "hb_font_set_ptem" hb_font_set_ptem :: Font_ -> Float -> IO ()
foreign import ccall "hb_font_set_scale" hb_font_set_scale :: Font_ -> Int -> Int -> IO ()
-foreign import ccall "hb_font_set_synthetic_slant" hb_font_set_synthetic_slant
- :: Font_ -> Float -> IO ()
-- Defer implementation of other functions...
@@ 409,7 379,7 @@ type Blob_ = Ptr Blob'
foreign import ccall "hb_blob_create" hb_blob_create :: Ptr Word8 -> Int -> Int
-> StablePtr ByteString -> FunPtr (StablePtr ByteString -> IO ()) -> IO Blob_
hb_MEMORY_MODE_READONLY = 1
-foreign import ccall "&hb_blob_destroy" hb_blob_destroy :: FunPtr (Blob_ -> IO ())
+foreign import ccall "hb_blob_destroy" hb_blob_destroy :: Blob_ -> IO ()
foreign import ccall "wrapper" hs_destructor
:: (StablePtr a -> IO ()) -> IO (FunPtr (StablePtr a -> IO ()))
@@ 419,7 389,7 @@ bs2blob bytes@(PS ptr offset length) = do
blob <- withForeignPtr ptr $ \ptr' ->
hb_blob_create (plusPtr ptr' offset) (length - offset)
hb_MEMORY_MODE_READONLY bytes' destructor
- newForeignPtr hb_blob_destroy blob
+ Conc.newForeignPtr blob $ hb_blob_destroy blob
faceFunc :: (Face_ -> a) -> (Face -> a)
faceFunc cb fce = unsafePerformIO $ withForeignPtr fce $ return . cb
M Main.hs => Main.hs +17 -4
@@ 1,12 1,25 @@
{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE OverloadedStrings #-}
module Main where
import "harfbuzz-pure" Data.Text.Glyphize
import "harfbuzz-pure" Data.Text.Glyphize.Buffer
+import "harfbuzz-pure" Data.Text.Glyphize.Font
+
+import System.Environment
+import Data.ByteString.Lazy as LBS
+import Data.ByteString as BS
+import Data.ByteString.Char8 as UTF8
+
+shapeStr font word = shape font $ defaultBuffer {
+ text = Right $ LBS.fromStrict $ UTF8.pack word
+ }
main :: IO ()
main = do
- buf' <- freeze' defaultBuffer
- buf <- thaw' buf'
- print buf
- -- TODO test I can shape text!
+ print $ guessSegmentProperties $ defaultBuffer { text = Right "Testing, testing"}
+
+ words <- getArgs
+ blob <- BS.readFile "assets/Lora-Regular.ttf"
+ let font = createFont $ createFace blob 0
+ print $ Prelude.map (shapeStr font) words
M harfbuzz-pure.cabal => harfbuzz-pure.cabal +1 -1
@@ 75,7 75,7 @@ executable shape-text
main-is: Main.hs
-- Other library packages from which modules are imported
- build-depends: base >=4.9 && <5, harfbuzz-pure
+ build-depends: base >=4.9 && <5, harfbuzz-pure, parallel, bytestring
-- Directories containing source files.
hs-source-dirs: .