~jaro/balkon

0cf536ef34fcaf0c4f02e9dd4dc47523a47f631f — Jaro 10 months ago d8540c0
Add internal structure for vertical offsets.
M balkon.cabal => balkon.cabal +2 -1
@@ 144,7 144,8 @@ library balkon-internal
        Data.Text.ParagraphLayout.Internal.ProtoLine,
        Data.Text.ParagraphLayout.Internal.ProtoRun,
        Data.Text.ParagraphLayout.Internal.Script,
        Data.Text.ParagraphLayout.Internal.SplitList
        Data.Text.ParagraphLayout.Internal.SplitList,
        Data.Text.ParagraphLayout.Internal.VerticalOffsets

    build-depends:
        base >=4.12 && < 4.16,

M src/Data/Text/ParagraphLayout/Internal/Layout.hs => src/Data/Text/ParagraphLayout/Internal/Layout.hs +31 -7
@@ 43,6 43,7 @@ import Data.Text.ParagraphLayout.Internal.Run
import Data.Text.ParagraphLayout.Internal.SplitList
import Data.Text.ParagraphLayout.Internal.TextContainer
import Data.Text.ParagraphLayout.Internal.TextOptions
import qualified Data.Text.ParagraphLayout.Internal.VerticalOffsets as VO
import Data.Text.ParagraphLayout.Internal.WithSpan

-- This is redundant.


@@ 178,19 179,42 @@ positionFragmentH line originY originX (WithBoxes lbs (WithSpan rs pf) rbs) =
            }
        userData = RS.spanUserData rs
        bs = ancestorBoxes lbs rbs rs
        contentRect = Rect contentX contentY contentWidth (-normalLineHeight)
        contentY = originY + penY + ascent
        rect = Rect contentX originY contentWidth (-lineHeight)
        contentRect = Rect contentX fontTop contentWidth (fontBottom - fontTop)
        rect = Rect contentX layoutTop contentWidth (layoutBottom - layoutTop)
        penX = 0
        penY = descent + leading `div` 2 - lineHeight
        lineHeight = case textLineHeight opts of
            Normal -> normalLineHeight
            Absolute h -> h
        penY = baseline - layoutTop
        VO.VerticalOffsets
            { VO.layoutTop = layoutTop
            , VO.fontTop = fontTop
            , VO.baseline = baseline
            , VO.fontBottom = fontBottom
            , VO.layoutBottom = layoutBottom
            } = VO.alignLayoutTop originY $ verticalOffsets (WithSpan rs pf)

-- | Vertical offsets for the given fragment, with baseline set to 0.
verticalOffsets :: ProtoFragmentWithSpan d -> VO.VerticalOffsets
verticalOffsets (WithSpan rs pf) = VO.VerticalOffsets
    { VO.layoutTop = ascent + topHalfLeading
    , VO.fontTop = ascent
    , VO.baseline = 0
    , VO.fontBottom = - descent
    , VO.layoutBottom = - descent - bottomHalfLeading
    }
    where
        -- non-negative leading values iff `lineHeight` > `normalLineHeight`
        leading = lineHeight - normalLineHeight
        topHalfLeading = -((-leading) `div` 2)
        bottomHalfLeading = leading `div` 2
        -- `normalLineHeight` > 0 for horizontal fonts
        normalLineHeight = ascent + descent
        -- `ascent` >= 0 for horizontal fonts
        ascent = ascender extents
        -- `descent` >= 0 for horizontal fonts
        descent = - descender extents
        extents = fontExtentsForDir (textFont opts) (Just $ PF.direction pf)
        lineHeight = case textLineHeight opts of
            Normal -> normalLineHeight
            Absolute h -> h
        opts = RS.spanTextOptions rs

ancestorBoxes

A src/Data/Text/ParagraphLayout/Internal/VerticalOffsets.hs => src/Data/Text/ParagraphLayout/Internal/VerticalOffsets.hs +47 -0
@@ 0,0 1,47 @@
module Data.Text.ParagraphLayout.Internal.VerticalOffsets
    ( VerticalOffsets (..)
    , alignLayoutTop
    , shift
    )
where

import Data.Int (Int32)

-- | Metrics used for vertical alignment of text fragments.
data VerticalOffsets = VerticalOffsets

    { layoutTop :: Int32
    -- ^ Y coordinate of the top edge of the fragment,
    -- including half-leading.

    , fontTop :: Int32
    -- ^ Y coordinate of the font's ascender.

    , baseline :: Int32
    -- ^ Y coordinate of the font's baseline.

    , fontBottom :: Int32
    -- ^ Y coordinate of the font's descender.

    , layoutBottom :: Int32
    -- ^ Y coordinate of the bottom edge of the fragment,
    -- including half-leading.

    }
    deriving (Eq, Show)

-- | Add a constant to each of the coordinates, effectively moving them
-- up by the given amount while preserving distances between them.
shift :: Int32 -> VerticalOffsets -> VerticalOffsets
shift d vo = vo
    { layoutTop = layoutTop vo + d
    , fontTop = fontTop vo + d
    , baseline = baseline vo + d
    , fontBottom = fontBottom vo + d
    , layoutBottom = layoutBottom vo + d
    }

-- | Set `layoutTop` to the given value and update all other coordinates
-- so that distances are preserved.
alignLayoutTop :: Int32 -> VerticalOffsets -> VerticalOffsets
alignLayoutTop x vo = shift (x - layoutTop vo) vo