wumpus-basic-0.24.0: src/Wumpus/Basic/Kernel/Base/FontSupport.hs
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS -Wall #-}
--------------------------------------------------------------------------------
-- |
-- Module : Wumpus.Basic.Kernel.Base.FontSupport
-- Copyright : (c) Stephen Tetley 2010-2011
-- License : BSD3
--
-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>
-- 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)