~jaro/balkon

3c9b4c5f866e6262c642e0ff9167adf42f837b83 — Jaro 1 year, 7 months ago a46ca7a
Integrate BiDi reordering.
M CHANGELOG.md => CHANGELOG.md +13 -0
@@ 2,6 2,19 @@

## 0.3.0.0 -- TBD

* Limited support for bidirectional text (LTR and RTL in the same paragraph).

    * The paragraph direction is assumed to be LTR.
      This will be configurable in a future interface.

    * Only strong directional characters are used to determine text direction.

    * The direction of weak directional characters and neutral characters is
      determined by the nearest preceding strong directional character, or if
      none is found, the nearest following strong directional character.

    * Explicit bidirectional formatting characters are ignored.

## 0.2.1.0 -- 2023-04-04

* Added pagination.

M src/Data/Text/ParagraphLayout/Internal/Plain.hs => src/Data/Text/ParagraphLayout/Internal/Plain.hs +2 -17
@@ 12,7 12,6 @@ import Data.Text.Foreign (lengthWord8)
import Data.Text.Glyphize
    ( Buffer (..)
    , ContentType (ContentTypeUnicode)
    , Direction (..)
    , FontExtents (..)
    , GlyphInfo
    , GlyphPos


@@ 25,6 24,7 @@ import qualified Data.Text.ICU as BreakStatus (Line (Hard))
import Data.Text.Internal (Text (Text))
import qualified Data.Text.Lazy as Lazy

import Data.Text.ParagraphLayout.Internal.BiDiReorder
import Data.Text.ParagraphLayout.Internal.Break
import Data.Text.ParagraphLayout.Internal.Fragment
import Data.Text.ParagraphLayout.Internal.LineHeight


@@ 70,28 70,13 @@ layoutAndAlignLines maxWidth runs = frags
    where
        frags = concatMap NonEmpty.toList fragsInLines
        (_, fragsInLines) = mapAccumL positionLineH originY canonicalLines
        canonicalLines = fmap canonicalOrder logicalLines
        canonicalLines = fmap reorder logicalLines
        logicalLines = nonEmptyItems $ layoutLines maxWidth runs
        originY = paragraphOriginY

nonEmptyItems :: Foldable t => t [a] -> [NonEmpty a]
nonEmptyItems = catMaybes . map nonEmpty . toList

-- | Reorder the given fragments from logical order to whatever order HarfBuzz
-- uses (LTR for horizontal text, TTB for vertical text), so that cluster order
-- is preserved even across runs.
canonicalOrder :: NonEmpty (WithSpan PF.ProtoFragment) ->
    NonEmpty (WithSpan PF.ProtoFragment)
canonicalOrder pfs@((WithSpan _ headPF) :| _) = case PF.direction headPF of
    -- TODO: Use BiDi levels to control reversing.
    Just DirLTR -> pfs
    Just DirRTL -> NonEmpty.reverse pfs
    Just DirTTB -> pfs
    Just DirBTT -> NonEmpty.reverse pfs
    -- If no guess can be made, use LTR.
    -- TODO: Add explicit direction to input interface.
    Nothing -> pfs

-- | Create a multi-line layout from the given runs, splitting them as
-- necessary to fit within the requested line width.
--

M src/Data/Text/ParagraphLayout/Internal/ProtoFragment.hs => src/Data/Text/ParagraphLayout/Internal/ProtoFragment.hs +13 -1
@@ 5,7 5,9 @@ module Data.Text.ParagraphLayout.Internal.ProtoFragment
where

import Data.Int (Int32)
import Data.Text.Glyphize (Direction, GlyphInfo, GlyphPos (x_advance))
import Data.Text.Glyphize (Direction (..), GlyphInfo, GlyphPos (x_advance))

import Data.Text.ParagraphLayout.Internal.BiDiReorder

-- | A box fragment which has not been positioned yet.
data ProtoFragment = ProtoFragment


@@ 23,3 25,13 @@ protoFragmentH :: Maybe Direction -> [(GlyphInfo, GlyphPos)] -> ProtoFragment
protoFragmentH dir gs = ProtoFragment dir adv gs
    where
        adv = sum $ map (x_advance . snd) gs

instance WithLevel ProtoFragment where
    level pf = case direction pf of
        -- TODO: Allow externally set paragraph embedding level.
        -- TODO: Properly calculate BiDi levels.
        Just DirLTR -> 0
        Just DirRTL -> 1
        Just DirTTB -> 0
        Just DirBTT -> 1
        Nothing -> 0

M src/Data/Text/ParagraphLayout/Internal/ResolvedSpan.hs => src/Data/Text/ParagraphLayout/Internal/ResolvedSpan.hs +4 -0
@@ 9,6 9,7 @@ import Data.Text (Text)
import Data.Text.Glyphize (Font)
import qualified Data.Text.ICU as BreakStatus (Line)

import Data.Text.ParagraphLayout.Internal.BiDiReorder
import Data.Text.ParagraphLayout.Internal.LineHeight
import Data.Text.ParagraphLayout.Internal.TextContainer



@@ 48,6 49,9 @@ instance SeparableTextContainer a => SeparableTextContainer (WithSpan a) where
    dropWhileStart p (WithSpan rs c) = WithSpan rs (dropWhileStart p c)
    dropWhileEnd p (WithSpan rs c) = WithSpan rs (dropWhileEnd p c)

instance WithLevel a => WithLevel (WithSpan a) where
    level (WithSpan _ x) = level x

splitBySpanIndex :: [WithSpan a] -> [[a]]
splitBySpanIndex xs = [getBySpanIndex i xs | i <- [0 ..]]