Skip to content

Commit d8d0c19

Browse files
committed
fix(docx): use proper DPI when creating fallback images
Introduce getOrCreateFallback, and pass the desired size in points to rsvg-convert. Otherwise it'll guess the size based on the SVG's viewbox and completely ignore the DPI argument. Signed-off-by: Edwin Török <[email protected]>
1 parent 6483e2f commit d8d0c19

File tree

3 files changed

+43
-33
lines changed

3 files changed

+43
-33
lines changed

src/Text/Pandoc/App.hs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module Text.Pandoc.App (
2828
, applyFilters
2929
) where
3030
import qualified Control.Exception as E
31-
import Control.Monad ( (>=>), when, forM, forM_ )
31+
import Control.Monad ( (>=>), when, forM )
3232
import Control.Monad.Trans ( MonadIO(..) )
3333
import Control.Monad.Catch ( MonadMask )
3434
import Control.Monad.Except (throwError)
@@ -49,7 +49,6 @@ import System.IO (nativeNewline, stdout)
4949
import qualified System.IO as IO (Newline (..))
5050
import Text.Pandoc
5151
import Text.Pandoc.Builder (setMeta)
52-
import Text.Pandoc.MediaBag (mediaItems)
5352
import Text.Pandoc.App.Opt (Opt (..), LineEnding (..), defaultOpts,
5453
IpynbOutput (..), OptInfo(..))
5554
import Text.Pandoc.App.CommandLineOptions (parseOptions, parseOptionsFromArgs,
@@ -66,7 +65,6 @@ import qualified Text.Pandoc.Format as Format
6665
import Text.Pandoc.PDF (makePDF)
6766
import Text.Pandoc.Scripting (ScriptingEngine (..), CustomComponents(..))
6867
import Text.Pandoc.SelfContained (makeSelfContained)
69-
import Text.Pandoc.Shared (tshow)
7068
import Text.Pandoc.Writers.Shared (lookupMetaString)
7169
import Text.Pandoc.Readers.Markdown (yamlToMeta)
7270
import qualified Text.Pandoc.UTF8 as UTF8
@@ -300,9 +298,6 @@ convertWithOpts' scriptingEngine istty datadir opts = do
300298
>=> maybe return extractMedia (optExtractMedia opts)
301299
)
302300

303-
when (format == "docx" && not (optSandbox opts)) $ do
304-
createPngFallbacks (writerDpi writerOptions)
305-
306301
output <- case writer of
307302
ByteStringWriter f
308303
| format == "chunkedhtml" -> ZipOutput <$> f writerOptions doc
@@ -367,21 +362,6 @@ readAbbreviations mbfilepath =
367362
>>= fmap (Set.fromList . filter (not . T.null) . T.lines) .
368363
toTextM (fromMaybe mempty mbfilepath)
369364

370-
createPngFallbacks :: (PandocMonad m) => Int -> m ()
371-
createPngFallbacks dpi = do
372-
-- create fallback pngs for svgs
373-
items <- mediaItems <$> getMediaBag
374-
forM_ items $ \(fp, mt, bs) ->
375-
case T.takeWhile (/=';') mt of
376-
"image/svg+xml" -> do
377-
res <- svgToPng dpi Nothing Nothing bs
378-
case res of
379-
Right bs' -> do
380-
let fp' = fp <> ".png"
381-
insertMedia fp' (Just "image/png") bs'
382-
Left e -> report $ CouldNotConvertImage (T.pack fp) (tshow e)
383-
_ -> return ()
384-
385365
getMetadataFromFiles :: PandocMonad m
386366
=> Text -> ReaderOptions -> [FilePath] -> m Meta
387367
getMetadataFromFiles readerFormat readerOpts = \case

src/Text/Pandoc/Image.hs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Portability : portable
1010
1111
Functions for converting images.
1212
-}
13-
module Text.Pandoc.Image ( svgToPngIO ) where
13+
module Text.Pandoc.Image ( createPngFallback, svgToPngIO ) where
1414
import Text.Pandoc.Process (pipeProcess)
1515
import qualified Data.ByteString.Lazy as L
1616
import System.Exit
@@ -20,6 +20,9 @@ import qualified Control.Exception as E
2020
import Control.Monad.IO.Class (MonadIO(liftIO))
2121
import Text.Pandoc.Class.PandocMonad
2222
import qualified Data.Text as T
23+
import Text.Pandoc.Logging (LogMessage(CouldNotConvertImage))
24+
import Data.ByteString.Lazy (ByteString)
25+
import Text.Pandoc.MediaBag (MediaItem, lookupMedia)
2326
import Text.Printf (printf)
2427

2528
-- | Convert svg image to png. rsvg-convert
@@ -44,4 +47,16 @@ svgToPngIO dpi widthPt heightPt bs = do
4447
else Left "conversion from SVG failed")
4548
(\(e :: E.SomeException) -> return $ Left $
4649
"check that rsvg-convert is in path.\n" <> tshow e)
47-
where pt name = maybe [] $ \points -> ["--" <> name, printf "%.6fpt" points]
50+
where pt name = maybe [] $ \points -> ["--" <> name, printf "%.6fpt" points]
51+
52+
createPngFallback :: (PandocMonad m) => Int -> (Double, Double) -> FilePath -> ByteString -> m (Maybe MediaItem)
53+
createPngFallback dpi (xPt, yPt) fp bs = do
54+
-- create fallback pngs for svgs
55+
res <- svgToPng dpi (Just xPt) (Just yPt) bs
56+
case res of
57+
Right bs' -> do
58+
insertMedia fp (Just "image/png") bs'
59+
lookupMedia fp <$> getMediaBag
60+
Left e -> do
61+
report $ CouldNotConvertImage (T.pack fp) (tshow e)
62+
return Nothing

src/Text/Pandoc/Writers/Docx/OpenXML.hs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Control.Monad (when, unless)
2424
import Control.Applicative ((<|>))
2525
import Control.Monad.Except (catchError)
2626
import Crypto.Hash (hashWith, SHA1(SHA1))
27+
import Data.ByteString (ByteString)
2728
import qualified Data.ByteString.Lazy as BL
2829
import Data.Char (isLetter, isSpace)
2930
import Text.Pandoc.Char (isCJK)
@@ -48,6 +49,7 @@ import Text.Pandoc.UTF8 (fromText)
4849
import Text.Pandoc.Definition
4950
import Text.Pandoc.Highlighting (highlight)
5051
import Text.Pandoc.Templates (compileDefaultTemplate, renderTemplate)
52+
import Text.Pandoc.Image (createPngFallback)
5153
import Text.Pandoc.ImageSize
5254
import Text.Pandoc.Logging
5355
import Text.Pandoc.MIME (extensionFromMimeType, getMimeType)
@@ -60,6 +62,7 @@ import Text.Pandoc.Walk
6062
import qualified Text.Pandoc.Writers.GridTable as Grid
6163
import Text.Pandoc.Writers.Math
6264
import Text.Pandoc.Writers.Shared
65+
import Text.Printf (printf)
6366
import Text.TeXMath
6467
import Text.Pandoc.Writers.OOXML
6568
import Text.Pandoc.XML.Light as XML
@@ -718,6 +721,16 @@ formattedRun els = do
718721
props <- getTextProps
719722
return $ mknode "w:r" [] $ props ++ els
720723

724+
getOrCreateFallback :: PandocMonad m => Int -> (Integer, Integer) -> FilePath -> ByteString -> m (Maybe MediaItem)
725+
getOrCreateFallback dpi (xemu, yemu) src' img = do
726+
mediabag <- getMediaBag
727+
let src = printf "%s_%d_%d.png" src' xemu yemu
728+
let xyPt = (fromIntegral xemu / 12700.0, fromIntegral yemu / 12700.0)
729+
case lookupMedia src mediabag of
730+
Just item -> return $ Just item
731+
Nothing -> createPngFallback dpi xyPt src $ BL.fromStrict img
732+
733+
-- | Convert an inline element to OpenXML.
721734
-- | Convert an inline element to OpenXML.
722735
inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Content]
723736
inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il
@@ -919,17 +932,26 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
919932
imgs <- gets stImages
920933
let
921934
stImage = M.lookup (T.unpack src) imgs
922-
generateImgElt (ident, _fp, mt, img) = do
935+
generateImgElt (ident, fp, mt, img) = do
923936
docprid <- getUniqueId
924937
nvpicprid <- getUniqueId
938+
let
939+
(xpt,ypt) = desiredSizeInPoints opts attr
940+
(either (const def) id (imageSize opts img))
941+
-- 12700 emu = 1 pt
942+
pageWidthPt = case dimension Width attr of
943+
Just (Percent a) -> pageWidth * floor (a * 127)
944+
_ -> pageWidth * 12700
945+
(xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) pageWidthPt
925946
(blipAttrs, blipContents) <-
926947
case T.takeWhile (/=';') <$> mt of
927948
Just "image/svg+xml" -> do
928949
-- get fallback png
929-
mediabag <- getMediaBag
950+
fallback <- getOrCreateFallback (writerDpi opts) (xemu, yemu) fp img
930951
mbFallback <-
931-
case lookupMedia (T.unpack (src <> ".png")) mediabag of
952+
case fallback of
932953
Just item -> do
954+
P.trace $ "Found fallback " <> tshow (mediaPath item)
933955
id' <- T.unpack . ("rId" <>) <$> getUniqueId
934956
let fp' = "media/" <> id' <> ".png"
935957
let imgdata = (id',
@@ -956,13 +978,6 @@ inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do
956978
[extLst])
957979
_ -> return ([("r:embed", T.pack ident)], [])
958980
let
959-
(xpt,ypt) = desiredSizeInPoints opts attr
960-
(either (const def) id (imageSize opts img))
961-
-- 12700 emu = 1 pt
962-
pageWidthPt = case dimension Width attr of
963-
Just (Percent a) -> pageWidth * floor (a * 127)
964-
_ -> pageWidth * 12700
965-
(xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) pageWidthPt
966981
cNvPicPr = mknode "pic:cNvPicPr" [] $
967982
mknode "a:picLocks" [("noChangeArrowheads","1")
968983
,("noChangeAspect","1")] ()

0 commit comments

Comments
 (0)