M src/Typograffiti.hs => src/Typograffiti.hs +2 -4
@@ 12,10 12,8 @@ module Typograffiti(
makeDrawGlyphs, AllocatedRendering(..), Layout(..),
SpatialTransform(..), TextTransform(..), move, scale, rotate, color, alpha,
withFontStore, newFontStore, FontStore(..), Font(..),
- makeDrawTextIndentedCached, makeDrawTextCached,
- makeDrawAsciiIndentedCached, makeDrawAsciiCached,
- makeDrawTextIndented, makeDrawTextIndented', makeDrawText, makeDrawText',
- makeDrawAsciiIndented, makeDrawAsciiIndented', makeDrawAscii, makeDrawAscii'
+ SampleText (..), defaultSample, addSampleFeature,
+ makeDrawTextCached, makeDrawText
) where
import Typograffiti.Atlas
M src/Typograffiti/Store.hs => src/Typograffiti/Store.hs +15 -12
@@ 31,11 31,12 @@ import Data.Text.Glyphize (defaultBuffer, Buffer(..), shape,
GlyphInfo(..), GlyphPos(..))
import qualified Data.Text.Glyphize as HB
import Data.Text.Lazy (Text, pack)
+import qualified Data.Text.Lazy as Txt
import FreeType.Core.Base
import Typograffiti.Atlas
import Typograffiti.Cache
-import Typograffiti.Text (GlyphSize(..), drawLinesWrapper)
+import Typograffiti.Text (GlyphSize(..), drawLinesWrapper, SampleText(..))
data FontStore n = FontStore {
fontMap :: TMVar (Map (FilePath, GlyphSize, Int) Font),
@@ 48,14 49,20 @@ data Font = Font {
atlases :: TMVar [(IS.IntSet, Atlas)]
}
-makeDrawTextIndentedCached store filepath index fontsize features sampletext indent = do
+makeDrawTextCached :: (MonadIO m, MonadFail m, MonadError TypograffitiError m,
+ MonadIO n, MonadFail n, MonadError TypograffitiError n) =>
+ FontStore n -> FilePath -> Int -> GlyphSize -> SampleText ->
+ m (String -> [HB.Feature] -> n (AllocatedRendering [TextTransform]))
+makeDrawTextCached store filepath index fontsize SampleText {..} = do
s <- liftIO $ atomically $ readTMVar $ fontMap store
font <- case M.lookup (filepath, fontsize, index) s of
Nothing -> allocFont store filepath index fontsize
Just font -> return font
let glyphs = map (codepoint . fst) $
- shape (harfbuzz font) defaultBuffer { text = sampletext } features
+ shape (harfbuzz font) defaultBuffer {
+ text = Txt.replicate (toEnum $ succ $ length sampleFeatures) sampleText
+ } sampleFeatures
let glyphset = IS.fromList $ map fromEnum glyphs
a <- liftIO $ atomically $ readTMVar $ atlases font
@@ 63,10 70,11 @@ makeDrawTextIndentedCached store filepath index fontsize features sampletext ind
(atlas:_) -> return atlas
_ -> allocAtlas' (atlases font) (freetype font) glyphset
- return $ drawLinesWrapper indent $ \string -> drawGlyphs store atlas $
- shape (harfbuzz font) defaultBuffer { text = pack string } features
+ return $ drawLinesWrapper tabwidth $ \string features -> drawGlyphs store atlas $
+ shape (harfbuzz font) defaultBuffer { text = pack string } []
-allocFont FontStore {..} filepath index fontsize = do
+allocFont :: (MonadIO m) => FontStore n -> FilePath -> Int -> GlyphSize -> m Font
+allocFont FontStore {..} filepath index fontsize = liftIO $ do
font <- ft_New_Face lib filepath $ toEnum index
case fontsize of
PixelSize w h -> ft_Set_Pixel_Sizes font (toEnum $ x2 w) (toEnum $ x2 h)
@@ 80,7 88,7 @@ allocFont FontStore {..} filepath index fontsize = do
atlases <- liftIO $ atomically $ newTMVar []
let ret = Font font' font atlases
- liftIO $ atomically $ do
+ atomically $ do
map <- takeTMVar fontMap
putTMVar fontMap $ M.insert (filepath, fontsize, index) ret map
return ret
@@ 109,8 117,3 @@ newFontStore lib = do
store <- liftIO $ atomically $ newTMVar M.empty
return $ FontStore store drawGlyphs lib
-
-makeDrawTextCached a b c d e f = makeDrawTextIndentedCached a b c d e f 4
-makeDrawAsciiIndentedCached a b c d e f =
- makeDrawTextIndentedCached a b c d e (pack $ map toEnum [32..126]) f
-makeDrawAsciiCached a b c d e = makeDrawTextCached a b c d e $ pack $ map toEnum [32..126]
M src/Typograffiti/Text.hs => src/Typograffiti/Text.hs +44 -23
@@ 3,6 3,7 @@
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards #-}
-- |
-- Module: Typograffiti.Monad
-- Copyright: (c) 2018 Schell Scivally
@@ 31,6 32,7 @@ import Data.Text.Glyphize (defaultBuffer, Buffer(..), shape, Glyph
import qualified Data.Text.Glyphize as HB
import FreeType.Core.Base
import Data.Text.Lazy (Text, pack)
+import qualified Data.Text.Lazy as Txt
import Typograffiti.Atlas
import Typograffiti.Cache
@@ 39,11 41,27 @@ data GlyphSize = CharSize Float Float Int Int
| PixelSize Int Int
deriving (Show, Eq, Ord)
-makeDrawTextIndented :: (MonadIO m, MonadFail m, MonadError TypograffitiError m,
+data SampleText = SampleText {
+ sampleFeatures :: [HB.Feature],
+ sampleText :: Text,
+ tabwidth :: Int
+}
+defaultSample = SampleText [] (pack $ map toEnum [32..126]) 4
+addSampleFeature name value sample@SampleText {..} = sample {
+ sampleFeatures =
+ HB.Feature (HB.tag_from_string name) value (n*i) (n*succ i) : sampleFeatures
+ }
+ where
+ n = w $ fromEnum $ Txt.length sampleText
+ i = w $ length sampleFeatures
+ w :: Int -> Word
+ w = toEnum
+
+makeDrawText :: (MonadIO m, MonadFail m, MonadError TypograffitiError m,
MonadIO n, MonadFail n, MonadError TypograffitiError n) =>
- FT_Library -> FilePath -> Int -> GlyphSize -> [HB.Feature] -> Text -> Int ->
- m (String -> n (AllocatedRendering [TextTransform]))
-makeDrawTextIndented lib filepath index fontsize features sampletext indent = do
+ FT_Library -> FilePath -> Int -> GlyphSize -> SampleText ->
+ m (String -> [HB.Feature] -> n (AllocatedRendering [TextTransform]))
+makeDrawText lib filepath index fontsize SampleText {..} = do
font <- liftIO $ ft_New_Face lib filepath $ toEnum index
liftIO $ case fontsize of
PixelSize w h -> ft_Set_Pixel_Sizes font (toEnum $ x2 w) (toEnum $ x2 h)
@@ 54,35 72,27 @@ makeDrawTextIndented lib filepath index fontsize features sampletext indent = do
bytes <- liftIO $ B.readFile filepath
let font' = HB.createFont $ HB.createFace bytes $ toEnum index
let glyphs = map (codepoint . fst) $
- shape font' defaultBuffer { text = sampletext } features
+ shape font' defaultBuffer {
+ text = Txt.replicate (toEnum $ succ $ length sampleFeatures) sampleText
+ } sampleFeatures
let glyphs' = map toEnum $ IS.toList $ IS.fromList $ map fromEnum glyphs
atlas <- allocAtlas (glyphRetriever font) glyphs'
liftIO $ ft_Done_Face font
drawGlyphs <- makeDrawGlyphs
- return $ drawLinesWrapper indent $ \string ->
+ return $ drawLinesWrapper tabwidth $ \string features ->
drawGlyphs atlas $ shape font' defaultBuffer { text = pack string } features
where x2 = (*2)
-makeDrawTextIndented' a b c d e f =
- ft_With_FreeType $ \ft -> runExceptT $ makeDrawTextIndented ft a b c d e f
-
-makeDrawText a b c d e f = makeDrawTextIndented a b c d e f 4
-makeDrawText' a b c d e = ft_With_FreeType $ \ft -> runExceptT $ makeDrawText ft a b c d e
-
--- Note: May glitch upon ligatures.
-makeDrawAsciiIndented a b c d e f =
- makeDrawTextIndented a b c d e (pack $ map toEnum [32..126]) f
-makeDrawAsciiIndented' a b c d e =
- ft_With_FreeType $ \ft -> runExceptT $ makeDrawAsciiIndented ft a b c d e
-makeDrawAscii a b c d e = makeDrawText a b c d e $ pack $ map toEnum [32..126]
-makeDrawAscii' a b c d = ft_With_FreeType $ \ft -> runExceptT $ makeDrawAscii ft a b c d
+makeDrawText' a b c d =
+ ft_With_FreeType $ \ft -> runExceptT $ makeDrawText ft a b c d
drawLinesWrapper :: (MonadIO m, MonadFail m) =>
- Int -> (String -> m (AllocatedRendering [TextTransform])) ->
- String -> m (AllocatedRendering [TextTransform])
-drawLinesWrapper indent cb string = do
- renderers <- mapM cb $ map processLine $ lines string
+ Int -> (String -> [HB.Feature] -> m (AllocatedRendering [TextTransform])) ->
+ String -> [HB.Feature] -> m (AllocatedRendering [TextTransform])
+drawLinesWrapper indent cb string features = do
+ let features' = splitFeatures 0 features $ lines string
+ renderers <- mapM (uncurry cb) $ flip zip features' $ map processLine $ lines string
let drawLine ts wsz y renderer = do
arDraw renderer (move 0 y:ts) wsz
let V2 _ height = arSize renderer
@@ 101,6 111,17 @@ drawLinesWrapper indent cb string = do
arSize = size
}
where
+ splitFeatures _ [] _ = []
+ splitFeatures _ _ [] = []
+ splitFeatures offset features' (line:lines') = let n = length line
+ in [feat {
+ HB.featStart = max 0 (start - offset),
+ HB.featEnd = min (toEnum n) (end - offset)
+ }
+ | feat@HB.Feature {HB.featStart = start, HB.featEnd = end} <- features',
+ fromEnum end <= n && end >= offset] :
+ splitFeatures (offset + toEnum n) features' lines'
+
processLine "" = " " -- enforce nonempty
processLine cs = expandTabs 0 cs
-- monospace tabshaping, good enough outside full line-layout.