SelfContained: fixed problem with embedded fonts.

Closes #3629.

However, there is still room for improvement.

`@import` with following media declaration is not
handled.

Also `@import` with a simple filename (rather than
`url(...)` is not handled.
This commit is contained in:
John MacFarlane 2017-05-20 17:09:47 +02:00
parent ca77f0a95e
commit 8d4fbe6a2a

View file

@ -35,6 +35,7 @@ import Codec.Compression.GZip as Gzip
import Control.Applicative ((<|>)) import Control.Applicative ((<|>))
import Control.Monad.Except (throwError) import Control.Monad.Except (throwError)
import Control.Monad.Trans (lift) import Control.Monad.Trans (lift)
import Data.Monoid ((<>))
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.ByteString.Base64 import Data.ByteString.Base64
import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Char8 as B
@ -95,9 +96,9 @@ convertTags sourceURL (t@(TagOpen "script" as):TagClose "script":ts) =
(("src",dataUri) : [(x,y) | (x,y) <- as, x /= "src"]) : (("src",dataUri) : [(x,y) | (x,y) <- as, x /= "src"]) :
TagClose "script" : rest TagClose "script" : rest
Right (mime, bs) Right (mime, bs)
| (mime == "text/javascript" || | ("text/javascript" `isPrefixOf` mime ||
mime == "application/javascript" || "application/javascript" `isPrefixOf` mime ||
mime == "application/x-javascript") && "application/x-javascript" `isPrefixOf` mime) &&
not ("</script" `B.isInfixOf` bs) -> not ("</script" `B.isInfixOf` bs) ->
return $ return $
TagOpen "script" [("type", typeAttr)|not (null typeAttr)] TagOpen "script" [("type", typeAttr)|not (null typeAttr)]
@ -121,11 +122,12 @@ convertTags sourceURL (t@(TagOpen "link" as):ts) =
(("href",dataUri) : [(x,y) | (x,y) <- as, x /= "href"]) : (("href",dataUri) : [(x,y) | (x,y) <- as, x /= "href"]) :
rest rest
Right (mime, bs) Right (mime, bs)
| mime == "text/css" && not ("</" `B.isInfixOf` bs) -> do | "text/css" `isPrefixOf` mime
&& not ("</" `B.isInfixOf` bs) -> do
rest <- convertTags sourceURL $ rest <- convertTags sourceURL $
dropWhile (==TagClose "link") ts dropWhile (==TagClose "link") ts
return $ return $
TagOpen "style" [("type", "text/css")] TagOpen "style" [("type", mime)]
: TagText (toString bs) : TagText (toString bs)
: TagClose "style" : TagClose "style"
: rest : rest
@ -149,7 +151,20 @@ cssURLs sourceURL d orig = do
parseCSSUrls :: PandocMonad m parseCSSUrls :: PandocMonad m
=> Maybe String -> FilePath -> ParsecT ByteString () m ByteString => Maybe String -> FilePath -> ParsecT ByteString () m ByteString
parseCSSUrls sourceURL d = B.concat <$> P.many parseCSSUrls sourceURL d = B.concat <$> P.many
(pCSSWhite <|> pCSSComment <|> pCSSUrl sourceURL d <|> pCSSOther) (pCSSWhite <|> pCSSComment <|> pCSSImport sourceURL d <|>
pCSSUrl sourceURL d <|> pCSSOther)
pCSSImport :: PandocMonad m => Maybe String -> FilePath
-> ParsecT ByteString () m ByteString
pCSSImport sourceURL d = P.try $ do
P.string "@import"
P.spaces
res <- pCSSUrl' sourceURL d
P.spaces
P.optional $ P.char ';' >> P.spaces
case res of
Left b -> return $ B.pack "@import " <> b
Right (_, b) -> return b
-- Note: some whitespace in CSS is significant, so we can't collapse it! -- Note: some whitespace in CSS is significant, so we can't collapse it!
pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString
@ -170,13 +185,25 @@ pCSSOther = do
pCSSUrl :: PandocMonad m pCSSUrl :: PandocMonad m
=> Maybe String -> FilePath -> ParsecT ByteString () m ByteString => Maybe String -> FilePath -> ParsecT ByteString () m ByteString
pCSSUrl sourceURL d = P.try $ do pCSSUrl sourceURL d = P.try $ do
res <- pCSSUrl' sourceURL d
case res of
Left b -> return b
Right (mt,b) -> do
let enc = makeDataURI (mt, b)
return (B.pack $ "url(" ++ enc ++ ")")
pCSSUrl' :: PandocMonad m
=> Maybe String -> FilePath
-> ParsecT ByteString () m (Either ByteString (MimeType, ByteString))
pCSSUrl' sourceURL d = P.try $ do
P.string "url(" P.string "url("
P.spaces P.spaces
quote <- P.option Nothing (Just <$> P.oneOf "\"'") quote <- P.option Nothing (Just <$> P.oneOf "\"'")
url <- P.manyTill P.anyChar (maybe (P.lookAhead (P.char ')')) P.char quote) url <- P.manyTill P.anyChar (maybe (P.lookAhead (P.char ')')) P.char quote)
P.spaces P.spaces
P.char ')' P.char ')'
let fallback = B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ let fallback = Left $
B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++
maybe "" (:[]) quote ++ ")") maybe "" (:[]) quote ++ ")")
-- pipes are used in URLs provided by Google Code fonts -- pipes are used in URLs provided by Google Code fonts
-- but parseURI doesn't like them, so we escape them: -- but parseURI doesn't like them, so we escape them:
@ -186,13 +213,14 @@ pCSSUrl sourceURL d = P.try $ do
u -> do let url' = if isURI u then u else d </> u u -> do let url' = if isURI u then u else d </> u
res <- lift $ getData sourceURL "" url' res <- lift $ getData sourceURL "" url'
case res of case res of
Left uri -> return (B.pack $ "url(" ++ uri ++ ")") Left uri -> return $ Left (B.pack $ "url(" ++ uri ++ ")")
Right (mt, raw) -> do Right (mt, raw) -> do
-- note that the downloaded content may -- note that the downloaded CSS may
-- itself contain url(...). -- itself contain url(...).
raw' <- cssURLs sourceURL d raw b <- if "text/css" `isPrefixOf` mt
let enc = makeDataURI (mt, raw') then cssURLs sourceURL d raw
return (B.pack $ "url(" ++ enc ++ ")") else return raw
return $ Right (mt, b)
getDataURI :: PandocMonad m => Maybe String -> MimeType -> String -> m String getDataURI :: PandocMonad m => Maybe String -> MimeType -> String -> m String
getDataURI sourceURL mimetype src = do getDataURI sourceURL mimetype src = do
@ -224,7 +252,7 @@ getData sourceURL mimetype src = do
uriQuery = "", uriQuery = "",
uriFragment = "" } uriFragment = "" }
_ -> Nothing _ -> Nothing
result <- if mime == "text/css" result <- if "text/css" `isPrefixOf` mime
then cssURLs cssSourceURL (takeDirectory src) raw' then cssURLs cssSourceURL (takeDirectory src) raw'
else return raw' else return raw'
return $ Right (mime, result) return $ Right (mime, result)