packages feed

balkon-1.1.0.0: test/Data/Text/ParagraphLayout/Internal/RunSpec.hs

module Data.Text.ParagraphLayout.Internal.RunSpec (spec) where

import Data.Text (Text, pack)
import Data.Text.Glyphize (Direction (..), emptyFont)

import Test.Hspec
import Data.Text.ParagraphLayout.Internal.BiDiLevels
import Data.Text.ParagraphLayout.Internal.BoxOptions
import Data.Text.ParagraphLayout.Internal.LineHeight
import Data.Text.ParagraphLayout.Internal.ResolvedBox
import Data.Text.ParagraphLayout.Internal.ResolvedSpan
import Data.Text.ParagraphLayout.Internal.Run
import Data.Text.ParagraphLayout.Internal.TextOptions
import Data.Text.ParagraphLayout.RunLengthEncoding
import Data.Text.ParagraphLayout.TextData

defaultBox :: Direction -> ResolvedBox ()
defaultBox dir = ResolvedBox () 0 defaultBoxOptions dir

sampleSpan :: (Direction, String, Text, a) -> TextLevels -> ResolvedSpan ()
sampleSpan (dir, lang, text, _) levels = ResolvedSpan
    { spanUserData = ()
    , spanIndex = 0
    , spanOffsetInParagraph = 0
    , spanText = text
    , spanTextOptions = (defaultTextOptions dir)
        { textFont = emptyFont
        , textLineHeight = Normal
        , textLanguage = lang
        }
    , spanBoxes = [defaultBox dir]
    , spanBiDiLevels = levels
    , spanLineBreaks = []
    , spanCharacterBreaks = []
    }

allLTR :: TextLevels
allLTR = TextLevels $ repeat 0

allRTL :: TextLevels
allRTL = TextLevels $ repeat 1

levelsRLE :: [(Int, Level)] -> TextLevels
levelsRLE rls = TextLevels $ runLengthDecode rls

spec :: Spec
spec = do
    describe "spanToRuns" $ do
        it "handles span with no text" $ do
            let inputSpan = sampleSpan englishEmpty allLTR
            let runs = spanToRuns inputSpan
            runs `shouldBe` []
        it "handles Czech hello" $ do
            let inputSpan = sampleSpan czechHello allLTR
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    { runOffsetInSpan = 0
                    , runText = spanText inputSpan
                    , runLevel = 0
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                ]
        it "handles Arabic hello" $ do
            let inputSpan = sampleSpan arabicHello allRTL
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    { runOffsetInSpan = 0
                    , runText = spanText inputSpan
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Arab"
                    }
                ]
        it "handles Serbian with mixed script" $ do
            let inputSpan = sampleSpan serbianMixedScript allLTR
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    -- TODO: We might want both parentheses in the same run.
                    { runOffsetInSpan = 0
                    , runText = pack "Vikipedija ("
                    , runLevel = 0
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                , Run
                    { runOffsetInSpan = 12
                    , runText = pack "Википедија)"
                    , runLevel = 0
                    , runDirection = DirLTR
                    , runScript = Just "Cyrl"
                    }
                ]
        it "handles mixed direction with base LTR" $ do
            let levels = levelsRLE [(7, 0), (3, 1), (6, 0)]
            let inputSpan = sampleSpan (mixedDirectionSimple DirLTR) levels
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    { runOffsetInSpan = 0
                    , runText = pack "bahrain"
                    , runLevel = 0
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                , Run
                    { runOffsetInSpan = 7
                    , runText = pack "مصر"
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Arab"
                    }
                , Run
                    { runOffsetInSpan = 13
                    , runText = pack "kuwait"
                    , runLevel = 0
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                ]
        it "handles mixed direction with base RTL" $ do
            let levels = levelsRLE [(7, 2), (3, 1), (6, 2)]
            let inputSpan = sampleSpan (mixedDirectionSimple DirRTL) levels
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    { runOffsetInSpan = 0
                    , runText = pack "bahrain"
                    , runLevel = 2
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                , Run
                    { runOffsetInSpan = 7
                    , runText = pack "مصر"
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Arab"
                    }
                , Run
                    { runOffsetInSpan = 13
                    , runText = pack "kuwait"
                    , runLevel = 2
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                ]
        it "handles Arabic text with English inside" $ do
            let levels = levelsRLE [(3, 1), (9, 2), (36, 1), (3, 2), (1, 1)]
            let inputSpan = sampleSpan arabicAroundEnglish levels
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    { runOffsetInSpan = 0
                    , runText = pack "في "
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Arab"
                    }
                , Run
                    { runOffsetInSpan = 5
                    , runText = pack "XHTML 1.0"
                    , runLevel = 2
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                , Run
                    { runOffsetInSpan = 14
                    , runText = pack " يتم تحقيق ذلك بإضافة العنصر المضمن "
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Arab"
                    }
                , Run
                    { runOffsetInSpan = 79
                    , runText = pack "bdo"
                    , runLevel = 2
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                , Run
                    { runOffsetInSpan = 82
                    , runText = pack "."
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Zyyy"
                    }
                ]
        it "handles English text with Arabic inside" $ do
            let levels = levelsRLE [(13, 0), (18, 1), (11, 0)]
            let inputSpan = sampleSpan englishAroundArabic levels
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    { runOffsetInSpan = 0
                    , runText = pack "The title is "
                    , runLevel = 0
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                , Run
                    { runOffsetInSpan = 13
                    , runText = pack "مفتاح معايير الويب"
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Arab"
                    }
                , Run
                    { runOffsetInSpan = 47
                    , runText = pack " in Arabic."
                    , runLevel = 0
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                ]
        -- Unrealistic example where text changes direction
        -- without changing script.
        it "handles script and direction changes" $ do
            let levels = levelsRLE [(4, 1), (4, 2), (8, 1), (4, 2), (3, 1)]
            let inputSpan = sampleSpan serbianMixedScript levels
            let runs = spanToRuns inputSpan
            runs `shouldBe`
                [ Run
                    { runOffsetInSpan = 0
                    , runText = pack "Viki"
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Latn"
                    }
                -- direction change
                , Run
                    { runOffsetInSpan = 4
                    , runText = pack "pedi"
                    , runLevel = 2
                    , runDirection = DirLTR
                    , runScript = Just "Latn"
                    }
                -- direction change
                , Run
                    { runOffsetInSpan = 8
                    , runText = pack "ja ("
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Latn"
                    }
                -- script change
                , Run
                    { runOffsetInSpan = 12
                    , runText = pack "Вики"
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Cyrl"
                    }
                -- direction change
                , Run
                    { runOffsetInSpan = 20
                    , runText = pack "педи"
                    , runLevel = 2
                    , runDirection = DirLTR
                    , runScript = Just "Cyrl"
                    }
                -- direction change
                , Run
                    { runOffsetInSpan = 28
                    , runText = pack "ја)"
                    , runLevel = 1
                    , runDirection = DirRTL
                    , runScript = Just "Cyrl"
                    }
                ]