{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE TupleSections     #-}
{- |
   Module      : Text.Pandoc.SelfContained
   Copyright   : Copyright (C) 2011-2024 John MacFarlane
   License     : GNU GPL, version 2 or above

   Maintainer  : John MacFarlane <jgm@berkeley.edu>
   Stability   : alpha
   Portability : portable

Functions for converting an HTML file into one that can be viewed
offline, by incorporating linked images, CSS, and scripts into
the HTML using data URIs.
-}
module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where
import Codec.Compression.GZip as Gzip
import Control.Applicative ((<|>))
import Data.ByteString (ByteString)
import Data.ByteString.Base64 (encode)
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy as L
import qualified Data.Text as T
import Data.Char (isAlphaNum, isAscii)
import Data.Digest.Pure.SHA (sha1, showDigest)
import Network.URI (escapeURIString)
import System.FilePath (takeDirectory, takeExtension, (</>))
import Text.HTML.TagSoup
import Text.Pandoc.Class.PandocMonad (PandocMonad (..), fetchItem,
                                      getInputFiles, report, setInputFiles)
import Text.Pandoc.Logging
import Text.Pandoc.Error (PandocError(..))
import Text.Pandoc.MIME (MimeType)
import Text.Pandoc.Shared (renderTags', trim, tshow, safeRead)
import Text.Pandoc.URI (isURI)
import Text.Pandoc.UTF8 (toString, toText, fromText)
import Text.Pandoc.Parsing (ParsecT, runParserT)
import qualified Text.Pandoc.Parsing as P
import Control.Monad.Except (throwError, catchError)
import Data.Either (lefts, rights)
import Data.Maybe (isNothing)
import qualified Data.Map as M
import Control.Monad.State
-- import Debug.Trace

isOk :: Char -> Bool
isOk :: Char -> Bool
isOk Char
c = Char -> Bool
isAscii Char
c Bool -> Bool -> Bool
&& Char -> Bool
isAlphaNum Char
c

makeDataURI :: (MimeType, ByteString) -> T.Text
makeDataURI :: (Text, ByteString) -> Text
makeDataURI (Text
mime, ByteString
raw) =
  if Bool
textual
     then Text
"data:" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
mime' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"," Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
T.pack ((Char -> Bool) -> String -> String
escapeURIString Char -> Bool
isOk (ByteString -> String
toString ByteString
raw))
     else Text
"data:" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
mime' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
";base64," Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
toText (ByteString -> ByteString
encode ByteString
raw')
  where textual :: Bool
textual = Text
"text/" Text -> Text -> Bool
`T.isPrefixOf` Text
mime
        raw' :: ByteString
raw' = if Text
"+xml" Text -> Text -> Bool
`T.isSuffixOf` Text
mime
                  then (Char -> Bool) -> ByteString -> ByteString
B.filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\r') ByteString
raw  -- strip off CRs
                  else ByteString
raw
        mime' :: Text
mime' = if Bool
textual Bool -> Bool -> Bool
&& (Char -> Bool) -> Text -> Bool
T.any (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
';') Text
mime
                   then Text
mime Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
";charset=utf-8"
                   else Text
mime  -- mime type already has charset

isSourceAttribute :: T.Text -> (T.Text, T.Text) -> Bool
isSourceAttribute :: Text -> (Text, Text) -> Bool
isSourceAttribute Text
tagname (Text
x,Text
_) =
  Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"src" Bool -> Bool -> Bool
||
  Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"data-src" Bool -> Bool -> Bool
||
  (Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"href" Bool -> Bool -> Bool
&& Text
tagname Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"link") Bool -> Bool -> Bool
||
  Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"poster" Bool -> Bool -> Bool
||
  Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"data-background-image"

data ConvertState =
  ConvertState
  { ConvertState -> Bool
isHtml5 :: Bool
  , ConvertState -> Map Text (Text, [(Text, Text)])
svgMap  :: M.Map T.Text (T.Text, [Attribute T.Text])
    -- map from hash to (id, svg attributes)
  } deriving (Int -> ConvertState -> String -> String
[ConvertState] -> String -> String
ConvertState -> String
(Int -> ConvertState -> String -> String)
-> (ConvertState -> String)
-> ([ConvertState] -> String -> String)
-> Show ConvertState
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> ConvertState -> String -> String
showsPrec :: Int -> ConvertState -> String -> String
$cshow :: ConvertState -> String
show :: ConvertState -> String
$cshowList :: [ConvertState] -> String -> String
showList :: [ConvertState] -> String -> String
Show)

convertTags :: PandocMonad m =>
               [Tag T.Text] -> StateT ConvertState m [Tag T.Text]
convertTags :: forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [] = [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return []
convertTags (t :: Tag Text
t@TagOpen{}:[Tag Text]
ts)
  | Text -> Tag Text -> Text
forall str.
(Show str, Eq str, StringLike str) =>
str -> Tag str -> str
fromAttrib Text
"data-external" Tag Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"1" = (Tag Text
tTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:) ([Tag Text] -> [Tag Text])
-> StateT ConvertState m [Tag Text]
-> StateT ConvertState m [Tag Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
convertTags (t :: Tag Text
t@(TagOpen Text
"style" [(Text, Text)]
_):[Tag Text]
ts) =
  case (Tag Text -> Bool) -> [Tag Text] -> ([Tag Text], [Tag Text])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Tag Text -> Bool
forall str. Tag str -> Bool
isTagText [Tag Text]
ts of
    ([Tag Text]
xs,[Tag Text]
rest) -> do
      [Tag Text]
xs' <- (Tag Text -> StateT ConvertState m (Tag Text))
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (\case
                    TagText Text
s -> Text -> Tag Text
forall str. str -> Tag str
TagText (Text -> Tag Text)
-> (ByteString -> Text) -> ByteString -> Tag Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
toText (ByteString -> Tag Text)
-> StateT ConvertState m ByteString
-> StateT ConvertState m (Tag Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> ByteString -> StateT ConvertState m ByteString
forall (m :: * -> *).
PandocMonad m =>
String -> ByteString -> m ByteString
cssURLs String
"" (Text -> ByteString
fromText Text
s)
                    Tag Text
tag -> Tag Text -> StateT ConvertState m (Tag Text)
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return Tag Text
tag) [Tag Text]
xs
      ((Tag Text
tTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:[Tag Text]
xs') [Tag Text] -> [Tag Text] -> [Tag Text]
forall a. [a] -> [a] -> [a]
++) ([Tag Text] -> [Tag Text])
-> StateT ConvertState m [Tag Text]
-> StateT ConvertState m [Tag Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
rest
convertTags (t :: Tag Text
t@(TagOpen Text
"script" [(Text, Text)]
as):tc :: Tag Text
tc@(TagClose Text
"script"):[Tag Text]
ts) =
  case Text -> Tag Text -> Text
forall str.
(Show str, Eq str, StringLike str) =>
str -> Tag str -> str
fromAttrib Text
"src" Tag Text
t of
       Text
""  -> (Tag Text
tTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:) ([Tag Text] -> [Tag Text])
-> StateT ConvertState m [Tag Text]
-> StateT ConvertState m [Tag Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
       Text
src -> do
           let typeAttr :: Text
typeAttr = Text -> Tag Text -> Text
forall str.
(Show str, Eq str, StringLike str) =>
str -> Tag str -> str
fromAttrib Text
"type" Tag Text
t
           GetDataResult
res <- Text -> Text -> StateT ConvertState m GetDataResult
forall (m :: * -> *).
PandocMonad m =>
Text -> Text -> m GetDataResult
getData Text
typeAttr Text
src
           [Tag Text]
rest <- [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
           case GetDataResult
res of
                AlreadyDataURI Text
dataUri -> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"script"
                     ((Text
"src",Text
dataUri) (Text, Text) -> [(Text, Text)] -> [(Text, Text)]
forall a. a -> [a] -> [a]
: [(Text
x,Text
y) | (Text
x,Text
y) <- [(Text, Text)]
as, Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"src"]) Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                     Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"script" Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: [Tag Text]
rest
                Fetched (Text
mime, ByteString
bs)
                  | (Text
"text/javascript" Text -> Text -> Bool
`T.isPrefixOf` Text
mime Bool -> Bool -> Bool
||
                     Text
"application/javascript" Text -> Text -> Bool
`T.isPrefixOf` Text
mime Bool -> Bool -> Bool
||
                     Text
"application/x-javascript" Text -> Text -> Bool
`T.isPrefixOf` Text
mime) Bool -> Bool -> Bool
&&
                     Bool -> Bool
not (ByteString
"</script" ByteString -> ByteString -> Bool
`B.isInfixOf` ByteString
bs) ->
                     [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$
                       Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"script" [(Text
"type", Text
typeAttr)|Bool -> Bool
not (Text -> Bool
T.null Text
typeAttr)]
                       Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: Text -> Tag Text
forall str. str -> Tag str
TagText (ByteString -> Text
toText ByteString
bs)
                       Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"script"
                       Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: [Tag Text]
rest
                  | Bool
otherwise ->
                       [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return  ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"script"
                         ((Text
"src",(Text, ByteString) -> Text
makeDataURI (Text
mime, ByteString
bs)) (Text, Text) -> [(Text, Text)] -> [(Text, Text)]
forall a. a -> [a] -> [a]
:
                          [(Text
x,Text
y) | (Text
x,Text
y) <- [(Text, Text)]
as, Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"src"]) Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                        Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"script" Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: [Tag Text]
rest
                CouldNotFetch PandocError
_ -> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Tag Text
tTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:Tag Text
tcTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:[Tag Text]
rest
convertTags (t :: Tag Text
t@(TagOpen Text
"link" [(Text, Text)]
as):[Tag Text]
ts) =
  case Text -> Tag Text -> Text
forall str.
(Show str, Eq str, StringLike str) =>
str -> Tag str -> str
fromAttrib Text
"href" Tag Text
t of
       Text
""  -> (Tag Text
tTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:) ([Tag Text] -> [Tag Text])
-> StateT ConvertState m [Tag Text]
-> StateT ConvertState m [Tag Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
       Text
src -> do
           GetDataResult
res <- Text -> Text -> StateT ConvertState m GetDataResult
forall (m :: * -> *).
PandocMonad m =>
Text -> Text -> m GetDataResult
getData (Text -> Tag Text -> Text
forall str.
(Show str, Eq str, StringLike str) =>
str -> Tag str -> str
fromAttrib Text
"type" Tag Text
t) Text
src
           case GetDataResult
res of
                AlreadyDataURI Text
dataUri -> do
                  [Tag Text]
rest <- [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
                  [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"link"
                     ((Text
"href",Text
dataUri) (Text, Text) -> [(Text, Text)] -> [(Text, Text)]
forall a. a -> [a] -> [a]
: [(Text
x,Text
y) | (Text
x,Text
y) <- [(Text, Text)]
as, Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"href"]) Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                     [Tag Text]
rest
                Fetched (Text
mime, ByteString
bs)
                  | Text
"text/css" Text -> Text -> Bool
`T.isPrefixOf` Text
mime
                    Bool -> Bool -> Bool
&& Text -> Bool
T.null (Text -> Tag Text -> Text
forall str.
(Show str, Eq str, StringLike str) =>
str -> Tag str -> str
fromAttrib Text
"media" Tag Text
t)
                    Bool -> Bool -> Bool
&& Bool -> Bool
not (ByteString
"</" ByteString -> ByteString -> Bool
`B.isInfixOf` ByteString
bs) -> do
                      [Tag Text]
rest <- [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$
                                 (Tag Text -> Bool) -> [Tag Text] -> [Tag Text]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Tag Text -> Tag Text -> Bool
forall a. Eq a => a -> a -> Bool
==Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"link") [Tag Text]
ts
                      [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$
                       Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"style" [(Text
"type", Text
"text/css")] -- see #5725
                       Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: Text -> Tag Text
forall str. str -> Tag str
TagText (ByteString -> Text
toText ByteString
bs)
                       Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"style"
                       Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: [Tag Text]
rest
                  | Bool
otherwise -> do
                      [Tag Text]
rest <- [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
                      [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"link"
                       ((Text
"href",(Text, ByteString) -> Text
makeDataURI (Text
mime, ByteString
bs)) (Text, Text) -> [(Text, Text)] -> [(Text, Text)]
forall a. a -> [a] -> [a]
:
                         [(Text
x,Text
y) | (Text
x,Text
y) <- [(Text, Text)]
as, Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"href"]) Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: [Tag Text]
rest
                CouldNotFetch PandocError
_ -> do
                      [Tag Text]
rest <- [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
                      [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Tag Text
tTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:[Tag Text]
rest
convertTags (t :: Tag Text
t@(TagOpen Text
tagname [(Text, Text)]
as):[Tag Text]
ts)
  | ((Text, Text) -> Bool) -> [(Text, Text)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Text -> (Text, Text) -> Bool
isSourceAttribute Text
tagname) [(Text, Text)]
as
     = do
       let inlineSvgs :: Bool
inlineSvgs = Text
tagname Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"img" Bool -> Bool -> Bool
&&
                        case Text -> [Text]
T.words (Text -> [Text]) -> Maybe Text -> Maybe [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"class" [(Text, Text)]
as of
                          Maybe [Text]
Nothing -> Bool
False
                          Just [Text]
cs -> Text
"inline-svg" Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
cs
       [Either (Text, [Tag Text]) (Text, Text)]
as' <- ((Text, Text)
 -> StateT ConvertState m (Either (Text, [Tag Text]) (Text, Text)))
-> [(Text, Text)]
-> StateT ConvertState m [Either (Text, [Tag Text]) (Text, Text)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Bool
-> (Text, Text)
-> StateT ConvertState m (Either (Text, [Tag Text]) (Text, Text))
forall {m :: * -> *}.
PandocMonad m =>
Bool -> (Text, Text) -> m (Either (Text, [Tag Text]) (Text, Text))
processAttribute Bool
inlineSvgs) [(Text, Text)]
as
       let attrs :: [(Text, Text)]
attrs = Text -> [(Text, Text)] -> [(Text, Text)]
addRole Text
"img" ([(Text, Text)] -> [(Text, Text)])
-> [(Text, Text)] -> [(Text, Text)]
forall a b. (a -> b) -> a -> b
$ [(Text, Text)] -> [(Text, Text)]
addAriaLabel ([(Text, Text)] -> [(Text, Text)])
-> [(Text, Text)] -> [(Text, Text)]
forall a b. (a -> b) -> a -> b
$ [Either (Text, [Tag Text]) (Text, Text)] -> [(Text, Text)]
forall a b. [Either a b] -> [b]
rights [Either (Text, [Tag Text]) (Text, Text)]
as'
       let svgContents :: [(Text, [Tag Text])]
svgContents = [Either (Text, [Tag Text]) (Text, Text)] -> [(Text, [Tag Text])]
forall a b. [Either a b] -> [a]
lefts [Either (Text, [Tag Text]) (Text, Text)]
as'
       [Tag Text]
rest <- [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts
       case [(Text, [Tag Text])]
svgContents of
         [] -> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
tagname [(Text, Text)]
attrs Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: [Tag Text]
rest
         ((Text
hash, [Tag Text]
tags) : [(Text, [Tag Text])]
_) -> do
             -- drop "</img>" if present
             let rest' :: [Tag Text]
rest' = case [Tag Text]
rest of
                           TagClose Text
tn : [Tag Text]
xs | Text
tn Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
tagname ->  [Tag Text]
xs
                           [Tag Text]
_ -> [Tag Text]
rest
             Map Text (Text, [(Text, Text)])
svgmap <- (ConvertState -> Map Text (Text, [(Text, Text)]))
-> StateT ConvertState m (Map Text (Text, [(Text, Text)]))
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets ConvertState -> Map Text (Text, [(Text, Text)])
svgMap
             case Text
-> Map Text (Text, [(Text, Text)]) -> Maybe (Text, [(Text, Text)])
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup Text
hash Map Text (Text, [(Text, Text)])
svgmap of
               Just (Text
svgid, [(Text, Text)]
svgattrs) -> do
                 let attrs' :: [(Text, Text)]
attrs' = [(Text
k,Text
v) | (Text
k,Text
v) <- [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
combineSvgAttrs [(Text, Text)]
svgattrs [(Text, Text)]
attrs
                                     , Text
k Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"id"]
                 [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"svg" [(Text, Text)]
attrs' Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                          Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"use" [(Text
"href", Text
"#" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
svgid),
                                         (Text
"width", Text
"100%"),
                                         (Text
"height", Text
"100%")] Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                          Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"use" Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                          Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"svg" Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                          [Tag Text]
rest'
               Maybe (Text, [(Text, Text)])
Nothing ->
                  case (Tag Text -> Bool) -> [Tag Text] -> [Tag Text]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Bool -> Bool
not (Bool -> Bool) -> (Tag Text -> Bool) -> Tag Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Tag Text -> Bool
forall str. Eq str => str -> Tag str -> Bool
isTagOpenName Text
"svg") [Tag Text]
tags of
                    TagOpen Text
"svg" [(Text, Text)]
svgattrs : [Tag Text]
tags' -> do
                      let attrs' :: [(Text, Text)]
attrs' = [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
combineSvgAttrs [(Text, Text)]
svgattrs [(Text, Text)]
attrs
                      let svgid :: Text
svgid = case Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"id" [(Text, Text)]
attrs' of
                                     Just Text
id' -> Text
id'
                                     Maybe Text
Nothing -> Text
"svg_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
hash
                      let attrs'' :: [(Text, Text)]
attrs'' = (Text
"id", Text
svgid) (Text, Text) -> [(Text, Text)] -> [(Text, Text)]
forall a. a -> [a] -> [a]
:
                                    [(Text
k,Text
v) | (Text
k,Text
v) <- [(Text, Text)]
attrs', Text
k Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"id"]
                      (ConvertState -> ConvertState) -> StateT ConvertState m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((ConvertState -> ConvertState) -> StateT ConvertState m ())
-> (ConvertState -> ConvertState) -> StateT ConvertState m ()
forall a b. (a -> b) -> a -> b
$ \ConvertState
st ->
                        ConvertState
st{ svgMap = M.insert hash (svgid, attrs'') (svgMap st) }
                      let fixUrl :: Text -> Text
fixUrl Text
x =
                            case HasCallStack => Text -> Text -> (Text, Text)
Text -> Text -> (Text, Text)
T.breakOn Text
"url(#" Text
x of
                              (Text
_,Text
"") -> Text
x
                              (Text
before, Text
after) -> Text
before Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                                  Text
"url(#" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
svgid Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text -> Text
T.drop Int
5 Text
after
                      let addIdPrefix :: (a, Text) -> (a, Text)
addIdPrefix (a
"id", Text
x) = (a
"id", Text
svgid Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
x)
                          addIdPrefix (a
k, Text
x)
                           | a
k a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
"xlink:href" Bool -> Bool -> Bool
|| a
k a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
"href" =
                            case Text -> Maybe (Char, Text)
T.uncons Text
x of
                              Just (Char
'#', Text
x') -> (a
k, Text
"#" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
svgid Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
x')
                              Maybe (Char, Text)
_ -> (a
k, Text
x)
                          -- this clause handles things like
                          -- style="fill:url(#radialGradient46);stroke:none",
                          -- adding the svg id prefix to the anchor:
                          addIdPrefix (a
k, Text
x) = (a
k, Text -> Text
fixUrl Text
x)
                      let ensureUniqueId :: Tag Text -> Tag Text
ensureUniqueId (TagOpen Text
tname [(Text, Text)]
ats) =
                            Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
tname (((Text, Text) -> (Text, Text)) -> [(Text, Text)] -> [(Text, Text)]
forall a b. (a -> b) -> [a] -> [b]
map (Text, Text) -> (Text, Text)
forall {a}. (Eq a, IsString a) => (a, Text) -> (a, Text)
addIdPrefix [(Text, Text)]
ats)
                          ensureUniqueId Tag Text
x = Tag Text
x
                      [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
"svg" [(Text, Text)]
attrs'' Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:
                                 (Tag Text -> Tag Text) -> [Tag Text] -> [Tag Text]
forall a b. (a -> b) -> [a] -> [b]
map Tag Text -> Tag Text
ensureUniqueId [Tag Text]
tags' [Tag Text] -> [Tag Text] -> [Tag Text]
forall a. [a] -> [a] -> [a]
++ [Tag Text]
rest'
                    [Tag Text]
_ -> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a. a -> StateT ConvertState m a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Tag Text] -> StateT ConvertState m [Tag Text])
-> [Tag Text] -> StateT ConvertState m [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, Text)] -> Tag Text
forall str. str -> [Attribute str] -> Tag str
TagOpen Text
tagname [(Text, Text)]
attrs Tag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
: [Tag Text]
rest
  where processAttribute :: Bool -> (Text, Text) -> m (Either (Text, [Tag Text]) (Text, Text))
processAttribute Bool
inlineSvgs (Text
x,Text
y) =
           if Text -> (Text, Text) -> Bool
isSourceAttribute Text
tagname (Text
x,Text
y)
              then do
                GetDataResult
res <- Text -> Text -> m GetDataResult
forall (m :: * -> *).
PandocMonad m =>
Text -> Text -> m GetDataResult
getData (Text -> Tag Text -> Text
forall str.
(Show str, Eq str, StringLike str) =>
str -> Tag str -> str
fromAttrib Text
"type" Tag Text
t) Text
y
                case GetDataResult
res of
                  AlreadyDataURI Text
enc -> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (Text, [Tag Text]) (Text, Text)
 -> m (Either (Text, [Tag Text]) (Text, Text)))
-> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a b. (a -> b) -> a -> b
$ (Text, Text) -> Either (Text, [Tag Text]) (Text, Text)
forall a b. b -> Either a b
Right (Text
x, Text
enc)
                  Fetched (Text
"image/svg+xml", ByteString
bs) | Bool
inlineSvgs -> do
                    -- we filter CR in the hash to ensure that Windows
                    -- and non-Windows tests agree:
                    let hash :: Text
hash = String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Int -> String -> String
forall a. Int -> [a] -> [a]
take Int
20 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Digest SHA1State -> String
forall t. Digest t -> String
showDigest (Digest SHA1State -> String) -> Digest SHA1State -> String
forall a b. (a -> b) -> a -> b
$
                                        ByteString -> Digest SHA1State
sha1 (ByteString -> Digest SHA1State) -> ByteString -> Digest SHA1State
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
L.fromStrict
                                             (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> ByteString -> ByteString
B.filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
'\r') ByteString
bs
                    Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (Text, [Tag Text]) (Text, Text)
 -> m (Either (Text, [Tag Text]) (Text, Text)))
-> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a b. (a -> b) -> a -> b
$ (Text, [Tag Text]) -> Either (Text, [Tag Text]) (Text, Text)
forall a b. a -> Either a b
Left (Text
hash, Text -> [Tag Text]
getSvgTags (ByteString -> Text
toText ByteString
bs))
                  Fetched (Text
mt,ByteString
bs) -> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (Text, [Tag Text]) (Text, Text)
 -> m (Either (Text, [Tag Text]) (Text, Text)))
-> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a b. (a -> b) -> a -> b
$ (Text, Text) -> Either (Text, [Tag Text]) (Text, Text)
forall a b. b -> Either a b
Right (Text
x, (Text, ByteString) -> Text
makeDataURI (Text
mt,ByteString
bs))
                  CouldNotFetch PandocError
_ -> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (Text, [Tag Text]) (Text, Text)
 -> m (Either (Text, [Tag Text]) (Text, Text)))
-> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a b. (a -> b) -> a -> b
$ (Text, Text) -> Either (Text, [Tag Text]) (Text, Text)
forall a b. b -> Either a b
Right (Text
x, Text
y)
              else Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (Text, [Tag Text]) (Text, Text)
 -> m (Either (Text, [Tag Text]) (Text, Text)))
-> Either (Text, [Tag Text]) (Text, Text)
-> m (Either (Text, [Tag Text]) (Text, Text))
forall a b. (a -> b) -> a -> b
$ (Text, Text) -> Either (Text, [Tag Text]) (Text, Text)
forall a b. b -> Either a b
Right (Text
x,Text
y)

convertTags (Tag Text
t:[Tag Text]
ts) = (Tag Text
tTag Text -> [Tag Text] -> [Tag Text]
forall a. a -> [a] -> [a]
:) ([Tag Text] -> [Tag Text])
-> StateT ConvertState m [Tag Text]
-> StateT ConvertState m [Tag Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
ts

addRole :: T.Text -> [(T.Text, T.Text)] -> [(T.Text, T.Text)]
addRole :: Text -> [(Text, Text)] -> [(Text, Text)]
addRole Text
role [(Text, Text)]
attrs =
  case Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"role" [(Text, Text)]
attrs of
    Maybe Text
Nothing -> (Text
"role", Text
role) (Text, Text) -> [(Text, Text)] -> [(Text, Text)]
forall a. a -> [a] -> [a]
: [(Text, Text)]
attrs
    Just Text
_ -> [(Text, Text)]
attrs

addAriaLabel :: [(T.Text, T.Text)] -> [(T.Text, T.Text)]
addAriaLabel :: [(Text, Text)] -> [(Text, Text)]
addAriaLabel [(Text, Text)]
attrs =
  case Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"aria-label" [(Text, Text)]
attrs of
    Just Text
_ -> [(Text, Text)]
attrs
    Maybe Text
Nothing -> case Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"alt" [(Text, Text)]
attrs of
                 Just Text
alt -> (Text
"aria-label", Text
alt) (Text, Text) -> [(Text, Text)] -> [(Text, Text)]
forall a. a -> [a] -> [a]
: [(Text, Text)]
attrs
                 Maybe Text
Nothing -> [(Text, Text)]
attrs

-- we want to drop spaces, <?xml>, and comments before <svg>
-- and anything after </svg>:
getSvgTags :: T.Text -> [Tag T.Text]
getSvgTags :: Text -> [Tag Text]
getSvgTags Text
t =
  case (Tag Text -> Bool) -> [Tag Text] -> [Tag Text]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Bool -> Bool
not (Bool -> Bool) -> (Tag Text -> Bool) -> Tag Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Tag Text -> Bool
forall str. Eq str => str -> Tag str -> Bool
isTagCloseName Text
"svg") ([Tag Text] -> [Tag Text])
-> ([Tag Text] -> [Tag Text]) -> [Tag Text] -> [Tag Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
       (Tag Text -> Bool) -> [Tag Text] -> [Tag Text]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Bool -> Bool
not (Bool -> Bool) -> (Tag Text -> Bool) -> Tag Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Tag Text -> Bool
forall str. Eq str => str -> Tag str -> Bool
isTagOpenName Text
"svg") ([Tag Text] -> [Tag Text]) -> [Tag Text] -> [Tag Text]
forall a b. (a -> b) -> a -> b
$ Text -> [Tag Text]
forall str. StringLike str => str -> [Tag str]
parseTags Text
t of
    [] -> []
    [Tag Text]
xs -> [Tag Text]
xs [Tag Text] -> [Tag Text] -> [Tag Text]
forall a. [a] -> [a] -> [a]
++ [Text -> Tag Text
forall str. str -> Tag str
TagClose Text
"svg"]

combineSvgAttrs :: [(T.Text, T.Text)] -> [(T.Text, T.Text)] -> [(T.Text, T.Text)]
combineSvgAttrs :: [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
combineSvgAttrs [(Text, Text)]
svgAttrs [(Text, Text)]
imgAttrs =
  case (Maybe (Double, Double, Double, Double)
mbViewBox, Maybe Int
mbHeight, Maybe Int
mbWidth) of
    (Maybe (Double, Double, Double, Double)
Nothing, Just Int
h, Just Int
w) -> -- calculate viewBox
      [(Text, Text)]
combinedAttrs [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
forall a. [a] -> [a] -> [a]
++ [(Text
"viewBox", [Text] -> Text
T.unwords [Text
"0", Text
"0", Int -> Text
forall a. Show a => a -> Text
tshow Int
w, Int -> Text
forall a. Show a => a -> Text
tshow Int
h])]
    (Just (Double
_minx,Double
_miny,Double
w,Double
h), Maybe Int
Nothing, Maybe Int
Nothing) ->
        [(Text, Text)]
combinedAttrs [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
forall a. [a] -> [a] -> [a]
++
        [ (Text
"width", Text -> Text
dropPointZero (Double -> Text
forall a. Show a => a -> Text
tshow Double
w)) |
            Maybe Text -> Bool
forall a. Maybe a -> Bool
isNothing (Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"width" [(Text, Text)]
combinedAttrs) ] [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
forall a. [a] -> [a] -> [a]
++
        [ (Text
"height", Text -> Text
dropPointZero (Double -> Text
forall a. Show a => a -> Text
tshow Double
h)) |
            Maybe Text -> Bool
forall a. Maybe a -> Bool
isNothing (Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"height" [(Text, Text)]
combinedAttrs) ]
    (Maybe (Double, Double, Double, Double), Maybe Int, Maybe Int)
_ -> [(Text, Text)]
combinedAttrs
 where
  dropPointZero :: Text -> Text
dropPointZero Text
t = case Text -> Text -> Maybe Text
T.stripSuffix Text
".0" Text
t of
                       Maybe Text
Nothing -> Text
t
                       Just Text
t' -> Text
t'
  combinedAttrs :: [(Text, Text)]
combinedAttrs =
    [(Text
k, Text
v) | (Text
k, Text
v) <- [(Text, Text)]
imgAttrs
            , Text
k Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
"class"] [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
forall a. [a] -> [a] -> [a]
++
    [(Text
k, Text
v) | (Text
k, Text
v) <- [(Text, Text)]
svgAttrs
            , Maybe Text -> Bool
forall a. Maybe a -> Bool
isNothing (Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
k [(Text, Text)]
imgAttrs)
            , Text
k Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Text
"xmlns", Text
"xmlns:xlink", Text
"version", Text
"class"]] [(Text, Text)] -> [(Text, Text)] -> [(Text, Text)]
forall a. [a] -> [a] -> [a]
++
    [(Text, Text)]
mergedClasses
  parseViewBox :: Text -> Maybe (d, d, d, d)
parseViewBox Text
t =
    case (Text -> Maybe d) -> [Text] -> [Maybe d]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> Maybe d
forall (m :: * -> *) a. (MonadPlus m, Read a) => Text -> m a
safeRead (Text -> Maybe d) -> (Text -> Text) -> Text -> Maybe d
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
addZero) ([Text] -> [Maybe d]) -> [Text] -> [Maybe d]
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
T.words Text
t of
      [Just d
llx, Just d
lly, Just d
urx, Just d
ury] -> (d, d, d, d) -> Maybe (d, d, d, d)
forall a. a -> Maybe a
Just (d
llx, d
lly, d
urx, d
ury)
      [Maybe d]
_ -> Maybe (d, d, d, d)
forall a. Maybe a
Nothing
  addZero :: Text -> Text
addZero Text
t =
    if Text
"-." Text -> Text -> Bool
`T.isPrefixOf` Text
t
       then Text
"-0." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text -> Text
T.drop Int
2 Text
t -- safeRead fails on -.33, needs -0.33
       else Text
t
  (Maybe (Double, Double, Double, Double)
mbViewBox :: Maybe (Double, Double, Double, Double)) =
        Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"viewBox" [(Text, Text)]
svgAttrs Maybe Text
-> (Text -> Maybe (Double, Double, Double, Double))
-> Maybe (Double, Double, Double, Double)
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe (Double, Double, Double, Double)
forall {d}. Read d => Text -> Maybe (d, d, d, d)
parseViewBox
  (Maybe Int
mbHeight :: Maybe Int) = Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"height" [(Text, Text)]
combinedAttrs Maybe Text -> (Text -> Maybe Int) -> Maybe Int
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Int
forall (m :: * -> *) a. (MonadPlus m, Read a) => Text -> m a
safeRead
  (Maybe Int
mbWidth :: Maybe Int) = Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"width" [(Text, Text)]
combinedAttrs Maybe Text -> (Text -> Maybe Int) -> Maybe Int
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Int
forall (m :: * -> *) a. (MonadPlus m, Read a) => Text -> m a
safeRead
  mergedClasses :: [(Text, Text)]
mergedClasses = case (Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"class" [(Text, Text)]
imgAttrs, Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"class" [(Text, Text)]
svgAttrs) of
                    (Just Text
c1, Just Text
c2) -> [(Text
"class", Text
c1 Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
c2)]
                    (Maybe Text, Maybe Text)
_ -> []

cssURLs :: PandocMonad m
        => FilePath -> ByteString -> m ByteString
cssURLs :: forall (m :: * -> *).
PandocMonad m =>
String -> ByteString -> m ByteString
cssURLs String
d ByteString
orig = do
  Either ParseError ByteString
res <- ParsecT ByteString () m ByteString
-> () -> String -> ByteString -> m (Either ParseError ByteString)
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> u -> String -> s -> m (Either ParseError a)
runParserT (String -> ParsecT ByteString () m ByteString
forall (m :: * -> *).
PandocMonad m =>
String -> ParsecT ByteString () m ByteString
parseCSSUrls String
d) () String
"css" ByteString
orig
  case Either ParseError ByteString
res of
       Left ParseError
e    -> do
         LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> m ()) -> LogMessage -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> LogMessage
CouldNotParseCSS (Text -> LogMessage) -> Text -> LogMessage
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ ParseError -> String
forall a. Show a => a -> String
show ParseError
e
         ByteString -> m ByteString
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
orig
       Right ByteString
bs  -> ByteString -> m ByteString
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
bs

parseCSSUrls :: PandocMonad m
             => FilePath -> ParsecT ByteString () m ByteString
parseCSSUrls :: forall (m :: * -> *).
PandocMonad m =>
String -> ParsecT ByteString () m ByteString
parseCSSUrls String
d = [ByteString] -> ByteString
B.concat ([ByteString] -> ByteString)
-> ParsecT ByteString () m [ByteString]
-> ParsecT ByteString () m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m [ByteString]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
P.many
  (ParsecT ByteString () m ByteString
forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m ByteString
pCSSWhite ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a.
ParsecT ByteString () m a
-> ParsecT ByteString () m a -> ParsecT ByteString () m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ParsecT ByteString () m ByteString
forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m ByteString
pCSSComment ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a.
ParsecT ByteString () m a
-> ParsecT ByteString () m a -> ParsecT ByteString () m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> String -> ParsecT ByteString () m ByteString
forall (m :: * -> *).
PandocMonad m =>
String -> ParsecT ByteString () m ByteString
pCSSImport String
d ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a.
ParsecT ByteString () m a
-> ParsecT ByteString () m a -> ParsecT ByteString () m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> String -> ParsecT ByteString () m ByteString
forall (m :: * -> *).
PandocMonad m =>
String -> ParsecT ByteString () m ByteString
pCSSUrl String
d ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a.
ParsecT ByteString () m a
-> ParsecT ByteString () m a -> ParsecT ByteString () m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ParsecT ByteString () m ByteString
forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m ByteString
pCSSOther)

pCSSImport :: PandocMonad m
           => FilePath -> ParsecT ByteString () m ByteString
pCSSImport :: forall (m :: * -> *).
PandocMonad m =>
String -> ParsecT ByteString () m ByteString
pCSSImport String
d = ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try (ParsecT ByteString () m ByteString
 -> ParsecT ByteString () m ByteString)
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a b. (a -> b) -> a -> b
$ do
  String -> ParsecT ByteString () m String
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
String -> ParsecT s u m String
P.string String
"@import"
  ParsecT ByteString () m ()
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m ()
P.spaces
  Either ByteString (Text, ByteString)
res <- (ParsecT ByteString () m (Text, ByteString)
forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m (Text, ByteString)
pQuoted ParsecT ByteString () m (Text, ByteString)
-> ParsecT ByteString () m (Text, ByteString)
-> ParsecT ByteString () m (Text, ByteString)
forall a.
ParsecT ByteString () m a
-> ParsecT ByteString () m a -> ParsecT ByteString () m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ParsecT ByteString () m (Text, ByteString)
forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m (Text, ByteString)
pUrl) ParsecT ByteString () m (Text, ByteString)
-> ((Text, ByteString)
    -> ParsecT ByteString () m (Either ByteString (Text, ByteString)))
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a b.
ParsecT ByteString () m a
-> (a -> ParsecT ByteString () m b) -> ParsecT ByteString () m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String
-> (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall (m :: * -> *).
PandocMonad m =>
String
-> (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
handleCSSUrl String
d
  ParsecT ByteString () m ()
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m ()
P.spaces
  Char -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
P.char Char
';'
  ParsecT ByteString () m ()
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m ()
P.spaces
  case Either ByteString (Text, ByteString)
res of
       Left ByteString
b       -> ByteString -> ParsecT ByteString () m ByteString
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> ParsecT ByteString () m ByteString)
-> ByteString -> ParsecT ByteString () m ByteString
forall a b. (a -> b) -> a -> b
$ String -> ByteString
B.pack String
"@import " ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
b
       Right (Text
_, ByteString
b) -> ByteString -> ParsecT ByteString () m ByteString
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
b

-- Note: some whitespace in CSS is significant, so we can't collapse it!
pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString
pCSSWhite :: forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m ByteString
pCSSWhite = Char -> ByteString
B.singleton (Char -> ByteString)
-> ParsecT ByteString () m Char
-> ParsecT ByteString () m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
P.space ParsecT ByteString () m ByteString
-> ParsecT ByteString () m () -> ParsecT ByteString () m ByteString
forall a b.
ParsecT ByteString () m a
-> ParsecT ByteString () m b -> ParsecT ByteString () m a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ParsecT ByteString () m ()
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m ()
P.spaces

pCSSComment :: PandocMonad m => ParsecT ByteString () m ByteString
pCSSComment :: forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m ByteString
pCSSComment = ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try (ParsecT ByteString () m ByteString
 -> ParsecT ByteString () m ByteString)
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a b. (a -> b) -> a -> b
$ do
  String -> ParsecT ByteString () m String
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
String -> ParsecT s u m String
P.string String
"/*"
  ParsecT ByteString () m Char
-> ParsecT ByteString () m String -> ParsecT ByteString () m String
forall s (m :: * -> *) t u a end.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
P.manyTill ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
P.anyChar (ParsecT ByteString () m String -> ParsecT ByteString () m String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try (String -> ParsecT ByteString () m String
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
String -> ParsecT s u m String
P.string String
"*/"))
  ByteString -> ParsecT ByteString () m ByteString
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
B.empty

pCSSOther :: PandocMonad m => ParsecT ByteString () m ByteString
pCSSOther :: forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m ByteString
pCSSOther =
  (String -> ByteString
B.pack (String -> ByteString)
-> ParsecT ByteString () m String
-> ParsecT ByteString () m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ByteString () m Char -> ParsecT ByteString () m String
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m [a]
P.many1 (String -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
String -> ParsecT s u m Char
P.noneOf String
"u/ \n\r\t")) ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a.
ParsecT ByteString () m a
-> ParsecT ByteString () m a -> ParsecT ByteString () m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  (Char -> ByteString
B.singleton (Char -> ByteString)
-> ParsecT ByteString () m Char
-> ParsecT ByteString () m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Char -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
P.char Char
'u') ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a.
ParsecT ByteString () m a
-> ParsecT ByteString () m a -> ParsecT ByteString () m a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  (Char -> ByteString
B.singleton (Char -> ByteString)
-> ParsecT ByteString () m Char
-> ParsecT ByteString () m ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Char -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
P.char Char
'/')

pCSSUrl :: PandocMonad m
        => FilePath -> ParsecT ByteString () m ByteString
pCSSUrl :: forall (m :: * -> *).
PandocMonad m =>
String -> ParsecT ByteString () m ByteString
pCSSUrl String
d = ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try (ParsecT ByteString () m ByteString
 -> ParsecT ByteString () m ByteString)
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m ByteString
forall a b. (a -> b) -> a -> b
$ do
  Either ByteString (Text, ByteString)
res <- ParsecT ByteString () m (Text, ByteString)
forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m (Text, ByteString)
pUrl ParsecT ByteString () m (Text, ByteString)
-> ((Text, ByteString)
    -> ParsecT ByteString () m (Either ByteString (Text, ByteString)))
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a b.
ParsecT ByteString () m a
-> (a -> ParsecT ByteString () m b) -> ParsecT ByteString () m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String
-> (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall (m :: * -> *).
PandocMonad m =>
String
-> (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
handleCSSUrl String
d
  case Either ByteString (Text, ByteString)
res of
       Left ByteString
b -> ByteString -> ParsecT ByteString () m ByteString
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
b
       Right (Text
mt,ByteString
b) -> do
         let enc :: Text
enc = (Text, ByteString) -> Text
makeDataURI (Text
mt, ByteString
b)
         ByteString -> ParsecT ByteString () m ByteString
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> ParsecT ByteString () m ByteString)
-> ByteString -> ParsecT ByteString () m ByteString
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
fromText (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Text
"url(" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
enc Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
")"

pQuoted :: PandocMonad m
        => ParsecT ByteString () m (T.Text, ByteString)
pQuoted :: forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m (Text, ByteString)
pQuoted = ParsecT ByteString () m (Text, ByteString)
-> ParsecT ByteString () m (Text, ByteString)
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try (ParsecT ByteString () m (Text, ByteString)
 -> ParsecT ByteString () m (Text, ByteString))
-> ParsecT ByteString () m (Text, ByteString)
-> ParsecT ByteString () m (Text, ByteString)
forall a b. (a -> b) -> a -> b
$ do
  Char
quote <- String -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
String -> ParsecT s u m Char
P.oneOf String
"\"'"
  Text
url <- String -> Text
T.pack (String -> Text)
-> ParsecT ByteString () m String -> ParsecT ByteString () m Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ByteString () m Char
-> ParsecT ByteString () m Char -> ParsecT ByteString () m String
forall s (m :: * -> *) t u a end.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
P.manyTill ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
P.anyChar (Char -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
P.char Char
quote)
  let fallback :: ByteString
fallback = Text -> ByteString
fromText (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Char -> Text
T.singleton Char
quote Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
trim Text
url Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Char -> Text
T.singleton Char
quote
  (Text, ByteString) -> ParsecT ByteString () m (Text, ByteString)
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Text
url, ByteString
fallback)

pUrl :: PandocMonad m
     => ParsecT ByteString () m (T.Text, ByteString)
pUrl :: forall (m :: * -> *).
PandocMonad m =>
ParsecT ByteString () m (Text, ByteString)
pUrl = ParsecT ByteString () m (Text, ByteString)
-> ParsecT ByteString () m (Text, ByteString)
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
P.try (ParsecT ByteString () m (Text, ByteString)
 -> ParsecT ByteString () m (Text, ByteString))
-> ParsecT ByteString () m (Text, ByteString)
-> ParsecT ByteString () m (Text, ByteString)
forall a b. (a -> b) -> a -> b
$ do
  String -> ParsecT ByteString () m String
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
String -> ParsecT s u m String
P.string String
"url("
  ParsecT ByteString () m ()
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m ()
P.spaces
  Maybe Char
quote <- Maybe Char
-> ParsecT ByteString () m (Maybe Char)
-> ParsecT ByteString () m (Maybe Char)
forall s (m :: * -> *) t a u.
Stream s m t =>
a -> ParsecT s u m a -> ParsecT s u m a
P.option Maybe Char
forall a. Maybe a
Nothing (Char -> Maybe Char
forall a. a -> Maybe a
Just (Char -> Maybe Char)
-> ParsecT ByteString () m Char
-> ParsecT ByteString () m (Maybe Char)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
String -> ParsecT s u m Char
P.oneOf String
"\"'")
  Text
url <- String -> Text
T.pack (String -> Text)
-> ParsecT ByteString () m String -> ParsecT ByteString () m Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ByteString () m Char
-> ParsecT ByteString () m Char -> ParsecT ByteString () m String
forall s (m :: * -> *) t u a end.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
P.manyTill ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
P.anyChar (ParsecT ByteString () m Char
-> (Char -> ParsecT ByteString () m Char)
-> Maybe Char
-> ParsecT ByteString () m Char
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (ParsecT ByteString () m Char -> ParsecT ByteString () m Char
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m a
P.lookAhead (Char -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
P.char Char
')')) Char -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
P.char Maybe Char
quote)
  ParsecT ByteString () m ()
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m ()
P.spaces
  Char -> ParsecT ByteString () m Char
forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
P.char Char
')'
  let fallback :: ByteString
fallback = Text -> ByteString
fromText (Text
"url(" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> (Char -> Text) -> Maybe Char -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" Char -> Text
T.singleton Maybe Char
quote Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
trim Text
url Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>
                            Text -> (Char -> Text) -> Maybe Char -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" Char -> Text
T.singleton Maybe Char
quote Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
")")
  (Text, ByteString) -> ParsecT ByteString () m (Text, ByteString)
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Text
url, ByteString
fallback)

handleCSSUrl :: PandocMonad m
             => FilePath -> (T.Text, ByteString)
             -> ParsecT ByteString () m
                  (Either ByteString (MimeType, ByteString))
handleCSSUrl :: forall (m :: * -> *).
PandocMonad m =>
String
-> (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
handleCSSUrl String
d (Text
url, ByteString
fallback) =
  case (Char -> Bool) -> String -> String
escapeURIString (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
'|') (Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ Text -> Text
trim Text
url) of
      Char
'#':String
_ -> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either ByteString (Text, ByteString)
 -> ParsecT ByteString () m (Either ByteString (Text, ByteString)))
-> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a b. (a -> b) -> a -> b
$ ByteString -> Either ByteString (Text, ByteString)
forall a b. a -> Either a b
Left ByteString
fallback
      Char
'd':Char
'a':Char
't':Char
'a':Char
':':String
_ -> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either ByteString (Text, ByteString)
 -> ParsecT ByteString () m (Either ByteString (Text, ByteString)))
-> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a b. (a -> b) -> a -> b
$ ByteString -> Either ByteString (Text, ByteString)
forall a b. a -> Either a b
Left ByteString
fallback
      String
u ->  do let url' :: Text
url' = if Text -> Bool
isURI (String -> Text
T.pack String
u) then String -> Text
T.pack String
u else String -> Text
T.pack (String
d String -> String -> String
</> String
u)
               GetDataResult
res <- m GetDataResult -> ParsecT ByteString () m GetDataResult
forall (m :: * -> *) a. Monad m => m a -> ParsecT ByteString () m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m GetDataResult -> ParsecT ByteString () m GetDataResult)
-> m GetDataResult -> ParsecT ByteString () m GetDataResult
forall a b. (a -> b) -> a -> b
$ Text -> Text -> m GetDataResult
forall (m :: * -> *).
PandocMonad m =>
Text -> Text -> m GetDataResult
getData Text
"" Text
url'
               case GetDataResult
res of
                    AlreadyDataURI Text
uri -> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either ByteString (Text, ByteString)
 -> ParsecT ByteString () m (Either ByteString (Text, ByteString)))
-> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a b. (a -> b) -> a -> b
$ ByteString -> Either ByteString (Text, ByteString)
forall a b. a -> Either a b
Left (Text -> ByteString
fromText (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Text
"url(" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
uri Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
")")
                    Fetched (Text
mt', ByteString
raw) -> do
                      -- note that the downloaded CSS may
                      -- itself contain url(...).
                      (Text
mt, ByteString
b) <- if Text
"text/css" Text -> Text -> Bool
`T.isPrefixOf` Text
mt'
                                    -- see #5725: in HTML5, content type
                                    -- isn't allowed on style type attribute
                                    then (Text
"text/css",) (ByteString -> (Text, ByteString))
-> ParsecT ByteString () m ByteString
-> ParsecT ByteString () m (Text, ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> ByteString -> ParsecT ByteString () m ByteString
forall (m :: * -> *).
PandocMonad m =>
String -> ByteString -> m ByteString
cssURLs String
d ByteString
raw
                                    else (Text, ByteString) -> ParsecT ByteString () m (Text, ByteString)
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Text
mt', ByteString
raw)
                      Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either ByteString (Text, ByteString)
 -> ParsecT ByteString () m (Either ByteString (Text, ByteString)))
-> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a b. (a -> b) -> a -> b
$ (Text, ByteString) -> Either ByteString (Text, ByteString)
forall a b. b -> Either a b
Right (Text
mt, ByteString
b)
                    CouldNotFetch PandocError
_ -> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a. a -> ParsecT ByteString () m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either ByteString (Text, ByteString)
 -> ParsecT ByteString () m (Either ByteString (Text, ByteString)))
-> Either ByteString (Text, ByteString)
-> ParsecT ByteString () m (Either ByteString (Text, ByteString))
forall a b. (a -> b) -> a -> b
$ ByteString -> Either ByteString (Text, ByteString)
forall a b. a -> Either a b
Left ByteString
fallback

data GetDataResult =
    AlreadyDataURI T.Text
  | CouldNotFetch PandocError
  | Fetched (MimeType, ByteString)
  deriving (Int -> GetDataResult -> String -> String
[GetDataResult] -> String -> String
GetDataResult -> String
(Int -> GetDataResult -> String -> String)
-> (GetDataResult -> String)
-> ([GetDataResult] -> String -> String)
-> Show GetDataResult
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> GetDataResult -> String -> String
showsPrec :: Int -> GetDataResult -> String -> String
$cshow :: GetDataResult -> String
show :: GetDataResult -> String
$cshowList :: [GetDataResult] -> String -> String
showList :: [GetDataResult] -> String -> String
Show)

getData :: PandocMonad m
        => MimeType -> T.Text
        -> m GetDataResult
getData :: forall (m :: * -> *).
PandocMonad m =>
Text -> Text -> m GetDataResult
getData Text
mimetype Text
src
  | Text
"data:" Text -> Text -> Bool
`T.isPrefixOf` Text
src = GetDataResult -> m GetDataResult
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (GetDataResult -> m GetDataResult)
-> GetDataResult -> m GetDataResult
forall a b. (a -> b) -> a -> b
$ Text -> GetDataResult
AlreadyDataURI Text
src -- already data: uri
  | Bool
otherwise = m GetDataResult
-> (PandocError -> m GetDataResult) -> m GetDataResult
forall a. m a -> (PandocError -> m a) -> m a
forall e (m :: * -> *) a.
MonadError e m =>
m a -> (e -> m a) -> m a
catchError m GetDataResult
fetcher PandocError -> m GetDataResult
forall {m :: * -> *}.
PandocMonad m =>
PandocError -> m GetDataResult
handler
 where
   fetcher :: m GetDataResult
fetcher = do
      let ext :: Text
ext = Text -> Text
T.toLower (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String -> String
takeExtension (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
src
      (ByteString
raw, Maybe Text
respMime) <- Text -> m (ByteString, Maybe Text)
forall (m :: * -> *).
PandocMonad m =>
Text -> m (ByteString, Maybe Text)
fetchItem Text
src
      let raw' :: ByteString
raw' = if Text
ext Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
".gz", Text
".svgz"]
                 then [ByteString] -> ByteString
B.concat ([ByteString] -> ByteString) -> [ByteString] -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> [ByteString]
L.toChunks (ByteString -> [ByteString]) -> ByteString -> [ByteString]
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
Gzip.decompress (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ [ByteString] -> ByteString
L.fromChunks [ByteString
raw]
                 else ByteString
raw
      let mime :: Text
mime = case (Text
mimetype, Maybe Text
respMime) of
                  (Text
"",Maybe Text
Nothing) -> Text
"application/octet-stream"
                  (Text
x, Maybe Text
Nothing) -> Text
x
                  (Text
_, Just Text
x ) -> Text
x
      ByteString
result <- if Text
"text/css" Text -> Text -> Bool
`T.isPrefixOf` Text
mime
                then do
                  [String]
oldInputs <- m [String]
forall (m :: * -> *). PandocMonad m => m [String]
getInputFiles
                  [String] -> m ()
forall (m :: * -> *). PandocMonad m => [String] -> m ()
setInputFiles [Text -> String
T.unpack Text
src]
                  ByteString
res <- String -> ByteString -> m ByteString
forall (m :: * -> *).
PandocMonad m =>
String -> ByteString -> m ByteString
cssURLs (String -> String
takeDirectory (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
src) ByteString
raw'
                  [String] -> m ()
forall (m :: * -> *). PandocMonad m => [String] -> m ()
setInputFiles [String]
oldInputs
                  ByteString -> m ByteString
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
res
               else ByteString -> m ByteString
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
raw'
      GetDataResult -> m GetDataResult
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (GetDataResult -> m GetDataResult)
-> GetDataResult -> m GetDataResult
forall a b. (a -> b) -> a -> b
$ (Text, ByteString) -> GetDataResult
Fetched (Text
mime, ByteString
result)
   handler :: PandocError -> m GetDataResult
handler PandocError
e = case PandocError
e of
                 PandocResourceNotFound Text
r -> do
                   LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> m ()) -> LogMessage -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> Text -> LogMessage
CouldNotFetchResource Text
r Text
""
                   GetDataResult -> m GetDataResult
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (GetDataResult -> m GetDataResult)
-> GetDataResult -> m GetDataResult
forall a b. (a -> b) -> a -> b
$ PandocError -> GetDataResult
CouldNotFetch PandocError
e
                 PandocHttpError Text
u HttpException
er -> do
                   LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (LogMessage -> m ()) -> LogMessage -> m ()
forall a b. (a -> b) -> a -> b
$ Text -> Text -> LogMessage
CouldNotFetchResource Text
u (HttpException -> Text
forall a. Show a => a -> Text
tshow HttpException
er)
                   GetDataResult -> m GetDataResult
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (GetDataResult -> m GetDataResult)
-> GetDataResult -> m GetDataResult
forall a b. (a -> b) -> a -> b
$ PandocError -> GetDataResult
CouldNotFetch PandocError
e
                 PandocError
_ -> PandocError -> m GetDataResult
forall a. PandocError -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError PandocError
e




-- | Convert HTML into self-contained HTML, incorporating images,
-- scripts, and CSS using data: URIs.
makeSelfContained :: PandocMonad m => T.Text -> m T.Text
makeSelfContained :: forall (m :: * -> *). PandocMonad m => Text -> m Text
makeSelfContained Text
inp = do
  let tags :: [Tag Text]
tags = Text -> [Tag Text]
forall str. StringLike str => str -> [Tag str]
parseTags Text
inp
  let html5 :: Bool
html5 = case [Tag Text]
tags of
                  (TagOpen Text
"!DOCTYPE" [(Text
"html",Text
"")]:[Tag Text]
_) -> Bool
True
                  [Tag Text]
_ -> Bool
False
  let convertState :: ConvertState
convertState = ConvertState { isHtml5 :: Bool
isHtml5 = Bool
html5,
                                    svgMap :: Map Text (Text, [(Text, Text)])
svgMap = Map Text (Text, [(Text, Text)])
forall a. Monoid a => a
mempty }
  [Tag Text]
out' <- StateT ConvertState m [Tag Text] -> ConvertState -> m [Tag Text]
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT ([Tag Text] -> StateT ConvertState m [Tag Text]
forall (m :: * -> *).
PandocMonad m =>
[Tag Text] -> StateT ConvertState m [Tag Text]
convertTags [Tag Text]
tags) ConvertState
convertState
  Text -> m Text
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> m Text) -> Text -> m Text
forall a b. (a -> b) -> a -> b
$ [Tag Text] -> Text
renderTags' [Tag Text]
out'