Process svgs to remove needless view boxes.

This commit is contained in:
David 2019-02-16 16:18:35 +01:00
commit 816c1d8d7e
3 changed files with 78 additions and 6 deletions

View file

@ -21,10 +21,11 @@ library
Reanimate.Examples Reanimate.Examples
Reanimate.Combinators Reanimate.Combinators
Reanimate.LaTeX Reanimate.LaTeX
Reanimate.Svg
other-modules: Reanimate.Misc other-modules: Reanimate.Misc
build-depends: base >=4.12 && <4.13, build-depends: base >=4.12 && <4.13,
lucid-svg, time, text, unix, lucid, filepath, process, directory, lucid-svg, time, text, unix, lucid, filepath, process, directory,
containers, svg-tree, xml, bytestring containers, svg-tree, xml, bytestring, lens
executable reanimate-viewer executable reanimate-viewer
main-is: SvgViewer.hs main-is: SvgViewer.hs
@ -34,10 +35,11 @@ executable reanimate-viewer
Reanimate.Examples Reanimate.Examples
Reanimate.Combinators Reanimate.Combinators
Reanimate.LaTeX Reanimate.LaTeX
Reanimate.Svg
Reanimate.Misc Reanimate.Misc
build-depends: base >=4.12 && <4.13, cairo >=0.13 && <0.14, gtk, build-depends: base >=4.12 && <4.13, cairo >=0.13 && <0.14, gtk,
svgcairo, lucid-svg, time, text, unix, lucid, reanimate, svgcairo, lucid-svg, time, text, unix, lucid, reanimate,
filepath, process, directory, containers, svg-tree, xml, filepath, process, directory, containers, svg-tree, xml,
bytestring bytestring, lens
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010

View file

@ -10,12 +10,25 @@ import qualified Data.Map as Map
import Lucid (ToHtml (..)) import Lucid (ToHtml (..))
import Lucid.Svg (Svg, fill_, font_size_, text_) import Lucid.Svg (Svg, fill_, font_size_, text_)
import Reanimate.Misc import Reanimate.Misc
import Reanimate.Svg
import System.FilePath (replaceExtension, takeFileName, (</>)) import System.FilePath (replaceExtension, takeFileName, (</>))
import System.IO.Unsafe (unsafePerformIO) import System.IO.Unsafe (unsafePerformIO)
import Graphics.Svg (loadSvgFile, parseSvgFile, import Graphics.Svg (loadSvgFile, parseSvgFile,
xmlOfDocument) xmlOfDocument, Tree, elements, defaultSvg, Document)
import Text.XML.Light.Output (ppcElement, prettyConfigPP) import Text.XML.Light.Output (ppcElement, ppcContent, prettyConfigPP)
import Text.XML.Light (elContent)
import Control.Lens (over, (^.),set, (.~), (&), (%~) )
-- instance ToHtml Document where
-- toHtml = toHtmlRaw
-- toHtmlRaw = toHtmlRaw . ppcElement prettyConfigPP . xmlOfDocument
instance ToHtml Document where
toHtml = toHtmlRaw
toHtmlRaw doc = toHtmlRaw $ unlines $ map (ppcContent prettyConfigPP) (elContent elt)
where
elt = xmlOfDocument doc
{-# NOINLINE cache #-} {-# NOINLINE cache #-}
cache :: IORef (Map String (Svg ())) cache :: IORef (Map String (Svg ()))
@ -43,13 +56,13 @@ latexToSVG tex = handle (\(e::SomeException) -> return (failedSvg tex)) $ do
runCmd latex ["-interaction=batchmode", "-halt-on-error", "-output-directory="++tmp_dir, tex_file] runCmd latex ["-interaction=batchmode", "-halt-on-error", "-output-directory="++tmp_dir, tex_file]
runCmd dvisvgm [ dvi_file runCmd dvisvgm [ dvi_file
, "--exact" -- better bboxes. , "--exact" -- better bboxes.
, "--bbox=1,1" -- increase bbox size. -- , "--bbox=1,1" -- increase bbox size.
, "--no-fonts" -- use glyphs instead of fonts. , "--no-fonts" -- use glyphs instead of fonts.
,"--verbosity=0", "-o",svg_file] ,"--verbosity=0", "-o",svg_file]
svg_data <- B.readFile svg_file svg_data <- B.readFile svg_file
case parseSvgFile svg_file svg_data of case parseSvgFile svg_file svg_data of
Nothing -> error "Malformed svg" Nothing -> error "Malformed svg"
Just svg -> return $ toHtmlRaw $ ppcElement prettyConfigPP (xmlOfDocument svg) Just svg -> return $ toHtmlRaw $ unbox $ replaceUses svg
failedSvg :: String -> Svg () failedSvg :: String -> Svg ()
failedSvg tex = failedSvg tex =

57
src/Reanimate/Svg.hs Normal file
View file

@ -0,0 +1,57 @@
module Reanimate.Svg where
import Data.Maybe
import qualified Data.Map as Map
import Control.Lens (over, (^.),set, (.~), (&), (%~) )
import Graphics.Svg
replaceUses :: Document -> Document
replaceUses doc = doc & elements %~ map (mapTree replace)
& definitions .~ Map.empty
where
replace (UseTree _ Just{}) = error "replaceUses: subtree in use?"
replace (UseTree use Nothing) =
case Map.lookup (use^.useName) idMap of
Nothing -> error $ "Unknown id: " ++ (use^.useName)
Just tree ->
GroupTree $
defaultSvg & groupChildren .~ [tree]
& drawAttr .~ (defaultSvg & transform .~ Just [baseToTransformation (use^.useBase)])
replace x = x
baseToTransformation (x,y) =
case (toUserUnit defaultDPI x, toUserUnit defaultDPI y) of
(Num a, Num b) -> Translate a b
_ -> TransformUnknown
defaultDPI = 96
docTree = GroupTree $ set groupChildren (doc^.elements) defaultSvg
idMap = foldTree updMap Map.empty docTree `Map.union`
Map.mapMaybe elementToTree (doc^.definitions)
updMap m tree =
case tree^.drawAttr.attrId of
Nothing -> m
Just tid -> Map.insert tid tree m
elementToTree (ElementGeometry t) = Just t
elementToTree _ = Nothing
docIds :: Document -> [String]
docIds doc = Map.keys idMap ++ Map.keys (doc^.definitions)
where
docTree = GroupTree $ set groupChildren (doc^.elements) defaultSvg
idMap = foldTree updMap Map.empty docTree
updMap m tree =
case tree^.drawAttr.attrId of
Nothing -> m
Just tid -> Map.insert tid tree m
-- Transform out viewbox. defs and CSS rules are discarded.
unbox :: Document -> Document
unbox doc@Document{_viewBox = Just (minx, minw, _width, _height)} =
doc & viewBox .~ Nothing
& width .~ Nothing
& height .~ Nothing
& elements .~
[ GroupTree $ defaultSvg
& groupChildren .~ doc^.elements
& drawAttr .~ (defaultSvg & transform .~ Just [Translate (-minx) (-minw)]) ]
unbox doc = doc