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 = Just 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 = Just 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 = Just DirLTR
, runScript = Just "Latn"
}
, Run
{ runOffsetInSpan = 12
, runText = pack "Википедија)"
, runLevel = 0
, runDirection = Just 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 = Just DirLTR
, runScript = Just "Latn"
}
, Run
{ runOffsetInSpan = 7
, runText = pack "مصر"
, runLevel = 1
, runDirection = Just DirRTL
, runScript = Just "Arab"
}
, Run
{ runOffsetInSpan = 13
, runText = pack "kuwait"
, runLevel = 0
, runDirection = Just 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 = Just DirLTR
, runScript = Just "Latn"
}
, Run
{ runOffsetInSpan = 7
, runText = pack "مصر"
, runLevel = 1
, runDirection = Just DirRTL
, runScript = Just "Arab"
}
, Run
{ runOffsetInSpan = 13
, runText = pack "kuwait"
, runLevel = 2
, runDirection = Just 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 = Just DirRTL
, runScript = Just "Arab"
}
, Run
{ runOffsetInSpan = 5
, runText = pack "XHTML 1.0"
, runLevel = 2
, runDirection = Just DirLTR
, runScript = Just "Latn"
}
, Run
{ runOffsetInSpan = 14
, runText = pack " يتم تحقيق ذلك بإضافة العنصر المضمن "
, runLevel = 1
, runDirection = Just DirRTL
, runScript = Just "Arab"
}
, Run
{ runOffsetInSpan = 79
, runText = pack "bdo"
, runLevel = 2
, runDirection = Just DirLTR
, runScript = Just "Latn"
}
, Run
{ runOffsetInSpan = 82
, runText = pack "."
, runLevel = 1
, runDirection = Just 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 = Just DirLTR
, runScript = Just "Latn"
}
, Run
{ runOffsetInSpan = 13
, runText = pack "مفتاح معايير الويب"
, runLevel = 1
, runDirection = Just DirRTL
, runScript = Just "Arab"
}
, Run
{ runOffsetInSpan = 47
, runText = pack " in Arabic."
, runLevel = 0
, runDirection = Just 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 = Just DirRTL
, runScript = Just "Latn"
}
-- direction change
, Run
{ runOffsetInSpan = 4
, runText = pack "pedi"
, runLevel = 2
, runDirection = Just DirLTR
, runScript = Just "Latn"
}
-- direction change
, Run
{ runOffsetInSpan = 8
, runText = pack "ja ("
, runLevel = 1
, runDirection = Just DirRTL
, runScript = Just "Latn"
}
-- script change
, Run
{ runOffsetInSpan = 12
, runText = pack "Вики"
, runLevel = 1
, runDirection = Just DirRTL
, runScript = Just "Cyrl"
}
-- direction change
, Run
{ runOffsetInSpan = 20
, runText = pack "педи"
, runLevel = 2
, runDirection = Just DirLTR
, runScript = Just "Cyrl"
}
-- direction change
, Run
{ runOffsetInSpan = 28
, runText = pack "ја)"
, runLevel = 1
, runDirection = Just DirRTL
, runScript = Just "Cyrl"
}
]