{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
module Graphics.PDF.Text(
PDFFont(..)
, FontName(..)
, TextMode(..)
, PDFText
, UnscaledUnit
, drawText
, text
, startNewLine
, displayGlyphs
, displayText
, textStart
, setFont
, leading
, charSpace
, wordSpace
, textScale
, renderMode
, rise
, setTextMatrix
, textWidth
, pdfGlyph
, glyph
) where
import Graphics.PDF.LowLevel.Types
import Graphics.PDF.Draw
import Control.Monad.State
import Graphics.PDF.Resources
import Control.Monad.Writer
import qualified Data.Set as Set
import Data.List(foldl')
import Data.Binary.Builder(Builder)
import Graphics.PDF.LowLevel.Serializer
import qualified Data.ByteString as S
import qualified Data.Text as T
import Graphics.PDF.Fonts.Font
import Graphics.PDF.Fonts.StandardFont
glyphStreamWidth :: PDFFont
-> PDFGlyph
-> PDFFloat
glyphStreamWidth (PDFFont f s) (PDFGlyph t) =
let w = foldl' (\a b -> a + glyphWidth f s (fromIntegral b)) 0 . S.unpack $ t
in
w + (foldl' (\a (x,y) -> a + getKern f s x y) 0 $ [(GlyphCode ca,GlyphCode cb) | (ca,cb) <- S.zip t (S.tail t)])
textWidth :: PDFFont -> T.Text -> PDFFloat
textWidth f t = glyphStreamWidth f . pdfGlyph f $ t
pdfGlyph :: PDFFont
-> T.Text
-> PDFGlyph
pdfGlyph (PDFFont f _) t = PDFGlyph . S.pack . map (fromIntegral . charGlyph f) . T.unpack $ t
type FontState = (Set.Set AnyFont)
data TextParameter = TextParameter { tc :: !PDFFloat
, tw :: !PDFFloat
, tz :: !PDFFloat
, tl :: !PDFFloat
, ts :: !PDFFloat
, fontState :: FontState
, currentFont :: Maybe PDFFont
}
defaultParameters :: TextParameter
defaultParameters = TextParameter 0 0 100 0 0 (Set.empty) Nothing
newtype PDFText a = PDFText {unText :: WriterT Builder (State TextParameter) a}
#ifndef __HADDOCK__
deriving(Monad,Applicative,Functor,MonadWriter Builder,MonadState TextParameter)
#else
instance Monad PDFText
instance Functor PDFText
instance MonadWriter Builder PDFText
instance MonadState TextParameter PDFText
#endif
instance MonadPath PDFText
type UnscaledUnit = PDFFloat
data TextMode = FillText
| StrokeText
| FillAndStrokeText
| InvisibleText
| FillTextAndAddToClip
| StrokeTextAndAddToClip
| FillAndStrokeTextAndAddToClip
| AddToClip
deriving(Eq,Ord,Enum)
setFont :: PDFFont -> PDFText ()
setFont f@(PDFFont n size) = PDFText $ do
lift (modifyStrict $ \s -> s {fontState = Set.insert n (fontState s), currentFont = Just f})
tell . mconcat$ [ serialize "\n/"
, serialize (name n)
, serialize ' '
, toPDF size
, serialize " Tf"
]
drawText :: PDFText a
-> Draw a
drawText t = do
let ((a,w),s) = (runState . runWriterT . unText $ t) defaultParameters
mapM_ addFontRsrc (Set.elems (fontState s))
tell . serialize $ "\nBT"
tell w
tell . serialize $ "\nET"
return a
where
addFontRsrc font = modifyStrict $ \s ->
s { rsrc = addResource (PDFName "Font") (PDFName (name font)) (toRsrc font) (rsrc s)}
textStart :: PDFFloat
-> PDFFloat
-> PDFText ()
textStart x y = tell . mconcat $ [ serialize '\n'
, toPDF x
, serialize ' '
, toPDF y
, serialize " Td"
]
glyph :: GlyphCode -> PDFGlyph
glyph c = PDFGlyph . S.singleton $ (fromIntegral c)
displayGlyphs :: PDFGlyph
-> PDFText ()
displayGlyphs t = do
tell $ serialize ' '
tell . toPDF $ t
tell . serialize $ " Tj"
displayText :: T.Text
-> PDFText ()
displayText t = do
f <- gets currentFont
case f of
Nothing -> return ()
Just aFont -> do
let g = pdfGlyph aFont t
displayGlyphs g
startNewLine :: PDFText ()
startNewLine = tell . serialize $ "\nT*"
leading :: UnscaledUnit -> PDFText ()
leading v = PDFText $ do
lift (modifyStrict $ \s -> s {tl = v})
tell . mconcat $ [ serialize '\n'
, toPDF v
, serialize " TL"
]
charSpace :: UnscaledUnit -> PDFText ()
charSpace v = PDFText $ do
lift (modifyStrict $ \s -> s {tc = v})
tell . mconcat $ [ serialize '\n'
, toPDF v
, serialize " Tc"
]
wordSpace :: UnscaledUnit -> PDFText ()
wordSpace v = PDFText $ do
lift (modifyStrict $ \s -> s {tw = v})
tell . mconcat $ [ serialize '\n'
, toPDF v
, serialize " Tw"
]
textScale :: PDFFloat -> PDFText ()
textScale v = PDFText $ do
lift (modifyStrict $ \s -> s {tz = v})
tell . mconcat $ [ serialize '\n'
, toPDF v
, serialize " Tz"
]
renderMode :: TextMode -> PDFText ()
renderMode v =
tell . mconcat $ [ serialize '\n'
, toPDF (fromEnum v)
, serialize " Tr"
]
rise :: UnscaledUnit -> PDFText ()
rise v = PDFText $ do
lift (modifyStrict $ \s -> s {ts = v})
tell . mconcat $ [ serialize '\n'
, toPDF v
, serialize " Ts"
]
setTextMatrix :: Matrix -> PDFText()
setTextMatrix (Matrix a b c d e f) =
tell . mconcat $[ serialize '\n'
, toPDF a
, serialize ' '
, toPDF b
, serialize ' '
, toPDF c
, serialize ' '
, toPDF d
, serialize ' '
, toPDF e
, serialize ' '
, toPDF f
, serialize " Tm"
]
text :: PDFFont
-> PDFFloat
-> PDFFloat
-> T.Text
-> PDFText ()
text f x y t = do
setFont f
let g = pdfGlyph f t
textStart x y
displayGlyphs g