{-# LANGUAGE CPP #-}
module Graphics.PDF.Typesetting.Horizontal (
HBox(..)
, mkHboxWithRatio
, horizontalPostProcess
) where
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid
#endif
import Graphics.PDF.LowLevel.Types
import Graphics.PDF.Typesetting.Breaking
import Graphics.PDF.Shapes
import Graphics.PDF.Draw
import Graphics.PDF.Coordinates
import qualified Data.ByteString as S(reverse,cons,singleton)
import Data.Maybe(isJust,fromJust)
import Data.List(foldl')
import Graphics.PDF.Colors
import Graphics.PDF.Text
import Graphics.PDF.Typesetting.Box
import Control.Monad.Writer(tell)
import Control.Monad(when)
import Graphics.PDF.LowLevel.Serializer
saveCurrentword :: PDFGlyph -> PDFGlyph
saveCurrentword (PDFGlyph g) = PDFGlyph . S.reverse $ g
createWords :: ComparableStyle s => PDFFloat
-> Maybe (s,PDFGlyph, PDFFloat)
-> [Letter s]
-> [HBox s]
createWords _ Nothing [] = []
createWords _ (Just (s,t,w)) [] = [createText s (saveCurrentword t) w]
createWords r Nothing ((AGlyph s t w):l) = createWords r (Just (s,PDFGlyph (S.singleton (fromIntegral t)),w)) l
createWords r (Just (s,PDFGlyph t,w)) ((AGlyph s' t' w'):l) | s `isSameStyleAs` s' = createWords r (Just (s,PDFGlyph (S.cons (fromIntegral t') t),w+w')) l
| otherwise = (createText s (saveCurrentword $ (PDFGlyph t)) w):createWords r (Just (s',PDFGlyph (S.singleton (fromIntegral t')),w')) l
createWords r (Just (s,t,w)) ((Glue w' y z (Just s')):l) = (createText s (saveCurrentword $ t) w):(HGlue w' (Just(y,z)) (Just s')):createWords r Nothing l
createWords r c (Penalty _:l) = createWords r c l
createWords r c (FlaggedPenalty _ _ _:l) = createWords r c l
createWords r Nothing ((Glue w' y z s):l) = (HGlue w' (Just(y,z)) s):createWords r Nothing l
createWords r (Just (s,t,w)) ((Glue w' y z Nothing):l) = (createText s (saveCurrentword $ t) w):(HGlue w' (Just(y,z)) Nothing):createWords r Nothing l
createWords r Nothing ((Kern w' s):l) = (HGlue w' Nothing s):createWords r Nothing l
createWords r (Just (s,t,w)) ((Kern w' s'):l) = (createText s (saveCurrentword $ t) w):(HGlue w' Nothing s'):createWords r Nothing l
createWords r Nothing ((Letter d a s):l) = (SomeHBox d a s):createWords r Nothing l
createWords r (Just (s,t,w)) ((Letter d a st):l) = (createText s (saveCurrentword $ t) w):(SomeHBox d a st):createWords r Nothing l
horizontalPostProcess :: (Style s)
=> [(PDFFloat,[Letter s],[Letter s])]
-> [(HBox s,[Letter s])]
horizontalPostProcess [] = []
horizontalPostProcess ((r,l',r'):l) = let l'' = createWords r Nothing . simplify $ l' in
if null l''
then
horizontalPostProcess l
else
((mkHboxWithRatio r l''),r'):horizontalPostProcess l
data HBox s = HBox !PDFFloat !PDFFloat !PDFFloat ![HBox s]
| HGlue !PDFFloat !(Maybe (PDFFloat,PDFFloat)) !(Maybe s)
| Text !s !PDFGlyph !PDFFloat
| SomeHBox !BoxDimension !AnyBox !(Maybe s)
withNewStyle :: s -> HBox s -> HBox s
withNewStyle _ a@(HBox _ _ _ _) = a
withNewStyle s (HGlue a b _) = HGlue a b (Just s)
withNewStyle s (Text _ a b) = Text s a b
withNewStyle s (SomeHBox d a _) = SomeHBox d a (Just s)
mkHboxWithRatio :: Style s => PDFFloat
-> [HBox s]
-> HBox s
mkHboxWithRatio _ [] = error "Cannot create an empty horizontal box"
mkHboxWithRatio r l =
let w = foldl' (\x y -> x + glueSizeWithRatio y r) 0.0 l
ascent = maximum . map boxAscent $ l
d = maximum . map boxDescent $ l
h = ascent + d
addBox (HGlue gw (Just(y,z)) s) (HBox w' h' d' l') = HBox w' h' d' (HGlue (glueSize gw y z r) Nothing s:l')
addBox a (HBox w' h' d' l') = HBox w' h' d' (a:l')
addBox _ _ = error "We can add boxes only to an horizontal list"
in
foldr addBox (HBox w h d []) l
instance Style s => MaybeGlue (HBox s) where
glueSizeWithRatio (HGlue w (Just(y,z)) _) r = glueSize w y z r
glueSizeWithRatio a _ = boxWidth a
glueY (HGlue _ (Just(y,_)) _) = y
glueY _ = 0
glueZ (HGlue _ (Just(_,z)) _) = z
glueZ _ = 0
createText :: s
-> PDFGlyph
-> PDFFloat
-> HBox s
createText s t w = Text s t w
instance Show (HBox s) where
show (HBox _ _ _ a) = "(HBox " ++ show a ++ ")"
show (HGlue a _ _) = "(HGlue " ++ show a ++ ")"
show (Text _ t _) = "(Text " ++ show t ++ ")"
show (SomeHBox _ t _) = "(SomeHBox " ++ show t ++ ")"
drawTextLine :: (Style s) => s -> [HBox s] -> PDFFloat -> PDFFloat -> Draw ()
drawTextLine _ [] _ _ = return ()
drawTextLine style l@(a:l') x y | (isJust . wordStyle $ style) = do
let h = boxHeight a
d = boxDescent a
y' = y + h - d
strokeBox (withNewStyle style a) x y'
drawTextLine (updateStyle style) l' (x + boxWidth a) y
| otherwise = drawWords style l x y
drawWords :: (Style s) => s -> [HBox s] -> PDFFloat -> PDFFloat -> Draw ()
drawWords _ [] _ _ = return ()
drawWords s ((Text _ t w):l) x y = do
(l',x') <- drawText $ do
drawTheTextBox StartText s x y (Just t)
drawPureWords s l (x + w) y
drawWords s l' x' y
drawWords s l@((HGlue _ _ _ ):_) x y = do
(l',x') <- drawText $ do
drawTheTextBox StartText s x y Nothing
drawPureWords s l x y
drawWords s l' x' y
drawWords s (a@(SomeHBox _ _ _):l) x y = do
let h = boxHeight a
d = boxDescent a
w = boxWidth a
y' = y - d + h
strokeBox a x y'
drawWords s l (x + w) y
drawWords _ _ _ _ = return ()
drawPureWords :: Style s => s -> [HBox s] -> PDFFloat -> PDFFloat -> PDFText ([HBox s],PDFFloat)
drawPureWords s [] x y = do
drawTheTextBox StopText s x y Nothing
return ([],x)
drawPureWords s ((Text _ t w):l) x y = do
drawTheTextBox ContinueText s x y (Just t)
drawPureWords s l (x + w) y
drawPureWords s ((HGlue w _ _):l) x y = do
drawTextGlue s w
drawPureWords s l (x + w) y
drawPureWords s l@((SomeHBox _ _ _):_) x y = do
drawTheTextBox StopText s x y Nothing
return (l,x)
drawPureWords s (_:l) x y = drawPureWords s l x y
startDrawingNewLineOfText :: (Style s) => PDFFloat -> PDFFloat -> [HBox s] -> PDFFloat -> PDFFloat -> s -> Draw ()
startDrawingNewLineOfText hl dl l x y style =
do
let y' = y - hl + dl
(l',l'') = span (isSameStyle style) l
w' = foldl' (\x' ny -> x' + boxWidth ny) 0.0 l'
if (isJust . sentenceStyle $ style)
then do
(fromJust . sentenceStyle $ style) (Rectangle (x :+ (y - hl)) ((x+w') :+ y)) (drawTextLine style l' x y')
else do
drawTextLine style l' x y'
drawLineOfHboxes hl dl l'' (x + w') y
drawLineOfHboxes :: (Style s) => PDFFloat
-> PDFFloat
-> [HBox s]
-> PDFFloat
-> PDFFloat
-> Draw ()
drawLineOfHboxes _ _ [] _ _ = return ()
drawLineOfHboxes hl dl l@((Text style _ _):_) x y = startDrawingNewLineOfText hl dl l x y style
drawLineOfHboxes hl dl l@((HGlue _ _ (Just style)):_) x y = startDrawingNewLineOfText hl dl l x y style
drawLineOfHboxes hl dl (a:l) x y = do
let h = boxHeight a
d = boxDescent a
y' = y - hl + dl - d + h
strokeBox a x y'
drawLineOfHboxes hl dl l (x + boxWidth a) y
instance Style s => Box (HBox s) where
boxWidth (Text _ _ w) = w
boxWidth (HBox w _ _ _) = w
boxWidth (SomeHBox d _ _) = boxWidth d
boxWidth (HGlue w _ _) = w
boxHeight (Text style _ _) = styleHeight style
boxHeight (HBox _ h _ _) = h
boxHeight (SomeHBox d _ _) = boxHeight d
boxHeight (HGlue _ _ (Just s)) = styleHeight s
boxHeight (HGlue _ _ _) = 0
boxDescent (Text style _ _) = styleDescent style
boxDescent (HBox _ _ d _) = d
boxDescent (SomeHBox d _ _) = boxDescent d
boxDescent (HGlue _ _ (Just s)) = styleDescent s
boxDescent (HGlue _ _ _) = 0
drawTheTextBox :: Style style => TextDrawingState
-> style
-> PDFFloat
-> PDFFloat
-> Maybe PDFGlyph
-> PDFText ()
drawTheTextBox state style x y t = do
when (state == StartText || state == OneBlock) $ (do
setFont (textFont . textStyle $ style)
strokeColor (textStrokeColor . textStyle $ style)
fillColor (textFillColor . textStyle $ style)
renderMode (textMode . textStyle $ style)
setWidth (penWidth . textStyle $ style)
textStart x y
tell $ mconcat [newline,lbracket])
when (state == StartText || state == OneBlock || state == ContinueText) $ (do
case t of
Nothing -> return ()
Just myText -> tell $ toPDF myText
)
when (state == StopText || state == OneBlock) $ (do
tell rbracket
tell $ serialize " TJ")
drawTextGlue :: Style style
=> style
-> PDFFloat
-> PDFText ()
drawTextGlue style w = do
let ws = spaceWidth style
PDFFont _ size = textFont . textStyle $ style
delta = w - ws
return ()
tell . mconcat $ [ lparen, bspace,rparen,bspace,toPDF ((-delta) * 1000.0 / (fromIntegral size) ), bspace]
data TextDrawingState = StartText
| ContinueText
| StopText
| OneBlock
deriving(Eq)
instance (Style s) => DisplayableBox (HBox s) where
strokeBox a@(HBox _ _ _ l) x y = do
let he = boxHeight a
de = boxDescent a
drawLineOfHboxes he de l x y
strokeBox a@(HGlue w _ (Just style)) x y = do
let de = boxDescent a
he = boxHeight a
y' = y - he + de
if (isJust . wordStyle $ style)
then
(fromJust . wordStyle $ style) (Rectangle (x :+ (y' - de)) ((x+w) :+ (y' - de + he))) DrawGlue (return ())
else
return ()
strokeBox a@(Text style t w) x y = do
let de = boxDescent a
he = boxHeight a
y' = y - he + de
if (isJust . wordStyle $ style)
then
(fromJust . wordStyle $ style) (Rectangle (x :+ (y' - de)) ((x+w) :+ (y' - de + he))) DrawWord (drawText $ drawTheTextBox OneBlock style x y' (Just t))
else
drawText $ drawTheTextBox OneBlock style x y' (Just t)
strokeBox (SomeHBox _ a _) x y = strokeBox a x y
strokeBox (HGlue _ _ _) _ _ = return ()
isSameStyle :: (Style s) => s
-> HBox s
-> Bool
isSameStyle s (Text style _ _) = s `isSameStyleAs` style
isSameStyle s (HGlue _ _ (Just style)) = s `isSameStyleAs` style
isSameStyle s (SomeHBox _ _ (Just style)) = s `isSameStyleAs` style
isSameStyle _ _ = False