{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveFunctor #-}
module Graphics.PDF.Typesetting(
Box(..)
, DisplayableBox(..)
, Letter(..)
, BoxDimension
, Style(..)
, TextStyle(..)
, StyleFunction(..)
, ParagraphStyle(..)
, MonadStyle(..)
, ComparableStyle(..)
, Para
, TM
, VBox
, VerState(..)
, Container
, Justification(..)
, Orientation(..)
, displayFormattedText
, styleFont
, txt
, kern
, addPenalty
, mkLetter
, mkDrawBox
, forceNewLine
, paragraph
, endPara
, startPara
, getParaStyle
, setParaStyle
, getWritingSystem
, setWritingSystem
, mkContainer
, fillContainer
, defaultVerState
, getBoxes
, containerX
, containerY
, containerWidth
, containerHeight
, containerContentHeight
, containerContentRightBorder
, containerContentLeftBorder
, containerCurrentHeight
, containerContentRectangle
, drawTextBox
, setFirstPassTolerance
, setSecondPassTolerance
, setHyphenPenaltyValue
, setFitnessDemerit
, setHyphenDemerit
, setLinePenalty
, getFirstPassTolerance
, getSecondPassTolerance
, getHyphenPenaltyValue
, getFitnessDemerit
, getHyphenDemerit
, getLinePenalty
, setJustification
, setBaseLineSkip
, setLineSkipLimit
, setLineSkip
, getBaseLineSkip
, getLineSkipLimit
, getLineSkip
, module Graphics.PDF.Typesetting.StandardStyle
) where
import Graphics.PDF.LowLevel.Types
import Graphics.PDF.Draw
import Graphics.PDF.Shapes
import Graphics.PDF.Coordinates
import Control.Monad.RWS
import Graphics.PDF.Typesetting.Breaking
import Graphics.PDF.Typesetting.Vertical
import Graphics.PDF.Typesetting.Layout
import Graphics.PDF.Typesetting.Box
import Graphics.PDF.Typesetting.StandardStyle
import Graphics.PDF.Typesetting.WritingSystem
import qualified Data.Text as T
displayFormattedText :: (ParagraphStyle ps s) => Rectangle
-> ps
-> s
-> TM ps s a
-> Draw a
displayFormattedText (Rectangle (xa :+ ya) (xb :+ yb)) defaultVStyle defaultHStyle t =
do
let (a, s', boxes) = (runRWS . unTM $ t >>= \x' -> do {return x'} ) () (defaultTmState defaultVStyle defaultHStyle)
c = mkContainer xa yb (xb-xa) (yb-ya) 0
(d,_,_) = fillContainer (pageSettings s') c boxes
d
return a
getBoxes :: (ParagraphStyle ps s) => ps
-> s
-> TM ps s a
-> [VBox ps s]
getBoxes defaultVStyle defaultHStyle t =
let (_, _ , boxes) = (runRWS . unTM $ t >>= \x' -> do {return x'} ) () (defaultTmState defaultVStyle defaultHStyle)
in boxes
addPenalty :: Int -> Para s ()
addPenalty f = tell $ [penalty f]
defaultTmState :: (ParagraphStyle ps s) => ps -> s -> TMState ps s
defaultTmState s' s = TMState { tmStyle = s
, paraSettings = defaultBreakingSettings
, pageSettings = defaultVerState s'
}
data TMState ps s = TMState { tmStyle :: !s
, paraSettings :: !BRState
, pageSettings :: !(VerState ps)
}
newtype TM ps s a = TM { unTM :: RWS () [VBox ps s] (TMState ps s) a}
#ifndef __HADDOCK__
deriving(Monad,Applicative,MonadWriter [VBox ps s], MonadState (TMState ps s), Functor)
#else
instance Monad TM
instance MonadWriter [VBox ps s] TM
instance MonadState (TMState ps s) TM
instance Functor TM
#endif
newtype Para s a = Para { unPara :: RWS BRState [Letter s] s a}
#ifndef __HADDOCK__
deriving(Monad,Applicative,MonadWriter [Letter s], MonadReader BRState, MonadState s, Functor)
#else
instance Monad Para
instance MonadWriter [Letter s] Para
instance MonadState s Para
instance Functor Para
instance MonadReader BRState Para
#endif
class (Style s, Monad m) => MonadStyle s m | m -> s where
setStyle :: s -> m ()
currentStyle :: m s
addBox :: (Show a, DisplayableBox a, Box a) => a
-> PDFFloat
-> PDFFloat
-> PDFFloat
-> m ()
glue :: PDFFloat
-> PDFFloat
-> PDFFloat
-> m ()
unstyledGlue :: PDFFloat
-> PDFFloat
-> PDFFloat
-> m ()
instance Style s => MonadStyle s (TM ps s) where
setStyle f = modifyStrict $ \s -> s {tmStyle = f}
currentStyle = gets tmStyle
addBox a w h d = do
style <- getParaStyle
tell $ ([SomeVBox 0 (w,h,d) (AnyBox a) (Just style)])
glue h y z = do
style <- getParaStyle
tell $ [vglue (Just style) h y z 0 0]
unstyledGlue h y z = do
tell $ [vglue Nothing h y z 0 0]
instance Style s => MonadStyle s (Para s) where
setStyle f = put $! f
currentStyle = get
addBox a w h d = do
f <- currentStyle
addLetter . mkLetter (w,h,d) (Just f) $ a
glue w y z = do
f <- currentStyle
tell $ [glueBox (Just f) w y z]
unstyledGlue w y z = do
tell $ [glueBox Nothing w y z]
forceNewLine :: Style s => Para s ()
forceNewLine = do
endPara
startPara
endFullyJustified :: Style s => Bool
-> Para s ()
endFullyJustified r = do
if r
then
glue 0 10000.0 0
else
tell $ [glueBox Nothing 0 10000.0 0]
addPenalty (-infinity)
endPara :: Style s => Para s ()
endPara = do
style <- ask
theStyle <- currentStyle
let w = spaceWidth theStyle
case centered style of
Centered -> do
addLetter (glueBox (Just theStyle) 0 (centeredDilatationFactor*w) 0)
addLetter (penalty (-infinity))
RightJustification -> addPenalty (-infinity)
_ -> endFullyJustified False
startPara :: Style s => Para s ()
startPara = do
style <- ask
theStyle <- currentStyle
let w = spaceWidth theStyle
case (centered style) of
Centered -> do
addLetter (kernBox (theStyle) 0)
addLetter $ penalty infinity
addLetter (glueBox (Just theStyle) 0 (centeredDilatationFactor*w) 0)
RightJustification -> do
addLetter (kernBox (theStyle) 0)
addLetter $ penalty infinity
addLetter (glueBox (Just theStyle) 0 (rightDilatationFactor*w) 0)
_ -> return ()
runPara :: Style s => Para s a -> TM ps s a
runPara m = do
TMState f settings pagesettings <- get
let (a, s', boxes) = (runRWS . unPara $ closedPara) settings f
put $! TMState s' settings pagesettings
style <- getParaStyle
tell $ [Paragraph 0 boxes (Just style) settings]
return a
where
closedPara = do
startPara
x <- m
endPara
return x
getWritingSystem :: TM ps s WritingSystem
getWritingSystem = do
s <- gets paraSettings
return (writingSystem s)
setWritingSystem :: WritingSystem -> TM ps s ()
setWritingSystem w = do
modifyStrict $ \s -> s {paraSettings = (paraSettings s){writingSystem = w}}
getParaStyle :: TM ps s ps
getParaStyle = gets pageSettings >>= TM . return . currentParagraphStyle
setParaStyle :: ParagraphStyle ps s => ps -> TM ps s ()
setParaStyle style = do
modifyStrict $ \s -> s {pageSettings = (pageSettings s){currentParagraphStyle = style}}
addLetter :: Letter s -> Para s ()
addLetter l = Para . tell $ [l]
paragraph :: Style s => Para s a -> TM ps s a
paragraph = runPara
txt :: Style s => T.Text -> Para s ()
txt t = do
f <- currentStyle
settings <- ask
tell $ splitText settings f t
kern :: Style s => PDFFloat -> Para s ()
kern w = do
f <- currentStyle
tell $ [kernBox f w]
setBaseLineSkip :: PDFFloat -> PDFFloat -> PDFFloat -> TM ps s ()
setBaseLineSkip w y z = modifyStrict $ \s -> s {pageSettings = (pageSettings s){baselineskip = (w,y,z)}}
getBaseLineSkip :: TM ps s (PDFFloat,PDFFloat,PDFFloat)
getBaseLineSkip = do
s <- gets pageSettings
return (baselineskip s)
setLineSkipLimit :: PDFFloat -> TM ps s ()
setLineSkipLimit l = modifyStrict $ \s -> s {pageSettings = (pageSettings s){lineskiplimit=l}}
getLineSkipLimit :: TM ps s PDFFloat
getLineSkipLimit = gets pageSettings >>= return . lineskiplimit
setLineSkip :: PDFFloat -> PDFFloat -> PDFFloat -> TM ps s ()
setLineSkip w y z = modifyStrict $ \s -> s {pageSettings = (pageSettings s){lineskip = (w,y,z)}}
getLineSkip :: TM ps s (PDFFloat,PDFFloat,PDFFloat)
getLineSkip = gets pageSettings >>= return . lineskip
setFirstPassTolerance :: PDFFloat -> TM ps s ()
setFirstPassTolerance x = modifyStrict $ \s -> s {paraSettings = (paraSettings s){firstPassTolerance = x}}
getFirstPassTolerance :: TM ps s PDFFloat
getFirstPassTolerance = gets paraSettings >>= return . firstPassTolerance
setSecondPassTolerance :: PDFFloat -> TM ps s ()
setSecondPassTolerance x = modifyStrict $ \s -> s {paraSettings = (paraSettings s){secondPassTolerance = x}}
getSecondPassTolerance :: TM ps s PDFFloat
getSecondPassTolerance = gets paraSettings >>= return . secondPassTolerance
setHyphenPenaltyValue :: Int -> TM ps s ()
setHyphenPenaltyValue x = modifyStrict $ \s -> s {paraSettings = (paraSettings s){hyphenPenaltyValue = x}}
getHyphenPenaltyValue :: TM ps s Int
getHyphenPenaltyValue = gets paraSettings >>= return . hyphenPenaltyValue
setFitnessDemerit :: PDFFloat -> TM ps s ()
setFitnessDemerit x = modifyStrict $ \s -> s {paraSettings = (paraSettings s){fitness_demerit = x}}
getFitnessDemerit :: TM ps s PDFFloat
getFitnessDemerit = gets paraSettings >>= return . fitness_demerit
setHyphenDemerit :: PDFFloat -> TM ps s ()
setHyphenDemerit x = modifyStrict $ \s -> s {paraSettings = (paraSettings s){flagged_demerit = x}}
getHyphenDemerit :: TM ps s PDFFloat
getHyphenDemerit = gets paraSettings >>= return . flagged_demerit
setLinePenalty :: PDFFloat -> TM ps s ()
setLinePenalty x = modifyStrict $ \s -> s {paraSettings = (paraSettings s){line_penalty = x}}
getLinePenalty :: TM ps s PDFFloat
getLinePenalty = gets paraSettings >>= return . line_penalty
setJustification :: Justification
-> TM ps s ()
setJustification j = modifyStrict $ \s -> s {paraSettings = (paraSettings s){centered = j}}
data Orientation = E | W | N | S | NE | NW | SE | SW deriving(Eq,Show)
drawTextBox :: (ParagraphStyle ps s, Style s)
=> PDFFloat
-> PDFFloat
-> PDFFloat
-> PDFFloat
-> Orientation
-> ps
-> s
-> TM ps s a
-> (Rectangle,Draw ())
drawTextBox x y w h ori ps p t =
let b = getBoxes ps p t
sh = styleHeight p
c = mkContainer 0 0 w h sh
(d,c',_) = fillContainer (defaultVerState ps) c b
Rectangle (xa :+ ya) (xb :+ yb) = containerContentRectangle c'
wc = xb - xa
hc = yb - ya
(dx,dy) = case ori of
NE -> (x,y)
NW -> (x - wc,y)
SE -> (x,y + hc)
SW -> (x - wc,y + hc)
E -> (x,y + hc / 2.0)
W -> (x - wc,y + hc / 2.0)
N -> (x - wc/2.0,y)
S -> (x - wc/2.0,y + hc)
box = withNewContext $ do
applyMatrix $ translate (dx :+ dy)
d
r = Rectangle ((xa + dx) :+ (ya + dy)) ((xb + dx) :+ (yb + dy))
in
(r,box)