module Graphics.PDF.Typesetting.Horizontal (
HBox(..)
, mkHboxWithRatio
, horizontalPostProcess
) where
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.Char8 as S(pack)
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
import Data.Monoid
saveCurrentword :: String -> PDFString
saveCurrentword = PDFString . S.pack . reverse
createWords :: ComparableStyle s => PDFFloat
-> Maybe (s,String, PDFFloat)
-> [Letter s]
-> [HBox s]
createWords _ Nothing [] = []
createWords _ (Just (s,t,w)) [] = [createText s (saveCurrentword t) w]
createWords r Nothing ((AChar s t w):l) = createWords r (Just (s,[t],w)) l
createWords r (Just (s,t,w)) ((AChar s' t' w'):l) | s `isSameStyleAs` s' = createWords r (Just (s,t':t,w+w')) l
| otherwise = (createText s (saveCurrentword $ t) w):createWords r (Just (s',[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 !PDFString !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
-> PDFString
-> 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 PDFString
-> 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 = (textWidth (textFont . textStyle $ style) (toPDFString " "))
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