packages feed

kb-text-shape-0.1.0.0: src/KB/Text/Shape/FFI/Structs.hsc

module KB.Text.Shape.FFI.Structs where

import Prelude hiding (id, error)

import Foreign
import Foreign.C

import KB.Text.Shape.FFI.Allocator (Allocator)
import KB.Text.Shape.FFI.Enums
import KB.Text.Shape.FFI.Flags
import KB.Text.Shape.FFI.Handles

#include "kb_text_shape.h"

data FontInfo = FontInfo
  { strings :: [Ptr CChar] -- ^ @char *Strings[KBTS_FONT_INFO_STRING_ID_COUNT]@
  , stringLengths :: [Word16] -- ^ @kbts_u16 StringLengths[KBTS_FONT_INFO_STRING_ID_COUNT]@

  , styleFlags :: FontStyleFlags
  , weight :: FontWeight
  , width :: FontWidth
  } deriving (Eq, Show)

instance Storable FontInfo where
  alignment ~_ = #alignment kbts_font_info
  sizeOf ~_ = #size kbts_font_info

  peek ptr = do
    strings <- peekArray FONT_INFO_STRING_ID_COUNT $ ptr `plusPtr` (#offset kbts_font_info, Strings)
    stringLengths <- peekArray FONT_INFO_STRING_ID_COUNT $ ptr `plusPtr` (#offset kbts_font_info, StringLengths)
    styleFlags <- (#peek kbts_font_info, StyleFlags) ptr
    weight <- (#peek kbts_font_info, Weight) ptr
    width <- (#peek kbts_font_info, Width) ptr
    pure FontInfo{..}

  poke ptr FontInfo{..} = do
    pokeArray (ptr `plusPtr` (#offset kbts_font_info, Strings)) strings
    pokeArray (ptr `plusPtr` (#offset kbts_font_info, StringLengths)) stringLengths
    (#poke kbts_font_info, StyleFlags) ptr styleFlags
    (#poke kbts_font_info, Weight) ptr weight
    (#poke kbts_font_info, Width) ptr width

data GlyphIterator = GlyphIterator
  { glyphStorage :: Ptr GlyphStorage
  , currentGlyph :: Ptr Glyph

  , lastAdvanceX :: Int
  , x :: Int
  , y :: Int
  }
  deriving (Show)

instance Storable GlyphIterator where
  alignment ~_ = #alignment kbts_glyph_iterator
  sizeOf ~_ = #size kbts_glyph_iterator

  peek ptr = do
    glyphStorage <- (#peek kbts_glyph_iterator, GlyphStorage) ptr
    currentGlyph <- (#peek kbts_glyph_iterator, CurrentGlyph) ptr
    lastAdvanceX <- fromIntegral @CInt <$> (#peek kbts_glyph_iterator, LastAdvanceX) ptr
    x <- fromIntegral @CInt <$> (#peek kbts_glyph_iterator, X) ptr
    y <- fromIntegral @CInt <$> (#peek kbts_glyph_iterator, Y) ptr
    pure GlyphIterator{..}

  poke ptr GlyphIterator{..} = do
    (#poke kbts_glyph_iterator, GlyphStorage) ptr glyphStorage
    (#poke kbts_glyph_iterator, CurrentGlyph) ptr currentGlyph
    (#poke kbts_glyph_iterator, LastAdvanceX) ptr $ fromIntegral @_ @CInt lastAdvanceX
    (#poke kbts_glyph_iterator, X) ptr $ fromIntegral @_ @CInt x
    (#poke kbts_glyph_iterator, Y) ptr $ fromIntegral @_ @CInt y

data CodepointIterator = CodepointIterator
  { codepoint :: Ptr Codepoint
  , context :: ShapeContext

  , endBlockIndex :: Word32
  , onePastLastCodepointIndex :: Word32
  , blockIndex :: Word32
  , codepointIndex :: Word32
  , currentBlockCodepointCount :: Word32
  , flatCodepointIndex :: Word32
  }
  deriving (Show)

instance Storable CodepointIterator where
  alignment ~_ = #alignment kbts_shape_codepoint_iterator
  sizeOf ~_ = #size kbts_shape_codepoint_iterator

  peek ptr = do
    codepoint <- (#peek kbts_shape_codepoint_iterator, Codepoint) ptr
    context <- (#peek kbts_shape_codepoint_iterator, Context) ptr
    endBlockIndex <- (#peek kbts_shape_codepoint_iterator, EndBlockIndex) ptr
    onePastLastCodepointIndex <- (#peek kbts_shape_codepoint_iterator, OnePastLastCodepointIndex) ptr
    blockIndex <- (#peek kbts_shape_codepoint_iterator, BlockIndex) ptr
    codepointIndex <- (#peek kbts_shape_codepoint_iterator, CodepointIndex) ptr
    currentBlockCodepointCount <- (#peek kbts_shape_codepoint_iterator, CurrentBlockCodepointCount) ptr
    flatCodepointIndex <- (#peek kbts_shape_codepoint_iterator, FlatCodepointIndex) ptr
    pure CodepointIterator{..}

  poke ptr CodepointIterator{..} = do
    (#poke kbts_shape_codepoint_iterator, Codepoint) ptr codepoint
    (#poke kbts_shape_codepoint_iterator, Context) ptr context
    (#poke kbts_shape_codepoint_iterator, EndBlockIndex) ptr endBlockIndex
    (#poke kbts_shape_codepoint_iterator, OnePastLastCodepointIndex) ptr onePastLastCodepointIndex
    (#poke kbts_shape_codepoint_iterator, BlockIndex) ptr blockIndex
    (#poke kbts_shape_codepoint_iterator, CodepointIndex) ptr codepointIndex
    (#poke kbts_shape_codepoint_iterator, CurrentBlockCodepointCount) ptr currentBlockCodepointCount
    (#poke kbts_shape_codepoint_iterator, FlatCodepointIndex) ptr flatCodepointIndex

data Run = Run
  { font :: Font
  , script :: Script
  , paragraphDirection :: Direction
  , direction :: Direction
  , flags :: BreakFlags

  , glyphs :: GlyphIterator
  }
  deriving (Show)

runGlyphIterator :: Ptr Run -> Ptr GlyphIterator
runGlyphIterator runPtr = runPtr `plusPtr` (#offset kbts_run, Glyphs)

instance Storable Run where
  alignment ~_ = #alignment kbts_run
  sizeOf ~_ = #size kbts_run

  peek ptr = do
    font <- (#peek kbts_run, Font) ptr
    script <- (#peek kbts_run, Script) ptr
    paragraphDirection <- (#peek kbts_run, ParagraphDirection) ptr
    direction <- (#peek kbts_run, Direction) ptr
    flags <- (#peek kbts_run, Flags) ptr

    glyphs <- (#peek kbts_run, Glyphs) ptr
    pure Run{..}

  poke ptr Run{..} = do
    (#poke kbts_run, Font) ptr font
    (#poke kbts_run, Script) ptr script
    (#poke kbts_run, ParagraphDirection) ptr paragraphDirection
    (#poke kbts_run, Direction) ptr direction
    (#poke kbts_run, Flags) ptr flags
    (#poke kbts_run, Glyphs) ptr glyphs

data Arena = Arena
  { allocator :: FunPtr Allocator
  , allocatorData :: Ptr ()

  , blockSentinel :: ArenaBlockHeader
  , freeBlockSentinel :: ArenaBlockHeader
  , error :: Int
  }
  deriving (Show)

instance Storable Arena where
  alignment ~_ = #alignment kbts_arena
  sizeOf ~_ = #size kbts_arena
  peek ptr = do
    allocator <- (#peek kbts_arena, Allocator) ptr
    allocatorData <- (#peek kbts_arena, AllocatorData) ptr
    blockSentinel <- (#peek kbts_arena, BlockSentinel) ptr
    freeBlockSentinel <- (#peek kbts_arena, FreeBlockSentinel) ptr
    error <- (#peek kbts_arena, Error) ptr
    pure Arena{..}
  poke ptr Arena{..} = do
    (#poke kbts_arena, Allocator) ptr allocator
    (#poke kbts_arena, AllocatorData) ptr allocatorData
    (#poke kbts_arena, BlockSentinel) ptr blockSentinel
    (#poke kbts_arena, FreeBlockSentinel) ptr freeBlockSentinel
    (#poke kbts_arena, Error) ptr error

data ArenaBlockHeader = ArenaBlockHeader
  { prev :: Ptr ArenaBlockHeader
  , next :: Ptr ArenaBlockHeader
  }
  deriving (Show)

instance Storable ArenaBlockHeader where
  alignment ~_ = #alignment kbts_arena_block_header
  sizeOf ~_ = #size kbts_arena_block_header
  peek ptr = do
    prev <- (#peek kbts_arena_block_header, Prev) ptr
    next <- (#peek kbts_arena_block_header, Next) ptr
    pure ArenaBlockHeader{..}
  poke ptr ArenaBlockHeader{..} = do
    (#poke kbts_arena_block_header, Prev) ptr prev
    (#poke kbts_arena_block_header, Next) ptr next

data GlyphStorage = GlyphStorage
  { arena :: Arena

  , glyphSentinel :: Glyph
  , freeGlyphSentinel :: Glyph
  }
  deriving (Show)

instance Storable GlyphStorage where
  alignment ~_ = #alignment kbts_glyph_storage
  sizeOf ~_ = #size kbts_glyph_storage
  peek ptr = do
    arena <- (#peek kbts_glyph_storage, Arena) ptr
    glyphSentinel <- (#peek kbts_glyph_storage, GlyphSentinel) ptr
    freeGlyphSentinel <- (#peek kbts_glyph_storage, FreeGlyphSentinel) ptr
    pure GlyphStorage{..}
  poke ptr GlyphStorage{..} = do
    (#poke kbts_glyph_storage, Arena) ptr arena
    (#poke kbts_glyph_storage, GlyphSentinel) ptr glyphSentinel
    (#poke kbts_glyph_storage, FreeGlyphSentinel) ptr freeGlyphSentinel

data Glyph = Glyph
  { prev :: Ptr Glyph
  , next :: Ptr Glyph

  , codepoint :: Word32
  , id :: Word16 -- ^ Glyph index. This is what you want to use to query outline data.
  , uid :: Word16

  , userIdOrCodepointIndex :: Int
    {- ^ This field is kept and returned as-is throughout the shaping process.

    When you are using the context API, it contains a codepoint index always!
    To get the original user ID with the context API, you need to get the corresponding kbts_shape_codepoint
    with kbts_ShapeGetShapeCodepoint(Context, Glyph->UserIdOrCodepointIndex, ...);
    -}

  -- Used by GPOS
  , offsetX :: Int32
  , offsetY :: Int32
  , advanceX :: Int32
  , advanceY :: Int32

  , attachGlyph :: Ptr Glyph
  {- ^ Set by GPOS attachments

    Earlier on, we used to assume that, if a glyph had no advance, or had the MARK glyph class, then
    it could be handled as a mark in layout operations. This is inaccurate.
    Unicode makes a distinction between attached marks and standalone marks. For our purposes, attached
    marks are marks that have found a valid base character to attach to. In practice, this means that the
    font contains a valid display position/configuration for it in the current context.
    In contrast, standalone marks are marks that aren't attached to anything. Fonts may still have glyphs
    for them, in which case we want to display those just like regular glyphs that take up horizontal space
    on the line. When fonts don't have glyphs for them, they simply stay around as zero-width glyphs.
    Standalone marks have notably different behavior compared to attached marks, and so, once we start
    applying positioning features, it becomes worthwhile to track exactly which glyph has attached to which.
  -}

  , config :: Ptr () -- kbts_glyph_config *Config;

  , decomposition :: Word64

  , classes :: Word32 -- kbts_glyph_classes Classes;
  , flags :: GlyphFlags -- kbts_glyph_flags Flags;
  , parentInfo :: Word32

  , ligatureUid :: Word16
    {- ^ This is set by GSUB and used by GPOS.
      A 0-index means that we should attach to the last component in the ligature.

      From the Microsoft docs:
        To correctly access the subtables, the client must keep track of the component associated with the mark.

        For a given mark assigned to a particular class, the appropriate base attachment point is determined by which
        ligature component the mark is associated with. This is dependent on the original character string and subsequent
        character- or glyph-sequence processing, not the font data alone. While a text-layout client is performing any
        character-based preprocessing or any glyph-substitution operations using the GSUB table, the text-layout client
        must keep track of associations of marks to particular ligature-glyph components.
    -}

  , ligatureComponentIndexPlusOne :: Word16
  , ligatureComponentCount :: Word16

  , joiningFeature :: UnicodeJoiningFeature
    -- ^ Set in GSUB and used in GPOS, for STCH.

  -- Unicode properties filled in by CodepointToGlyph.
  , joiningType :: UnicodeJoiningType
  , unicodeFlags :: Word8
  , syllabicClass :: Word8
  , syllabicPosition :: Word8
  , useClass :: Word8
  , combiningClass :: Word8

  , markOrdering :: Word8 -- ^ Only used temporarily in NORMALIZE for Arabic mark reordering.
  }
  deriving (Show)

instance Storable Glyph where
  alignment ~_ = #alignment kbts_glyph
  sizeOf ~_ = #size kbts_glyph

  peek ptr = do
    prev <- (#peek kbts_glyph, Prev) ptr
    next <- (#peek kbts_glyph, Next) ptr
    codepoint <- (#peek kbts_glyph, Codepoint) ptr
    id <- (#peek kbts_glyph, Id) ptr
    uid <- (#peek kbts_glyph, Uid) ptr
    userIdOrCodepointIndex <- fromIntegral @CInt <$> (#peek kbts_glyph, UserIdOrCodepointIndex) ptr
    offsetX <- (#peek kbts_glyph, OffsetX) ptr
    offsetY <- (#peek kbts_glyph, OffsetY) ptr
    advanceX <- (#peek kbts_glyph, AdvanceX) ptr
    advanceY <- (#peek kbts_glyph, AdvanceY) ptr
    attachGlyph <- (#peek kbts_glyph, AttachGlyph) ptr
    config <- (#peek kbts_glyph, Config) ptr
    decomposition <- (#peek kbts_glyph, Decomposition) ptr
    classes <- (#peek kbts_glyph, Classes) ptr
    flags <- (#peek kbts_glyph, Flags) ptr
    parentInfo <- (#peek kbts_glyph, ParentInfo) ptr
    ligatureUid <- (#peek kbts_glyph, LigatureUid) ptr
    ligatureComponentIndexPlusOne <- (#peek kbts_glyph, LigatureComponentIndexPlusOne) ptr
    ligatureComponentCount <- (#peek kbts_glyph, LigatureComponentCount) ptr
    joiningFeature <- (#peek kbts_glyph, JoiningFeature) ptr
    joiningType <- (#peek kbts_glyph, JoiningType) ptr
    unicodeFlags <- (#peek kbts_glyph, UnicodeFlags) ptr
    syllabicClass <- (#peek kbts_glyph, SyllabicClass) ptr
    syllabicPosition <- (#peek kbts_glyph, SyllabicPosition) ptr
    useClass <- (#peek kbts_glyph, UseClass) ptr
    combiningClass <- (#peek kbts_glyph, CombiningClass) ptr
    markOrdering <- (#peek kbts_glyph, MarkOrdering) ptr
    pure Glyph{..}

  poke ptr Glyph{..} = do
    (#poke kbts_glyph, Prev) ptr prev
    (#poke kbts_glyph, Next) ptr next
    (#poke kbts_glyph, Codepoint) ptr codepoint
    (#poke kbts_glyph, Id) ptr id
    (#poke kbts_glyph, Uid) ptr uid
    (#poke kbts_glyph, UserIdOrCodepointIndex) ptr $ fromIntegral @_ @CInt userIdOrCodepointIndex
    (#poke kbts_glyph, OffsetX) ptr offsetX
    (#poke kbts_glyph, OffsetY) ptr offsetY
    (#poke kbts_glyph, AdvanceX) ptr advanceX
    (#poke kbts_glyph, AdvanceY) ptr advanceY
    (#poke kbts_glyph, AttachGlyph) ptr attachGlyph
    (#poke kbts_glyph, Config) ptr config
    (#poke kbts_glyph, Decomposition) ptr decomposition
    (#poke kbts_glyph, Classes) ptr classes
    (#poke kbts_glyph, Flags) ptr flags
    (#poke kbts_glyph, ParentInfo) ptr parentInfo
    (#poke kbts_glyph, LigatureUid) ptr ligatureUid
    (#poke kbts_glyph, LigatureComponentIndexPlusOne) ptr ligatureComponentIndexPlusOne
    (#poke kbts_glyph, LigatureComponentCount) ptr ligatureComponentCount
    (#poke kbts_glyph, JoiningFeature) ptr joiningFeature
    (#poke kbts_glyph, JoiningType) ptr joiningType
    (#poke kbts_glyph, UnicodeFlags) ptr unicodeFlags
    (#poke kbts_glyph, SyllabicClass) ptr syllabicClass
    (#poke kbts_glyph, SyllabicPosition) ptr syllabicPosition
    (#poke kbts_glyph, UseClass) ptr useClass
    (#poke kbts_glyph, CombiningClass) ptr combiningClass

data Codepoint = Codepoint
  { font :: Font -- ^ Only set when @(BreakFlags & KBTS_BREAK_FLAG_GRAPHEME) != 0@.
  , config :: Ptr () -- kbts_glyph_config*

  , codepoint :: Int
  , userId :: Int

  , breakFlags :: BreakFlags
  , script :: Script -- ^ Only set when @(BreakFlags & KBTS_BREAK_FLAG_SCRIPT) != 0@.
  , direction :: Direction -- ^ Only set when @(BreakFlags & KBTS_BREAK_FLAG_DIRECTION) != 0@.
  , paragraphDirection :: Direction -- ^ Only set when @(BreakFlags & KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION) != 0@.
  }
  deriving (Show)

instance Storable Codepoint where
  alignment ~_ = #alignment kbts_shape_codepoint
  sizeOf ~_ = #size kbts_shape_codepoint

  peek ptr = do
    font <- (#peek kbts_shape_codepoint, Font) ptr
    config <- (#peek kbts_shape_codepoint, Config) ptr
    codepoint <- fromIntegral @CInt <$> (#peek kbts_shape_codepoint, Codepoint) ptr
    userId <- fromIntegral @CInt <$> (#peek kbts_shape_codepoint, UserId) ptr
    breakFlags <- (#peek kbts_shape_codepoint, BreakFlags) ptr
    script <- (#peek kbts_shape_codepoint, Script) ptr
    direction <- (#peek kbts_shape_codepoint, Direction) ptr
    paragraphDirection <- (#peek kbts_shape_codepoint, ParagraphDirection) ptr
    pure Codepoint{..}

  poke ptr Codepoint{..} = do
    (#poke kbts_shape_codepoint, Font) ptr font
    (#poke kbts_shape_codepoint, Codepoint) ptr $ fromIntegral @_ @CInt codepoint
    (#poke kbts_shape_codepoint, UserId) ptr $ fromIntegral @_ @CInt userId
    (#poke kbts_shape_codepoint, BreakFlags) ptr breakFlags
    (#poke kbts_shape_codepoint, Script) ptr script
    (#poke kbts_shape_codepoint, Direction) ptr direction
    (#poke kbts_shape_codepoint, ParagraphDirection) ptr paragraphDirection

data FeatureOverride = FeatureOverride
  { tag :: FeatureTag
  , value :: Int
  }
  deriving (Eq, Ord, Show)

instance Storable FeatureOverride where
  alignment ~_ = #alignment kbts_feature_override
  sizeOf ~_ = #size kbts_feature_override
  peek ptr = do
    tag <- peekByteOff ptr (#offset kbts_feature_override, Tag)
    value <- fromIntegral @CInt <$> peekByteOff ptr (#offset kbts_feature_override, Value)
    pure FeatureOverride{..}
  poke ptr FeatureOverride{..} = do
    pokeByteOff ptr (#offset kbts_feature_override, Tag) tag
    pokeByteOff ptr (#offset kbts_feature_override, Value) $ fromIntegral @_ @CInt value

data GlyphParent = GlyphParent
  { decomposition :: Word64
  , codepoint :: Word32
  }
  deriving (Eq, Ord, Show)

instance Storable GlyphParent where
  alignment ~_ = #alignment kbts_glyph_parent
  sizeOf ~_ = #size kbts_glyph_parent
  peek ptr = do
    decomposition <- (#peek kbts_glyph_parent, Decomposition) ptr
    codepoint <- (#peek kbts_glyph_parent, Codepoint) ptr
    pure GlyphParent{..}
  poke ptr GlyphParent{..} = do
    (#poke kbts_glyph_parent, Decomposition) ptr decomposition
    (#poke kbts_glyph_parent, Codepoint) ptr codepoint

pattern MAXIMUM_RECOMPOSITION_PARENTS :: Int
pattern MAXIMUM_RECOMPOSITION_PARENTS = #const KBTS_MAXIMUM_RECOMPOSITION_PARENTS

data FontCoverageTest = FontCoverageTest
  { font :: Font
  , baseCodepoint :: Word32

  , currentBaseError :: Int
  , error :: Int

  , baseParents :: Ptr GlyphParent -- ^ @kbts_glyph_parent BaseParents[KBTS_MAXIMUM_RECOMPOSITION_PARENTS];@
  , baseParentCount :: Word32
  }
  deriving (Show)

instance Storable FontCoverageTest where
  alignment ~_ = #alignment kbts_font_coverage_test
  sizeOf ~_ = #size kbts_font_coverage_test
  peek ptr = do
    font <- (#peek kbts_font_coverage_test, Font) ptr
    baseCodepoint <- (#peek kbts_font_coverage_test, BaseCodepoint) ptr
    currentBaseError <- fromIntegral @CInt <$> (#peek kbts_font_coverage_test, CurrentBaseError) ptr
    error <- fromIntegral @CInt <$> (#peek kbts_font_coverage_test, Error) ptr
    baseParents <- (#peek kbts_font_coverage_test, BaseParents) ptr
    baseParentCount <- (#peek kbts_font_coverage_test, BaseParentCount) ptr
    pure FontCoverageTest{..}
  poke ptr FontCoverageTest{..} = do
    (#poke kbts_font_coverage_test, Font) ptr font
    (#poke kbts_font_coverage_test, BaseCodepoint) ptr baseCodepoint
    (#poke kbts_font_coverage_test, CurrentBaseError) ptr $ fromIntegral @_ @CInt currentBaseError
    (#poke kbts_font_coverage_test, Error) ptr $ fromIntegral @_ @CInt error
    (#poke kbts_font_coverage_test, BaseParents) ptr baseParents
    (#poke kbts_font_coverage_test, BaseParentCount) ptr baseParentCount

data Break = Break
  { position :: Int
    {- ^
      The break code mostly works in relative positions, but we convert to absolute positions for the user.
      That way, breaks can be trivially stored and compared and such and it just works.
    -}
  , flags :: BreakFlags
  , direction :: Direction -- ^ Only valid if 'KBTS_BREAK_FLAG_DIRECTION' is set.
  , paragraphDirection :: Direction -- ^ Only valid if 'KBTS_BREAK_FLAG_PARAGRAPH_DIRECTION' is set.
  , script :: Script -- ^ Only valid if 'KBTS_BREAK_FLAG_SCRIPT' is set.
  }
  deriving (Show, Eq)

instance Storable Break where
  alignment ~_ = #alignment kbts_break
  sizeOf ~_ = #size kbts_break
  peek ptr = do
    position <- fromIntegral @CInt <$> (#peek kbts_break, Position) ptr
    flags <- (#peek kbts_break, Flags) ptr
    direction <- (#peek kbts_break, Direction) ptr
    paragraphDirection <- (#peek kbts_break, ParagraphDirection) ptr
    script <- (#peek kbts_break, Script) ptr
    pure Break{..}
  poke ptr Break{..} = do
    (#poke kbts_break, Position) ptr $ fromIntegral @_ @CInt position
    (#poke kbts_break, Flags) ptr flags
    (#poke kbts_break, Direction) ptr direction
    (#poke kbts_break, ParagraphDirection) ptr paragraphDirection
    (#poke kbts_break, Script) ptr script