{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Graphics.Text.PCF.Types ( PCF(..), PCFGlyph(..), Prop(..), Table(..), Metrics(..), TableMeta(..), PCFTableType(..), PCFText(..), glyph_ascii, glyph_ascii_lines, pcf_text_string, pcf_text_ascii ) where import Data.Binary import Data.Bits import Data.Int import Data.Monoid import Data.List import Data.Vector (Vector) import Data.IntMap (IntMap) import Data.ByteString.Lazy.Char8 (ByteString) import qualified Data.ByteString.Lazy as B (concatMap, take) import qualified Data.ByteString.Lazy.Char8 as B (unpack, splitAt, intercalate, concat) import qualified Data.Vector.Storable as VS -- | Container of tables extracted from a PCF font file. data PCF = PCF { pcf_properties :: (TableMeta, Table) , pcf_metrics :: (TableMeta, Table) , pcf_bitmaps :: (TableMeta, Table) , pcf_bdf_encodings :: (TableMeta, Table) , pcf_swidths :: (TableMeta, Table) , pcf_accelerators :: (TableMeta, Table) , pcf_glyph_names :: Maybe (TableMeta, Table) , pcf_ink_metrics :: Maybe (TableMeta, Table) } data Table = PROPERTIES { properties_props :: [Prop] , properties_strings :: ByteString } | BITMAPS { bitmaps_glyph_count :: Word32 , bitmaps_offsets :: Vector Word32 , bitmaps_sizes :: (Word32, Word32, Word32, Word32) , bitmaps_data :: ByteString } | METRICS { metrics_ink_type :: Bool , metrics_compressed :: Bool , metrics_metrics :: Vector Metrics } | SWIDTHS { swidths_swidths :: [Word32] } | ACCELERATORS { accel_no_overlap :: Bool , accel_constant_metrics :: Bool , accel_terminal_font :: Bool , accel_constant_width :: Bool , accel_ink_inside :: Bool , accel_ink_metrics :: Bool , accel_draw_direction :: Bool -- ^ False = left to right, True = right to left , accel_font_ascent :: Word32 , accel_font_descent :: Word32 , accel_max_overlap :: Word32 , accel_min_bounds :: Metrics , accel_max_bounds :: Metrics , accel_ink_min_max_bounds :: Maybe (Metrics, Metrics) } | GLYPH_NAMES { glyph_names_offsets :: [Word32] , glyph_names_string :: ByteString } | BDF_ENCODINGS { encodings_cols :: (Word16, Word16) , encodings_rows :: (Word16, Word16) , encodings_default_char :: Word16 , encodings_glyph_indices :: IntMap Word16 } data Prop = Prop { prop_name_offset :: Word32 , prop_is_string :: Word8 , prop_value :: Word32 } deriving (Eq) -- | Container of glyph dimension and position metrics. data Metrics = Metrics { metrics_left_sided_bearings :: Int16 , metrics_right_sided_bearings :: Int16 , metrics_character_width :: Int16 , metrics_character_ascent :: Int16 , metrics_character_descent :: Int16 , metrics_character_attributes :: Int16 } deriving (Eq, Show) data TableMeta = TableMeta { tableMetaType :: PCFTableType -- ^ Table type , tableMetaFormat :: Word32 -- ^ Whole format field for reconstructing , tableMetaGlyphPad :: Word8 -- ^ Level of padding applied to glyph bitmaps , tableMetaScanUnit :: Word8 -- ^ ? , tableMetaByte :: Bool -- ^ Byte-wise endianess , tableMetaBit :: Bool -- ^ Bit-wise endianess , tableMetaSize :: Word32 -- ^ Number of bytes used by the table , tableMetaOffset :: Word32 -- ^ Byte offset to table from beginning of file } data PCFTableType = PCF_PROPERTIES | PCF_ACCELERATORS | PCF_METRICS | PCF_BITMAPS | PCF_INK_METRICS | PCF_BDF_ENCODINGS | PCF_SWIDTHS | PCF_GLYPH_NAMES | PCF_BDF_ACCELERATORS deriving (Eq, Ord, Show) -- | Container of a single glyph bitmap and its metadata. data PCFGlyph = PCFGlyph { glyph_metrics :: Metrics , glyph_char :: Char -- ^ Unicode character corresponding to glyph , glyph_width :: Int -- ^ Pixel width of glyph once rendered , glyph_height :: Int -- ^ Pixel height of glyph once rendered , glyph_pitch :: Int -- ^ Number of bytes in each bitmap row , glyph_bitmap :: ByteString -- ^ `glyph_height` rows of `glyph_pitch` bytes containing the glyph's bitmap image starting from the left-most bit and ending at the `glyph_width` bit in each row } instance Show PCFGlyph where show g@PCFGlyph{..} = "PCFGlyph {glyph_metrics = " ++ show glyph_metrics ++ ", glyph_char = " ++ show glyph_char ++ ", glyph_width = " ++ show glyph_width ++ ", glyph_height = " ++ show glyph_height ++ ", glyph_pitch = " ++ show glyph_pitch ++ ", glyph_bitmap = " ++ show glyph_bitmap ++ "}\n" ++ glyph_ascii g -- | Render glyph bitmap as a string where 'X' represents opaque pixels and whitespace represents blank pixels. glyph_ascii :: PCFGlyph -> String glyph_ascii = B.unpack . mconcat . map (<> "\n") . glyph_ascii_lines_bs -- | Render glyph bitmap as a list of strings representing lines where 'X' represents opaque pixels and whitespace represents blank pixels. glyph_ascii_lines :: PCFGlyph -> [String] glyph_ascii_lines = map B.unpack . glyph_ascii_lines_bs glyph_ascii_lines_bs :: PCFGlyph -> [ByteString] glyph_ascii_lines_bs PCFGlyph{..} = map (B.take (fromIntegral glyph_width) . showBits) rs where rs = rows glyph_bitmap rows bs = case B.splitAt (fromIntegral glyph_pitch) bs of (r, "") -> [r] (r, t) -> r : rows t showBits = B.concatMap $ mconcat $ map showBit [7,6..0] showBit :: Int -> Word8 -> ByteString showBit i w | testBit w i = "X" | otherwise = " " -- | Representation of string and its corresponding bitmap content. Metadata regarding source font is not included. data PCFText = PCFText { pcf_text_glyphs :: [PCFGlyph] -- ^ Metadata of each glyph rendered to the image bitmap , pcf_text_width :: Int -- ^ Width of the rendered bitmap image , pcf_text_height :: Int -- ^ Height of the rendered bitmap image , pcf_text_image :: VS.Vector Word8 -- ^ Text rendered as a bitmap image where 0 is opaque and 255 is empty in each cell } -- | String represented by PCFText rendering. pcf_text_string :: PCFText -> String pcf_text_string = map glyph_char . pcf_text_glyphs -- | ASCII rendering of a whole PCFText string rendering. pcf_text_ascii :: PCFText -> String pcf_text_ascii = B.unpack . B.intercalate "\n" . map B.concat . transpose . map glyph_ascii_lines_bs . pcf_text_glyphs