Dump the loca table
This commit is contained in:
		@@ -3,7 +3,6 @@
 | 
			
		||||
   obtain one at https://mozilla.org/MPL/2.0/. -}
 | 
			
		||||
 | 
			
		||||
{-# LANGUAGE DataKinds #-}
 | 
			
		||||
{-# LANGUAGE NamedFieldPuns #-}
 | 
			
		||||
{-# LANGUAGE OverloadedStrings #-}
 | 
			
		||||
{-# LANGUAGE TypeApplications #-}
 | 
			
		||||
{-# LANGUAGE RecordWildCards #-}
 | 
			
		||||
@@ -15,6 +14,8 @@ module Graphics.Fountainhead.Dumper
 | 
			
		||||
    , dumpCmap
 | 
			
		||||
    , dumpHead
 | 
			
		||||
    , dumpHmtx
 | 
			
		||||
    , dumpHhea
 | 
			
		||||
    , dumpLoca
 | 
			
		||||
    , dumpTrueType
 | 
			
		||||
    , dumpOffsetTable
 | 
			
		||||
    ) where
 | 
			
		||||
@@ -22,7 +23,7 @@ module Graphics.Fountainhead.Dumper
 | 
			
		||||
import Data.ByteString (ByteString)
 | 
			
		||||
import qualified Data.ByteString.Char8 as Char8
 | 
			
		||||
import Data.Int (Int64)
 | 
			
		||||
import Data.Word (Word16)
 | 
			
		||||
import Data.Word (Word16, Word32)
 | 
			
		||||
import qualified Data.IntMap as IntMap
 | 
			
		||||
import qualified Data.Text.Encoding as Text
 | 
			
		||||
import qualified Data.Text.Lazy as Text.Lazy
 | 
			
		||||
@@ -46,6 +47,8 @@ import Graphics.Fountainhead.TrueType
 | 
			
		||||
    , CmapFormat4Table(..)
 | 
			
		||||
    , FontStyle(..)
 | 
			
		||||
    , LongHorMetric(..)
 | 
			
		||||
    , LocaTable(..)
 | 
			
		||||
    , IndexToLocFormat(..)
 | 
			
		||||
    )
 | 
			
		||||
import qualified Text.Megaparsec as Megaparsec
 | 
			
		||||
import Graphics.Fountainhead.Parser
 | 
			
		||||
@@ -55,6 +58,7 @@ import Graphics.Fountainhead.Parser
 | 
			
		||||
    , headTableP
 | 
			
		||||
    , hheaTableP
 | 
			
		||||
    , hmtxTableP
 | 
			
		||||
    , locaTableP
 | 
			
		||||
    )
 | 
			
		||||
import Graphics.Fountainhead.Type (Fixed32(..), ttfEpoch)
 | 
			
		||||
import Data.Foldable (Foldable(..), find)
 | 
			
		||||
@@ -67,6 +71,11 @@ data DumpError
 | 
			
		||||
    = DumpParseError (Megaparsec.ParseErrorBundle ByteString Void)
 | 
			
		||||
    | DumpRequiredTableMissingError String
 | 
			
		||||
 | 
			
		||||
data RequiredTables = RequiredTables
 | 
			
		||||
    { hheaTable :: HheaTable
 | 
			
		||||
    , headTable :: HeadTable
 | 
			
		||||
    } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
paddedHexadecimal :: Integral a => a -> Text.Builder.Builder
 | 
			
		||||
paddedHexadecimal = ("0x" <>)
 | 
			
		||||
    . Text.Builder.fromLazyText
 | 
			
		||||
@@ -180,9 +189,13 @@ dumpHead HeadTable{..}
 | 
			
		||||
    <> "         macStyle bits:          " <> "0x" <> dumpFontStyle macStyle <> newlineBuilder
 | 
			
		||||
    <> "         lowestRecPPEM           " <> Text.Builder.decimal lowestRecPPEM <> newlineBuilder
 | 
			
		||||
    <> "         fontDirectionHint       " <> dumpFontDirectionHint fontDirectionHint <> newlineBuilder
 | 
			
		||||
    <> "         indexToLocFormat        " <> Text.Builder.decimal indexToLocFormat <> newlineBuilder
 | 
			
		||||
    <> "         indexToLocFormat        " <> dumpIndexToLocFormat indexToLocFormat <> newlineBuilder
 | 
			
		||||
    <> "         glyphDataFormat         " <> Text.Builder.decimal glyphDataFormat <> newlineBuilder
 | 
			
		||||
 | 
			
		||||
dumpIndexToLocFormat :: IndexToLocFormat -> Text.Builder.Builder
 | 
			
		||||
dumpIndexToLocFormat ShortOffsetIndexToLocFormat = "0"
 | 
			
		||||
dumpIndexToLocFormat LongOffsetIndexToLocFormat = "1"
 | 
			
		||||
 | 
			
		||||
dumpFontDirectionHint :: FontDirectionHint -> Text.Builder.Builder
 | 
			
		||||
dumpFontDirectionHint = \case
 | 
			
		||||
    MixedDirectionalGlyphs -> "0"
 | 
			
		||||
@@ -303,34 +316,60 @@ dumpCmap CmapTable{..}
 | 
			
		||||
    dumpGlyphAtIndex index element = "                 glyphIdArray[" <> Text.Builder.decimal index <> "] =  "
 | 
			
		||||
        <> Text.Builder.decimal element <> newlineBuilder
 | 
			
		||||
 | 
			
		||||
dumpLoca :: LocaTable -> Text.Builder.Builder
 | 
			
		||||
dumpLoca table =
 | 
			
		||||
    dumpCaption "'loca' Table - Index to Location"
 | 
			
		||||
    <> go table
 | 
			
		||||
  where
 | 
			
		||||
    go (LongLocaTable elements) = dumpElements elements
 | 
			
		||||
    go (ShortLocaTable elements) = dumpElements
 | 
			
		||||
        $ (* 2)
 | 
			
		||||
        . (fromIntegral :: Word16 -> Word32)
 | 
			
		||||
        <$> elements
 | 
			
		||||
    dumpElements elements =
 | 
			
		||||
        case Vector.unsnoc elements of
 | 
			
		||||
            Just (init', last')
 | 
			
		||||
                -> foldMap dumpLocaLine (Vector.indexed init')
 | 
			
		||||
                <> "         Ended at " <> paddedHexadecimal last'
 | 
			
		||||
            Nothing -> mempty
 | 
			
		||||
    dumpLocaLine :: Integral a => (Int, a) -> Text.Builder.Builder
 | 
			
		||||
    dumpLocaLine (index, element)
 | 
			
		||||
        = "         Idx " <> justifyNumber 6 index
 | 
			
		||||
        <> " -> GlyphOffset " <> paddedHexadecimal element <> newlineBuilder
 | 
			
		||||
 | 
			
		||||
dumpTables
 | 
			
		||||
    :: Megaparsec.State ByteString Void
 | 
			
		||||
    -> FontDirectory
 | 
			
		||||
    -> Either DumpError Text.Builder.Builder
 | 
			
		||||
dumpTables processedState directory@FontDirectory{..} =
 | 
			
		||||
    findRequired "hhea" hheaTableP >>= traverseDirectory
 | 
			
		||||
dumpTables processedState directory@FontDirectory{..}
 | 
			
		||||
    = parseRequired >>= traverseDirectory
 | 
			
		||||
  where
 | 
			
		||||
    traverseDirectory parsedHhea =
 | 
			
		||||
    traverseDirectory parsedRequired =
 | 
			
		||||
        let initial = Right $ dumpOffsetTable directory
 | 
			
		||||
         in foldl' (go parsedHhea) initial tableDirectory
 | 
			
		||||
         in foldl' (go parsedRequired) initial tableDirectory
 | 
			
		||||
    parseRequired = RequiredTables
 | 
			
		||||
        <$> findRequired "hhea" hheaTableP
 | 
			
		||||
        <*> findRequired "head" headTableP
 | 
			
		||||
    findRequired tableName parser = 
 | 
			
		||||
        let missingError = Left $ DumpRequiredTableMissingError tableName
 | 
			
		||||
            parseRequired tableEntry = parseTable tableEntry parser processedState
 | 
			
		||||
         in maybe missingError (first DumpParseError . parseRequired)
 | 
			
		||||
            parseFound tableEntry = parseTable tableEntry parser processedState
 | 
			
		||||
         in maybe missingError (first DumpParseError . parseFound)
 | 
			
		||||
            $ find ((== Char8.pack tableName) . getField @"tag") tableDirectory
 | 
			
		||||
    go _ (Left accumulator) _ = Left accumulator
 | 
			
		||||
    go hheaTable (Right accumulator) tableEntry
 | 
			
		||||
    go parsedRequired (Right accumulator) tableEntry
 | 
			
		||||
        = maybe (Right accumulator) (concatDump accumulator . first DumpParseError)
 | 
			
		||||
        $ dumpSubTable hheaTable tableEntry
 | 
			
		||||
        $ dumpSubTable parsedRequired tableEntry
 | 
			
		||||
    concatDump accumulator builderDump = ((accumulator <> newlineBuilder) <>)
 | 
			
		||||
        <$> builderDump
 | 
			
		||||
    dumpSubTable hheaTable@HheaTable{ numOfLongHorMetrics } tableEntry =
 | 
			
		||||
    dumpSubTable RequiredTables{..} tableEntry =
 | 
			
		||||
        case getField @"tag" tableEntry of
 | 
			
		||||
            "cmap" -> Just $ dumpCmap <$> parseTable tableEntry cmapTableP processedState
 | 
			
		||||
            "head" -> Just $ dumpHead <$> parseTable tableEntry headTableP processedState
 | 
			
		||||
            "head" -> Just $ Right $ dumpHead headTable
 | 
			
		||||
            "hhea" -> Just $ Right $ dumpHhea hheaTable
 | 
			
		||||
            "hmtx" -> Just $ dumpHmtx
 | 
			
		||||
                <$> parseTable tableEntry (hmtxTableP numOfLongHorMetrics) processedState
 | 
			
		||||
                <$> parseTable tableEntry (hmtxTableP $ getField @"numOfLongHorMetrics" hheaTable) processedState
 | 
			
		||||
            "loca" -> Just $ dumpLoca
 | 
			
		||||
                <$> parseTable tableEntry (locaTableP $ getField @"indexToLocFormat" headTable) processedState
 | 
			
		||||
            _ -> Nothing
 | 
			
		||||
 | 
			
		||||
dumpTrueType :: ByteString -> FilePath -> Either DumpError Text.Builder.Builder
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ module Graphics.Fountainhead.Parser
 | 
			
		||||
    , headTableP
 | 
			
		||||
    , hheaTableP
 | 
			
		||||
    , hmtxTableP
 | 
			
		||||
    , locaTableP
 | 
			
		||||
    , longDateTimeP
 | 
			
		||||
    , longLocaTableP
 | 
			
		||||
    , maxpTableP
 | 
			
		||||
@@ -107,6 +108,7 @@ import Graphics.Fountainhead.TrueType
 | 
			
		||||
    , HeadTable(..)
 | 
			
		||||
    , HheaTable(..)
 | 
			
		||||
    , HmtxTable(..)
 | 
			
		||||
    , IndexToLocFormat(..)
 | 
			
		||||
    , LocaTable(..)
 | 
			
		||||
    , LongHorMetric(..)
 | 
			
		||||
    , MaxpTable(..)
 | 
			
		||||
@@ -293,9 +295,17 @@ headTableP = HeadTable
 | 
			
		||||
    <*> fontStyleP
 | 
			
		||||
    <*> Megaparsec.Binary.word16be
 | 
			
		||||
    <*> fontDirectionHintP
 | 
			
		||||
    <*> Megaparsec.Binary.word16be
 | 
			
		||||
    <*> indexToLocFormatP
 | 
			
		||||
    <*> Megaparsec.Binary.word16be
 | 
			
		||||
    <* Megaparsec.eof
 | 
			
		||||
  where
 | 
			
		||||
    indexToLocFormatP = do
 | 
			
		||||
        indexToLocFormat' <- Megaparsec.Binary.int16be
 | 
			
		||||
        case indexToLocFormat' of
 | 
			
		||||
            0 -> pure ShortOffsetIndexToLocFormat
 | 
			
		||||
            1 -> pure LongOffsetIndexToLocFormat
 | 
			
		||||
            _ -> fail $ "Unknown loca table format indexToLocFormat: "
 | 
			
		||||
                <> show indexToLocFormat'
 | 
			
		||||
 | 
			
		||||
fontStyleP :: Parser FontStyle
 | 
			
		||||
fontStyleP = go <$> Megaparsec.Binary.word16be
 | 
			
		||||
@@ -330,6 +340,10 @@ shortLocaTableP = ShortLocaTable
 | 
			
		||||
    <$> vectorP Megaparsec.Binary.word16be
 | 
			
		||||
    <?> "loca table, short version"
 | 
			
		||||
 | 
			
		||||
locaTableP :: IndexToLocFormat -> Parser LocaTable
 | 
			
		||||
locaTableP ShortOffsetIndexToLocFormat = shortLocaTableP
 | 
			
		||||
locaTableP LongOffsetIndexToLocFormat = longLocaTableP
 | 
			
		||||
 | 
			
		||||
-- * Horizontal metrics table
 | 
			
		||||
 | 
			
		||||
longHorMetricP :: Parser LongHorMetric
 | 
			
		||||
@@ -587,9 +601,8 @@ simpleGlyphDefinitionP numberOfContours' = do
 | 
			
		||||
                    , thisXIsSame = testBit flag 4
 | 
			
		||||
                    , thisYIsSame = testBit flag 5
 | 
			
		||||
                    }
 | 
			
		||||
                repeat = testBit flag 3
 | 
			
		||||
            repeatN <- 
 | 
			
		||||
                if repeat
 | 
			
		||||
                if testBit flag 3
 | 
			
		||||
                then (1 +)
 | 
			
		||||
                    . fromIntegral
 | 
			
		||||
                    <$> Megaparsec.Binary.word8
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,7 @@ module Graphics.Fountainhead.TrueType
 | 
			
		||||
    , HeadTable(..)
 | 
			
		||||
    , HheaTable(..)
 | 
			
		||||
    , HmtxTable(..)
 | 
			
		||||
    , IndexToLocFormat(..)
 | 
			
		||||
    , LocaTable(..)
 | 
			
		||||
    , LongHorMetric(..)
 | 
			
		||||
    , MaxpTable(..)
 | 
			
		||||
@@ -180,6 +181,11 @@ data HheaTable = HheaTable
 | 
			
		||||
 | 
			
		||||
-- * Font header table
 | 
			
		||||
 | 
			
		||||
data IndexToLocFormat
 | 
			
		||||
    = ShortOffsetIndexToLocFormat
 | 
			
		||||
    | LongOffsetIndexToLocFormat
 | 
			
		||||
    deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
data HeadTable = HeadTable
 | 
			
		||||
    { version :: Fixed32 -- ^ 0x00010000 if (version 1.0).
 | 
			
		||||
    , fontRevision :: Fixed32 -- ^ Set by font manufacturer.
 | 
			
		||||
@@ -196,7 +202,7 @@ data HeadTable = HeadTable
 | 
			
		||||
    , macStyle :: FontStyle
 | 
			
		||||
    , lowestRecPPEM :: Word16 -- ^ Smallest readable size in pixels.
 | 
			
		||||
    , fontDirectionHint :: FontDirectionHint -- ^ 0 Mixed directional glyphs.
 | 
			
		||||
    , indexToLocFormat :: Word16 -- ^ 0 for short offsets, 1 for long.
 | 
			
		||||
    , indexToLocFormat :: IndexToLocFormat -- ^ 0 for short offsets, 1 for long.
 | 
			
		||||
    , glyphDataFormat :: Word16 -- ^ 0 for current format.
 | 
			
		||||
    } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user