diff options
Diffstat (limited to 'src/Graphics/Fountainhead/Parser.hs')
| -rw-r--r-- | src/Graphics/Fountainhead/Parser.hs | 1244 |
1 files changed, 0 insertions, 1244 deletions
diff --git a/src/Graphics/Fountainhead/Parser.hs b/src/Graphics/Fountainhead/Parser.hs deleted file mode 100644 index 31dcd0e..0000000 --- a/src/Graphics/Fountainhead/Parser.hs +++ /dev/null @@ -1,1244 +0,0 @@ -{- 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 DataKinds #-} -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE ExplicitForAll #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE PatternGuards #-} -{-# LANGUAGE TypeApplications #-} - --- | Font parser. -module Graphics.Fountainhead.Parser - ( Parser - , ParseErrorBundle - , cmapTableP - , cvTableP - , f2Dot14P - , fixedP - , fontDirectoryP - , fpgmTableP - , gaspTableP - , glyfTableP - , hdmxTableP - , headTableP - , hheaTableP - , hmtxTableP - , locaTableP - , longDateTimeP - , longLocaTableP - , maxpTableP - , nameTableP - , os2TableP - , panoseP - , parseTable - , pascalStringP - , postTableP - , prepTableP - , shortLocaTableP - , word24P - ) where - -import Control.Applicative (Alternative(..)) -import Control.Monad (foldM, void) -import Data.Bits (Bits(..)) -import Data.ByteString (ByteString) -import qualified Data.ByteString as ByteString -import qualified Data.ByteString.Builder as ByteString.Builder -import qualified Data.ByteString.Lazy as ByteString.Lazy -import Data.Foldable (Foldable(..)) -import Data.Int (Int8, Int16) -import Data.IntMap (IntMap) -import qualified Data.IntMap as IntMap -import Data.Functor (($>)) -import Data.List (sortOn, nubBy, sortBy) -import Data.List.NonEmpty (NonEmpty(..)) -import qualified Data.List.NonEmpty as NonEmpty -import Data.Time - ( LocalTime(..) - , addDays - , secondsToDiffTime - , timeToTimeOfDay - ) -import Data.Vector (Vector) -import qualified Data.Vector as Vector -import Data.Void (Void) -import Data.Word (Word8, Word16, Word32) -import GHC.Records (HasField(..)) -import Graphics.Fountainhead.TrueType - ( BArmStyle(..) - , BContrast(..) - , BFamilyType(..) - , BMidline(..) - , BLetterform(..) - , BProportion(..) - , BSerifStyle(..) - , BStrokeVariation(..) - , BWeight(..) - , BXHeight(..) - , CVTable(..) - , CmapSubtable(..) - , CmapTable(..) - , CmapEncoding(..) - , CmapFormat0Table(..) - , CmapFormat2Subheader(..) - , CmapFormat2Table(..) - , CmapFormat4Table(..) - , CmapFormat6Table(..) - , CmapGroup(..) - , CmapFormat8Table(..) - , CmapFormat10Table(..) - , CmapFormat12Table(..) - , CmapFormat13Table - , CmapFormat14Table(..) - , ComponentGlyphFlags(..) - , ComponentGlyphPartDescription(..) - , CompoundGlyphDefinition(..) - , FpgmTable(..) - , FontDirectionHint(..) - , FontDirectory(..) - , FontStyle(..) - , GASPRange(..) - , GASPTable(..) - , GlyfTable(..) - , GlyphArgument(..) - , GlyphCoordinate(..) - , GlyphDefinition(..) - , GlyphDescription(..) - , GlyphTransformationOption(..) - , HdmxTable(..) - , DeviceRecord(..) - , HeadTable(..) - , HheaTable(..) - , HmtxTable(..) - , IndexToLocFormat(..) - , LocaTable(..) - , LongHorMetric(..) - , MaxpTable(..) - , NameRecord(..) - , NameTable(..) - , OffsetSubtable(..) - , OutlineFlag(..) - , OpenMaxpTable(..) - , Os2BaseFields(..) - , Os2MicrosoftFields(..) - , Os2Version1Fields(..) - , Os2Version4Fields(..) - , Os2Version5Fields(..) - , Os2Table(..) - , Panose(..) - , PostFormat2Table(..) - , PostHeader(..) - , PostSubtable(..) - , PostTable(..) - , PrepTable(..) - , SimpleGlyphDefinition(..) - , TableDirectory(..) - , TrueMaxpTable(..) - , UVSOffset(..) - , UVSMapping(..) - , UnicodeValueRange(..) - , VariationSelectorMap - , unLocaTable - ) -import Graphics.Fountainhead.Type - ( F2Dot14(..) - , Fixed32(..) - , succIntegral - , ttfEpoch - ) -import Text.Megaparsec ((<?>)) -import qualified Text.Megaparsec as Megaparsec -import qualified Text.Megaparsec.Byte.Binary as Megaparsec.Binary - -type Parser = Megaparsec.Parsec Void ByteString -type ParseErrorBundle = Megaparsec.ParseErrorBundle ByteString Void - --- * Font directory - -offsetSubtableP :: Parser OffsetSubtable -offsetSubtableP = OffsetSubtable - <$> Megaparsec.Binary.word32be - <*> (fromIntegral <$> Megaparsec.Binary.word16be) - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - -tagP :: Parser ByteString -tagP = ByteString.Lazy.toStrict - . ByteString.Builder.toLazyByteString - . ByteString.Builder.word32BE - <$> Megaparsec.Binary.word32be - -tableDirectoryP :: Parser TableDirectory -tableDirectoryP = TableDirectory - <$> tagP - <*> Megaparsec.Binary.word32be - <*> (fromIntegral <$> Megaparsec.Binary.word32be) - <*> (fromIntegral <$> Megaparsec.Binary.word32be) - -fontDirectoryP :: Parser FontDirectory -fontDirectoryP = do - offsetSubtable'@OffsetSubtable{ numTables } <- offsetSubtableP - tableDirectories <- Megaparsec.count numTables tableDirectoryP - pure $ FontDirectory - { offsetSubtable = offsetSubtable' - , tableDirectory = tableDirectories - } - --- * Name table - -nameTableP :: Parser NameTable -nameTableP = do - format' <- Megaparsec.Binary.word16be - count' <- fromIntegral <$> Megaparsec.Binary.word16be - stringOffset' <- fromIntegral <$> Megaparsec.Binary.word16be - nameRecord' <- Megaparsec.count count' nameRecordP - -- 12 is the size of a single record, 6 is the header size. - let padding = stringOffset' - count' * 12 - 6 - Megaparsec.skipCount padding Megaparsec.Binary.word8 - variable' <- Megaparsec.takeRest - pure $ NameTable - { format = format' - , nameRecord = nameRecord' - , variable = parseVariable variable' <$> nameRecord' - } - where - parseVariable variable' NameRecord{ offset, length = length' } = - ByteString.take length' $ ByteString.drop offset variable' - -nameRecordP :: Parser NameRecord -nameRecordP = NameRecord - <$> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> (fromIntegral <$> Megaparsec.Binary.word16be) - <*> (fromIntegral <$> Megaparsec.Binary.word16be) - --- * 'cvt ' table - -cvTableP :: Parser CVTable -cvTableP = CVTable - <$> Megaparsec.many Megaparsec.Binary.int16be - <* Megaparsec.eof - --- * Maximum profile table - -trueMaxpTableP :: Parser TrueMaxpTable -trueMaxpTableP - = Megaparsec.chunk (ByteString.pack [0, 1, 0, 0]) - *> subparser - where - subparser = - TrueMaxpTable (Fixed32 0x00010000) - <$> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - -openMaxpTableP :: Parser OpenMaxpTable -openMaxpTableP - = Megaparsec.chunk (ByteString.pack [0, 0, 0x50, 0]) - *> subparser - where - subparser = - OpenMaxpTable (Fixed32 0x00005000) - <$> Megaparsec.Binary.word16be - <* Megaparsec.eof - -maxpTableP :: Parser MaxpTable -maxpTableP - = TrueMaxp <$> trueMaxpTableP - <|> OpenMaxp <$> openMaxpTableP - --- * Horizontal header table - -hheaTableP :: Parser HheaTable -hheaTableP = HheaTable - <$> fixedP - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <* Megaparsec.Binary.int16be - <* Megaparsec.Binary.int16be - <* Megaparsec.Binary.int16be - <* Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word16be - <* Megaparsec.eof - --- * Font header table - -headTableP :: Parser HeadTable -headTableP = HeadTable - <$> fixedP - <*> fixedP - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> longDateTimeP - <*> longDateTimeP - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> fontStyleP - <*> Megaparsec.Binary.word16be - <*> fontDirectionHintP - <*> 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 - where - go fontStyle' = FontStyle - { bold = testBit fontStyle' 0 - , italic = testBit fontStyle' 1 - , underline = testBit fontStyle' 2 - , outline = testBit fontStyle' 3 - , shadow = testBit fontStyle' 4 - , condensed = testBit fontStyle' 5 - , extended = testBit fontStyle' 6 - } - -fontDirectionHintP :: Parser FontDirectionHint -fontDirectionHintP - = (Megaparsec.chunk (ByteString.pack [0, 0]) $> MixedDirectionalGlyphs) - <|> (Megaparsec.chunk (ByteString.pack [0, 1]) $> StronglyLeftToRightGlyphs) - <|> (Megaparsec.chunk (ByteString.pack [0, 2]) $> LeftToRightGlyphsWithNeutrals) - <|> (Megaparsec.chunk (ByteString.pack [0xff, 0xff]) $> StronglyRightToLeftGlyphs) - <|> (Megaparsec.chunk (ByteString.pack [0xff, 0xfe]) $> RightToLeftGlyphsWithNeutrals) - --- * Glyph data location table - -longLocaTableP :: Parser LocaTable -longLocaTableP = LongLocaTable - <$> vectorP Megaparsec.Binary.word32be - <?> "loca table, long version" - -shortLocaTableP :: Parser LocaTable -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 -longHorMetricP = LongHorMetric - <$> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.int16be - -hmtxTableP :: Word16 -> Parser HmtxTable -hmtxTableP numOfLongHorMetrics = HmtxTable - <$> countP (fromIntegral numOfLongHorMetrics) longHorMetricP - <*> Megaparsec.many Megaparsec.Binary.int16be - --- * Glyph name and PostScript font table - -postTableP :: Parser PostTable -postTableP = do - header'@PostHeader{ format } <- postHeaderP - subtable' <- case format of - Fixed32 0x00010000 -> pure None - Fixed32 0x00020000 -> PostFormat2 <$> postFormat2TableP - Fixed32 0x00025000 -> PostFormat25 <$> postFormat25TableP - Fixed32 0x00030000 -> pure None - Fixed32 0x00040000 -> PostFormat4 <$> postFormat4TableP - _ -> fail $ "Unsupported post table format: " <> show format - Megaparsec.eof - pure $ PostTable - { postHeader = header' - , postSubtable = subtable' - } - -postFormat2TableP :: Parser PostFormat2Table -postFormat2TableP = do - numberOfGlyphs <- fromIntegral <$> Megaparsec.Binary.word16be - glyphNameIndex' <- Megaparsec.count numberOfGlyphs Megaparsec.Binary.word16be - rest <- Megaparsec.many pascalStringP - pure $ PostFormat2Table - { glyphNameIndex = Vector.fromList glyphNameIndex' - , names = Vector.fromList rest - } - -postFormat25TableP :: Parser (Vector Int8) -postFormat25TableP = Megaparsec.Binary.word16be - >>= fmap Vector.fromList - . flip Megaparsec.count Megaparsec.Binary.int8 - . fromIntegral - -postFormat4TableP :: Parser (Vector Word16) -postFormat4TableP = Vector.fromList - <$> Megaparsec.many Megaparsec.Binary.word16be - -postHeaderP :: Parser PostHeader -postHeaderP = PostHeader - <$> fixedP - <*> fixedP - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - --- * Font program table - -fpgmTableP :: Parser FpgmTable -fpgmTableP = FpgmTable - <$> vectorP Megaparsec.Binary.word8 - --- * Prep table - -prepTableP :: Parser PrepTable -prepTableP = PrepTable - <$> vectorP Megaparsec.Binary.word8 - --- * Horizontal device metrics table - -deviceRecordP :: Int -> Parser DeviceRecord -deviceRecordP size = do - pixelSize' <- Megaparsec.Binary.word8 - maximumWidth' <- Megaparsec.Binary.word8 - widths' <- vectorNP size Megaparsec.Binary.word8 - let paddingLength = 4 - ((Vector.length widths' + 2) `mod` 4) - Megaparsec.skipCount paddingLength - $ Megaparsec.chunk - $ ByteString.pack [0] - pure $ DeviceRecord - { pixelSize = pixelSize' - , maximumWidth = maximumWidth' - , widths = widths' - } - -hdmxTableP :: Parser HdmxTable -hdmxTableP = do - void $ Megaparsec.chunk $ ByteString.pack [0, 0] - numberOfDeviceRecords <- fromIntegral <$> Megaparsec.Binary.int16be - sizeOfDeviceRecord <- fromIntegral <$> Megaparsec.Binary.int32be - records' <- Megaparsec.count numberOfDeviceRecords - $ deviceRecordP sizeOfDeviceRecord - Megaparsec.eof - pure $ HdmxTable 0 records' - --- * Glyph outline table - -glyphDescriptionP :: Parser GlyphDescription -glyphDescriptionP = do - numberOfContours' <- fromIntegral - <$> Megaparsec.Binary.int16be - <?> "numberOfContours" - xMin' <- Megaparsec.Binary.int16be <?> "xMin" - yMin' <- Megaparsec.Binary.int16be <?> "yMin" - xMax' <- Megaparsec.Binary.int16be <?> "xMax" - yMax' <- Megaparsec.Binary.int16be <?> "yMax" - glyphDefinition <- - if numberOfContours' >= 0 - then SimpleGlyph <$> simpleGlyphDefinitionP numberOfContours' - else CompoundGlyph <$> compoundGlyphDefinitionP - pure $ GlyphDescription - { numberOfContours = numberOfContours' - , xMin = xMin' - , yMin = yMin' - , xMax = xMax' - , yMax = yMax' - , definition = glyphDefinition - } - -glyphInstructionsP :: Parser (Vector Word8) -glyphInstructionsP = Megaparsec.Binary.word16be - >>= flip vectorNP (Megaparsec.Binary.word8 <?> "compound glyph instruction") - . fromIntegral - -compoundGlyphDefinitionP :: Parser CompoundGlyphDefinition -compoundGlyphDefinitionP = do - components' <- componentGlyphPartDescriptionP mempty - let instructions' = - if Vector.any (weHaveInstructions . getField @"flags") components' - then glyphInstructionsP - else pure mempty - CompoundGlyphDefinition components' <$> instructions' - -componentGlyphPartDescriptionP - ::Vector ComponentGlyphPartDescription - -> Parser (Vector ComponentGlyphPartDescription) -componentGlyphPartDescriptionP accumulator = do - flags' <- Megaparsec.Binary.word16be <?> "flags" - glyphIndex' <- Megaparsec.Binary.word16be <?> "glyphIndex" - let arg1And2AreWords = testBit flags' 0 - argsAreXyValues = testBit flags' 1 - weHaveAScale = testBit flags' 3 - weHaveAnXAndYScale = testBit flags' 6 - weHaveATwoByTwo = testBit flags' 7 - argument1 <- glyphArgumentP arg1And2AreWords argsAreXyValues <?> "argument1" - argument2 <- glyphArgumentP arg1And2AreWords argsAreXyValues <?> "argument2" - transformationOption' <- transformationOptionP weHaveAScale weHaveAnXAndYScale weHaveATwoByTwo - <?> "transformation option" - - let updated = Vector.snoc accumulator $ ComponentGlyphPartDescription - { flags = ComponentGlyphFlags - { roundXyToGrid = testBit flags' 2 - , weHaveInstructions = testBit flags' 8 - , useMyMetrics = testBit flags' 9 - , overlapCompound = testBit flags' 10 - } - , glyphIndex = glyphIndex' - , argument1 = argument1 - , argument2 = argument2 - , transformationOption = transformationOption' - } - -- MORE_COMPONENTS. - if testBit flags' 5 then componentGlyphPartDescriptionP updated else pure updated - --- | Arguments are: WE_HAVE_A_SCALE, WE_HAVE_AN_X_AND_Y_SCALE and --- WE_HAVE_A_TWO_BY_TWO. -transformationOptionP :: Bool -> Bool -> Bool -> Parser GlyphTransformationOption -transformationOptionP True _ _ = GlyphScale <$> f2Dot14P <?> "scale" -transformationOptionP _ True _ = GlyphXyScale - <$> f2Dot14P - <*> f2Dot14P - <?> "xy-scale" -transformationOptionP _ _ True = Glyph2By2Scale - <$> f2Dot14P - <*> f2Dot14P - <*> f2Dot14P - <*> f2Dot14P - <?> "2 by 2 transformation" -transformationOptionP _ _ _ = pure GlyphNoScale - --- | Arguments are: ARG_1_AND_2_ARE_WORDS and ARGS_ARE_XY_VALUES. -glyphArgumentP :: Bool -> Bool -> Parser GlyphArgument -glyphArgumentP True True = GlyphInt16Argument - <$> Megaparsec.Binary.int16be - <?> "int16 argument" -glyphArgumentP True False = GlyphWord16Argument - <$> Megaparsec.Binary.word16be - <?> "uint16 argument" -glyphArgumentP False True = GlyphInt8Argument - <$> Megaparsec.Binary.int8 - <?> "int8 argument" -glyphArgumentP False False = GlyphWord8Argument - <$> Megaparsec.Binary.word8 - <?> "uint8 argument" - -simpleGlyphDefinitionP :: Int -> Parser SimpleGlyphDefinition -simpleGlyphDefinitionP numberOfContours' = do - endPtsOfContours' <- vectorNP numberOfContours' Megaparsec.Binary.word16be - <?> "endPtsOfContours" - let numberOfPoints = - if Vector.null endPtsOfContours' - then 0 - else fromIntegral $ Vector.last endPtsOfContours' - instructionLength <- fromIntegral - <$> Megaparsec.Binary.word16be - <?> "instructionLength" - instructions' <- vectorNP instructionLength - (Megaparsec.Binary.word8 <?> "simple glyph instruction") - flags' <- flagsP numberOfPoints mempty <?> "flags" - xs <- Vector.foldM (coordinatesP xFlagPair) mempty flags' - ys <- Vector.foldM (coordinatesP yFlagPair) mempty flags' - pure $ SimpleGlyphDefinition - { endPtsOfContours = endPtsOfContours' - , instructions = instructions' - , flags = flags' - , coordinates = mkCoordinate <$> Vector.zip xs ys - } - where - mkCoordinate (x, y) = GlyphCoordinate x y - xFlagPair :: OutlineFlag -> (Bool, Bool) - xFlagPair OutlineFlag{ xShortVector, thisXIsSame } = - (xShortVector, thisXIsSame) - yFlagPair :: OutlineFlag -> (Bool, Bool) - yFlagPair OutlineFlag{ yShortVector, thisYIsSame } = - (yShortVector, thisYIsSame) - coordinateP :: Bool -> Bool -> Parser Int16 - coordinateP True True = fromIntegral - <$> Megaparsec.Binary.word8 - <?> "1 byte long positive coordinate" - coordinateP True False = negate . fromIntegral - <$> Megaparsec.Binary.word8 - <?> "1 byte long negative coordinate" - coordinateP False False = Megaparsec.Binary.int16be - <?> "2 bytes long coordinate" - coordinateP False True = pure 0 - coordinatesP - :: (OutlineFlag -> (Bool, Bool)) - -> Vector Int16 - -> OutlineFlag - -> Parser (Vector Int16) - coordinatesP get accumulator first = - let parser = uncurry coordinateP $ get first - repeatN = succIntegral $ getField @"repeat" first - in (accumulator <>) <$> vectorNP repeatN parser - flagsP :: Int -> Vector OutlineFlag -> Parser (Vector OutlineFlag) - flagsP remaining accumulator - | remaining < 0 = pure accumulator - | otherwise = do - flag <- Megaparsec.Binary.word8 <?> "outline flags" - repeatN <- - if testBit flag 3 - then fromIntegral - <$> Megaparsec.Binary.word8 - <?> "flag repeat count" - else pure 0 - let flag' = OutlineFlag - { onCurve = testBit flag 0 - , xShortVector = testBit flag 1 - , yShortVector = testBit flag 2 - , repeat = fromIntegral repeatN - , thisXIsSame = testBit flag 4 - , thisYIsSame = testBit flag 5 - } - flagsP (remaining - repeatN - 1) - $ Vector.snoc accumulator flag' - -glyfTableP :: LocaTable -> Parser GlyfTable -glyfTableP locaTable - | locaTable' <- unLocaTable locaTable - , not $ Vector.null locaTable' = - let locaInit = Vector.init locaTable' - offsets = traverse go - $ nubBy duplicate - $ sortOn fst - $ filter filterNullLength - $ Vector.toList - $ Vector.zip locaInit - $ Vector.tail locaTable' - in GlyfTable - . Vector.generate (Vector.length locaInit) - . generateTable locaInit - . IntMap.fromList - <$> offsets - | otherwise = pure $ GlyfTable mempty - where - filterNullLength (x, y) = x /= y - duplicate x y = fst x == fst y - generateTable :: Vector Word32 -> IntMap GlyphDescription -> Int -> GlyphDescription - generateTable locaInit offsetMap index = - offsetMap IntMap.! fromIntegral (locaInit Vector.! index) - go (locaOffset, nextOffset) = do - startOffset <- Megaparsec.getOffset - result <- glyphDescriptionP - endOffset <- Megaparsec.getOffset - flip Megaparsec.skipCount Megaparsec.Binary.word8 - $ fromIntegral nextOffset - - fromIntegral locaOffset - - endOffset - + startOffset - pure (fromIntegral locaOffset, result) - --- * Character to glyph mapping table - -cmapTableP :: Parser CmapTable -cmapTableP = do - initialOffset <- Megaparsec.getOffset - version' <- Megaparsec.Binary.word16be - numberSubtables <- fromIntegral <$> Megaparsec.Binary.word16be - encodings' <- sortOn (getField @"offset") - <$> Megaparsec.count numberSubtables cmapHeaderP - parsedSubtables <- Megaparsec.some (subtableAtOffset initialOffset) - Megaparsec.eof - pure $ CmapTable - { version = version' - , encodings = encodings' - , subtables = IntMap.fromList parsedSubtables - } - where - subtableAtOffset initialOffset = do - currentOffset <- flip (-) initialOffset <$> Megaparsec.getOffset - parsedSubtable <- cmapFormatTableP - pure (currentOffset, parsedSubtable) - -cmapHeaderP :: Parser CmapEncoding -cmapHeaderP = CmapEncoding - <$> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word32be - -cmapFormatTableP :: Parser CmapSubtable -cmapFormatTableP = do - format' <- Megaparsec.Binary.word16be - case format' of - 0 -> CmapFormat0 <$> cmapFormat0TableP - 2 -> CmapFormat2 <$> cmapFormat2TableP - 4 -> CmapFormat4 <$> cmapFormat4TableP - 6 -> CmapFormat6 <$> cmapFormat6TableP - 8 -> CmapFormat8 <$> cmapFormat8TableP - 10 -> CmapFormat10 <$> cmapFormat10TableP - 12 -> CmapFormat12 <$> cmapFormat12TableP - 13 -> CmapFormat13 <$> cmapFormat13TableP - 14 -> CmapFormat14 <$> cmapFormat14TableP - _ -> fail $ "Unsupported format " <> show format' <> "." - -cmapFormat14TableP :: Parser CmapFormat14Table -cmapFormat14TableP = do - initialOffset <- (+ (-2)) <$> Megaparsec.getOffset - void Megaparsec.Binary.word32be -- Length. - numVarSelectorRecords <- fromIntegral <$> Megaparsec.Binary.word32be - variationSelectorRecords' <- sortBy sortOffset . fold - <$> Megaparsec.count numVarSelectorRecords variationSelectorRecordP - let parseByOffset' = parseByOffset initialOffset - CmapFormat14Table <$> foldM parseByOffset' IntMap.empty variationSelectorRecords' - where - parseByOffset - :: Int - -> VariationSelectorMap - -> UVSOffset Word32 Word32 - -> Parser VariationSelectorMap - parseByOffset _ accumulator uvsOffset' - | uvsOffset uvsOffset' == 0 = pure accumulator - parseByOffset tableOffset accumulator (DefaultUVSOffset varSelector' relativeOffset) - -- If the records at this offset were already parsed, look at any parsed - -- record and duplicate it updating the varSelector. The same logic - -- applies for the next pattern match, but for non-default UVS. - | Just member@(DefaultUVSOffset _ record :| _) <- - IntMap.lookup (fromIntegral relativeOffset) accumulator = - - let newRecord = DefaultUVSOffset varSelector' record NonEmpty.<| member - relativeOffset' = fromIntegral relativeOffset - in pure $ IntMap.insert relativeOffset' newRecord accumulator - | otherwise = do - currentOffset <- Megaparsec.getOffset - let emptyBytes = tableOffset + fromIntegral relativeOffset - currentOffset - relativeOffset' = fromIntegral relativeOffset - void $ Megaparsec.takeP Nothing emptyBytes - entryCount <- fromIntegral <$> Megaparsec.Binary.word32be - valueRanges <- vectorNP entryCount unicodeValueRangeP - pure $ IntMap.insert relativeOffset' (DefaultUVSOffset varSelector' valueRanges :| []) accumulator - parseByOffset tableOffset accumulator (NonDefaultUVSOffset varSelector' relativeOffset) - | Just member@(NonDefaultUVSOffset _ record :| _) <- - IntMap.lookup (fromIntegral relativeOffset) accumulator = - - let newRecord = NonDefaultUVSOffset varSelector' record NonEmpty.<| member - relativeOffset' = fromIntegral relativeOffset - in pure $ IntMap.insert relativeOffset' newRecord accumulator - | otherwise = do - currentOffset <- Megaparsec.getOffset - let emptyBytes = tableOffset + fromIntegral relativeOffset - currentOffset - void $ Megaparsec.takeP Nothing emptyBytes - entryCount <- fromIntegral <$> Megaparsec.Binary.word32be - flip (IntMap.insert $ fromIntegral relativeOffset) accumulator - . pure - . NonDefaultUVSOffset varSelector' - <$> vectorNP entryCount uvsMappingP - sortOffset x y = compare (uvsOffset x) (uvsOffset y) - -uvsOffset :: forall a. UVSOffset a a -> a -uvsOffset (DefaultUVSOffset _ x) = x -uvsOffset (NonDefaultUVSOffset _ x) = x - -variationSelectorRecordP :: Parser [UVSOffset Word32 Word32] -variationSelectorRecordP = do - varSelector' <- word24P - defaultUVSOffset' <- Megaparsec.Binary.word32be - nonDefaultUVSOffset' <- Megaparsec.Binary.word32be - - pure - [ DefaultUVSOffset varSelector' defaultUVSOffset' - , NonDefaultUVSOffset varSelector' nonDefaultUVSOffset' - ] - -uvsMappingP :: Parser UVSMapping -uvsMappingP = UVSMapping - <$> word24P - <*> Megaparsec.Binary.word16be - -unicodeValueRangeP :: Parser UnicodeValueRange -unicodeValueRangeP = UnicodeValueRange - <$> word24P - <*> Megaparsec.Binary.word8 - -cmapFormat13TableP :: Parser CmapFormat13Table -cmapFormat13TableP = cmapFormat12TableP - -cmapFormat12TableP :: Parser CmapFormat12Table -cmapFormat12TableP = do - void $ Megaparsec.takeP Nothing 6 -- Reserved and length. - language' <- Megaparsec.Binary.word32be - nGroups <- fromIntegral <$> Megaparsec.Binary.word32be - groups' <- vectorNP nGroups cmapGroupP - - pure $ CmapFormat12Table - { language = language' - , groups = groups' - } - -cmapFormat10TableP :: Parser CmapFormat10Table -cmapFormat10TableP = do - void $ Megaparsec.takeP Nothing 2 -- Reserved. - length' <- fromIntegral <$> Megaparsec.Binary.word32be - language' <- Megaparsec.Binary.word32be - startCharCode' <- Megaparsec.Binary.word32be - numChars' <- Megaparsec.Binary.word32be - let remainingLength = div (length' - 24) 2 - glyphs' <- vectorNP remainingLength Megaparsec.Binary.word16be - - pure $ CmapFormat10Table - { language = language' - , startCharCode = startCharCode' - , numChars = numChars' - , glyphs = glyphs' - } - -cmapFormat8TableP :: Parser CmapFormat8Table -cmapFormat8TableP = do - void $ Megaparsec.takeP Nothing 6 -- Reserved and length. - language' <- Megaparsec.Binary.word32be - is32' <- Megaparsec.takeP Nothing 65536 - nGroups <- fromIntegral <$> Megaparsec.Binary.word32be - groups' <- vectorNP nGroups cmapGroupP - - pure $ CmapFormat8Table - { language = language' - , is32 = ByteString.unpack is32' - , groups = groups' - } - -cmapGroupP :: Parser CmapGroup -cmapGroupP = CmapGroup - <$> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - -cmapFormat6TableP :: Parser CmapFormat6Table -cmapFormat6TableP = do - void Megaparsec.Binary.word16be -- Length. - language' <- Megaparsec.Binary.word16be - firstCode' <- Megaparsec.Binary.word16be - entryCount' <- fromIntegral <$> Megaparsec.Binary.word16be - glyphIndexArray' <- vectorNP entryCount' Megaparsec.Binary.word16be - - pure $ CmapFormat6Table - { language = language' - , firstCode = firstCode' - , glyphIndexArray = glyphIndexArray' - } - -cmapFormat4TableP :: Parser CmapFormat4Table -cmapFormat4TableP = do - length' <- fromIntegral <$> Megaparsec.Binary.word16be - language' <- Megaparsec.Binary.word16be - segCount <- fromIntegral . (`div` 2) <$> Megaparsec.Binary.word16be - searchRange' <- Megaparsec.Binary.word16be - entrySelector' <- Megaparsec.Binary.word16be - rangeShift' <- Megaparsec.Binary.word16be - endCode' <- vectorNP segCount Megaparsec.Binary.word16be - void $ Megaparsec.chunk $ ByteString.pack [0, 0] -- reservedPad 0. - startCode' <- vectorNP segCount Megaparsec.Binary.word16be - idDelta' <- vectorNP segCount Megaparsec.Binary.word16be - idRangeOffset' <- vectorNP segCount Megaparsec.Binary.word16be - let glyphIndexLength = div (length' - 16 - segCount * 8) 2 - glyphIndexArray' <- vectorNP glyphIndexLength Megaparsec.Binary.word16be - - pure $ CmapFormat4Table - { language = language' - , searchRange = searchRange' - , entrySelector = entrySelector' - , rangeShift = rangeShift' - , endCode = endCode' - , startCode = startCode' - , idDelta = idDelta' - , idRangeOffset = idRangeOffset' - , glyphIndexArray = glyphIndexArray' - } - -cmapFormat2TableP :: Parser CmapFormat2Table -cmapFormat2TableP = do - length' <- fromIntegral <$> Megaparsec.Binary.word16be - language' <- Megaparsec.Binary.word16be - subHeaderKeys' <- vectorNP 256 Megaparsec.Binary.word16be - let maxIndex = succIntegral $ Vector.maximum $ fmap (`div` 8) subHeaderKeys' - subHeaders' <- vectorNP maxIndex cmapFormat2SubheaderP - let glyphIndexLength = div (length' - 518 - maxIndex * 8) 2 - glyphIndexArray' <- vectorNP glyphIndexLength Megaparsec.Binary.word16be - - pure $ CmapFormat2Table - { language = language' - , subHeaderKeys = subHeaderKeys' - , subHeaders = subHeaders' - , glyphIndexArray = glyphIndexArray' - } - -cmapFormat2SubheaderP :: Parser CmapFormat2Subheader -cmapFormat2SubheaderP = CmapFormat2Subheader - <$> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word16be - -cmapFormat0TableP :: Parser CmapFormat0Table -cmapFormat0TableP = CmapFormat0Table - <$> Megaparsec.Binary.word16be - <* Megaparsec.Binary.word16be - <*> vectorNP 256 Megaparsec.Binary.word8 - --- * Generic parsing utilities - -word24P :: Parser Word32 -word24P = foldr unstep 0 . ByteString.unpack - <$> Megaparsec.takeP (Just "word24") 3 - where - unstep b a = a `shiftL` 8 .|. fromIntegral b - -f2Dot14P :: Parser F2Dot14 -f2Dot14P = F2Dot14 <$> Megaparsec.Binary.int16be - -vectorP :: forall a. Parser a -> Parser (Vector a) -vectorP = fmap Vector.fromList . Megaparsec.many - -vectorNP :: forall a. Int -> Parser a -> Parser (Vector a) -vectorNP size = fmap Vector.fromList . Megaparsec.count size - -pascalStringP :: Parser ByteString -pascalStringP = Megaparsec.Binary.word8 - >>= fmap ByteString.pack - . flip Megaparsec.count Megaparsec.Binary.word8 - . fromIntegral - -countP :: forall a. Int -> Parser a -> Parser (NonEmpty a) -countP number parser - = (:|) - <$> parser - <*> Megaparsec.count (number - 1) parser - -longDateTimeP :: Parser LocalTime -longDateTimeP = go <$> Megaparsec.Binary.int64be - where - go totalSeconds = - let (totalDays, secondsOfDay) = totalSeconds `divMod` (3600 * 24) - in LocalTime - { localDay = addDays (fromIntegral totalDays) ttfEpoch - , localTimeOfDay = timeToTimeOfDay - $ secondsToDiffTime - $ fromIntegral secondsOfDay - } - -fixedP :: Parser Fixed32 -fixedP = Fixed32 . fromIntegral <$> Megaparsec.Binary.word32be - -parseTable - :: TableDirectory - -> Parser a - -> Megaparsec.State ByteString Void - -> Either ParseErrorBundle a -parseTable TableDirectory{ offset, length = length' } parser state = snd - $ Megaparsec.runParser' parser - $ state - { Megaparsec.stateInput = stateInput - , Megaparsec.stateOffset = stateOffset - , Megaparsec.statePosState = posState - { Megaparsec.pstateInput = stateInput - , Megaparsec.pstateOffset = stateOffset - } - } - where - posState = Megaparsec.statePosState state - stateInput = ByteString.take length' - $ ByteString.drop (offset - Megaparsec.stateOffset state) - $ Megaparsec.stateInput state - stateOffset = offset - --- * OS/2 table - -os2TableP :: Parser Os2Table -os2TableP = do - baseFields <- os2BaseFieldsP - result <- case getField @"version" baseFields of - 0 -> Os2Version0 baseFields - <$> Megaparsec.optional os2MicrosoftFieldsP - 1 -> Os2Version1 baseFields - <$> os2MicrosoftFieldsP - <*> os2Version1FieldsP - 2 -> Os2Version2 baseFields - <$> os2MicrosoftFieldsP - <*> os2Version4FieldsP - 3 -> Os2Version3 baseFields - <$> os2MicrosoftFieldsP - <*> os2Version4FieldsP - 4 -> Os2Version4 baseFields - <$> os2MicrosoftFieldsP - <*> os2Version4FieldsP - 5 -> Os2Version5 baseFields - <$> os2MicrosoftFieldsP - <*> os2Version5FieldsP - unsupportedVersion -> fail - $ "Unsupported OS/2 version: " <> show unsupportedVersion - Megaparsec.eof - pure result - -os2BaseFieldsP :: Parser Os2BaseFields -os2BaseFieldsP = Os2BaseFields - <$> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> panoseP - <*> vectorNP 4 Megaparsec.Binary.word32be - <*> vectorNP 4 Megaparsec.Binary.int8 - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - -os2MicrosoftFieldsP :: Parser Os2MicrosoftFields -os2MicrosoftFieldsP = Os2MicrosoftFields - <$> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - -os2Version1FieldsP :: Parser Os2Version1Fields -os2Version1FieldsP = Os2Version1Fields - <$> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - -os2Version4FieldsP :: Parser Os2Version4Fields -os2Version4FieldsP = Os2Version4Fields - <$> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - -os2Version5FieldsP :: Parser Os2Version5Fields -os2Version5FieldsP = Os2Version5Fields - <$> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.word32be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.int16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be - -panoseP :: Parser Panose -panoseP = Panose - <$> bFamilyTypeP - <*> bSerifStyleP - <*> bWeightP - <*> bProportionP - <*> bContrastP - <*> bStrokeVariationP - <*> bArmStyleP - <*> bLetterformP - <*> bMidlineP - <*> bXHeightP - -bFamilyTypeP :: Parser BFamilyType -bFamilyTypeP - = (Megaparsec.single 0 $> AnyFamilyType) - <|> (Megaparsec.single 1 $> NoFitFamilyType) - <|> (Megaparsec.single 2 $> TextAndDisplayFamilyType) - <|> (Megaparsec.single 3 $> ScriptFamilyType) - <|> (Megaparsec.single 4 $> DecorativeFamilyType) - <|> (Megaparsec.single 5 $> PictorialFamilyType) - <?> "bFamilyType" - -bSerifStyleP :: Parser BSerifStyle -bSerifStyleP - = (Megaparsec.single 0 $> AnySerifStyle) - <|> (Megaparsec.single 1 $> NoFitSerifStyle) - <|> (Megaparsec.single 2 $> CoveSerifStyle) - <|> (Megaparsec.single 3 $> ObtuseCoveSerifStyle) - <|> (Megaparsec.single 4 $> SquareCoveSerifStyle) - <|> (Megaparsec.single 5 $> ObtuseSquareCoveSerifStyle) - <|> (Megaparsec.single 6 $> SquareSerifStyle) - <|> (Megaparsec.single 7 $> ThinSerifStyle) - <|> (Megaparsec.single 8 $> BoneSerifStyle) - <|> (Megaparsec.single 9 $> ExaggeratedSerifStyle) - <|> (Megaparsec.single 10 $> TriangleSerifStyle) - <|> (Megaparsec.single 11 $> NormalSansSerifStyle) - <|> (Megaparsec.single 12 $> ObtuseSansSerifStyle) - <|> (Megaparsec.single 13 $> PerpSansSerifStyle) - <|> (Megaparsec.single 14 $> FlaredSerifStyle) - <|> (Megaparsec.single 15 $> RoundedSerifStyle) - <?> "bSerifStyle" - -bWeightP :: Parser BWeight -bWeightP - = (Megaparsec.single 0 $> AnyWeight) - <|> (Megaparsec.single 1 $> NoFitWeight) - <|> (Megaparsec.single 2 $> VeryLightWeight) - <|> (Megaparsec.single 3 $> LightWeight) - <|> (Megaparsec.single 4 $> ThinWeight) - <|> (Megaparsec.single 5 $> BookWeight) - <|> (Megaparsec.single 6 $> MediumWeight) - <|> (Megaparsec.single 7 $> DemiWeight) - <|> (Megaparsec.single 8 $> BoldWeight) - <|> (Megaparsec.single 9 $> HeavyWeight) - <|> (Megaparsec.single 10 $> BlackWeight) - <|> (Megaparsec.single 11 $> NordWeight) - <?> "bWeight" - -bProportionP :: Parser BProportion -bProportionP - = (Megaparsec.single 0 $> AnyProportion) - <|> (Megaparsec.single 1 $> NoFitProportion) - <|> (Megaparsec.single 2 $> OldStyleProportion) - <|> (Megaparsec.single 3 $> ModernProportion) - <|> (Megaparsec.single 4 $> EvenWidthProportion) - <|> (Megaparsec.single 5 $> ExpandedProportion) - <|> (Megaparsec.single 6 $> CondensedProportion) - <|> (Megaparsec.single 7 $> VeryExpandedProportion) - <|> (Megaparsec.single 8 $> VeryCondensedProportion) - <|> (Megaparsec.single 9 $> MonospacedProportion) - <?> "bProportion" - -bContrastP :: Parser BContrast -bContrastP - = (Megaparsec.single 0 $> AnyContrast) - <|> (Megaparsec.single 1 $> NoFitContrast) - <|> (Megaparsec.single 2 $> NoneContrast) - <|> (Megaparsec.single 3 $> VeryLowContrast) - <|> (Megaparsec.single 4 $> LowContrast) - <|> (Megaparsec.single 5 $> MediumLowContrast) - <|> (Megaparsec.single 6 $> MediumContrast) - <|> (Megaparsec.single 7 $> MediumHighContrast) - <|> (Megaparsec.single 8 $> HighContrast) - <|> (Megaparsec.single 9 $> VeryHighContrast) - <?> "bContrast" - -bStrokeVariationP :: Parser BStrokeVariation -bStrokeVariationP - = (Megaparsec.single 0 $> AnyStrokeVariation) - <|> (Megaparsec.single 1 $> NoFitStrokeVariation) - <|> (Megaparsec.single 2 $> GradualDiagonalStrokeVariation) - <|> (Megaparsec.single 3 $> GradualTransitionalStrokeVariation) - <|> (Megaparsec.single 4 $> GradualVerticalStrokeVariation) - <|> (Megaparsec.single 5 $> GradualHorizontalStrokeVariation) - <|> (Megaparsec.single 6 $> RapidVerticalStrokeVariation) - <|> (Megaparsec.single 7 $> RapidHorizontalStrokeVariation) - <|> (Megaparsec.single 8 $> InstantVerticalStrokeVariation) - <?> "bStrokeVariation" - -bArmStyleP :: Parser BArmStyle -bArmStyleP - = (Megaparsec.single 0 $> AnyArmStyle) - <|> (Megaparsec.single 1 $> NoFitArmStyle) - <|> (Megaparsec.single 2 $> StraightArmsHorizontalArmStyle) - <|> (Megaparsec.single 3 $> StraightArmsWedgeArmStyle) - <|> (Megaparsec.single 4 $> StraightArmsVerticalArmStyle) - <|> (Megaparsec.single 5 $> StraightArmsSingleSerifArmStyle) - <|> (Megaparsec.single 6 $> StraightArmsDoubleSerifArmStyle) - <|> (Megaparsec.single 7 $> NonStraightArmsHorizontalArmStyle) - <|> (Megaparsec.single 8 $> NonStraightArmsWedgeArmStyle) - <|> (Megaparsec.single 9 $> NonStraightArmsVerticalArmStyle) - <|> (Megaparsec.single 10 $> NonStraightArmsSingleSerifArmStyle) - <|> (Megaparsec.single 11 $> NonStraightArmsDoubleSerifArmStyle) - <?> "bArmStyle" - -bLetterformP :: Parser BLetterform -bLetterformP - = (Megaparsec.single 0 $> AnyLetterform) - <|> (Megaparsec.single 1 $> NoFitLetterform) - <|> (Megaparsec.single 2 $> NormalContactLetterform) - <|> (Megaparsec.single 3 $> NormalWeightedLetterform) - <|> (Megaparsec.single 4 $> NormalBoxedLetterform) - <|> (Megaparsec.single 5 $> NormalFlattenedLetterform) - <|> (Megaparsec.single 6 $> NormalRoundedLetterform) - <|> (Megaparsec.single 7 $> NormalOffCenterLetterform) - <|> (Megaparsec.single 8 $> NormalSquareLetterform) - <|> (Megaparsec.single 9 $> ObliqueContactLetterform) - <|> (Megaparsec.single 10 $> ObliqueWeightedLetterform) - <|> (Megaparsec.single 11 $> ObliqueBoxedLetterform) - <|> (Megaparsec.single 12 $> ObliqueFlattenedLetterform) - <|> (Megaparsec.single 13 $> ObliqueRoundedLetterform) - <|> (Megaparsec.single 14 $> ObliqueOffCenterLetterform) - <|> (Megaparsec.single 15 $> ObliqueSquareLetterform) - <?> "bLetterform" - -bXHeightP :: Parser BXHeight -bXHeightP - = (Megaparsec.single 0 $> AnyXHeight) - <|> (Megaparsec.single 1 $> NoFitXHeight) - <|> (Megaparsec.single 2 $> ConstantSmallXHeight) - <|> (Megaparsec.single 3 $> ConstantStandardXHeight) - <|> (Megaparsec.single 4 $> ConstantLargeXHeight) - <|> (Megaparsec.single 5 $> DuckingSmallXHeight) - <|> (Megaparsec.single 6 $> DuckingStandardXHeight) - <|> (Megaparsec.single 7 $> DuckingLargeXHeight) - <?> "bXHeight" - -bMidlineP :: Parser BMidline -bMidlineP - = (Megaparsec.single 0 $> AnyMidline) - <|> (Megaparsec.single 1 $> NoFitMidline) - <|> (Megaparsec.single 2 $> StandardTrimmedMidline) - <|> (Megaparsec.single 3 $> StandardPointedMidline) - <|> (Megaparsec.single 4 $> StandardSerifedMidline) - <|> (Megaparsec.single 5 $> HighTrimmedMidline) - <|> (Megaparsec.single 6 $> HighPointedMidline) - <|> (Megaparsec.single 7 $> HighSerifedMidline) - <|> (Megaparsec.single 8 $> ConstantTrimmedMidline) - <|> (Megaparsec.single 9 $> ConstantPointedMidline) - <|> (Megaparsec.single 10 $> ConstantSerifedMidline) - <|> (Megaparsec.single 11 $> LowTrimmedMidline) - <|> (Megaparsec.single 12 $> LowPointedMidline) - <|> (Megaparsec.single 13 $> LowSerifedMidline) - <?> "bMidline" - --- * Grid-fitting And Scan-conversion Procedure. - -gaspTableP :: Parser GASPTable -gaspTableP = do - version' <- Megaparsec.Binary.word16be - numberRanges <- fromIntegral <$> Megaparsec.Binary.word16be - parsedRanges <- Megaparsec.count numberRanges gaspRangeP - Megaparsec.eof - pure $ GASPTable - { version = version' - , gaspRange = parsedRanges - } - where - gaspRangeP = GASPRange - <$> Megaparsec.Binary.word16be - <*> Megaparsec.Binary.word16be |
