Extract some convenience parsing functions
This commit is contained in:
parent
23271d6f6c
commit
c5f715ac7c
@ -65,3 +65,15 @@ executable fountainhead
|
|||||||
time
|
time
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
||||||
|
test-suite fountainhead-test
|
||||||
|
import: dependencies
|
||||||
|
type: exitcode-stdio-1.0
|
||||||
|
main-is: Spec.hs
|
||||||
|
hs-source-dirs: test
|
||||||
|
other-modules:
|
||||||
|
Graphics.Fountainhead.MetricsSpec
|
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||||
|
build-depends:
|
||||||
|
fountainhead,
|
||||||
|
hspec >= 2.9 && < 3
|
||||||
|
@ -11,37 +11,24 @@ module Graphics.Fountainhead
|
|||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.Void (Void)
|
import Data.Void (Void)
|
||||||
import Graphics.Fountainhead.Dumper (dumpTable, dumpTables, DumpError(..))
|
import Graphics.Fountainhead.Dumper (dumpTable, dumpTables, DumpError(..))
|
||||||
import Graphics.Fountainhead.Parser (ParseErrorBundle, fontDirectoryP)
|
import Graphics.Fountainhead.Parser (ParseErrorBundle, parseFontDirectory)
|
||||||
import Graphics.Fountainhead.TrueType (FontDirectory(..))
|
import Graphics.Fountainhead.TrueType (FontDirectory(..))
|
||||||
import qualified Text.Megaparsec as Megaparsec
|
import Text.Megaparsec (State(..))
|
||||||
import Text.Megaparsec (PosState(..), State(..))
|
|
||||||
import System.IO (IOMode(..), withBinaryFile)
|
import System.IO (IOMode(..), withBinaryFile)
|
||||||
import Data.Bifunctor (Bifunctor(..))
|
import Data.Bifunctor (Bifunctor(..))
|
||||||
import qualified Data.Text.Lazy.Builder as Text.Builder
|
import qualified Data.Text.Lazy.Builder as Text.Builder
|
||||||
import Graphics.Fountainhead.Compression (hDecompress)
|
import Graphics.Fountainhead.Compression (hDecompress)
|
||||||
|
|
||||||
|
-- | Does initial parsing of the font at the given path and returns the font
|
||||||
|
-- directory and parsing state that can be used to parse other tables in the
|
||||||
|
-- font.
|
||||||
parseFontDirectoryFromFile :: FilePath
|
parseFontDirectoryFromFile :: FilePath
|
||||||
-> IO (State ByteString Void, Either ParseErrorBundle FontDirectory)
|
-> IO (State ByteString Void, Either ParseErrorBundle FontDirectory)
|
||||||
parseFontDirectoryFromFile fontFile =
|
parseFontDirectoryFromFile fontFile = withBinaryFile fontFile ReadMode
|
||||||
withBinaryFile fontFile ReadMode withFontHandle
|
$ fmap (parseFontDirectory fontFile) . hDecompress
|
||||||
where
|
|
||||||
withFontHandle fontHandle = doParsing
|
|
||||||
<$> hDecompress fontHandle
|
|
||||||
doParsing ttfContents =
|
|
||||||
let initialState = Megaparsec.State
|
|
||||||
{ stateInput = ttfContents
|
|
||||||
, stateOffset = 0
|
|
||||||
, statePosState = Megaparsec.PosState
|
|
||||||
{ pstateInput = ttfContents
|
|
||||||
, pstateOffset = 0
|
|
||||||
, pstateSourcePos = Megaparsec.initialPos fontFile
|
|
||||||
, pstateTabWidth = Megaparsec.defaultTabWidth
|
|
||||||
, pstateLinePrefix = ""
|
|
||||||
}
|
|
||||||
, stateParseErrors = []
|
|
||||||
}
|
|
||||||
in Megaparsec.runParser' fontDirectoryP initialState
|
|
||||||
|
|
||||||
|
-- | Dumps the contents of the font in the file. If the table name is given,
|
||||||
|
-- dumps only this one table.
|
||||||
dumpFontFile :: FilePath -> Maybe String -> IO (Either DumpError Text.Builder.Builder)
|
dumpFontFile :: FilePath -> Maybe String -> IO (Either DumpError Text.Builder.Builder)
|
||||||
dumpFontFile fontFile tableName = do
|
dumpFontFile fontFile tableName = do
|
||||||
let dumpRequest = maybe dumpTables dumpTable tableName
|
let dumpRequest = maybe dumpTables dumpTable tableName
|
||||||
|
@ -26,13 +26,10 @@ module Graphics.Fountainhead.Dumper
|
|||||||
, dumpPost
|
, dumpPost
|
||||||
, dumpTable
|
, dumpTable
|
||||||
, dumpTables
|
, dumpTables
|
||||||
, dumpTrueType
|
|
||||||
, dumpOffsetTable
|
, dumpOffsetTable
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.ByteString (ByteString)
|
|
||||||
import qualified Data.ByteString as ByteString
|
import qualified Data.ByteString as ByteString
|
||||||
import qualified Data.ByteString.Char8 as Char8
|
|
||||||
import Data.Int (Int64, Int16)
|
import Data.Int (Int64, Int16)
|
||||||
import Data.Word (Word8, Word16, Word32)
|
import Data.Word (Word8, Word16, Word32)
|
||||||
import qualified Data.IntMap as IntMap
|
import qualified Data.IntMap as IntMap
|
||||||
@ -44,7 +41,6 @@ import qualified Data.Text.Lazy.Builder.Int as Text.Builder
|
|||||||
import qualified Data.Text.Lazy.Builder.RealFloat as Text.Builder
|
import qualified Data.Text.Lazy.Builder.RealFloat as Text.Builder
|
||||||
import Data.Vector (Vector)
|
import Data.Vector (Vector)
|
||||||
import qualified Data.Vector as Vector
|
import qualified Data.Vector as Vector
|
||||||
import Data.Void
|
|
||||||
import GHC.Records (HasField(..))
|
import GHC.Records (HasField(..))
|
||||||
import Graphics.Fountainhead.TrueType
|
import Graphics.Fountainhead.TrueType
|
||||||
( CmapTable(..)
|
( CmapTable(..)
|
||||||
@ -94,12 +90,12 @@ import Graphics.Fountainhead.TrueType
|
|||||||
, OutlineFlag(..)
|
, OutlineFlag(..)
|
||||||
, ComponentGlyphFlags(..)
|
, ComponentGlyphFlags(..)
|
||||||
, GlyphTransformationOption(..)
|
, GlyphTransformationOption(..)
|
||||||
|
, findTableByTag
|
||||||
)
|
)
|
||||||
import qualified Text.Megaparsec as Megaparsec
|
import qualified Text.Megaparsec as Megaparsec
|
||||||
import Graphics.Fountainhead.Parser
|
import Graphics.Fountainhead.Parser
|
||||||
( ParseErrorBundle
|
( ParseErrorBundle
|
||||||
, ParseState
|
, ParseState
|
||||||
, fontDirectoryP
|
|
||||||
, parseTable
|
, parseTable
|
||||||
, cmapTableP
|
, cmapTableP
|
||||||
, headTableP
|
, headTableP
|
||||||
@ -118,10 +114,9 @@ import Graphics.Fountainhead.Type
|
|||||||
( Fixed32(..)
|
( Fixed32(..)
|
||||||
, succIntegral
|
, succIntegral
|
||||||
, ttfEpoch
|
, ttfEpoch
|
||||||
, newlineBuilder
|
|
||||||
, fixed2Double
|
, fixed2Double
|
||||||
)
|
)
|
||||||
import Data.Foldable (Foldable(..), find)
|
import Data.Foldable (Foldable(..))
|
||||||
import Data.Maybe (fromMaybe, catMaybes)
|
import Data.Maybe (fromMaybe, catMaybes)
|
||||||
import Data.Time (LocalTime(..), NominalDiffTime, diffLocalTime, midnight)
|
import Data.Time (LocalTime(..), NominalDiffTime, diffLocalTime, midnight)
|
||||||
import Data.Bits (Bits(..), (.>>.))
|
import Data.Bits (Bits(..), (.>>.))
|
||||||
@ -130,7 +125,7 @@ import Data.List (intersperse)
|
|||||||
import Prelude hiding (repeat)
|
import Prelude hiding (repeat)
|
||||||
|
|
||||||
data DumpError
|
data DumpError
|
||||||
= DumpParseError (Megaparsec.ParseErrorBundle ByteString Void)
|
= DumpParseError ParseErrorBundle
|
||||||
| DumpRequiredTableMissingError String
|
| DumpRequiredTableMissingError String
|
||||||
| DumpRequestedTableMissingError String
|
| DumpRequestedTableMissingError String
|
||||||
deriving Eq
|
deriving Eq
|
||||||
@ -149,6 +144,9 @@ data RequiredTables = RequiredTables
|
|||||||
, locaTable :: LocaTable
|
, locaTable :: LocaTable
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
newlineBuilder :: Text.Builder.Builder
|
||||||
|
newlineBuilder = Text.Builder.singleton '\n'
|
||||||
|
|
||||||
paddedHexadecimal :: Integral a => a -> Text.Builder.Builder
|
paddedHexadecimal :: Integral a => a -> Text.Builder.Builder
|
||||||
paddedHexadecimal = ("0x" <>)
|
paddedHexadecimal = ("0x" <>)
|
||||||
. Text.Builder.fromLazyText
|
. Text.Builder.fromLazyText
|
||||||
@ -797,9 +795,9 @@ dumpTable
|
|||||||
-> ParseState
|
-> ParseState
|
||||||
-> FontDirectory
|
-> FontDirectory
|
||||||
-> Either DumpError Text.Builder.Builder
|
-> Either DumpError Text.Builder.Builder
|
||||||
dumpTable needle processedState FontDirectory{..}
|
dumpTable needle processedState fontDirectory
|
||||||
| Just neededTable <- find ((needle ==) . Char8.unpack . getField @"tag") tableDirectory
|
| Just neededTable <- findTableByTag needle fontDirectory
|
||||||
= parseRequired processedState tableDirectory
|
= parseRequired processedState fontDirectory
|
||||||
>>= maybe (pure mempty) (first DumpParseError)
|
>>= maybe (pure mempty) (first DumpParseError)
|
||||||
. dumpSubTable processedState neededTable
|
. dumpSubTable processedState neededTable
|
||||||
| otherwise = Left $ DumpRequestedTableMissingError needle
|
| otherwise = Left $ DumpRequestedTableMissingError needle
|
||||||
@ -809,7 +807,7 @@ dumpTables
|
|||||||
-> FontDirectory
|
-> FontDirectory
|
||||||
-> Either DumpError Text.Builder.Builder
|
-> Either DumpError Text.Builder.Builder
|
||||||
dumpTables processedState directory@FontDirectory{..}
|
dumpTables processedState directory@FontDirectory{..}
|
||||||
= parseRequired processedState tableDirectory >>= traverseDirectory
|
= parseRequired processedState directory >>= traverseDirectory
|
||||||
where
|
where
|
||||||
traverseDirectory parsedRequired =
|
traverseDirectory parsedRequired =
|
||||||
let initial = Right $ dumpOffsetTable directory
|
let initial = Right $ dumpOffsetTable directory
|
||||||
@ -821,12 +819,8 @@ dumpTables processedState directory@FontDirectory{..}
|
|||||||
concatDump accumulator builderDump = ((accumulator <> newlineBuilder) <>)
|
concatDump accumulator builderDump = ((accumulator <> newlineBuilder) <>)
|
||||||
<$> builderDump
|
<$> builderDump
|
||||||
|
|
||||||
parseRequired
|
parseRequired :: ParseState -> FontDirectory -> Either DumpError RequiredTables
|
||||||
:: (Foldable t)
|
parseRequired processedState fontDirectory = do
|
||||||
=> ParseState
|
|
||||||
-> t TableDirectory
|
|
||||||
-> Either DumpError RequiredTables
|
|
||||||
parseRequired processedState tableDirectory = do
|
|
||||||
requiredHhea <- findRequired "hhea" hheaTableP
|
requiredHhea <- findRequired "hhea" hheaTableP
|
||||||
requiredHead@HeadTable{ indexToLocFormat } <-
|
requiredHead@HeadTable{ indexToLocFormat } <-
|
||||||
findRequired "head" headTableP
|
findRequired "head" headTableP
|
||||||
@ -841,7 +835,7 @@ parseRequired processedState tableDirectory = do
|
|||||||
let missingError = Left $ DumpRequiredTableMissingError tableName
|
let missingError = Left $ DumpRequiredTableMissingError tableName
|
||||||
parseFound tableEntry = parseTable tableEntry parser processedState
|
parseFound tableEntry = parseTable tableEntry parser processedState
|
||||||
in maybe missingError (first DumpParseError . parseFound)
|
in maybe missingError (first DumpParseError . parseFound)
|
||||||
$ find ((== Char8.pack tableName) . getField @"tag") tableDirectory
|
$ findTableByTag tableName fontDirectory
|
||||||
|
|
||||||
dumpSubTable
|
dumpSubTable
|
||||||
:: ParseState
|
:: ParseState
|
||||||
@ -864,21 +858,3 @@ dumpSubTable processedState tableEntry RequiredTables{..} =
|
|||||||
"gasp" -> Just $ dumpGASP <$> parseTable tableEntry gaspTableP processedState
|
"gasp" -> Just $ dumpGASP <$> parseTable tableEntry gaspTableP processedState
|
||||||
"glyf" -> Just $ dumpGlyf <$> parseTable tableEntry (glyfTableP locaTable) processedState
|
"glyf" -> Just $ dumpGlyf <$> parseTable tableEntry (glyfTableP locaTable) processedState
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
dumpTrueType :: ByteString -> FilePath -> Either DumpError Text.Builder.Builder
|
|
||||||
dumpTrueType ttfContents fontFile =
|
|
||||||
let initialState = Megaparsec.State
|
|
||||||
{ stateInput = ttfContents
|
|
||||||
, stateOffset = 0
|
|
||||||
, statePosState = Megaparsec.PosState
|
|
||||||
{ pstateInput = ttfContents
|
|
||||||
, pstateOffset = 0
|
|
||||||
, pstateSourcePos = Megaparsec.initialPos fontFile
|
|
||||||
, pstateTabWidth = Megaparsec.defaultTabWidth
|
|
||||||
, pstateLinePrefix = ""
|
|
||||||
}
|
|
||||||
, stateParseErrors = []
|
|
||||||
}
|
|
||||||
(processedState, initialResult) = Megaparsec.runParser' fontDirectoryP initialState
|
|
||||||
|
|
||||||
in first DumpParseError initialResult >>= dumpTables processedState
|
|
||||||
|
@ -7,11 +7,22 @@
|
|||||||
module Graphics.Fountainhead.Metrics
|
module Graphics.Fountainhead.Metrics
|
||||||
( FontBBox(..)
|
( FontBBox(..)
|
||||||
, FontDescriptor(..)
|
, FontDescriptor(..)
|
||||||
|
, MetricsError(..)
|
||||||
, Number
|
, Number
|
||||||
, FontDescriptorFlag(..)
|
, FontDescriptorFlag(..)
|
||||||
|
, collectMetrics
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
import Graphics.Fountainhead.TrueType (findTableByTag)
|
||||||
|
import Graphics.Fountainhead.Parser
|
||||||
|
( ParseErrorBundle
|
||||||
|
, nameTableP
|
||||||
|
, parseFontDirectory
|
||||||
|
, parseTable
|
||||||
|
)
|
||||||
|
import qualified Text.Megaparsec as Megaparsec
|
||||||
|
|
||||||
type Number = Double
|
type Number = Double
|
||||||
|
|
||||||
@ -66,3 +77,27 @@ data FontDescriptor = FontDescriptor
|
|||||||
, ascender :: Number
|
, ascender :: Number
|
||||||
, descender :: Number
|
, descender :: Number
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
data MetricsError
|
||||||
|
= MetricsParseError ParseErrorBundle
|
||||||
|
| MetricsRequiredTableMissingError String
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show MetricsError
|
||||||
|
where
|
||||||
|
show (MetricsParseError errorBundle) = Megaparsec.errorBundlePretty errorBundle
|
||||||
|
show (MetricsRequiredTableMissingError tableName) =
|
||||||
|
"Required table " <> tableName <> " is missing."
|
||||||
|
|
||||||
|
collectMetrics :: FilePath -> ByteString -> Either MetricsError FontDescriptor
|
||||||
|
collectMetrics fontFile ttfContents =
|
||||||
|
case parseFontDirectory fontFile ttfContents of
|
||||||
|
(_processedState, Left initialResult) -> Left
|
||||||
|
$ MetricsParseError initialResult
|
||||||
|
(processedState, Right initialResult)
|
||||||
|
| Just tableEntry <- findTableByTag "name" initialResult
|
||||||
|
, Right parsedNameTable <- parseTable tableEntry nameTableP processedState ->
|
||||||
|
pure $ FontDescriptor
|
||||||
|
{
|
||||||
|
}
|
||||||
|
| otherwise -> Left $ MetricsRequiredTableMissingError "name"
|
||||||
|
@ -33,6 +33,7 @@ module Graphics.Fountainhead.Parser
|
|||||||
, nameTableP
|
, nameTableP
|
||||||
, os2TableP
|
, os2TableP
|
||||||
, panoseP
|
, panoseP
|
||||||
|
, parseFontDirectory
|
||||||
, parseTable
|
, parseTable
|
||||||
, pascalStringP
|
, pascalStringP
|
||||||
, postTableP
|
, postTableP
|
||||||
@ -157,6 +158,29 @@ type Parser = Megaparsec.Parsec Void ByteString
|
|||||||
type ParseErrorBundle = Megaparsec.ParseErrorBundle ByteString Void
|
type ParseErrorBundle = Megaparsec.ParseErrorBundle ByteString Void
|
||||||
type ParseState = Megaparsec.State ByteString Void
|
type ParseState = Megaparsec.State ByteString Void
|
||||||
|
|
||||||
|
-- | Does initial parsing and returns the font directory and parsing state
|
||||||
|
-- that can be used to parse other tables in the font.
|
||||||
|
--
|
||||||
|
-- Font file name can be empty.
|
||||||
|
parseFontDirectory
|
||||||
|
:: FilePath
|
||||||
|
-> ByteString
|
||||||
|
-> (ParseState, Either ParseErrorBundle FontDirectory)
|
||||||
|
parseFontDirectory fontFile ttfContents =
|
||||||
|
let initialState = Megaparsec.State
|
||||||
|
{ stateInput = ttfContents
|
||||||
|
, stateOffset = 0
|
||||||
|
, statePosState = Megaparsec.PosState
|
||||||
|
{ pstateInput = ttfContents
|
||||||
|
, pstateOffset = 0
|
||||||
|
, pstateSourcePos = Megaparsec.initialPos fontFile
|
||||||
|
, pstateTabWidth = Megaparsec.defaultTabWidth
|
||||||
|
, pstateLinePrefix = ""
|
||||||
|
}
|
||||||
|
, stateParseErrors = []
|
||||||
|
}
|
||||||
|
in Megaparsec.runParser' fontDirectoryP initialState
|
||||||
|
|
||||||
-- * Font directory
|
-- * Font directory
|
||||||
|
|
||||||
offsetSubtableP :: Parser OffsetSubtable
|
offsetSubtableP :: Parser OffsetSubtable
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
obtain one at https://mozilla.org/MPL/2.0/. -}
|
obtain one at https://mozilla.org/MPL/2.0/. -}
|
||||||
|
|
||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
@ -81,11 +82,13 @@ module Graphics.Fountainhead.TrueType
|
|||||||
, UVSMapping(..)
|
, UVSMapping(..)
|
||||||
, UnicodeValueRange(..)
|
, UnicodeValueRange(..)
|
||||||
, VariationSelectorMap
|
, VariationSelectorMap
|
||||||
|
, findTableByTag
|
||||||
, unLocaTable
|
, unLocaTable
|
||||||
, nameStringOffset
|
, nameStringOffset
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
|
import qualified Data.ByteString.Char8 as Char8
|
||||||
import Data.Int (Int8, Int16)
|
import Data.Int (Int8, Int16)
|
||||||
import Data.IntMap (IntMap)
|
import Data.IntMap (IntMap)
|
||||||
import Data.List.NonEmpty (NonEmpty(..))
|
import Data.List.NonEmpty (NonEmpty(..))
|
||||||
@ -93,6 +96,8 @@ import Data.Time (LocalTime(..))
|
|||||||
import Data.Vector (Vector)
|
import Data.Vector (Vector)
|
||||||
import Data.Word (Word8, Word16, Word32)
|
import Data.Word (Word8, Word16, Word32)
|
||||||
import Graphics.Fountainhead.Type (F2Dot14(..), Fixed32(..), FWord, UFWord)
|
import Graphics.Fountainhead.Type (F2Dot14(..), Fixed32(..), FWord, UFWord)
|
||||||
|
import GHC.Records (HasField(..))
|
||||||
|
import Data.Foldable (find)
|
||||||
|
|
||||||
-- * Font directory
|
-- * Font directory
|
||||||
|
|
||||||
@ -101,6 +106,10 @@ data FontDirectory = FontDirectory
|
|||||||
, tableDirectory :: [TableDirectory]
|
, tableDirectory :: [TableDirectory]
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
findTableByTag :: String -> FontDirectory -> Maybe TableDirectory
|
||||||
|
findTableByTag needle = find ((needle ==) . Char8.unpack . getField @"tag")
|
||||||
|
. getField @"tableDirectory"
|
||||||
|
|
||||||
data OffsetSubtable = OffsetSubtable
|
data OffsetSubtable = OffsetSubtable
|
||||||
{ scalerType :: Word32
|
{ scalerType :: Word32
|
||||||
, numTables :: Int
|
, numTables :: Int
|
||||||
|
@ -9,12 +9,10 @@ module Graphics.Fountainhead.Type
|
|||||||
, FWord
|
, FWord
|
||||||
, UFWord
|
, UFWord
|
||||||
, fixed2Double
|
, fixed2Double
|
||||||
, newlineBuilder
|
|
||||||
, succIntegral
|
, succIntegral
|
||||||
, ttfEpoch
|
, ttfEpoch
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.Text.Lazy.Builder as Text.Builder
|
|
||||||
import Data.Bits ((.>>.), (.&.))
|
import Data.Bits ((.>>.), (.&.))
|
||||||
import Data.Int (Int16)
|
import Data.Int (Int16)
|
||||||
import Data.Word (Word16, Word32)
|
import Data.Word (Word16, Word32)
|
||||||
@ -41,6 +39,3 @@ fixed2Double (F2Dot14 fixed) =
|
|||||||
let mantissa = realToFrac (fixed .>>. 14)
|
let mantissa = realToFrac (fixed .>>. 14)
|
||||||
fraction = realToFrac (fixed .&. 0x3fff) / 16384.0
|
fraction = realToFrac (fixed .&. 0x3fff) / 16384.0
|
||||||
in mantissa + fraction
|
in mantissa + fraction
|
||||||
|
|
||||||
newlineBuilder :: Text.Builder.Builder
|
|
||||||
newlineBuilder = Text.Builder.singleton '\n'
|
|
||||||
|
32
test/Graphics/Fountainhead/MetricsSpec.hs
Normal file
32
test/Graphics/Fountainhead/MetricsSpec.hs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{- This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
obtain one at https://mozilla.org/MPL/2.0/. -}
|
||||||
|
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Graphics.Fountainhead.MetricsSpec
|
||||||
|
( spec
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Graphics.Fountainhead.Metrics
|
||||||
|
import Test.Hspec (Spec, describe, it, shouldBe)
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec =
|
||||||
|
describe "collectMetrics" $
|
||||||
|
it "collects information from the name table" $ do
|
||||||
|
let expected = FontDescriptor
|
||||||
|
{ fontName = "fontName"
|
||||||
|
, flags = []
|
||||||
|
, fullName = "fullName"
|
||||||
|
, familyName = "familyName"
|
||||||
|
, weight = "weight"
|
||||||
|
, fontBBox = FontBBox 0 0 0 0
|
||||||
|
, version = "1.0.0"
|
||||||
|
, notice = "Notice"
|
||||||
|
, encodingScheme = "encodingScheme"
|
||||||
|
, isFixedPitch = False
|
||||||
|
, ascender = 0
|
||||||
|
, descender = 0
|
||||||
|
}
|
||||||
|
in collectMetrics `shouldBe` expected
|
5
test/Spec.hs
Normal file
5
test/Spec.hs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{- This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
obtain one at https://mozilla.org/MPL/2.0/. -}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
|
Loading…
Reference in New Issue
Block a user