{-# LANGUAGE RankNTypes #-} {-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel.Base.FontSupport -- Copyright : (c) Stephen Tetley 2010-2011 -- License : BSD3 -- -- Maintainer : Stephen Tetley -- Stability : highly unstable -- Portability : GHC -- -- Data types representing font metrics. -- -------------------------------------------------------------------------------- module Wumpus.Basic.Kernel.Base.FontSupport ( FontName , CodePoint , FontDef(..) , FontFamily(..) , regularWeight , boldWeight , italicWeight , boldItalicWeight , CharWidthLookup , FontMetrics(..) , FontTable , emptyFontTable , lookupFont , insertFont , FontLoadMsg , FontLoadLog , fontLoadMsg , FontLoadResult(..) , printLoadErrors , monospace_metrics ) where import Wumpus.Basic.Utils.HList import Wumpus.Core -- package: wumpus-core import qualified Data.Map as Map import Data.Monoid type FontName = String -- | A Unicode code-point. -- type CodePoint = Int -- | FontDef wraps @FontFace@ from Wumpus-Core with file name -- information for the font loaders. -- data FontDef = FontDef { font_def_face :: FontFace , gs_file_name :: String , afm_file_name :: String } deriving (Eq,Ord,Show) -- | A family group of FontDefs (regular, bold, italic and -- bold-italic). -- -- It is convenient for some higher-level text objects in Wumpus -- (particularly @Doc@ in Wumpus-Drawing) to treat a font and its -- standard weights as the same entity. This allows @Doc@ API to -- provide a @bold@ operation to simply change to the the bold -- weight of the current family, rather than use the primitive -- @set_font@ operation to change to an explicitly named font. -- -- data FontFamily = FontFamily { ff_regular :: FontDef , ff_bold :: Maybe FontDef , ff_italic :: Maybe FontDef , ff_bold_italic :: Maybe FontDef } -- | Extract the regular weight 'FontDef' from a 'FontFamily'. -- regularWeight :: FontFamily -> FontDef regularWeight = ff_regular -- | Extract the bold weight 'FontDef' from a 'FontFamily'. -- -- Note - this falls back to the regular weight if the font family -- has no bold weight. To get the bold weight or @Nothing@ if it -- is not present use the record selector @ff_bold@. -- boldWeight :: FontFamily -> FontDef boldWeight s = maybe (ff_regular s) id $ ff_bold s -- | Extract the @italic@ weight 'FontDef' from a 'FontFamily'. -- -- Note - this falls back to the regular weight if the font family -- has no italic weight. To get the italic weight or @Nothing@ if -- it is not present use the record selector @ff_italic@. -- italicWeight :: FontFamily -> FontDef italicWeight s = maybe (ff_regular s) id $ ff_italic s -- | Extract the @bold-italic@ weight 'FontDef' from a -- 'FontFamily'. -- -- Note - this falls back to the regular weight if the font family -- has no bold-italic weight. To get the bold-italic weight or -- @Nothing@ if it is not present use the record selector -- @ff_bold_italic@. -- boldItalicWeight :: FontFamily -> FontDef boldItalicWeight s = maybe (ff_regular s) id $ ff_bold_italic s -- | A lookup function from code point to /width vector/. -- -- The unit is always stored as a Double representing PostScript -- points. -- -- Note - in PostScript terminology a width vector is not obliged -- to be left-to-right (writing direction 0). It could be -- top-to-bottom (writing direction 1). -- type CharWidthLookup = CodePoint -> Vec2 Double -- | 'FontMetrics' store a subset of the properties available in -- a font file - enough to calculate accurate bounding boxes and -- positions for text. -- -- > Bounding box representing the maximum glyph area. -- > Width vectors for each character. -- > Cap height -- > Descender depth. -- -- Because Wumpus always needs font metrics respective to the -- current point size, the actual fields are all functions. -- data FontMetrics = FontMetrics { get_bounding_box :: FontSize -> BoundingBox Double , get_cw_table :: FontSize -> CharWidthLookup , get_cap_height :: FontSize -> Double , get_descender :: FontSize -> Double , get_underline_position :: FontSize -> Double , get_underline_thickness :: FontSize -> Double } -- | A map between a font name and the respective FontMetrics. -- newtype FontTable = FontTable { getFontTable :: Map.Map FontName FontMetrics } instance Monoid FontTable where mempty = emptyFontTable a `mappend` b = FontTable $ getFontTable a `mappend` getFontTable b emptyFontTable :: FontTable emptyFontTable = FontTable $ Map.empty -- | 'FontLoadMsg' - type synonym for String. -- type FontLoadMsg = String -- | 'FontLoadLog' is a Hughes list of Strings, so it supports -- efficient append. -- newtype FontLoadLog = FontLoadLog { getFontLoadLog :: H FontLoadMsg } instance Monoid FontLoadLog where mempty = FontLoadLog $ emptyH a `mappend` b = FontLoadLog $ getFontLoadLog a `appendH` getFontLoadLog b fontLoadMsg :: String -> FontLoadLog fontLoadMsg = FontLoadLog . wrapH -- Need a synonym for @FontLoading@... data FontLoadResult = FontLoadResult { loaded_font_table :: FontTable , loader_errors :: FontLoadLog } -- | Print the loader errors from the 'FontLoadResult' to std-out. -- printLoadErrors :: FontLoadResult -> IO () printLoadErrors = mapM_ putStrLn . toListH . getFontLoadLog . loader_errors -------------------------------------------------------------------------------- -- | 'lookupFont' : @ name * font_table -> Maybe FontMetrics @ -- -- Lookup a font in the font_table. -- lookupFont :: FontName -> FontTable -> Maybe FontMetrics lookupFont name = Map.lookup name . getFontTable -- | 'insertFont' : @ name * font_metrics * font_table -> FontTable @ -- -- Insert a named font into the font_table. -- insertFont :: FontName -> FontMetrics -> FontTable -> FontTable insertFont name ops = FontTable . Map.insert name ops . getFontTable -- | This ignores the Char code lookup and just returns the -- default advance vector. -- monospace_metrics :: FontMetrics monospace_metrics = FontMetrics { get_bounding_box = \sz -> BBox (lowerLeft sz) (upperRight sz) , get_cw_table = \sz _ -> hvec (upscale sz width_vec) , get_cap_height = \sz -> upscale sz cap_height , get_descender = \sz -> upscale sz descender , get_underline_position = \sz -> upscale sz underline_pos , get_underline_thickness = \sz -> upscale sz underline_width } where llx = (-23) / 1000 lly = (-250) / 1000 urx = 715 / 1000 ury = 805 / 1000 width_vec = 600 / 1000 cap_height = 562 / 1000 descender = (-157) / 1000 underline_pos = (-100) / 1000 underline_width = 50 / 1000 upscale sz d = d * fromIntegral sz lowerLeft sz = P2 (upscale sz llx) (upscale sz lly) upperRight sz = P2 (upscale sz urx) (upscale sz ury)