~jaro/balkon

f389fa5a8b81f95f28500b723792d9803bf9b6aa — Jaro 1 year, 2 months ago 9514097
Implement absolute line heights with half-leadings.
A .golden/lineHeightLarger/golden => .golden/lineHeightLarger/golden +5 -0
@@ 0,0 1,5 @@
ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = 1600}, spanLayouts = [
    SpanLayout [Fragment {fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = 1600}, fragmentPen = (0,428), fragmentGlyphs =
        [(GlyphInfo {codepoint = 68, cluster = 0, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0})]
    }]
]}

A .golden/lineHeightNormal/golden => .golden/lineHeightNormal/golden +5 -0
@@ 0,0 1,5 @@
ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = 1121}, spanLayouts = [
    SpanLayout [Fragment {fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = 1121}, fragmentPen = (0,189), fragmentGlyphs =
        [(GlyphInfo {codepoint = 68, cluster = 0, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0})]
    }]
]}

A .golden/lineHeightSmaller/golden => .golden/lineHeightSmaller/golden +5 -0
@@ 0,0 1,5 @@
ParagraphLayout {paragraphRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = 599}, spanLayouts = [
    SpanLayout [Fragment {fragmentRect = Rect {x_origin = 0, y_origin = 0, x_size = 522, y_size = 599}, fragmentPen = (0,-72), fragmentGlyphs =
        [(GlyphInfo {codepoint = 68, cluster = 0, unsafeToBreak = False, unsafeToConcat = False, safeToInsertTatweel = False},GlyphPos {x_advance = 522, y_advance = 0, x_offset = 0, y_offset = 0})]
    }]
]}

M balkon.cabal => balkon.cabal +1 -0
@@ 98,6 98,7 @@ library
    exposed-modules:
        Data.Text.ParagraphLayout,
        Data.Text.ParagraphLayout.Fragment,
        Data.Text.ParagraphLayout.LineHeight,
        Data.Text.ParagraphLayout.Plain,
        Data.Text.ParagraphLayout.Rect,
        Data.Text.ParagraphLayout.ResolvedSpan,

A src/Data/Text/ParagraphLayout/LineHeight.hs => src/Data/Text/ParagraphLayout/LineHeight.hs +15 -0
@@ 0,0 1,15 @@
module Data.Text.ParagraphLayout.LineHeight (LineHeight(..))
where

import Data.Int (Int32)

data LineHeight

    = Normal
    -- ^ Determine the preferred line height automatically using its ascent and
    -- descent metrics.

    | Absolute Int32
    -- ^ Set the preferred line height independently of the font.

    deriving (Eq, Show)

M src/Data/Text/ParagraphLayout/Plain.hs => src/Data/Text/ParagraphLayout/Plain.hs +18 -16
@@ 29,6 29,7 @@ import Data.Text.Glyphize
    ,ContentType(ContentTypeUnicode)
    ,Font
    ,FontExtents(..)
    ,GlyphInfo
    ,GlyphPos(x_advance)
    ,defaultBuffer
    ,fontExtentsForDir


@@ 38,6 39,7 @@ import Data.Text.Internal (Text(Text))
import qualified Data.Text.Lazy as Lazy

import Data.Text.ParagraphLayout.Fragment
import Data.Text.ParagraphLayout.LineHeight
import Data.Text.ParagraphLayout.Rect
import qualified Data.Text.ParagraphLayout.ResolvedSpan as RS
import Data.Text.ParagraphLayout.Run


@@ 72,15 74,6 @@ data ParagraphOptions = ParagraphOptions
    , paragraphMaxWidth :: Int32
    }

data LineHeight

    = Normal
    -- ^ Determine the preferred line height automatically using its ascent and
    -- descent metrics.

    | Absolute Int32
    -- ^ Set the preferred line height independently of the font.

-- | The resulting layout of the whole paragraph.
data ParagraphLayout = ParagraphLayout
    { paragraphRect :: Rect Int32


@@ 130,30 123,38 @@ layoutPlain paragraph = ParagraphLayout pRect arrangedLayouts
layoutSpan :: RS.ResolvedSpan -> SpanLayout
layoutSpan rs = SpanLayout (map layoutRun $ spanToRuns rs)

-- TODO: Calculate line height and pen position.
layoutRun :: Run -> Fragment
layoutRun run = Fragment rect (penX, penY) glyphs
    where
        rect = containGlyphsH lineHeight $ map snd $ glyphs
        -- TODO: Add half-leadings as required by ParagraphOptions.
        penX = 0 -- for horizontal text
        penY = descent
        lineHeight = ascent + descent
        penY = descent + leading `div` 2
        glyphs = shapeRun run
        lineHeight = case RS.spanLineHeight rs of
            Normal -> normalLineHeight
            Absolute h -> h
        leading = lineHeight - normalLineHeight
        normalLineHeight = ascent + descent
        ascent = ascender extents
        descent = - descender extents
        extents = fontExtentsForDir font dir
        glyphs = shape font buffer features
        font = RS.spanFont rs
        dir = runDirection run
        rs = runOriginalSpan run

shapeRun :: Run -> [(GlyphInfo, GlyphPos)]
shapeRun run = shape font buffer features
    where
        font = RS.spanFont rs
        -- TODO: Set beginsText / endsText.
        buffer = defaultBuffer
            { text = Lazy.fromStrict $ runText run
            , contentType = Just ContentTypeUnicode
            , direction = dir
            , direction = runDirection run
            , script = runScript run
            , language = Just $ RS.spanLanguage rs
            }
        features = []
        dir = runDirection run
        rs = runOriginalSpan run

resolveSpans :: Paragraph -> [RS.ResolvedSpan]


@@ 162,6 163,7 @@ resolveSpans (Paragraph arr off spans opts) = map resolve $ zip spans texts
        resolve (s, t) = RS.ResolvedSpan
            { RS.spanText = t
            , RS.spanFont = paragraphFont opts
            , RS.spanLineHeight = paragraphLineHeight opts
            , RS.spanLanguage = spanLanguage s
            }
        texts = cuts arr off spans

M src/Data/Text/ParagraphLayout/ResolvedSpan.hs => src/Data/Text/ParagraphLayout/ResolvedSpan.hs +3 -0
@@ 4,11 4,14 @@ where
import Data.Text (Text)
import Data.Text.Glyphize (Font)

import Data.Text.ParagraphLayout.LineHeight

-- | Internal structure containing resolved values that may be shared with
-- other spans across the paragraph.
data ResolvedSpan = ResolvedSpan
    { spanText :: Text
    , spanFont :: Font
    , spanLineHeight :: LineHeight
    , spanLanguage :: String
    }
    deriving (Eq, Show)

M test/Data/Text/ParagraphLayout/ParagraphData.hs => test/Data/Text/ParagraphLayout/ParagraphData.hs +4 -0
@@ 4,6 4,7 @@ module Data.Text.ParagraphLayout.ParagraphData
    ,emptySpanParagraph
    ,mixedLanguageLTRParagraph
    ,mixedScriptSerbianParagraph
    ,trivialParagraph
    )
where



@@ 16,6 17,9 @@ emptyParagraph = "" |<>| ""
emptySpanParagraph :: ParagraphOptions -> Paragraph
emptySpanParagraph = "" |< "en"~"" >| ""

trivialParagraph :: ParagraphOptions -> Paragraph
trivialParagraph = "" |< "en"~"a" >| ""

czechHelloParagraph :: ParagraphOptions -> Paragraph
czechHelloParagraph = "" |< "cs"~"Ahoj, světe!" >| ""


M test/Data/Text/ParagraphLayout/PlainSpec.hs => test/Data/Text/ParagraphLayout/PlainSpec.hs +16 -0
@@ 8,6 8,7 @@ import Test.Hspec.Golden
import System.FilePath ((</>))
import Data.Text.ParagraphLayout.FontLoader
import Data.Text.ParagraphLayout.Fragment
import Data.Text.ParagraphLayout.LineHeight
import Data.Text.ParagraphLayout.ParagraphData
import Data.Text.ParagraphLayout.Plain



@@ 89,3 90,18 @@ spec = do
        it "handles mixed languages in LTR layout" $ \font -> do
            let result = layoutPlain $ mixedLanguageLTRParagraph $ opts font
            result `shouldBeGolden` "mixedLanguageLTRParagraph"
        it "handles normal line height" $ \font -> do
            let result = layoutPlain $ trivialParagraph $ (opts font) {
                paragraphLineHeight = Normal
            }
            result `shouldBeGolden` "lineHeightNormal"
        it "handles larger line height" $ \font -> do
            let result = layoutPlain $ trivialParagraph $ (opts font) {
                paragraphLineHeight = Absolute 1600
            }
            result `shouldBeGolden` "lineHeightLarger"
        it "handles smaller line height" $ \font -> do
            let result = layoutPlain $ trivialParagraph $ (opts font) {
                paragraphLineHeight = Absolute 599
            }
            result `shouldBeGolden` "lineHeightSmaller"

M test/Data/Text/ParagraphLayout/SpanData.hs => test/Data/Text/ParagraphLayout/SpanData.hs +4 -0
@@ 7,12 7,14 @@ where

import Data.Text (pack)
import Data.Text.Glyphize (Font)
import Data.Text.ParagraphLayout.LineHeight (LineHeight(Normal))
import Data.Text.ParagraphLayout.ResolvedSpan (ResolvedSpan(..))

emptySpan :: Font -> ResolvedSpan
emptySpan font = ResolvedSpan
    { spanText = pack ""
    , spanFont = font
    , spanLineHeight = Normal
    , spanLanguage = "en"
    }



@@ 20,6 22,7 @@ czechHello :: Font -> ResolvedSpan
czechHello font = ResolvedSpan
    { spanText = pack "Ahoj, světe!"
    , spanFont = font
    , spanLineHeight = Normal
    , spanLanguage = "cs"
    }



@@ 27,5 30,6 @@ serbianMixedScript :: Font -> ResolvedSpan
serbianMixedScript font = ResolvedSpan
    { spanText = pack "Vikipedija (Википедија)"
    , spanFont = font
    , spanLineHeight = Normal
    , spanLanguage = "sr"
    }