├── Setup.hs ├── .gitignore ├── w3csvg ├── Nested.png ├── Skew.png ├── Units.png ├── Use01.png ├── Use02.png ├── Use03.png ├── Use04.png ├── arcs01.png ├── arcs02.png ├── line01.png ├── marker.png ├── mask01.png ├── quad01.png ├── rect01.png ├── rect02.png ├── text01.png ├── toap01.png ├── toap02.png ├── toap03.png ├── toap04.png ├── tref01.png ├── ViewBox.png ├── circle01.png ├── cubic01.png ├── cubic02.png ├── feBlend.png ├── linejoin.png ├── rtl-text.png ├── tspan01.png ├── tspan02.png ├── tspan03.png ├── tspan04.png ├── tspan05.png ├── NewCoordSys.png ├── RotateScale.png ├── ellipse01.png ├── feComposite.png ├── feImage-01.png ├── filters00.png ├── filters01.png ├── inheritance.png ├── lingrad01.png ├── opacity01.png ├── pattern01.png ├── polygon01.png ├── polyline01.png ├── radgrad01.png ├── rtl-complex.png ├── test_image.jpg ├── triangle01.png ├── InitialCoords.png ├── OrigCoordSys.png ├── feColorMatrix.png ├── feMorphology.png ├── feTurbulence.png ├── fillrule-evenodd.png ├── fillrule-nonzero.png ├── PreserveAspectRatio.png ├── enable-background-01.png ├── feComponentTransfer.png ├── primitive-subregion-01.png ├── rtl-text.svg ├── image01.svg ├── triangle01.svg ├── Use01.svg ├── circle01.svg ├── rect01.svg ├── rtl-complex.svg ├── text01.svg ├── Use03.svg ├── OrigCoordSys.svg ├── tspan04.svg ├── tspan01.svg ├── rect02.svg ├── ellipse01.svg ├── inheritance.svg ├── marker.svg ├── polygon01.svg ├── tspan03.svg ├── InitialCoords.svg ├── tref01.svg ├── tspan02.svg ├── Use02.svg ├── polyline01.svg ├── pattern01.svg ├── lingrad01.svg ├── line01.svg ├── toap01.svg ├── toap03.svg ├── arcs01.svg ├── radgrad01.svg ├── textdecoration01.svg ├── toap02.svg ├── ViewBox.svg ├── quad01.svg ├── tspan05.svg ├── NewCoordSys.svg ├── Use04.svg ├── mask01.svg ├── feImage-01.svg ├── RotateScale.svg ├── Skew.svg ├── linejoin.svg ├── feMorphology.svg ├── Nested.svg ├── primitive-subregion-01.svg ├── linecap.svg ├── Units.svg ├── cubic01.svg ├── filters01.svg ├── feBlend.svg ├── opacity01.svg ├── filters00.svg ├── arcs02.svg ├── feColorMatrix.svg ├── fillrule-evenodd.svg ├── fillrule-nonzero.svg ├── feComponentTransfer.svg ├── feTurbulence.svg ├── enable-background-01.svg ├── PreserveAspectRatio.svg ├── cubic02.svg └── feComposite.svg ├── Makefile ├── exec-src ├── benching.hs ├── svgtest.hs └── svgrender.hs ├── README.md ├── LICENSE ├── changelog.md ├── rasterific-svg.cabal ├── src └── Graphics │ └── Rasterific │ ├── Svg │ ├── ArcConversion.hs │ ├── MeshConverter.hs │ ├── PathConverter.hs │ ├── RenderContext.hs │ └── RasterificTextRendering.hs │ └── Svg.hs └── test └── reduced.svg /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .cabal-sandbox 3 | .stack-work 4 | cabal.sandbox.config 5 | gen_test 6 | -------------------------------------------------------------------------------- /w3csvg/Nested.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/Nested.png -------------------------------------------------------------------------------- /w3csvg/Skew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/Skew.png -------------------------------------------------------------------------------- /w3csvg/Units.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/Units.png -------------------------------------------------------------------------------- /w3csvg/Use01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/Use01.png -------------------------------------------------------------------------------- /w3csvg/Use02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/Use02.png -------------------------------------------------------------------------------- /w3csvg/Use03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/Use03.png -------------------------------------------------------------------------------- /w3csvg/Use04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/Use04.png -------------------------------------------------------------------------------- /w3csvg/arcs01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/arcs01.png -------------------------------------------------------------------------------- /w3csvg/arcs02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/arcs02.png -------------------------------------------------------------------------------- /w3csvg/line01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/line01.png -------------------------------------------------------------------------------- /w3csvg/marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/marker.png -------------------------------------------------------------------------------- /w3csvg/mask01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/mask01.png -------------------------------------------------------------------------------- /w3csvg/quad01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/quad01.png -------------------------------------------------------------------------------- /w3csvg/rect01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/rect01.png -------------------------------------------------------------------------------- /w3csvg/rect02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/rect02.png -------------------------------------------------------------------------------- /w3csvg/text01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/text01.png -------------------------------------------------------------------------------- /w3csvg/toap01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/toap01.png -------------------------------------------------------------------------------- /w3csvg/toap02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/toap02.png -------------------------------------------------------------------------------- /w3csvg/toap03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/toap03.png -------------------------------------------------------------------------------- /w3csvg/toap04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/toap04.png -------------------------------------------------------------------------------- /w3csvg/tref01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/tref01.png -------------------------------------------------------------------------------- /w3csvg/ViewBox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/ViewBox.png -------------------------------------------------------------------------------- /w3csvg/circle01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/circle01.png -------------------------------------------------------------------------------- /w3csvg/cubic01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/cubic01.png -------------------------------------------------------------------------------- /w3csvg/cubic02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/cubic02.png -------------------------------------------------------------------------------- /w3csvg/feBlend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/feBlend.png -------------------------------------------------------------------------------- /w3csvg/linejoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/linejoin.png -------------------------------------------------------------------------------- /w3csvg/rtl-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/rtl-text.png -------------------------------------------------------------------------------- /w3csvg/tspan01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/tspan01.png -------------------------------------------------------------------------------- /w3csvg/tspan02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/tspan02.png -------------------------------------------------------------------------------- /w3csvg/tspan03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/tspan03.png -------------------------------------------------------------------------------- /w3csvg/tspan04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/tspan04.png -------------------------------------------------------------------------------- /w3csvg/tspan05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/tspan05.png -------------------------------------------------------------------------------- /w3csvg/NewCoordSys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/NewCoordSys.png -------------------------------------------------------------------------------- /w3csvg/RotateScale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/RotateScale.png -------------------------------------------------------------------------------- /w3csvg/ellipse01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/ellipse01.png -------------------------------------------------------------------------------- /w3csvg/feComposite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/feComposite.png -------------------------------------------------------------------------------- /w3csvg/feImage-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/feImage-01.png -------------------------------------------------------------------------------- /w3csvg/filters00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/filters00.png -------------------------------------------------------------------------------- /w3csvg/filters01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/filters01.png -------------------------------------------------------------------------------- /w3csvg/inheritance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/inheritance.png -------------------------------------------------------------------------------- /w3csvg/lingrad01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/lingrad01.png -------------------------------------------------------------------------------- /w3csvg/opacity01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/opacity01.png -------------------------------------------------------------------------------- /w3csvg/pattern01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/pattern01.png -------------------------------------------------------------------------------- /w3csvg/polygon01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/polygon01.png -------------------------------------------------------------------------------- /w3csvg/polyline01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/polyline01.png -------------------------------------------------------------------------------- /w3csvg/radgrad01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/radgrad01.png -------------------------------------------------------------------------------- /w3csvg/rtl-complex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/rtl-complex.png -------------------------------------------------------------------------------- /w3csvg/test_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/test_image.jpg -------------------------------------------------------------------------------- /w3csvg/triangle01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/triangle01.png -------------------------------------------------------------------------------- /w3csvg/InitialCoords.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/InitialCoords.png -------------------------------------------------------------------------------- /w3csvg/OrigCoordSys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/OrigCoordSys.png -------------------------------------------------------------------------------- /w3csvg/feColorMatrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/feColorMatrix.png -------------------------------------------------------------------------------- /w3csvg/feMorphology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/feMorphology.png -------------------------------------------------------------------------------- /w3csvg/feTurbulence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/feTurbulence.png -------------------------------------------------------------------------------- /w3csvg/fillrule-evenodd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/fillrule-evenodd.png -------------------------------------------------------------------------------- /w3csvg/fillrule-nonzero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/fillrule-nonzero.png -------------------------------------------------------------------------------- /w3csvg/PreserveAspectRatio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/PreserveAspectRatio.png -------------------------------------------------------------------------------- /w3csvg/enable-background-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/enable-background-01.png -------------------------------------------------------------------------------- /w3csvg/feComponentTransfer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/feComponentTransfer.png -------------------------------------------------------------------------------- /w3csvg/primitive-subregion-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Twinside/rasterific-svg/HEAD/w3csvg/primitive-subregion-01.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | stack build 4 | 5 | prof: 6 | stack build --profile --ghc-options="-rtsopts" 7 | 8 | lint: 9 | hlint . 10 | 11 | doc: 12 | cabal haddock 13 | 14 | -------------------------------------------------------------------------------- /w3csvg/rtl-text.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | Right-to-left Text 6 | 7 | A simple example for using the 'direction' property in documents 8 | that predominantly use right-to-left languages. 9 | 10 | 11 | داستان SVG 1.1 SE طولا ني است. 12 | 13 | -------------------------------------------------------------------------------- /w3csvg/image01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | This graphic links to an external image 7 | 8 | 11 | My image 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /w3csvg/triangle01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example triangle01- simple example of a 'path' 7 | A path that draws a triangle 8 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /w3csvg/Use01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example Use01 - Simple case of 'use' on a 'rect' 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /w3csvg/circle01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example circle01 - circle filled with red and stroked with blue 7 | 8 | 9 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /w3csvg/rect01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example rect01 - rectangle with sharp corners 7 | 8 | 9 | 11 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /w3csvg/rtl-complex.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Right-to-left Text 7 | 8 | An example for using the 'direction' and 'unicode-bidi' properties 9 | in documents that predominantly use right-to-left languages. 10 | 11 | 12 | כתובת 13 | MAC:‏ 14 | 00-24-AF-2A-55-FC 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /w3csvg/text01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example text01 - 'Hello, out there' in blue 7 | 8 | 10 | Hello, out there 11 | 12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /exec-src/benching.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | 3 | #if !MIN_VERSION_base(4,8,0) 4 | import Control.Applicative( (<$>) ) 5 | #endif 6 | 7 | import Criterion 8 | import Criterion.Main 9 | import Graphics.Text.TrueType( emptyFontCache ) 10 | import Graphics.Svg 11 | import Graphics.Rasterific.Svg 12 | 13 | main :: IO () 14 | main = do 15 | f <- loadSvgFile "test/tiger.svg" 16 | {-cache <- loadCreateFontCache-} 17 | case f of 18 | Nothing -> putStrLn "Error while loading SVG" 19 | Just doc -> do 20 | let loader = fst <$> renderSvgDocument emptyFontCache Nothing 96 doc 21 | defaultMainWith defaultConfig 22 | [ bench "Tiger render" $ whnfIO loader ] 23 | 24 | -------------------------------------------------------------------------------- /w3csvg/Use03.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example Use03 - 'use' with a 'transform' attribute 7 | 8 | 9 | 10 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /w3csvg/OrigCoordSys.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example OrigCoordSys - Simple transformations: original picture 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ABC (orig coord system) 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /w3csvg/tspan04.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | Example tspan04 - The number of rotate values is less than the number of 8 | characters in the string. 9 | 10 | 11 | 12 | Hello, out there 13 | 14 | 15 | 16 | 18 | -------------------------------------------------------------------------------- /w3csvg/tspan01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example tspan01 - using tspan to change visual attributes 7 | 8 | 9 | 10 | You are 11 | not 12 | a banana. 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /w3csvg/rect02.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example rect02 - rounded rectangles 7 | 8 | 9 | 11 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /w3csvg/ellipse01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example ellipse01 - examples of ellipses 7 | 8 | 9 | 11 | 12 | 13 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /w3csvg/inheritance.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Gradients apply to leaf nodes 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /w3csvg/marker.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 13 | 14 | 15 | 16 | 18 | Placing an arrowhead at the end of a path. 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /w3csvg/polygon01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example polygon01 - star and hexagon 7 | 8 | 9 | 11 | 12 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /w3csvg/tspan03.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example tspan03 - using tspan's x and y attributes 7 | for multiline text and precise glyph positioning 8 | 9 | 10 | 11 | 12 | Cute and 13 | 14 | 15 | fuzzy 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /w3csvg/InitialCoords.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example InitialCoords - SVG's initial coordinate system 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | (0,0) 19 | (300,0) 20 | (0,100) 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /w3csvg/tref01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | Referenced character data 9 | 10 | 11 | Example tref01 - inline vs reference text content 12 | 13 | 14 | Inline character data 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /w3csvg/tspan02.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example tspan02 - using tspan's dx and dy attributes 7 | for incremental positioning adjustments 8 | 9 | 10 | 11 | But you 12 | 13 | are 14 | 15 | 16 | a peach! 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /w3csvg/Use02.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example Use02 - 'use' on a 'symbol' 7 | 8 | 9 | MySymbol - four rectangles in a grid 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /w3csvg/polyline01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example polyline01 - increasingly larger bars 7 | 8 | 9 | 11 | 12 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /w3csvg/pattern01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 20 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rasterific-svg 2 | ============== 3 | 4 | [![Hackage](https://img.shields.io/hackage/v/rasterific-svg.svg)](http://hackage.haskell.org/package/rasterific-svg) 5 | SVGTiny loader/renderer/serializer based on Rasterific. 6 | 7 | The library is available on [Hackage](http://hackage.haskell.org/package/rasterific-svg) 8 | 9 | Current capabilities 10 | -------------------- 11 | 12 | The current version implements SVG Tiny1.2 with the exception of: 13 | 14 | * non-scaling stroke. 15 | * textArea element. 16 | 17 | The implementation also implements feature from SVG 1.1 like: 18 | 19 | * Advanced text handling (text on path, dx/dy), but with 20 | low support for Unicode, right to left and vertical text. 21 | * CSS Styling, using CSS2 styling engine. 22 | * `pattern` element handling 23 | * `marker` element hadnling 24 | * Path arcs. 25 | 26 | And from SVG 2.0 draft: 27 | 28 | * Gradient mesh 29 | 30 | This package can render SVG to an image or transform it to a PDF. 31 | 32 | -------------------------------------------------------------------------------- /w3csvg/lingrad01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example lingrad01 - fill a rectangle using a 7 | linear gradient paint server 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /w3csvg/line01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example line01 - lines expressed in user coordinates 7 | 8 | 9 | 11 | 12 | 13 | 15 | 17 | 19 | 21 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /w3csvg/toap01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 12 | 13 | Example toap01 - simple text on a path 14 | 15 | 16 | 17 | 18 | We go up, then we go down, then up again 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /w3csvg/toap03.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 12 | 13 | Example toap03 - text on a path with startOffset attribute 14 | 15 | 16 | 17 | 18 | We go up, then we go down, then up again 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /w3csvg/arcs01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example arcs01 - arc commands in path data 7 | Picture of a pie chart with two pie wedges and 8 | a picture of a line with arc blips 9 | 11 | 12 | 14 | 16 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /w3csvg/radgrad01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example radgrad01 - fill a rectangle by referencing a 7 | radial gradient paint server 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /w3csvg/textdecoration01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example textdecoration01 - behavior of 'text-decoration' property 7 | 8 | 9 | Normal text 10 | Text with line-through 11 | Underlined text 12 | 13 | One 14 | word 15 | has 16 | different 17 | underlining 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /w3csvg/toap02.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 12 | 13 | Example toap02 - tspan within textPath 14 | 15 | 16 | 17 | 18 | We go 19 | 20 | up 21 | 22 | 23 | , 24 | 25 | then we go down, then up again 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /w3csvg/ViewBox.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | Example ViewBox - uses the viewBox 8 | attribute to automatically create an initial user coordinate 9 | system which causes the graphic to scale to fit into the 10 | viewport no matter what size the viewport is. 11 | 12 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Stretch to fit 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /w3csvg/quad01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example quad01 - quadratic Bézier commands in path data 7 | Picture showing a "Q" a "T" command, 8 | along with annotations showing the control points 9 | and end points 10 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /w3csvg/tspan05.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | Example tspan05 - propagation of rotation values to nested tspan elements. 8 | 9 | 11 | Not 12 | 13 | 14 | all characters 15 | 16 | 17 | in 18 | 19 | 20 | the 21 | 22 | 23 | 24 | 25 | text 26 | 27 | 28 | have a 29 | 30 | 31 | 32 | specified 33 | 34 | 35 | rotation 36 | 37 | 38 | 39 | 41 | 42 | -------------------------------------------------------------------------------- /w3csvg/NewCoordSys.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example NewCoordSys - New user coordinate system 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ABC (orig coord system) 15 | 16 | 17 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | ABC (translated coord system) 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /w3csvg/Use04.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example Use04 - 'use' with CSS styling 7 | 8 | 11 | 12 | 24 | 25 | 27 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /w3csvg/mask01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example mask01 - blue text masked with gradient against red background 7 | 8 | 9 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 20 | Masked text 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /w3csvg/feImage-01.svg: -------------------------------------------------------------------------------- 1 | 4 | Example feImage - Examples of feImage use 5 | Three examples of using feImage, the first showing the 6 | default rendering, the second showing the image fit 7 | to a box and the third showing the image 8 | shifted and clipped. 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Vincent Berthoux 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Vincent Berthoux nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /w3csvg/RotateScale.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example RotateScale - Rotate and scale transforms 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ABC (rotate) 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ABC (scale) 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /w3csvg/Skew.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example Skew - Show effects of skewX and skewY 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ABC (skewX) 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | ABC (skewY) 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /w3csvg/linejoin.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example linecap - demonstrates three stroke-linecap values 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 'miter' join 23 | 24 | 25 | 26 | 27 | 28 | 'round' join 29 | 30 | 31 | 32 | 33 | 34 | 'bevel' join 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /w3csvg/feMorphology.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example feMorphology - Examples of erode and dilate 7 | Five text strings drawn as outlines. 8 | The first is unfiltered. The second and third use 'erode'. 9 | The fourth and fifth use 'dilate'. 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 29 | Unfiltered 30 | Erode radius 3 31 | Erode radius 6 32 | Dilate radius 3 33 | Dilate radius 6 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /w3csvg/Nested.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example Nested - Nested transformations 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ....Translate(1) 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ....Rotate(2) 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ....Translate(3) 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /w3csvg/primitive-subregion-01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /w3csvg/linecap.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example linecap - demonstrates three stroke-linecap values 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 'butt' cap 26 | 27 | 28 | 29 | 30 | 31 | 'round' cap 32 | 33 | 34 | 35 | 36 | 37 | 'square' cap 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /w3csvg/Units.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example Units 7 | Illustrates various units options 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | Abs. units: 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Rel. units: 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Percentages: 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /w3csvg/cubic01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example cubic01- cubic Bézier commands in path data 7 | Picture showing a simple example of path data 8 | using both a "C" and an "S" command, 9 | along with annotations showing the control points 10 | and end points 11 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | M100,200 C100,100 250,100 250,200 37 | S400,300 400,200 39 | 40 | -------------------------------------------------------------------------------- /w3csvg/filters01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example filters01.svg - introducing filter effects 7 | An example which combines multiple filter primitives 8 | to produce a 3D lighting effect on a graphic consisting 9 | of the string "SVG" sitting on top of oval filled in red 10 | and surrounded by an oval outlined in red. 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 36 | 37 | SVG 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /w3csvg/feBlend.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example feBlend - Examples of feBlend modes 7 | Five text strings blended into a gradient, 8 | with one text string for each of the five feBlend modes. 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | Normal 39 | Multiply 40 | Screen 41 | Darken 42 | Lighten 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /w3csvg/opacity01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example opacity01 - opacity property 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /w3csvg/filters00.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example filters00.svg - filter effects before and after 7 | Picture of the before and after affects of filtering. 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 40 | 41 | Original source graphic 42 | 43 | Filter 44 | Effect 45 | 46 | Result of filter effect 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | Change log 2 | ========== 3 | 4 | v0.3.3.2 October 2018 5 | --------------------- 6 | 7 | * GHC 8.6 version bump 8 | 9 | v0.3.3.1 March 2018 10 | ------------------- 11 | 12 | * Providing Semigroup instances 13 | 14 | v0.3.3 2017 15 | ----------- 16 | 17 | * Fix: Arc rendering, some cases where mishandled 18 | * Addition: linked patterns handling 19 | * Fix: gradient transformation handling 20 | * Fix: better handling of viewbox attribute. 21 | 22 | v0.3.2.1 November 2016 23 | ---------------------- 24 | * Fix: handling of "matrix()" transform 25 | * Fix: stroking with evenodd fill method. 26 | * Fix: handling of miter-limit value 27 | 28 | v0.3.2 October 2016 29 | ------------------- 30 | * Bumping Rasterific dep 31 | * Bumping svg-tree dep 32 | * Adding SVG2 gradient mesh rendering 33 | 34 | v0.3.1.2 May 2016 35 | ----------------- 36 | * Fix: Bumping for GHC 8.0 37 | 38 | v0.3.1.1 March 2016 39 | ------------------- 40 | * Fix: Bumping to svg-tree 0.5 41 | * Fix: Bumping linear to 0.20 42 | 43 | v0.3 February 2016 44 | ------------------ 45 | * Fix: Updating to handle svg-tree 0.4 46 | 47 | v0.2.3.2 October 2015 48 | --------------------- 49 | * Fix: bumping optparse-applicative upper bound 50 | 51 | v0.2.3.1 May 2015 52 | ----------------- 53 | * Fix: Bumping Rasterific version to compiler 54 | without problems with GHC 7.6 55 | 56 | v0.2.3 May 2015 57 | --------------- 58 | 59 | * Adding: PDF output 60 | * Fix: font cache created in temp dir 61 | 62 | v0.2.2.1 May 2015 63 | ----------------- 64 | 65 | * Fix: GHC < 7.10 compilation 66 | 67 | v0.2.2 May 2015 68 | --------------- 69 | 70 | * Fix: lens upper bound, and removing it. 71 | 72 | v0.2.1 May 2015 73 | --------------- 74 | 75 | * Adding: support for arc in path 76 | 77 | v0.2 April 2015 78 | --------------- 79 | 80 | * Bumping: using svg-tree 0.3 81 | 82 | v0.1.1 April 2015 83 | ----------------- 84 | 85 | * Fix: Fixing GHC 7.10.1 related warnings 86 | * Fix: Group transparency. 87 | 88 | v0.1.0.3 March 2015 89 | ------------------- 90 | 91 | * Fix: Bumping lens dependency 92 | 93 | v0.1.0.2 February 2015 94 | ---------------------- 95 | 96 | * Fix: Removing all test suites from distribution package. 97 | * Fix: Lowering some low version bounds. 98 | 99 | v0.1.0.1 February 2015 100 | ---------------------- 101 | 102 | * Fix: Removing bench from test suite. 103 | 104 | v0.1 February 2015 105 | ------------------ 106 | 107 | * Initial release 108 | 109 | -------------------------------------------------------------------------------- /w3csvg/arcs02.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example arcs02 - arc options in paths 7 | Pictures showing the result of setting 8 | large-arc-flag and sweep-flag to the four 9 | possible combinations of 0 and 1. 10 | 11 | 12 | 13 | 15 | 17 | Arc start 18 | Arc end 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | large-arc-flag=0 30 | sweep-flag=0 31 | 32 | 34 | 35 | 36 | large-arc-flag=0 37 | sweep-flag=1 38 | 39 | 41 | 42 | 43 | large-arc-flag=1 44 | sweep-flag=0 45 | 46 | 48 | 49 | 50 | large-arc-flag=1 51 | sweep-flag=1 52 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /w3csvg/feColorMatrix.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example feColorMatrix - Examples of feColorMatrix operations 7 | Five text strings showing the effects of feColorMatrix: 8 | an unfiltered text string acting as a reference, 9 | use of the feColorMatrix matrix option to convert to grayscale, 10 | use of the feColorMatrix saturate option, 11 | use of the feColorMatrix hueRotate option, 12 | and use of the feColorMatrix luminanceToAlpha option. 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 28 | 29 | 31 | 32 | 33 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 45 | 47 | 48 | Unfiltered 49 | Matrix 50 | Saturate 51 | HueRotate 52 | Luminance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /w3csvg/fillrule-evenodd.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example fillrule-evenodd - demonstrates fill-rule:evenodd 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /w3csvg/fillrule-nonzero.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example fillrule-nonzero - demonstrates fill-rule:nonzero 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /w3csvg/feComponentTransfer.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example feComponentTransfer - Examples of feComponentTransfer operations 7 | Four text strings showing the effects of feComponentTransfer: 8 | an identity function acting as a reference, 9 | use of the feComponentTransfer table option, 10 | use of the feComponentTransfer linear option, 11 | and use of the feComponentTransfer gamma option. 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 56 | 58 | 59 | Identity 60 | TableLookup 61 | LinearFunc 62 | GammaFunc 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /w3csvg/feTurbulence.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example feTurbulence - Examples of feTurbulence operations 7 | Six rectangular areas showing the effects of 8 | various parameter settings for feTurbulence. 9 | 10 | 11 | 13 | 14 | 15 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 26 | 27 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | type=turbulence 42 | baseFrequency=0.05 43 | numOctaves=2 44 | 45 | 46 | type=turbulence 47 | baseFrequency=0.1 48 | numOctaves=2 49 | 50 | 51 | type=turbulence 52 | baseFrequency=0.05 53 | numOctaves=8 54 | 55 | 56 | type=fractalNoise 57 | baseFrequency=0.1 58 | numOctaves=4 59 | 60 | 61 | type=fractalNoise 62 | baseFrequency=0.4 63 | numOctaves=4 64 | 65 | 66 | type=fractalNoise 67 | baseFrequency=0.1 68 | numOctaves=1 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /w3csvg/enable-background-01.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example enable-background01 7 | This test case shows five pictures which illustrate the rules 8 | for background image processing. 9 | 10 | 11 | 13 | 14 | This filter discards the SourceGraphic, if any, and just produces 15 | a result consisting of the BackgroundImage shifted down 125 units 16 | and then blurred. 17 | 18 | 19 | 20 | 21 | 23 | 24 | This filter takes the BackgroundImage, shifts it down 125 units, blurs it, 25 | and then renders the SourceGraphic on top of the shifted/blurred background. 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | The first picture is our reference graphic without filters. 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | The second adds an empty 'g' element which invokes ShiftBGAndBlur. 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | The third invokes ShiftBGAndBlur on the inner group. 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | The fourth invokes ShiftBGAndBlur on the triangle. 69 | 70 | 71 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | The fifth invokes ShiftBGAndBlur_WithSourceGraphic on the triangle. 80 | 81 | 82 | 83 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /rasterific-svg.cabal: -------------------------------------------------------------------------------- 1 | -- Initial svg.cabal generated by cabal init. For further documentation, 2 | -- see http://haskell.org/cabal/users-guide/ 3 | name: rasterific-svg 4 | version: 0.3.3.2 5 | synopsis: SVG renderer based on Rasterific. 6 | description: SVG renderer that will let you render svg-tree parsed 7 | SVG file to a JuicyPixel image or Rasterific Drawing. 8 | license: BSD3 9 | license-file: LICENSE 10 | author: Vincent Berthoux 11 | maintainer: Vincent Berthoux 12 | -- copyright: 13 | extra-source-files: changelog.md, README.md 14 | category: Graphics, Svg 15 | build-type: Simple 16 | cabal-version: >=1.10 17 | 18 | Source-Repository head 19 | Type: git 20 | Location: git://github.com/Twinside/rasterific-svg.git 21 | 22 | Source-Repository this 23 | Type: git 24 | Location: git://github.com/Twinside/rasterific-svg.git 25 | Tag: v0.3.3.2 26 | 27 | library 28 | hs-source-dirs: src 29 | default-language: Haskell2010 30 | Ghc-options: -O3 -Wall 31 | -- -auto-all 32 | exposed-modules: Graphics.Rasterific.Svg 33 | other-modules: Graphics.Rasterific.Svg.RenderContext 34 | , Graphics.Rasterific.Svg.PathConverter 35 | , Graphics.Rasterific.Svg.MeshConverter 36 | , Graphics.Rasterific.Svg.RasterificRender 37 | , Graphics.Rasterific.Svg.RasterificTextRendering 38 | , Graphics.Rasterific.Svg.ArcConversion 39 | 40 | if impl(ghc >= 8.0) 41 | ghc-options: -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances 42 | else 43 | -- provide/emulate `Control.Monad.Fail` and `Data.Semigroups` API for pre-GHC8 44 | build-depends: fail == 4.9.*, semigroups == 0.18.* 45 | 46 | build-depends: base >= 4.5 && < 6 47 | , directory 48 | , bytestring >= 0.10 49 | , filepath 50 | , binary >= 0.7 51 | , scientific >= 0.3 52 | , JuicyPixels >= 3.2 53 | , containers >= 0.5 54 | , Rasterific >= 0.7.4.4 && < 0.8 55 | , FontyFruity >= 0.5.2.1 && < 0.6 56 | , svg-tree >= 0.6.2 && < 0.7 57 | , lens >= 4.5 58 | , linear >= 1.20 59 | , vector >= 0.10 60 | , text >= 1.2 61 | , transformers >= 0.3 62 | , mtl >= 2.1 63 | , primitive 64 | 65 | Executable svgrender 66 | default-language: Haskell2010 67 | hs-source-dirs: exec-src 68 | Main-Is: svgrender.hs 69 | Ghc-options: -O3 -Wall 70 | Build-Depends: base >= 4.6 71 | , optparse-applicative >= 0.11 72 | , directory >= 1.0 73 | , bytestring 74 | , rasterific-svg 75 | , Rasterific 76 | , JuicyPixels 77 | , filepath 78 | , FontyFruity 79 | , svg-tree 80 | 81 | Executable svgtest 82 | --type: exitcode-stdio-1.0 83 | default-language: Haskell2010 84 | hs-source-dirs: exec-src 85 | Main-Is: svgtest.hs 86 | Ghc-options: -O3 -Wall 87 | ghc-prof-options: -rtsopts -Wall -prof -auto-all 88 | Build-Depends: base >= 4.6 89 | , linear 90 | , rasterific-svg 91 | , Rasterific 92 | , JuicyPixels 93 | , directory 94 | , filepath 95 | , attoparsec 96 | , text 97 | , FontyFruity 98 | , binary 99 | , bytestring 100 | , svg-tree 101 | , blaze-html 102 | 103 | Test-Suite bench 104 | type: exitcode-stdio-1.0 105 | hs-source-dirs: exec-src 106 | Main-Is: benching.hs 107 | default-language: Haskell2010 108 | Ghc-options: -O3 -Wall 109 | ghc-prof-options: -rtsopts -Wall -prof -auto-all 110 | Build-Depends: base >= 4.6 111 | , rasterific-svg 112 | , Rasterific 113 | , JuicyPixels 114 | , FontyFruity 115 | , criterion >= 1.0 116 | , deepseq 117 | , svg-tree 118 | 119 | -------------------------------------------------------------------------------- /src/Graphics/Rasterific/Svg/ArcConversion.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE BangPatterns #-} 2 | -- | Conversion from SVG arcs to bezier curves 3 | -- see https://github.com/GNOME/librsvg/blob/ebcbfae24321f22cd8c04a4951bbaf70b60d7f29/rust/src/path_builder.rs 4 | module Graphics.Rasterific.Svg.ArcConversion( arcToSegments ) where 5 | 6 | import Graphics.Svg.Types 7 | import Linear( M22, nearZero, (!*), V2( V2 ), norm, quadrance ) 8 | 9 | toRadian :: Floating a => a -> a 10 | toRadian v = v / 180 * pi 11 | 12 | -- | Create a 2 dimensional rotation matrix given an angle 13 | -- expressed in radians. 14 | mkRotation :: Floating a => a -> M22 a 15 | mkRotation angle = 16 | V2 (V2 ca (-sa)) 17 | (V2 sa ca) 18 | where 19 | ca = cos angle 20 | sa = sin angle 21 | 22 | mkRota' :: Floating a => a -> M22 a 23 | mkRota' angle = 24 | V2 (V2 ca sa) 25 | (V2 (-sa) ca) 26 | where 27 | ca = cos angle 28 | sa = sin angle 29 | 30 | arcSegment :: V2 Double -> Double -> Double -> V2 Double -> Double 31 | -> PathCommand 32 | arcSegment c th0 th1 r angle = comm where 33 | !comm = CurveTo OriginAbsolute 34 | [( c + (finalRotation !* p1) 35 | , c + (finalRotation !* p2) 36 | , c + (finalRotation !* p3) 37 | )] 38 | 39 | !finalRotation = mkRotation $ toRadian angle 40 | 41 | !th_half = 0.5 * (th1 - th0) 42 | !t = (8.0 / 3.0) * 43 | sin (th_half * 0.5) * 44 | sin (th_half * 0.5) / 45 | sin th_half 46 | 47 | !cosTh0 = cos th0 48 | !sinTh0 = sin th0 49 | !cosTh1 = cos th1 50 | !sinTh1 = sin th1 51 | 52 | !p1 = r * V2 (cosTh0 - t * sinTh0) (sinTh0 + t * cosTh0) 53 | !p3 = r * V2 cosTh1 sinTh1 54 | !p2 = p3 + r * V2 (t * sinTh1) (-t * cosTh1) 55 | 56 | -- See Appendix F.6 Elliptical arc implementation notes 57 | -- http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes */ 58 | arc :: V2 Double -> Double -> Double -> Double -> Bool -> Bool -> V2 Double 59 | -> [PathCommand] 60 | arc p1 rxOrig ryOrig x_axis_rotation is_large_arc is_sweep p2 61 | | p1 == p2 = mempty 62 | | nearZero (abs rxOrig) || nearZero (abs ryOrig) = [LineTo OriginAbsolute [p2]] 63 | | kCheck == 0 = mempty 64 | | norm kk == 0 = mempty 65 | | k5Norm == 0 = mempty 66 | | otherwise = segs 67 | where 68 | f = toRadian x_axis_rotation 69 | 70 | k = (p1 - p2) * 0.5 71 | p1_@(V2 x1_ y1_) = mkRota' f !* k 72 | 73 | radius@(V2 rx ry) 74 | | gamma > 1 = V2 (abs rxOrig * sqrt gamma) (abs ryOrig * sqrt gamma) 75 | | otherwise = V2 (abs rxOrig) (abs ryOrig) 76 | where gamma = (x1_ * x1_) / (rxOrig * rxOrig) + (y1_ * y1_) / (ryOrig * ryOrig) 77 | 78 | sweepCoeff | is_sweep == is_large_arc = -1 79 | | otherwise = 1 80 | 81 | -- Compute the center 82 | kCheck = rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_ 83 | 84 | kc = (sweepCoeff *) . sqrt . abs $ (rx * rx * ry * ry) / kCheck - 1.0 85 | 86 | c_ = V2 (kc * rx * y1_ / ry) (-kc * ry * x1_ / rx) 87 | c = (mkRotation f !* c_) + (p1 + p2) * 0.5 88 | 89 | -- Compute start angle 90 | kk@(V2 k1 k2) = (p1_ - c_) / radius 91 | kkk@(V2 k3 k4) = ((-p1_) - c_) / radius 92 | 93 | theta1 = (if k2 < 0 then negate else id) . acos . min 1 . max (-1) $ k1 / norm kk 94 | 95 | -- Compute delta_theta 96 | k5Norm = sqrt $ quadrance kk * quadrance kkk 97 | 98 | delta_theta 99 | | is_sweep && v < 0.0 = v + 2 * pi 100 | | not is_sweep && v > 0.0 = v - 2 * pi 101 | | otherwise = v 102 | where 103 | vBase = acos . min 1 . max (-1) $ (k1 * k3 + k2 * k4) / k5Norm; 104 | v | k1 * k4 - k3 * k2 < 0.0 = - vBase 105 | | otherwise = vBase 106 | 107 | -- Now draw the arc 108 | n_segs :: Int 109 | n_segs = ceiling . abs $ delta_theta / (pi * 0.5 + 0.001) 110 | 111 | angleAt v = theta1 + fromIntegral v * delta_theta / fromIntegral n_segs 112 | 113 | segs = 114 | [arcSegment c (angleAt i) (angleAt $ i + 1) (V2 rx ry) x_axis_rotation 115 | | i <- [0 .. n_segs - 1]] 116 | 117 | arcToSegments :: RPoint -> (Coord, Coord, Coord, Bool, Bool, RPoint) 118 | -> [PathCommand] 119 | arcToSegments orig (radX, radY, rotateX, large, sweep, pos) = 120 | arc orig radX radY rotateX large sweep pos 121 | 122 | -------------------------------------------------------------------------------- /w3csvg/PreserveAspectRatio.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | "> 13 | "> 15 | "> 17 | ]> 18 | 19 | 21 | Example PreserveAspectRatio - illustrates preserveAspectRatio attribute 22 | 24 | 25 | SVG to fit 26 | &Smile; 27 | Viewport 1 28 | &Viewport1; 29 | Viewport 2 30 | &Viewport2; 31 | 32 | 33 | --------------- meet --------------- 34 | xMin*&Viewport1; 35 | &Smile; 37 | xMid*&Viewport1; 38 | &Smile; 40 | xMax*&Viewport1; 41 | &Smile; 43 | 44 | 45 | 46 | ---------- meet ---------- 47 | *YMin&Viewport2; 48 | &Smile; 50 | *YMid&Viewport2; 51 | &Smile; 53 | *YMax&Viewport2; 54 | &Smile; 56 | 57 | 58 | 59 | ---------- slice ---------- 60 | xMin*&Viewport2; 61 | &Smile; 63 | xMid*&Viewport2; 64 | &Smile; 66 | xMax*&Viewport2; 67 | &Smile; 69 | 70 | 71 | 72 | --------------- slice --------------- 73 | *YMin&Viewport1; 74 | &Smile; 76 | *YMid&Viewport1; 77 | &Smile; 79 | *YMax&Viewport1; 80 | &Smile; 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /exec-src/svgtest.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | #if !MIN_VERSION_base(4,8,0) 3 | import Control.Applicative( (<$>) ) 4 | import Data.Foldable( foldMap ) 5 | #endif 6 | 7 | import Control.Monad( forM_ ) 8 | import Data.Monoid( (<>) ) 9 | import Data.List( isSuffixOf, sort ) 10 | import System.Environment( getArgs ) 11 | import System.Directory( createDirectoryIfMissing 12 | , getDirectoryContents 13 | ) 14 | import qualified Data.ByteString.Lazy as LB 15 | import qualified Text.Blaze.Html5 as HT 16 | import qualified Text.Blaze.Html5 as H 17 | import qualified Text.Blaze.Html5.Attributes as H 18 | import qualified Text.Blaze.Html.Renderer.String as H 19 | import System.FilePath( dropExtension, (), (<.>), splitFileName ) 20 | 21 | import Codec.Picture( writePng ) 22 | 23 | import Graphics.Text.TrueType( FontCache ) 24 | import Graphics.Rasterific.Svg 25 | import Graphics.Svg hiding ( text, path ) 26 | {-import Debug.Trace-} 27 | {-import Text.Printf-} 28 | {-import Text.Groom-} 29 | 30 | loadRender :: [String] -> IO () 31 | loadRender [] = putStrLn "not enough arguments" 32 | loadRender [_] = putStrLn "not enough arguments" 33 | loadRender (svgfilename:pngfilename:_) = do 34 | f <- loadSvgFile svgfilename 35 | case f of 36 | Nothing -> putStrLn "Error while loading SVG" 37 | Just doc -> do 38 | cache <- loadCreateFontCache "fonty-texture-cache" 39 | (finalImage, _) <- renderSvgDocument cache Nothing 96 doc 40 | writePng pngfilename finalImage 41 | 42 | testOutputFolder :: FilePath 43 | testOutputFolder = "gen_test" 44 | 45 | img :: FilePath -> Int -> Int -> H.Html 46 | img path _w _h = H.img H.! H.src (H.toValue path) 47 | 48 | table :: [H.Html] -> [[H.Html]] -> H.Html 49 | table headers cells = 50 | H.table $ header <> foldMap (H.tr . foldMap H.td) cells 51 | where header = H.tr $ foldMap H.th headers 52 | 53 | testFileOfPath :: FilePath -> FilePath 54 | testFileOfPath path = testOutputFolder base <.> "png" 55 | where (_, base) = splitFileName path 56 | 57 | svgTestFileOfPath :: FilePath -> FilePath 58 | svgTestFileOfPath path = testOutputFolder base <.> "svg" 59 | where (_, base) = splitFileName path 60 | 61 | pdfTestFileOfPath :: FilePath -> FilePath 62 | pdfTestFileOfPath path = testOutputFolder base <.> "pdf" 63 | where (_, base) = splitFileName path 64 | 65 | text :: String -> H.Html 66 | text txt = H.toHtml txt <> H.br 67 | 68 | generateFileInfo :: FilePath -> [H.Html] 69 | generateFileInfo path = 70 | [ text path, img path 0 0 71 | , img pngRef 0 0 72 | , img (testFileOfPath path) 0 0 73 | , img (svgTestFileOfPath path) 0 0] 74 | where 75 | pngRef = dropExtension path <.> "png" 76 | 77 | toHtmlDocument :: H.Html -> String 78 | toHtmlDocument html = H.renderHtml $ 79 | H.html $ H.head (HT.title $ H.toHtml "Test results") 80 | <> H.body html 81 | 82 | analyzeFolder :: FontCache -> FilePath -> IO () 83 | analyzeFolder cache folder = do 84 | createDirectoryIfMissing True testOutputFolder 85 | fileList <- sort . filter (".svg" `isSuffixOf`) <$> getDirectoryContents folder 86 | let hdr = H.toHtml <$> ["name", "W3C Svg", "W3C ref PNG", "mine", "svgmine"] 87 | all_table = 88 | table hdr . map generateFileInfo $ map (folder ) fileList 89 | doc = toHtmlDocument all_table 90 | (_, folderBase) = splitFileName folder 91 | 92 | print fileList 93 | 94 | writeFile (folder ".." folderBase <.> "html") doc 95 | forM_ fileList $ \p -> do 96 | let realFilename = folder p 97 | putStrLn $ "Loading: " ++ realFilename 98 | svg <- loadSvgFile realFilename 99 | {-putStrLn $ groom svg-} 100 | {-putStrLn $ show svg-} 101 | case svg of 102 | Nothing -> putStrLn $ "Failed to load " ++ p 103 | Just d -> do 104 | putStrLn $ " => Rendering " ++ show (documentSize 96 d) 105 | (finalImage, _) <- renderSvgDocument cache Nothing 96 d 106 | writePng (testFileOfPath p) finalImage 107 | 108 | putStrLn " => XMLize" 109 | saveXmlFile (svgTestFileOfPath p) d 110 | 111 | putStrLn " => PDFize" 112 | (pdf, _) <- pdfOfSvgDocument cache Nothing 96 d 113 | LB.writeFile (pdfTestFileOfPath p) pdf 114 | 115 | 116 | 117 | testSuite :: IO () 118 | testSuite = do 119 | cache <- loadCreateFontCache "rasterific-svg-font-cache" 120 | analyzeFolder cache "w3csvg" 121 | {-analyzeFolder cache "test"-} 122 | 123 | main :: IO () 124 | main = do 125 | args <- getArgs 126 | case args of 127 | "test":_ -> testSuite 128 | _ -> loadRender args 129 | 130 | -------------------------------------------------------------------------------- /w3csvg/cubic02.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example cubic02 - cubic Bézier commands in path data 7 | Picture showing examples of "C" and "S" commands, 8 | along with annotations showing the control points 9 | and end points 10 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | M100,200 C100,100 400,100 400,200 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | M100,500 C25,400 475,400 400,500 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | M100,800 C175,700 325,700 400,800 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | M600,200 C675,100 975,100 900,200 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | M600,500 C600,350 900,650 900,500 71 | 72 | 73 | 74 | 75 | 76 | 77 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | M600,800 C625,700 725,700 750,800 87 | S875,900 900,800 88 | 89 | -------------------------------------------------------------------------------- /exec-src/svgrender.hs: -------------------------------------------------------------------------------- 1 | 2 | {-# LANGUAGE CPP #-} 3 | #if !MIN_VERSION_base(4,8,0) 4 | import Control.Applicative( (<$>), (<*>), pure ) 5 | #endif 6 | 7 | import Control.Applicative( (<|>) ) 8 | import Control.Monad( when ) 9 | import qualified Data.ByteString.Lazy as LB 10 | import Data.Monoid( (<>) ) 11 | import Codec.Picture( writePng ) 12 | import System.Directory( getTemporaryDirectory ) 13 | import System.FilePath( (), replaceExtension ) 14 | 15 | import Options.Applicative( Parser 16 | , ParserInfo 17 | , argument 18 | , execParser 19 | , fullDesc 20 | , header 21 | , help 22 | , helper 23 | , info 24 | , long 25 | , metavar 26 | , progDesc 27 | , str 28 | , switch 29 | , auto 30 | , option 31 | ) 32 | 33 | import Graphics.Rasterific.Svg( loadCreateFontCache 34 | , renderSvgDocument 35 | , pdfOfSvgDocument 36 | ) 37 | import Graphics.Svg( loadSvgFile 38 | , documentSize ) 39 | import System.Exit( ExitCode( ExitFailure, ExitSuccess ) 40 | , exitWith ) 41 | 42 | data Options = Options 43 | { _inputFile :: !FilePath 44 | , _outputFile :: !FilePath 45 | , _verbose :: !Bool 46 | , _asPdf :: !Bool 47 | , _width :: !Int 48 | , _height :: !Int 49 | , _dpi :: !Int 50 | } 51 | 52 | 53 | argParser :: Parser Options 54 | argParser = Options 55 | <$> ( argument str 56 | (metavar "SVGINPUTFILE" 57 | <> help "SVG file to render to png")) 58 | <*> ( argument str 59 | (metavar "OUTPUTFILE" 60 | <> help ("Output file name, same as input with" 61 | <> " different extension if unspecified.")) 62 | <|> pure "" ) 63 | <*> ( switch (long "verbose" <> help "Display more information") ) 64 | <*> ( switch (long "pdf" <> help "Convert to a PDF" ) ) 65 | <*> ( option auto 66 | ( long "width" 67 | <> help "Force the width of the rendered PNG" 68 | <> metavar "WIDTH" ) 69 | <|> pure 0 70 | ) 71 | <*> ( option auto 72 | ( long "height" 73 | <> help "Force the height of the rendered PNG" 74 | <> metavar "HEIGHT" ) 75 | <|> pure 0 ) 76 | <*> ( option auto 77 | ( long "dpi" 78 | <> help "DPI used for text rendering and various real life sizes" 79 | <> metavar "DPI" ) 80 | <|> pure 96 ) 81 | 82 | progOptions :: ParserInfo Options 83 | progOptions = info (helper <*> argParser) 84 | ( fullDesc 85 | <> progDesc "Convert SVGINPUTFILE into a png or pdf OUTPUTFILE" 86 | <> header "svgrender svg file renderer." ) 87 | 88 | outFileName :: Options -> FilePath 89 | outFileName Options { _outputFile = "", _inputFile = inf } = 90 | replaceExtension inf "png" 91 | outFileName opt = _outputFile opt 92 | 93 | fixSize :: Options -> (Int, Int) -> (Int, Int) 94 | fixSize opt (w, h) = (notNull (_width opt) w, notNull (_height opt) h) 95 | where 96 | notNull v v' = if v <= 0 then v' else v 97 | 98 | runConversion :: Options -> IO () 99 | runConversion options = do 100 | tempDir <- getTemporaryDirectory 101 | cache <- loadCreateFontCache $ tempDir "rasterific-svg-font-cache" 102 | let filename = _inputFile options 103 | whenVerbose = when (_verbose options) . putStrLn 104 | whenVerbose $ "Loading: " ++ filename 105 | 106 | svg <- loadSvgFile filename 107 | case svg of 108 | Nothing -> do 109 | putStrLn $ "Failed to load " ++ filename 110 | exitWith $ ExitFailure 1 111 | 112 | Just d -> do 113 | let dpi = _dpi options 114 | size = fixSize options $ documentSize dpi d 115 | whenVerbose $ "Rendering at " ++ show size 116 | if _asPdf options then do 117 | whenVerbose $ "Writing PDF at " ++ outFileName options 118 | (doc, _) <- pdfOfSvgDocument cache (Just size) dpi d 119 | LB.writeFile (outFileName options) doc 120 | exitWith ExitSuccess 121 | else do 122 | whenVerbose $ "Writing PNG at " ++ outFileName options 123 | (finalImage, _) <- renderSvgDocument cache (Just size) dpi d 124 | writePng (outFileName options) finalImage 125 | exitWith ExitSuccess 126 | 127 | main :: IO () 128 | main = execParser progOptions >>= runConversion 129 | 130 | -------------------------------------------------------------------------------- /src/Graphics/Rasterific/Svg.hs: -------------------------------------------------------------------------------- 1 | -- | Svg renderer based on Rasterific. 2 | -- 3 | -- Here is a simple example of loading a SVG file (using svg-tree) 4 | -- rendering it to a picture, and saving it to a PNG (using Juicy.Pixels) 5 | -- 6 | -- @ 7 | -- import Codec.Picture( writePng ) 8 | -- import Graphics.Svg( loadSvgFile ) 9 | -- import Graphics.Rasterific.Svg( loadCreateFontCache 10 | -- , renderSvgDocument 11 | -- ) 12 | -- loadRender :: FilePath -> FilePath -> IO () 13 | -- loadRender svgfilename pngfilename = do 14 | -- f <- loadSvgFile svgfilename 15 | -- case f of 16 | -- Nothing -> putStrLn "Error while loading SVG" 17 | -- Just doc -> do 18 | -- cache <- loadCreateFontCache "fonty-texture-cache" 19 | -- (finalImage, _) <- renderSvgDocument cache Nothing 96 doc 20 | -- writePng pngfilename finalImage 21 | -- @ 22 | -- 23 | module Graphics.Rasterific.Svg 24 | ( -- * Main functions 25 | drawingOfSvgDocument 26 | , renderSvgDocument 27 | , pdfOfSvgDocument 28 | , loadCreateFontCache 29 | -- * Types 30 | , LoadedElements( .. ) 31 | , Result( .. ) 32 | , DrawResult( .. ) 33 | , Dpi 34 | -- * Other helper functions 35 | , renderSvgFile 36 | ) where 37 | 38 | import qualified Data.ByteString.Lazy as B 39 | import Graphics.Rasterific.Svg.RasterificRender( DrawResult( .. ) ) 40 | import qualified Graphics.Rasterific.Svg.RasterificRender as RR 41 | import Data.Binary( encodeFile, decodeOrFail ) 42 | import Graphics.Svg.Types hiding ( Dpi ) 43 | import Graphics.Svg hiding ( Dpi ) 44 | import Graphics.Rasterific.Svg.RenderContext 45 | 46 | import System.Directory( doesFileExist ) 47 | import Graphics.Text.TrueType 48 | 49 | 50 | import qualified Codec.Picture as CP 51 | import Codec.Picture( PixelRGBA8( .. ) 52 | , writePng ) 53 | 54 | {-import Graphics.Svg.CssParser-} 55 | 56 | -- | Render an svg document to an image. 57 | -- If you provide a size, the document will be stretched to 58 | -- match the provided size. 59 | -- 60 | -- The DPI parameter really should depend of your screen, but 61 | -- a good default value is 96 62 | -- 63 | -- The use of the IO Monad is there to allow loading of fonts 64 | -- and referenced images. 65 | renderSvgDocument :: FontCache -- ^ Structure used to access fonts 66 | -> Maybe (Int, Int) -- ^ Optional document size 67 | -> Dpi -- ^ Current resolution for text and elements 68 | -> Document -- ^ Svg document 69 | -> IO (CP.Image PixelRGBA8, LoadedElements) 70 | renderSvgDocument cache size dpi = 71 | RR.renderSvgDocument cache size dpi . applyCSSRules . resolveUses 72 | 73 | pdfOfSvgDocument :: FontCache -> Maybe (Int, Int) -> Dpi -> Document 74 | -> IO (B.ByteString, LoadedElements) 75 | pdfOfSvgDocument cache sizes dpi = 76 | RR.pdfOfSvgDocument cache sizes dpi . applyCSSRules . resolveUses 77 | 78 | -- | Render an svg document to a Rasterific Drawing. 79 | -- If you provide a size, the document will be stretched to 80 | -- match the provided size. 81 | -- 82 | -- The DPI parameter really should depend of your screen, but 83 | -- a good default value is 96 84 | -- 85 | -- The use of the IO Monad is there to allow loading of fonts 86 | -- and referenced images. 87 | drawingOfSvgDocument :: FontCache -- ^ Structure used to access fonts 88 | -> Maybe (Int, Int) -- ^ Optional document size 89 | -> Dpi -- ^ Current resolution for text and elements 90 | -> Document -- ^ Svg document 91 | -> IO (DrawResult, LoadedElements) 92 | drawingOfSvgDocument cache size dpi = 93 | RR.drawingOfSvgDocument cache size dpi . applyCSSRules . resolveUses 94 | 95 | -- | Rendering status. 96 | data Result 97 | = ResultSuccess -- ^ No problem found 98 | | ResultError String -- ^ Error with message 99 | deriving (Eq, Show) 100 | 101 | -- | Convert an SVG file to a PNG file, return True 102 | -- if the operation went without problems. 103 | -- 104 | -- This function will call loadCreateFontCache with 105 | -- the filename "fonty-texture-cache" 106 | renderSvgFile :: FilePath -> FilePath -> IO Result 107 | renderSvgFile svgfilename pngfilename = do 108 | f <- loadSvgFile svgfilename 109 | case f of 110 | Nothing -> return $ ResultError "Error while loading SVG" 111 | Just doc -> do 112 | cache <- loadCreateFontCache "fonty-texture-cache" 113 | (finalImage, _) <- renderSvgDocument cache Nothing 96 doc 114 | writePng pngfilename finalImage 115 | return ResultSuccess 116 | 117 | -- | This function will create a font cache, 118 | -- a structure allowing to quickly match a font 119 | -- family name and style to a specific true type font 120 | -- on disk. 121 | -- 122 | -- The cache is saved on disk at the filepath given 123 | -- as parameter. If a cache is found it is automatically 124 | -- loaded from the file. 125 | -- 126 | -- Creating the cache is a rather long operation (especially 127 | -- on Windows), that's why you may want to keep the cache 128 | -- around. 129 | loadCreateFontCache :: FilePath -> IO FontCache 130 | loadCreateFontCache filename = do 131 | exist <- doesFileExist filename 132 | if exist then loadCache else createWrite 133 | where 134 | loadCache = do 135 | bstr <- B.readFile filename 136 | case decodeOrFail bstr of 137 | Left _ -> createWrite 138 | Right (_, _, v) -> return v 139 | 140 | createWrite = do 141 | cache <- buildCache 142 | encodeFile filename cache 143 | return cache 144 | 145 | -------------------------------------------------------------------------------- /src/Graphics/Rasterific/Svg/MeshConverter.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | module Graphics.Rasterific.Svg.MeshConverter 4 | ( mapMeshBaseCoordiantes 5 | , convertGradientMesh ) where 6 | 7 | #if !MIN_VERSION_base(4,8,0) 8 | import Data.Monoid( mconcat ) 9 | import Control.Applicative( pure, (<$>) ) 10 | #endif 11 | 12 | import Control.Monad.Primitive( PrimMonad, PrimState ) 13 | import Control.Monad.Reader.Class( MonadReader ) 14 | import Graphics.Rasterific.Linear( (^+^) 15 | , (^-^) 16 | , lerp 17 | ) 18 | import qualified Linear as L 19 | import qualified Graphics.Rasterific as R 20 | import Data.Vector( (//) ) 21 | import qualified Data.Vector as V 22 | 23 | import Codec.Picture( PixelRGBA8( .. ) ) 24 | 25 | import Graphics.Svg.Types 26 | import Graphics.Rasterific.MeshPatch 27 | {-import Graphics.Rasterific.Svg.RenderContext-} 28 | 29 | toBaseX :: R.PlaneBound -> MeshGradient -> Float 30 | toBaseX bounds mesh = case _meshGradientX mesh of 31 | Num n -> realToFrac n 32 | Percent p -> miniX + (maxiX - miniX) * realToFrac p 33 | Px n -> realToFrac n 34 | Em n -> realToFrac n 35 | Pc n -> realToFrac n 36 | Mm n -> realToFrac n 37 | Cm n -> realToFrac n 38 | Point n -> realToFrac n 39 | Inches n -> realToFrac n 40 | where 41 | R.PlaneBound (R.V2 miniX _miniY) (R.V2 maxiX _maxiY) = bounds 42 | 43 | toBaseY :: R.PlaneBound -> MeshGradient -> Float 44 | toBaseY bounds mesh = case _meshGradientY mesh of 45 | Num n -> realToFrac n 46 | Percent p -> miniY + (maxiY - miniY) * realToFrac p 47 | Px n -> realToFrac n 48 | Em n -> realToFrac n 49 | Pc n -> realToFrac n 50 | Mm n -> realToFrac n 51 | Cm n -> realToFrac n 52 | Point n -> realToFrac n 53 | Inches n -> realToFrac n 54 | where 55 | R.PlaneBound (R.V2 _miniX miniY) (R.V2 _maxiX maxiY) = bounds 56 | 57 | mapMeshBaseCoordiantes :: ((Number, Number) -> (Number, Number)) -> MeshGradient 58 | -> MeshGradient 59 | mapMeshBaseCoordiantes f m = m { _meshGradientX = x, _meshGradientY = y } 60 | where (x, y) = f (_meshGradientX m, _meshGradientY m) 61 | 62 | convertGradientMesh :: R.PlaneBound -> R.PlaneBound -> MeshGradient -> MeshPatch PixelRGBA8 63 | convertGradientMesh globalBounds bounds mesh = scaler rmesh where 64 | (_, rmesh) = withMesh baseGrid (gatherGeometry svgBasePoint mesh) 65 | (w, h) = svgMeshSize mesh 66 | colors = gatherColors mesh w h 67 | baseGrid = generateLinearGrid w h svgBasePoint svgBasePoint colors 68 | 69 | svgBasePoint = 70 | R.V2 (toBaseX startBounds mesh) (toBaseY startBounds mesh) 71 | 72 | startBounds = case _meshGradientUnits mesh of 73 | CoordUserSpace -> globalBounds 74 | CoordBoundingBox -> R.PlaneBound (R.V2 0 0) (R.V2 1 1) 75 | 76 | delta = R._planeMaxBound bounds ^-^ R._planeMinBound bounds 77 | toBoundingBox p = R._planeMinBound bounds ^+^ delta * p 78 | 79 | scaler :: MeshPatch px -> MeshPatch px 80 | scaler = case _meshGradientUnits mesh of 81 | CoordUserSpace -> id 82 | CoordBoundingBox -> R.transform toBoundingBox 83 | 84 | 85 | gatherGeometry :: (MonadReader (MutableMesh (PrimState m) px) m, PrimMonad m) 86 | => R.Point -> MeshGradient -> m () 87 | gatherGeometry basePoint = mapM_ goRow . zip [0 ..] . _meshGradientRows where 88 | toCurve firstPatchPoint lastPoint p = case _gradientPath p of 89 | Just pp -> svgPathToPrimitives firstPatchPoint lastPoint pp 90 | Nothing -> lastPoint `straightLine` firstPatchPoint 91 | 92 | lastOf (R.CubicBezier _ _ _ a) = a 93 | firstOf (R.CubicBezier a _ _ _) = a 94 | goRow (y, r) = mapM_ (goPatch y) . zip [0 ..] $ _meshGradientRowPatches r 95 | goPatch y (x, patch) = case _meshGradientPatchStops patch of 96 | -- A B 97 | -- +---+ 98 | -- | | 99 | -- +---+ 100 | -- D C 101 | [a, b, c, d] -> do 102 | let toC = toCurve basePoint 103 | northEast = toC basePoint a 104 | eastSouth = toC (lastOf northEast) b 105 | southWest = toC (lastOf eastSouth) c 106 | westNorth = toC (lastOf southWest) d 107 | setVertice x y $ firstOf northEast 108 | setVertice (x + 1) y $ lastOf northEast 109 | setVertice (x + 1) (y + 1) $ firstOf southWest 110 | setVertice x (y + 1) $ lastOf southWest 111 | horizOrdered northEast 112 | horizUnordered southWest 113 | vertUnordered westNorth 114 | vertOrdered eastSouth 115 | 116 | -- A B 117 | -- +---+ 118 | -- | 119 | -- +---+ 120 | -- C 121 | [a, b, c] | y == 0 -> do 122 | firstPoint <- getVertice x y 123 | closePoint <- getVertice x (y + 1) 124 | let toC = toCurve closePoint 125 | northEast = toC firstPoint a 126 | eastSouth = toC (lastOf northEast) b 127 | southWest = toC (lastOf eastSouth) c 128 | setVertice (x + 1) y $ firstOf eastSouth 129 | setVertice (x + 1) (y + 1) $ lastOf eastSouth 130 | horizOrdered northEast 131 | horizUnordered southWest 132 | vertOrdered eastSouth 133 | 134 | 135 | -- B 136 | -- + + 137 | -- | | 138 | -- +---+ 139 | -- D C 140 | [b, c, d] -> do 141 | firstPoint <- getVertice (x + 1) y 142 | closePoint <- getVertice x y 143 | let toC = toCurve closePoint 144 | eastSouth = toC firstPoint b 145 | southWest = toC (lastOf eastSouth) c 146 | westNorth = toC (lastOf southWest) d 147 | setVertice (x + 1) (y + 1) $ firstOf southWest 148 | setVertice x (y + 1) $ lastOf southWest 149 | horizUnordered southWest 150 | vertUnordered westNorth 151 | vertOrdered eastSouth 152 | 153 | -- B 154 | -- + 155 | -- | 156 | -- +---+ 157 | -- C 158 | [b, c] -> do 159 | firstPoint <- getVertice (x + 1) y 160 | closePoint <- getVertice x (y + 1) 161 | let toC = toCurve closePoint 162 | eastSouth = toC firstPoint b 163 | southWest = toC (lastOf eastSouth) c 164 | setVertice (x + 1) (y + 1) $ firstOf southWest 165 | horizUnordered southWest 166 | vertOrdered eastSouth 167 | _ -> return () 168 | where 169 | horizOrdered (R.CubicBezier _ b c _) = setHorizPoints x y $ InterBezier b c 170 | horizUnordered (R.CubicBezier _ b c _) = setHorizPoints x (y + 1) $ InterBezier c b 171 | vertUnordered (R.CubicBezier _ b c _) = setVertPoints x y $ InterBezier c b 172 | vertOrdered (R.CubicBezier _ b c _) = setVertPoints (x + 1) y $ InterBezier b c 173 | 174 | 175 | gatherColors :: MeshGradient -> Int -> Int -> V.Vector PixelRGBA8 176 | gatherColors mesh w h = baseVec // foldMap goRow (zip [0 ..] $ _meshGradientRows mesh) where 177 | baseVec = V.replicate ((w + 1) * (h + 1)) $ PixelRGBA8 0 0 0 255 178 | 179 | goRow (y, row) = foldMap (goPatch y) . zip [0 ..] $ _meshGradientRowPatches row 180 | 181 | goPatch y (x, patch) = case _meshGradientPatchStops patch of 182 | -- A B 183 | -- +---+ 184 | -- | | 185 | -- +---+ 186 | -- D C 187 | [a, b, c, d] -> 188 | [setAt 0 0 a, setAt 1 0 b, setAt 1 1 c, setAt 0 1 d] 189 | -- A B 190 | -- +---+ 191 | -- | 192 | -- +---+ 193 | -- C 194 | [_a, b, c] | y == 0 -> [setAt 1 0 b, setAt 1 1 c] 195 | -- B 196 | -- + + 197 | -- | | 198 | -- +---+ 199 | -- D C 200 | [_b, c, d] -> [setAt 1 1 c, setAt 0 1 d] 201 | -- B 202 | -- + 203 | -- | 204 | -- +---+ 205 | -- C 206 | [_b, c] -> [setAt 1 1 c] 207 | 208 | _ -> [] 209 | where 210 | colorOf s = case _gradientOpacity s of 211 | Nothing -> _gradientColor s 212 | Just a -> PixelRGBA8 r g b . floor $ 255 * a 213 | where 214 | PixelRGBA8 r g b _ = _gradientColor s 215 | 216 | 217 | setAt dx dy stop = (idx, colorOf stop) where 218 | idx = (y + dy) * (w + 1) + x + dx 219 | 220 | 221 | svgMeshSize :: MeshGradient -> (Int, Int) 222 | svgMeshSize mesh = (w, h) where 223 | h = length $ _meshGradientRows mesh 224 | w = maximum $ length . _meshGradientRowPatches <$> _meshGradientRows mesh 225 | 226 | svgPathToPrimitives :: R.Point -> R.Point -> GradientPathCommand -> R.CubicBezier 227 | svgPathToPrimitives firstPatchPoint = go where 228 | go o GClose = o `straightLine` firstPatchPoint 229 | go o (GLine OriginRelative c) = o `straightLine` (o ^+^ mp c) 230 | go o (GLine OriginAbsolute p) = o `straightLine` mp p 231 | go o (GCurve OriginAbsolute c1 c2 e) = 232 | R.CubicBezier o (toR c1) (toR c2) (mp e) 233 | go o (GCurve OriginRelative c1 c2 e) = 234 | R.CubicBezier o (o ^+^ toR c1) (o ^+^ toR c2) (o ^+^ mp e) 235 | 236 | mp Nothing = firstPatchPoint 237 | mp (Just p) = toR p 238 | 239 | toR :: RPoint -> R.Point 240 | {-# INLINE toR #-} 241 | toR (L.V2 x y) = realToFrac <$> R.V2 x y 242 | 243 | straightLine :: R.Point -> R.Point -> R.CubicBezier 244 | straightLine a b = R.CubicBezier a p1 p2 b where 245 | p1 = lerp (1/3) a b 246 | p2 = lerp (2/3) a b 247 | -------------------------------------------------------------------------------- /w3csvg/feComposite.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | Example feComposite - Examples of feComposite operations 7 | Four rows of six pairs of overlapping triangles depicting 8 | the six different feComposite operators under different 9 | opacity values and different clearing of the background. 10 | 11 | Define two sets of six filters for each of the six compositing operators. 12 | The first set wipes out the background image by flooding with opaque white. 13 | The second set does not wipe out the background, with the result 14 | that the background sometimes shines through and is other cases 15 | is blended into itself (i.e., "double-counting"). 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Render the examples using the filters that draw on top of 89 | an opaque white surface, thus obliterating the background. 90 | 91 | opacity 1.0 92 | (with feFlood) 93 | opacity 0.5 94 | (with feFlood) 95 | 96 | 97 | 98 | 99 | over 100 | 101 | 102 | 103 | 104 | in 105 | 106 | 107 | 108 | 109 | out 110 | 111 | 112 | 113 | 114 | atop 115 | 116 | 117 | 118 | 119 | xor 120 | 121 | 122 | 123 | 124 | arithmetic 125 | 126 | 127 | 128 | Render the examples using the filters that do not obliterate 129 | the background, thus sometimes causing the background to continue 130 | to appear in some cases, and in other cases the background 131 | image blends into itself ("double-counting"). 132 | opacity 1.0 133 | (without feFlood) 134 | opacity 0.5 135 | (without feFlood) 136 | 137 | 138 | 139 | 140 | over 141 | 142 | 143 | 144 | 145 | in 146 | 147 | 148 | 149 | 150 | out 151 | 152 | 153 | 154 | 155 | atop 156 | 157 | 158 | 159 | 160 | xor 161 | 162 | 163 | 164 | 165 | arithmetic 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/Graphics/Rasterific/Svg/PathConverter.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | module Graphics.Rasterific.Svg.PathConverter 3 | ( svgPathToPrimitives 4 | , svgPathToRasterificPath 5 | ) where 6 | 7 | #if !MIN_VERSION_base(4,8,0) 8 | import Data.Monoid( mconcat ) 9 | import Control.Applicative( pure, (<$>) ) 10 | #endif 11 | 12 | import Data.List( mapAccumL ) 13 | import Graphics.Rasterific.Linear( (^+^) 14 | , (^-^) 15 | , (^*) 16 | , norm 17 | , nearZero 18 | , zero ) 19 | import qualified Graphics.Rasterific as R 20 | import qualified Linear as L 21 | import Graphics.Svg.Types 22 | import Graphics.Rasterific.Svg.ArcConversion 23 | 24 | singularize :: [PathCommand] -> [PathCommand] 25 | singularize = concatMap go 26 | where 27 | go (MoveTo _ []) = [] 28 | go (MoveTo o (x: xs)) = MoveTo o [x] : go (LineTo o xs) 29 | go (LineTo o lst) = LineTo o . pure <$> lst 30 | go (HorizontalTo o lst) = HorizontalTo o . pure <$> lst 31 | go (VerticalTo o lst) = VerticalTo o . pure <$> lst 32 | go (CurveTo o lst) = CurveTo o . pure <$> lst 33 | go (SmoothCurveTo o lst) = SmoothCurveTo o . pure <$> lst 34 | go (QuadraticBezier o lst) = QuadraticBezier o . pure <$> lst 35 | go (SmoothQuadraticBezierCurveTo o lst) = 36 | SmoothQuadraticBezierCurveTo o . pure <$> lst 37 | go (EllipticalArc o lst) = EllipticalArc o . pure <$> lst 38 | go EndPath = [EndPath] 39 | 40 | toR :: RPoint -> R.Point 41 | {-# INLINE toR #-} 42 | toR (L.V2 x y) = realToFrac <$> R.V2 x y 43 | 44 | fromR :: R.Point -> RPoint 45 | {-# INLINE fromR #-} 46 | fromR (R.V2 x y) = realToFrac <$> L.V2 x y 47 | 48 | svgPathToPrimitives :: Bool -> [PathCommand] -> [R.Primitive] 49 | svgPathToPrimitives shouldClose lst 50 | | shouldClose && not (nearZero $ norm (lastPoint ^-^ firstPoint)) = 51 | concat $ prims ++ [R.line lastPoint firstPoint] 52 | | otherwise = concat prims 53 | where 54 | ((lastPoint, _, firstPoint), prims) = 55 | mapAccumL go (zero, zero, zero) $ singularize lst 56 | 57 | go (latest, p, first) EndPath = 58 | ((first, p, first), R.line latest first) 59 | 60 | go o (HorizontalTo _ []) = (o, []) 61 | go o (VerticalTo _ []) = (o, []) 62 | go o (MoveTo _ []) = (o, []) 63 | go o (LineTo _ []) = (o, []) 64 | go o (CurveTo _ []) = (o, []) 65 | go o (SmoothCurveTo _ []) = (o, []) 66 | go o (QuadraticBezier _ []) = (o, []) 67 | go o (SmoothQuadraticBezierCurveTo _ []) = (o, []) 68 | go o (EllipticalArc _ []) = (o, []) 69 | 70 | go (_, _, _) (MoveTo OriginAbsolute (p:_)) = ((p', p', p'), []) 71 | where p' = toR p 72 | go (o, _, _) (MoveTo OriginRelative (p:_)) = 73 | ((pp, pp, pp), []) where pp = o ^+^ toR p 74 | 75 | go (o@(R.V2 _ y), _, fp) (HorizontalTo OriginAbsolute (c:_)) = 76 | ((p, p, fp), R.line o p) where p = R.V2 (realToFrac c) y 77 | go (o@(R.V2 x y), _, fp) (HorizontalTo OriginRelative (c:_)) = 78 | ((p, p, fp), R.line o p) where p = R.V2 (x + realToFrac c) y 79 | 80 | go (o@(R.V2 x _), _, fp) (VerticalTo OriginAbsolute (c:_)) = 81 | ((p, p, fp), R.line o p) where p = R.V2 x (realToFrac c) 82 | go (o@(R.V2 x y), _, fp) (VerticalTo OriginRelative (c:_)) = 83 | ((p, p, fp), R.line o p) where p = R.V2 x (realToFrac c + y) 84 | 85 | go (o, _, fp) (LineTo OriginRelative (c:_)) = 86 | ((p, p, fp), R.line o p) where p = o ^+^ toR c 87 | 88 | go (o, _, fp) (LineTo OriginAbsolute (p:_)) = 89 | ((p', p', fp), R.line o $ toR p) 90 | where p' = toR p 91 | 92 | go (o, _, fp) (CurveTo OriginAbsolute ((c1, c2, e):_)) = 93 | ((e', c2', fp), 94 | [R.CubicBezierPrim $ R.CubicBezier o (toR c1) c2' e']) 95 | where e' = toR e 96 | c2' = toR c2 97 | 98 | go (o, _, fp) (CurveTo OriginRelative ((c1, c2, e):_)) = 99 | ((e', c2', fp), [R.CubicBezierPrim $ R.CubicBezier o c1' c2' e']) 100 | where c1' = o ^+^ toR c1 101 | c2' = o ^+^ toR c2 102 | e' = o ^+^ toR e 103 | 104 | go (o, control, fp) (SmoothCurveTo OriginAbsolute ((c2, e):_)) = 105 | ((e', c2', fp), [R.CubicBezierPrim $ R.CubicBezier o c1' c2' e']) 106 | where c1' = o ^* 2 ^-^ control 107 | c2' = toR c2 108 | e' = toR e 109 | 110 | go (o, control, fp) (SmoothCurveTo OriginRelative ((c2, e):_)) = 111 | ((e', c2', fp), [R.CubicBezierPrim $ R.CubicBezier o c1' c2' e']) 112 | where c1' = o ^* 2 ^-^ control 113 | c2' = o ^+^ toR c2 114 | e' = o ^+^ toR e 115 | 116 | go (o, _, fp) (QuadraticBezier OriginAbsolute ((c1, e):_)) = 117 | ((e', c1', fp), [R.BezierPrim $ R.Bezier o c1' e']) 118 | where e' = toR e 119 | c1' = toR c1 120 | 121 | go (o, _, fp) (QuadraticBezier OriginRelative ((c1, e):_)) = 122 | ((e', c1', fp), [R.BezierPrim $ R.Bezier o c1' e']) 123 | where c1' = o ^+^ toR c1 124 | e' = o ^+^ toR e 125 | 126 | go (o, control, fp) 127 | (SmoothQuadraticBezierCurveTo OriginAbsolute (e:_)) = 128 | ((e', c1', fp), [R.BezierPrim $ R.Bezier o c1' e']) 129 | where c1' = o ^* 2 ^-^ control 130 | e' = toR e 131 | 132 | go (o, control, fp) 133 | (SmoothQuadraticBezierCurveTo OriginRelative (e:_)) = 134 | ((e', c1', fp), [R.BezierPrim $ R.Bezier o c1' e']) 135 | where c1' = o ^* 2 ^-^ control 136 | e' = o ^+^ toR e 137 | 138 | go acc@(o, _, _) (EllipticalArc OriginAbsolute (e:_)) = 139 | (accFinal, mconcat outList) 140 | where 141 | (accFinal, outList) = mapAccumL go acc $ arcToSegments (fromR o) e 142 | 143 | go back@(o,_,_) (EllipticalArc OriginRelative ((rx, ry, rot, f1, f2, p): _)) = 144 | go back $ EllipticalArc OriginAbsolute [new] 145 | where p' = p L.^+^ (fromR o) 146 | new = (rx, ry, rot, f1, f2, p') 147 | 148 | 149 | -- | Conversion function between svg path to the rasterific one. 150 | svgPathToRasterificPath :: Bool -> [PathCommand] -> R.Path 151 | svgPathToRasterificPath shouldClose lst = 152 | R.Path firstPoint shouldClose $ concat commands 153 | where 154 | lineTo p = [R.PathLineTo p] 155 | cubicTo e1 e2 e3 = [R.PathCubicBezierCurveTo e1 e2 e3] 156 | quadTo e1 e2 = [R.PathQuadraticBezierCurveTo e1 e2] 157 | 158 | ((_, _, firstPoint), commands) = 159 | mapAccumL go (zero, zero, zero) $ singularize lst 160 | 161 | go (_, p, first) EndPath = 162 | ((first, p, first), []) 163 | 164 | go o (HorizontalTo _ []) = (o, []) 165 | go o (VerticalTo _ []) = (o, []) 166 | go o (MoveTo _ []) = (o, []) 167 | go o (LineTo _ []) = (o, []) 168 | go o (CurveTo _ []) = (o, []) 169 | go o (SmoothCurveTo _ []) = (o, []) 170 | go o (QuadraticBezier _ []) = (o, []) 171 | go o (SmoothQuadraticBezierCurveTo _ []) = (o, []) 172 | go o (EllipticalArc _ []) = (o, []) 173 | 174 | go (_, _, _) (MoveTo OriginAbsolute (p:_)) = 175 | ((pp, pp, pp), []) where pp = toR p 176 | go (o, _, _) (MoveTo OriginRelative (p:_)) = 177 | ((pp, pp, pp), []) where pp = o ^+^ toR p 178 | 179 | go (R.V2 _ y, _, fp) (HorizontalTo OriginAbsolute (c:_)) = 180 | ((p, p, fp), lineTo p) where p = R.V2 (realToFrac c) y 181 | go (R.V2 x y, _, fp) (HorizontalTo OriginRelative (c:_)) = 182 | ((p, p, fp), lineTo p) where p = R.V2 (x + realToFrac c) y 183 | 184 | go (R.V2 x _, _, fp) (VerticalTo OriginAbsolute (c:_)) = 185 | ((p, p, fp), lineTo p) where p = R.V2 x (realToFrac c) 186 | go (R.V2 x y, _, fp) (VerticalTo OriginRelative (c:_)) = 187 | ((p, p, fp), lineTo p) where p = R.V2 x (realToFrac c + y) 188 | 189 | go (o, _, fp) (LineTo OriginRelative (c:_)) = 190 | ((p, p, fp), lineTo p) where p = o ^+^ toR c 191 | 192 | go (_, _, fp) (LineTo OriginAbsolute (p:_)) = 193 | ((p', p', fp), lineTo p') 194 | where p' = toR p 195 | 196 | go (_, _, fp) (CurveTo OriginAbsolute ((c1, c2, e):_)) = 197 | ((e', c2', fp), cubicTo c1' c2' e') 198 | where e' = toR e 199 | c2' = toR c2 200 | c1' = toR c1 201 | 202 | go (o, _, fp) (CurveTo OriginRelative ((c1, c2, e):_)) = 203 | ((e', c2', fp), cubicTo c1' c2' e') 204 | where c1' = o ^+^ toR c1 205 | c2' = o ^+^ toR c2 206 | e' = o ^+^ toR e 207 | 208 | go (o, control, fp) (SmoothCurveTo OriginAbsolute ((c2, e):_)) = 209 | ((e', c2', fp), cubicTo c1' c2' e') 210 | where c1' = o ^* 2 ^-^ control 211 | c2' = toR c2 212 | e' = toR e 213 | 214 | go (o, control, fp) (SmoothCurveTo OriginRelative ((c2, e):_)) = 215 | ((e', c2', fp), cubicTo c1' c2' e') 216 | where c1' = o ^* 2 ^-^ control 217 | c2' = o ^+^ toR c2 218 | e' = o ^+^ toR e 219 | 220 | go (_, _, fp) (QuadraticBezier OriginAbsolute ((c1, e):_)) = 221 | ((e', c1', fp), quadTo c1' e') 222 | where e' = toR e 223 | c1' = toR c1 224 | 225 | go (o, _, fp) (QuadraticBezier OriginRelative ((c1, e):_)) = 226 | ((e', c1', fp), quadTo c1' e') 227 | where c1' = o ^+^ toR c1 228 | e' = o ^+^ toR e 229 | 230 | go (o, control, fp) 231 | (SmoothQuadraticBezierCurveTo OriginAbsolute (e:_)) = 232 | ((e', c1', fp), quadTo c1' e') 233 | where c1' = o ^* 2 ^-^ control 234 | e' = toR e 235 | 236 | go (o, control, fp) 237 | (SmoothQuadraticBezierCurveTo OriginRelative (e:_)) = 238 | ((e', c1', fp), quadTo c1' e') 239 | where c1' = o ^* 2 ^-^ control 240 | e' = o ^+^ toR e 241 | 242 | go back@(o, _, _) (EllipticalArc OriginAbsolute (com:_)) = (nextState, mconcat pathCommands) 243 | where 244 | (nextState, pathCommands) = 245 | mapAccumL go back $ arcToSegments (fromR o) com 246 | go back@(o, _, _) (EllipticalArc OriginRelative ((rx, ry, rot, f1, f2, p):_)) = 247 | go back $ EllipticalArc OriginAbsolute [new] 248 | where p' = p L.^+^ (fromR o) 249 | new = (rx, ry, rot, f1, f2, p') 250 | 251 | -------------------------------------------------------------------------------- /src/Graphics/Rasterific/Svg/RenderContext.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | module Graphics.Rasterific.Svg.RenderContext 3 | ( RenderContext( .. ) 4 | , LoadedElements( .. ) 5 | , loadedFonts 6 | , loadedImages 7 | , IODraw 8 | , ViewBox 9 | , toRadian 10 | , capOfSvg 11 | , joinOfSvg 12 | , stripUnits 13 | , boundingBoxLength 14 | , boundbingBoxLinearise 15 | , lineariseXLength 16 | , lineariseYLength 17 | , linearisePoint 18 | , lineariseLength 19 | , prepareTexture 20 | , fillAlphaCombine 21 | , fillMethodOfSvg 22 | , emTransform 23 | , toTransformationMatrix 24 | ) 25 | where 26 | 27 | #if !MIN_VERSION_base(4,8,0) 28 | import Control.Applicative( (<$>) ) 29 | import Data.Monoid( Monoid( .. ) ) 30 | #endif 31 | 32 | import Control.Lens( (&), (.~) ) 33 | import Control.Monad.Trans.State.Strict( StateT ) 34 | import Codec.Picture( PixelRGBA8( .. ) ) 35 | import qualified Codec.Picture as CP 36 | import qualified Data.Foldable as F 37 | import qualified Data.Map as M 38 | import Data.Monoid( Last( .. ) ) 39 | import Data.Semigroup( Semigroup( .. )) 40 | import Control.Lens( Lens', lens ) 41 | 42 | import Graphics.Rasterific.Linear( (^-^) ) 43 | import qualified Graphics.Rasterific as R 44 | import qualified Graphics.Rasterific.Transformations as RT 45 | import qualified Graphics.Rasterific.Texture as RT 46 | import Graphics.Text.TrueType 47 | import Graphics.Svg.Types 48 | import Graphics.Rasterific.Svg.MeshConverter 49 | 50 | import Debug.Trace 51 | import Text.Printf 52 | 53 | toRadian :: Floating a => a -> a 54 | toRadian v = v / 180 * pi 55 | 56 | type Definitions = M.Map String Element 57 | 58 | data RenderContext = RenderContext 59 | { _initialViewBox :: !(R.Point, R.Point) 60 | , _renderViewBox :: !(R.Point, R.Point) 61 | , _renderDpi :: !Int 62 | , _contextDefinitions :: !Definitions 63 | , _fontCache :: !FontCache 64 | , _subRender :: !(Document -> IODraw (R.Drawing PixelRGBA8 ())) 65 | , _basePath :: !FilePath 66 | } 67 | 68 | data LoadedElements = LoadedElements 69 | { _loadedFonts :: M.Map FilePath Font 70 | , _loadedImages :: M.Map FilePath (CP.Image PixelRGBA8) 71 | } 72 | 73 | instance Semigroup LoadedElements where 74 | (<>) (LoadedElements a b) (LoadedElements a' b') = 75 | LoadedElements (a `mappend` a') (b `mappend` b') 76 | 77 | instance Monoid LoadedElements where 78 | mempty = LoadedElements mempty mempty 79 | mappend = (<>) 80 | 81 | globalBounds :: RenderContext -> R.PlaneBound 82 | globalBounds RenderContext { _renderViewBox = (p1, p2) } = 83 | R.PlaneBound p1 p2 84 | 85 | loadedFonts :: Lens' LoadedElements (M.Map FilePath Font) 86 | loadedFonts = lens _loadedFonts (\a b -> a { _loadedFonts = b }) 87 | 88 | loadedImages :: Lens' LoadedElements (M.Map FilePath (CP.Image PixelRGBA8)) 89 | loadedImages = lens _loadedImages (\a b -> a { _loadedImages = b }) 90 | 91 | type IODraw = StateT LoadedElements IO 92 | 93 | type ViewBox = (R.Point, R.Point) 94 | 95 | capOfSvg :: DrawAttributes -> (R.Cap, R.Cap) 96 | capOfSvg attrs = 97 | case getLast $ _strokeLineCap attrs of 98 | Nothing -> (R.CapStraight 1, R.CapStraight 1) 99 | Just CapSquare -> (R.CapStraight 1, R.CapStraight 1) 100 | Just CapButt -> (R.CapStraight 0, R.CapStraight 0) 101 | Just CapRound -> (R.CapRound, R.CapRound) 102 | 103 | 104 | joinOfSvg :: DrawAttributes -> R.Join 105 | joinOfSvg attrs = 106 | case (getLast $ _strokeLineJoin attrs, getLast $ _strokeMiterLimit attrs) of 107 | (Nothing, _) -> R.JoinRound 108 | (Just JoinMiter, Just v) -> R.JoinMiter $ 1 / realToFrac v 109 | (Just JoinMiter, _) -> R.JoinMiter 0 110 | (Just JoinBevel, _) -> R.JoinMiter 5 111 | (Just JoinRound, _) -> R.JoinRound 112 | 113 | stripUnits :: RenderContext -> Number -> Number 114 | stripUnits ctxt = toUserUnit (_renderDpi ctxt) 115 | 116 | boundingBoxLength :: RenderContext -> DrawAttributes -> R.PlaneBound -> Number 117 | -> Float 118 | boundingBoxLength ctxt attr (R.PlaneBound mini maxi) = go where 119 | R.V2 actualWidth actualHeight = 120 | abs <$> (maxi ^-^ mini) 121 | two = 2 :: Int 122 | coeff = sqrt (actualWidth ^^ two + actualHeight ^^ two) 123 | / sqrt 2 :: Float 124 | go num = case num of 125 | Num n -> realToFrac n 126 | Em n -> emTransform attr $ realToFrac n 127 | Percent p -> realToFrac p * coeff 128 | _ -> go $ stripUnits ctxt num 129 | 130 | boundbingBoxLinearise :: RenderContext -> DrawAttributes -> R.PlaneBound -> Point 131 | -> R.Point 132 | boundbingBoxLinearise 133 | ctxt attr (R.PlaneBound mini@(R.V2 xi yi) maxi) (xp, yp) = R.V2 (finalX xp) (finalY yp) 134 | where 135 | R.V2 w h = abs <$> (maxi ^-^ mini) 136 | finalX nu = case nu of 137 | Num n -> realToFrac n 138 | Em n -> emTransform attr $ realToFrac n 139 | Percent p -> realToFrac p * w + xi 140 | _ -> finalX $ stripUnits ctxt nu 141 | 142 | finalY nu = case nu of 143 | Num n -> realToFrac n 144 | Em n -> emTransform attr $ realToFrac n 145 | Percent p -> realToFrac p * h + yi 146 | _ -> finalY $ stripUnits ctxt nu 147 | 148 | lineariseXLength :: RenderContext -> DrawAttributes -> Number 149 | -> Float 150 | lineariseXLength _ _ (Num i) = realToFrac i 151 | lineariseXLength _ attr (Em i) = emTransform attr $ realToFrac i 152 | lineariseXLength ctxt _ (Percent p) = abs (xe - xs) * realToFrac p 153 | where 154 | (R.V2 xs _, R.V2 xe _) = _renderViewBox ctxt 155 | lineariseXLength ctxt attr num = 156 | lineariseXLength ctxt attr $ stripUnits ctxt num 157 | 158 | lineariseYLength :: RenderContext -> DrawAttributes -> Number 159 | -> Float 160 | lineariseYLength _ _ (Num i) = realToFrac i 161 | lineariseYLength _ attr (Em n) = emTransform attr $ realToFrac n 162 | lineariseYLength ctxt _ (Percent p) = abs (ye - ys) * (realToFrac p) 163 | where 164 | (R.V2 _ ys, R.V2 _ ye) = _renderViewBox ctxt 165 | lineariseYLength ctxt attr num = 166 | lineariseYLength ctxt attr $ stripUnits ctxt num 167 | 168 | 169 | linearisePoint :: RenderContext -> DrawAttributes -> Point 170 | -> R.Point 171 | linearisePoint ctxt attr (p1, p2) = 172 | R.V2 (xs + lineariseXLength ctxt attr p1) 173 | (ys + lineariseYLength ctxt attr p2) 174 | where (R.V2 xs ys, _) = _renderViewBox ctxt 175 | 176 | emTransform :: DrawAttributes -> Float -> Float 177 | emTransform attr n = case getLast $ _fontSize attr of 178 | Nothing -> 16 * realToFrac n 179 | Just (Num v) -> realToFrac v * n 180 | Just _ -> 16 * n 181 | 182 | lineariseLength :: RenderContext -> DrawAttributes -> Number 183 | -> Float 184 | lineariseLength _ _ (Num i) = realToFrac i 185 | lineariseLength _ attr (Em i) = emTransform attr $ realToFrac i 186 | lineariseLength ctxt _ (Percent v) = realToFrac v * coeff 187 | where 188 | (R.V2 x1 y1, R.V2 x2 y2) = _renderViewBox ctxt 189 | actualWidth = abs $ x2 - x1 190 | actualHeight = abs $ y2 - y1 191 | two = 2 :: Int 192 | coeff = sqrt (actualWidth ^^ two + actualHeight ^^ two) 193 | / sqrt 2 194 | lineariseLength ctxt attr num = 195 | lineariseLength ctxt attr $ stripUnits ctxt num 196 | 197 | prepareGradientMeshTexture 198 | :: RenderContext -> DrawAttributes 199 | -> MeshGradient -> [R.Primitive] 200 | -> R.Texture PixelRGBA8 201 | prepareGradientMeshTexture ctxt _attr mesh prims = 202 | let bounds = F.foldMap R.planeBounds prims 203 | strip (x, y) = (stripUnits ctxt x, stripUnits ctxt y) 204 | mesh' = mapMeshBaseCoordiantes strip mesh 205 | gradTransform = toTransformer $ _meshGradientTransform mesh 206 | interp = case _meshGradientType mesh of 207 | GradientBilinear -> R.PatchBilinear 208 | GradientBicubic -> R.PatchBicubic 209 | in 210 | RT.meshPatchTexture interp $ 211 | R.transform gradTransform $ convertGradientMesh (globalBounds ctxt) bounds mesh' 212 | 213 | toTransformer :: [Transformation] -> R.Point -> R.Point 214 | toTransformer [] = id 215 | toTransformer lst = RT.applyTransformation combined where 216 | combined = F.foldMap toTransformationMatrix lst 217 | 218 | prepareLinearGradientTexture 219 | :: RenderContext -> DrawAttributes 220 | -> LinearGradient -> Float -> [R.Primitive] 221 | -> R.Texture PixelRGBA8 222 | prepareLinearGradientTexture ctxt attr grad opa prims = 223 | let bounds = F.foldMap R.planeBounds prims 224 | lineariser = case _linearGradientUnits grad of 225 | CoordUserSpace -> linearisePoint ctxt attr 226 | CoordBoundingBox -> boundbingBoxLinearise ctxt attr bounds 227 | toA = maybe 1 id 228 | gradTransform = toTransformer $ _linearGradientTransform grad 229 | gradient = 230 | [(offset, fillAlphaCombine (opa * toA opa2) color) 231 | | GradientStop offset color _ opa2 <- _linearGradientStops grad] 232 | startPoint = lineariser $ _linearGradientStart grad 233 | stopPoint = lineariser $ _linearGradientStop grad 234 | in 235 | RT.linearGradientTexture gradient (gradTransform startPoint) (gradTransform stopPoint) 236 | 237 | prepareRadialGradientTexture 238 | :: RenderContext -> DrawAttributes 239 | -> RadialGradient -> Float -> [R.Primitive] 240 | -> R.Texture PixelRGBA8 241 | prepareRadialGradientTexture ctxt attr grad opa prims = 242 | let bounds = F.foldMap R.planeBounds prims 243 | (lineariser, lengthLinearise) = case _radialGradientUnits grad of 244 | CoordUserSpace -> 245 | (linearisePoint ctxt attr, lineariseLength ctxt attr) 246 | CoordBoundingBox -> 247 | (boundbingBoxLinearise ctxt attr bounds, 248 | boundingBoxLength ctxt attr bounds) 249 | toA = maybe 1 id 250 | gradTransform = toTransformer $ _radialGradientTransform grad 251 | gradient = 252 | [(offset, fillAlphaCombine (opa * toA opa2) color) 253 | | GradientStop offset color _ opa2 <- _radialGradientStops grad] 254 | center = gradTransform . lineariser $ _radialGradientCenter grad 255 | radius = lengthLinearise $ _radialGradientRadius grad 256 | in 257 | case (_radialGradientFocusX grad, 258 | _radialGradientFocusY grad) of 259 | (Nothing, Nothing) -> 260 | RT.radialGradientTexture gradient center radius 261 | (Just fx, Nothing) -> 262 | RT.radialGradientWithFocusTexture gradient center radius 263 | $ lineariser (fx, snd $ _radialGradientCenter grad) 264 | (Nothing, Just fy) -> 265 | RT.radialGradientWithFocusTexture gradient center radius 266 | $ lineariser (fst $ _radialGradientCenter grad, fy) 267 | (Just fx, Just fy) -> 268 | RT.radialGradientWithFocusTexture gradient center radius 269 | $ lineariser (fx, fy) 270 | 271 | fillMethodOfSvg :: DrawAttributes -> R.FillMethod 272 | fillMethodOfSvg attr = case getLast $ _fillRule attr of 273 | Nothing -> R.FillWinding 274 | Just FillNonZero -> R.FillWinding 275 | Just FillEvenOdd -> R.FillEvenOdd 276 | 277 | fillAlphaCombine :: Float -> PixelRGBA8 -> PixelRGBA8 278 | fillAlphaCombine opacity (PixelRGBA8 r g b a) = 279 | PixelRGBA8 r g b alpha 280 | where 281 | a' = fromIntegral a / 255.0 282 | alpha = floor . max 0 . min 255 $ opacity * a' * 255 283 | 284 | scalesOfTransformation :: RT.Transformation -> (Float, Float) 285 | scalesOfTransformation (RT.Transformation a c _e 286 | b d _f) = (widthScale, heightScale) 287 | where 288 | widthScale = sqrt $ a * a + c * c 289 | heightScale = sqrt $ b * b + d * d 290 | 291 | 292 | documentOfPattern :: Definitions -> RT.Transformation -> Int -> Int -> Pattern -> String 293 | -> Document 294 | documentOfPattern defs trans w h pat loc = Document 295 | { _viewBox = _patternViewBox pat 296 | , _width = return . Num $ fromIntegral tileWidth 297 | , _height = return . Num $ fromIntegral tileHeight 298 | , _elements = _patternElements pat -- [GroupTree asTransformedGroup] 299 | , _definitions = defs 300 | , _styleRules = [] 301 | , _description = "" 302 | , _documentLocation = loc 303 | } 304 | where 305 | (widthScale, heightScale) = scalesOfTransformation trans 306 | tileWidth, tileHeight :: Int 307 | tileWidth = floor $ widthScale * fromIntegral w 308 | tileHeight = floor $ heightScale * fromIntegral h 309 | _asGroup = defaultSvg { _groupChildren = _patternElements pat } 310 | _transfo = Scale 311 | (realToFrac widthScale) 312 | (Just . realToFrac $ heightScale) 313 | _asTransformedGroup = _asGroup & drawAttr . transform .~ Just [_transfo] 314 | 315 | 316 | toTransformationMatrix :: Transformation -> RT.Transformation 317 | toTransformationMatrix = go where 318 | rf = realToFrac 319 | go (TransformMatrix a d b e c f) = 320 | RT.Transformation (rf a) (rf b) (rf c) (rf d) (rf e) (rf f) 321 | go (Translate x y) = RT.translate $ R.V2 (rf x) (rf y) 322 | go (Scale xs Nothing) = RT.scale (rf xs) (rf xs) 323 | go (Scale xs (Just ys)) = RT.scale (rf xs) (rf ys) 324 | go (Rotate angle Nothing) = 325 | RT.rotate . toRadian $ rf angle 326 | go (Rotate angle (Just (cx, cy))) = 327 | RT.rotateCenter (toRadian $ rf angle) $ R.V2 (rf cx) (rf cy) 328 | go (SkewX v) = RT.skewX . toRadian $ rf v 329 | go (SkewY v) = RT.skewY . toRadian $ rf v 330 | go TransformUnknown = mempty 331 | 332 | 333 | prepareTexture :: RenderContext -> DrawAttributes 334 | -> Texture -> Float 335 | -> [R.Primitive] 336 | -> IODraw (Maybe (R.Texture PixelRGBA8)) 337 | prepareTexture _ _ FillNone _opacity _ = return Nothing 338 | prepareTexture _ _ (ColorRef color) opacity _ = 339 | return . Just . RT.uniformTexture $ fillAlphaCombine opacity color 340 | prepareTexture ctxt attr (TextureRef ref) opacity prims = 341 | maybe (return Nothing) (prepare mempty) $ M.lookup ref (_contextDefinitions ctxt) where 342 | prepare rootTrans e = case e of 343 | ElementGeometry _ -> return Nothing 344 | ElementMarker _ -> return Nothing 345 | ElementMask _ -> return Nothing 346 | ElementClipPath _ -> return Nothing 347 | ElementMeshGradient mesh -> 348 | return . Just $ prepareGradientMeshTexture ctxt attr mesh prims 349 | ElementLinearGradient grad -> 350 | return . Just $ prepareLinearGradientTexture ctxt attr grad opacity prims 351 | ElementRadialGradient grad -> 352 | return . Just $ prepareRadialGradientTexture ctxt attr grad opacity prims 353 | ElementPattern pat@Pattern { _patternHref = "" } -> do 354 | let doc = documentOfPattern (_contextDefinitions ctxt) rootTrans w h pat (_basePath ctxt) 355 | dpi = _renderDpi ctxt 356 | w = floor . lineariseXLength ctxt attr $ _patternWidth pat 357 | h = floor . lineariseYLength ctxt attr $ _patternHeight pat 358 | patDrawing <- _subRender ctxt doc 359 | return . Just $ RT.patternTexture w h dpi (PixelRGBA8 0 0 0 0) patDrawing 360 | ElementPattern pat -> do 361 | let _inverser = maybe id RT.transformTexture . RT.inverseTransformation 362 | _applyTransformation = RT.transformTexture 363 | trans = maybe mempty (F.foldMap toTransformationMatrix) $ _patternTransform pat 364 | nextRef = _patternHref pat 365 | maybe (return Nothing) (prepare (rootTrans `mappend` trans)) $ M.lookup nextRef (_contextDefinitions ctxt) 366 | 367 | -------------------------------------------------------------------------------- /test/reduced.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 20 | 24 | 28 | 34 | 38 | 39 | 45 | 52 | 53 | 59 | 66 | 67 | 68 | 69 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/Graphics/Rasterific/Svg/RasterificTextRendering.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TupleSections #-} 2 | {-# LANGUAGE CPP #-} 3 | module Graphics.Rasterific.Svg.RasterificTextRendering 4 | ( renderText ) where 5 | 6 | #if !MIN_VERSION_base(4,8,0) 7 | import Control.Applicative( (<*>), (<$>) ) 8 | import Data.Monoid( mappend, mempty ) 9 | #endif 10 | 11 | import Control.Monad( foldM ) 12 | import Control.Monad.IO.Class( liftIO ) 13 | import Control.Monad.Identity( Identity ) 14 | import Control.Monad.Trans.State.Strict( execState 15 | , StateT 16 | , modify 17 | , gets ) 18 | import Control.Applicative( (<|>) ) 19 | import Control.Lens( at, (?=) ) 20 | import qualified Control.Lens as L 21 | import Codec.Picture( PixelRGBA8( .. ) ) 22 | import qualified Data.Foldable as F 23 | import Data.Monoid( (<>), Last( .. ), First( .. ) ) 24 | import Data.Maybe( fromMaybe ) 25 | import qualified Data.Text as T 26 | import Graphics.Rasterific.Linear( (^+^), (^-^) ) 27 | import Graphics.Rasterific hiding ( Path, Line, Texture, transform ) 28 | import qualified Graphics.Rasterific as R 29 | import qualified Graphics.Rasterific.Outline as RO 30 | import Graphics.Rasterific.Immediate 31 | import qualified Graphics.Rasterific.Transformations as RT 32 | import Graphics.Rasterific.PathWalker 33 | import Graphics.Text.TrueType 34 | import Graphics.Svg.Types 35 | import Graphics.Rasterific.Svg.RenderContext 36 | import Graphics.Rasterific.Svg.PathConverter 37 | {-import Graphics.Svg.XmlParser-} 38 | 39 | {-import Debug.Trace-} 40 | {-import Text.Printf-} 41 | 42 | loadFont :: FilePath -> IODraw (Maybe Font) 43 | loadFont fontPath = do 44 | loaded <- L.use $ loadedFonts . at fontPath 45 | case loaded of 46 | Just v -> return $ Just v 47 | Nothing -> do 48 | file <- liftIO $ loadFontFile fontPath 49 | case file of 50 | Left _ -> return Nothing 51 | Right f -> do 52 | loadedFonts . at fontPath ?= f 53 | return $ Just f 54 | 55 | data RenderableString px = RenderableString 56 | { _renderableAttributes :: !DrawAttributes 57 | , _renderableSize :: !Float 58 | , _renderableFont :: !Font 59 | , _renderableString :: ![(Char, CharInfo px)] 60 | } 61 | 62 | data CharInfo px = CharInfo 63 | { _charX :: Maybe Number 64 | , _charY :: Maybe Number 65 | , _charDx :: Maybe Number 66 | , _charDy :: Maybe Number 67 | , _charRotate :: Maybe Float 68 | , _charStroke :: Maybe (Float, R.Texture px, R.Join, (R.Cap, R.Cap)) 69 | } 70 | 71 | emptyCharInfo :: CharInfo px 72 | emptyCharInfo = CharInfo 73 | { _charX = Nothing 74 | , _charY = Nothing 75 | , _charDx = Nothing 76 | , _charDy = Nothing 77 | , _charRotate = Nothing 78 | , _charStroke = Nothing 79 | } 80 | 81 | propagateTextInfo :: TextInfo -> TextInfo -> TextInfo 82 | propagateTextInfo parent current = TextInfo 83 | { _textInfoX = combine _textInfoX 84 | , _textInfoY = combine _textInfoY 85 | , _textInfoDX = combine _textInfoDX 86 | , _textInfoDY = combine _textInfoDY 87 | , _textInfoRotate = combine _textInfoRotate 88 | , _textInfoLength = _textInfoLength current 89 | } 90 | where 91 | combine f = case f current of 92 | [] -> f parent 93 | lst -> lst 94 | 95 | textInfoRests :: TextInfo -> TextInfo -> TextInfo 96 | -> TextInfo 97 | textInfoRests this parent sub = TextInfo 98 | { _textInfoX = decideWith _textInfoX 99 | , _textInfoY = decideWith _textInfoY 100 | , _textInfoDX = decideWith _textInfoDX 101 | , _textInfoDY = decideWith _textInfoDY 102 | , _textInfoRotate = decideWith _textInfoRotate 103 | , _textInfoLength = _textInfoLength parent 104 | } 105 | where 106 | decideWith f = decide (f this) (f parent) (f sub) 107 | 108 | decide [] _ ssub = ssub 109 | decide _ top _ = top 110 | 111 | unconsTextInfo :: RenderContext -> DrawAttributes -> TextInfo 112 | -> IODraw (CharInfo PixelRGBA8, TextInfo) 113 | unconsTextInfo ctxt attr nfo = do 114 | texture <- textureOf ctxt attr _strokeColor _strokeOpacity 115 | return (charInfo texture, restText) 116 | where 117 | unconsInf lst = case lst of 118 | [] -> (Nothing, []) 119 | (x:xs) -> (Just x, xs) 120 | 121 | (xC, xRest) = unconsInf $ _textInfoX nfo 122 | (yC, yRest) = unconsInf $ _textInfoY nfo 123 | (dxC, dxRest) = unconsInf $ _textInfoDX nfo 124 | (dyC, dyRest) = unconsInf $ _textInfoDY nfo 125 | (rotateC, rotateRest) = unconsInf $ _textInfoRotate nfo 126 | 127 | restText = TextInfo 128 | { _textInfoX = xRest 129 | , _textInfoY = yRest 130 | , _textInfoDX = dxRest 131 | , _textInfoDY = dyRest 132 | , _textInfoRotate = rotateRest 133 | , _textInfoLength = _textInfoLength nfo 134 | } 135 | 136 | sWidth = 137 | lineariseLength ctxt attr <$> getLast (_strokeWidth attr) 138 | 139 | charInfo tex = CharInfo 140 | { _charX = xC 141 | , _charY = yC 142 | , _charDx = dxC 143 | , _charDy = dyC 144 | , _charRotate = realToFrac <$> rotateC 145 | , _charStroke = 146 | (,, joinOfSvg attr, capOfSvg attr) <$> sWidth <*> tex 147 | } 148 | 149 | repeatLast :: [a] -> [a] 150 | repeatLast = go where 151 | go lst = case lst of 152 | [] -> [] 153 | [x] -> repeat x 154 | (x:xs) -> x : go xs 155 | 156 | infinitizeTextInfo :: TextInfo -> TextInfo 157 | infinitizeTextInfo nfo = 158 | nfo { _textInfoRotate = repeatLast $ _textInfoRotate nfo } 159 | 160 | 161 | -- | Monadic version of mapAccumL 162 | mapAccumLM :: Monad m 163 | => (acc -> x -> m (acc, y)) -- ^ combining funcction 164 | -> acc -- ^ initial state 165 | -> [x] -- ^ inputs 166 | -> m (acc, [y]) -- ^ final state, outputs 167 | mapAccumLM _ s [] = return (s, []) 168 | mapAccumLM f s (x:xs) = do 169 | (s1, x') <- f s x 170 | (s2, xs') <- mapAccumLM f s1 xs 171 | return (s2, x' : xs') 172 | 173 | mixWithRenderInfo :: RenderContext -> DrawAttributes 174 | -> TextInfo -> String 175 | -> IODraw (TextInfo, [(Char, CharInfo PixelRGBA8)]) 176 | mixWithRenderInfo ctxt attr = mapAccumLM go where 177 | go info c = do 178 | (thisInfo, rest) <- unconsTextInfo ctxt attr info 179 | return (rest, (c, thisInfo)) 180 | 181 | 182 | data LetterTransformerState = LetterTransformerState 183 | { _charactersInfos :: ![CharInfo PixelRGBA8] 184 | , _characterCurrent :: !(CharInfo PixelRGBA8) 185 | , _currentCharDelta :: !R.Point 186 | , _currentAbsoluteDelta :: !R.Point 187 | , _currentDrawing :: Drawing PixelRGBA8 () 188 | , _stringBounds :: !PlaneBound 189 | } 190 | 191 | type GlyphPlacer = StateT LetterTransformerState Identity 192 | 193 | unconsCurrentLetter :: GlyphPlacer () 194 | unconsCurrentLetter = modify $ \s -> 195 | case _charactersInfos s of 196 | [] -> s 197 | (x:xs) -> s { _charactersInfos = xs 198 | , _characterCurrent = x 199 | } 200 | 201 | prepareCharRotation :: CharInfo px -> R.PlaneBound -> RT.Transformation 202 | prepareCharRotation info bounds = case _charRotate info of 203 | Nothing -> mempty 204 | Just angle -> RT.rotateCenter (toRadian angle) lowerLeftCorner 205 | where 206 | lowerLeftCorner = boundLowerLeftCorner bounds 207 | 208 | prepareCharTranslation :: RenderContext -> CharInfo px -> R.PlaneBound 209 | -> R.Point -> R.Point 210 | -> (R.Point, R.Point, RT.Transformation) 211 | prepareCharTranslation ctxt info bounds prevDelta prevAbsolute = go where 212 | lowerLeftCorner = boundLowerLeftCorner bounds 213 | toRPoint a b = linearisePoint ctxt mempty (a, b) 214 | mzero = Just $ Num 0 215 | V2 pmx pmy = Just . Num . realToFrac <$> prevAbsolute 216 | 217 | mayForcedPoint = case (_charX info, _charY info) of 218 | (Nothing, Nothing) -> Nothing 219 | (mx, my) -> toRPoint <$> (mx <|> pmx) <*> (my <|> pmy) 220 | 221 | delta = fromMaybe 0 $ 222 | toRPoint <$> (_charDx info <|> mzero) 223 | <*> (_charDy info <|> mzero) 224 | 225 | go = case mayForcedPoint of 226 | Nothing -> 227 | let newDelta = prevDelta ^+^ delta 228 | trans = RT.translate $ newDelta ^+^ prevAbsolute in 229 | (newDelta, prevAbsolute, trans) 230 | 231 | Just p -> 232 | let newDelta = prevDelta ^+^ delta 233 | positionDelta = (realToFrac <$> p) ^-^ lowerLeftCorner 234 | trans = RT.translate $ positionDelta ^+^ newDelta in 235 | (newDelta, positionDelta, trans) 236 | 237 | transformPlaceGlyph :: RenderContext 238 | -> RT.Transformation 239 | -> R.PlaneBound 240 | -> DrawOrder PixelRGBA8 241 | -> GlyphPlacer () 242 | transformPlaceGlyph ctxt pathTransformation bounds order = do 243 | unconsCurrentLetter 244 | info <- gets _characterCurrent 245 | delta <- gets _currentCharDelta 246 | absoluteDelta <- gets _currentAbsoluteDelta 247 | let rotateTrans = prepareCharRotation info bounds 248 | (newDelta, newAbsolute, placement) = 249 | prepareCharTranslation ctxt info bounds delta absoluteDelta 250 | finalTrans = pathTransformation <> placement <> rotateTrans 251 | newGeometry = 252 | R.transform (RT.applyTransformation finalTrans) $ _orderPrimitives order 253 | newOrder = order { _orderPrimitives = newGeometry } 254 | 255 | 256 | stroking Nothing = return () 257 | stroking (Just (w, texture, rjoin, cap)) = 258 | orderToDrawing $ newOrder { 259 | _orderPrimitives = stroker <$> _orderPrimitives newOrder, 260 | _orderTexture = texture 261 | } 262 | where 263 | stroker = RO.strokize w rjoin cap 264 | 265 | modify $ \s -> s 266 | { _currentCharDelta = newDelta 267 | , _currentAbsoluteDelta = newAbsolute 268 | , _stringBounds = _stringBounds s <> bounds 269 | , _currentDrawing = do 270 | _currentDrawing s 271 | orderToDrawing newOrder 272 | stroking $ _charStroke info 273 | } 274 | 275 | prepareFontFamilies :: DrawAttributes -> [String] 276 | prepareFontFamilies = (++ defaultFont) 277 | . fmap replaceDefault 278 | . fromMaybe [] 279 | . getLast 280 | . _fontFamily 281 | where 282 | defaultFont = ["Arial"] 283 | -- using "safe" web font, hoping they are present on 284 | -- the system. 285 | replaceDefault s = case s of 286 | "monospace" -> "Courier New" 287 | "sans-serif" -> "Arial" 288 | "serif" -> "Times New Roman" 289 | _ -> s 290 | 291 | fontOfAttributes :: FontCache -> DrawAttributes -> IODraw (Maybe Font) 292 | fontOfAttributes fontCache attr = case fontFilename of 293 | Nothing -> return Nothing 294 | Just fn -> loadFont fn 295 | where 296 | fontFilename = 297 | getFirst . F.foldMap fontFinder $ prepareFontFamilies attr 298 | noStyle = FontStyle 299 | { _fontStyleBold = False 300 | , _fontStyleItalic = False } 301 | 302 | italic = noStyle { _fontStyleItalic = True } 303 | 304 | style = case getLast $ _fontStyle attr of 305 | Nothing -> noStyle 306 | Just FontStyleNormal -> noStyle 307 | Just FontStyleItalic -> italic 308 | Just FontStyleOblique -> italic 309 | 310 | fontFinder ff = 311 | First $ findFontInCache fontCache descriptor 312 | where descriptor = FontDescriptor 313 | { _descriptorFamilyName = T.pack ff 314 | , _descriptorStyle = style } 315 | 316 | 317 | prepareRenderableString :: RenderContext -> DrawAttributes -> Text 318 | -> IODraw [RenderableString PixelRGBA8] 319 | prepareRenderableString ctxt ini_attr root = 320 | fst <$> everySpan ini_attr mempty (_textRoot root) where 321 | 322 | everySpan attr originalInfo tspan = 323 | foldM (everyContent subAttr) (mempty, nfo) $ _spanContent tspan 324 | where 325 | subAttr = attr <> _spanDrawAttributes tspan 326 | nfo = propagateTextInfo originalInfo 327 | . infinitizeTextInfo 328 | $ _spanInfo tspan 329 | 330 | everyContent _attr (acc, info) (SpanTextRef _) = return (acc, info) 331 | everyContent attr (acc, info) (SpanSub thisSpan) = do 332 | let thisTextInfo = _spanInfo thisSpan 333 | (drawn, newInfo) <- everySpan attr info thisSpan 334 | return (acc <> drawn, textInfoRests thisTextInfo info newInfo) 335 | everyContent attr (acc, info) (SpanText txt) = do 336 | font <- fontOfAttributes (_fontCache ctxt) attr 337 | case font of 338 | Nothing -> return (acc, info) 339 | Just f -> do 340 | (info', str) <- mixWithRenderInfo ctxt attr info $ T.unpack txt 341 | let finalStr = RenderableString attr size f str 342 | return (acc <> [finalStr], info') 343 | 344 | where 345 | size = case getLast $ _fontSize attr of 346 | Just v -> lineariseLength ctxt attr v 347 | Nothing -> 16 348 | 349 | 350 | anchorStringRendering :: TextAnchor -> LetterTransformerState 351 | -> Drawing PixelRGBA8 () 352 | anchorStringRendering anchor st = case anchor of 353 | TextAnchorStart -> _currentDrawing st 354 | TextAnchorMiddle -> 355 | withTransformation (RT.translate (V2 (negate $ stringWidth / 2) 0)) $ 356 | _currentDrawing st 357 | TextAnchorEnd -> 358 | withTransformation (RT.translate (V2 (- stringWidth) 0)) $ _currentDrawing st 359 | where 360 | stringWidth = boundWidth $ _stringBounds st 361 | 362 | notWhiteSpace :: (Char, a) -> Bool 363 | notWhiteSpace (c, _) = c /= ' ' && c /= '\t' 364 | 365 | initialLetterTransformerState :: [RenderableString PixelRGBA8] -> LetterTransformerState 366 | initialLetterTransformerState str = LetterTransformerState 367 | { _charactersInfos = 368 | fmap snd . filter notWhiteSpace . concat $ _renderableString <$> str 369 | , _characterCurrent = emptyCharInfo 370 | , _currentCharDelta = V2 0 0 371 | , _currentAbsoluteDelta = V2 0 0 372 | , _currentDrawing = mempty 373 | , _stringBounds = mempty 374 | } 375 | 376 | executePlacer :: Monad m => PathDrawer m px -> [DrawOrder px] -> m () 377 | executePlacer placer = F.mapM_ exec where 378 | exec order | bounds == mempty = return () 379 | | otherwise = placer mempty bounds order 380 | where 381 | bounds = F.foldMap (F.foldMap planeBounds) 382 | $ _orderPrimitives order 383 | 384 | textureOf :: RenderContext 385 | -> DrawAttributes 386 | -> (DrawAttributes -> Last Texture) 387 | -> (DrawAttributes -> Maybe Float) 388 | -> IODraw (Maybe (R.Texture PixelRGBA8)) 389 | textureOf ctxt attr colorAccessor opacityAccessor = 390 | case getLast $ colorAccessor attr of 391 | Nothing -> return Nothing 392 | Just svgTexture -> 393 | prepareTexture ctxt attr svgTexture opacity [] 394 | where opacity = fromMaybe 1.0 $ opacityAccessor attr 395 | 396 | renderString :: RenderContext -> Maybe (Float, R.Path) -> TextAnchor 397 | -> [RenderableString PixelRGBA8] 398 | -> IODraw (Drawing PixelRGBA8 ()) 399 | renderString ctxt mayPath anchor str = do 400 | textRanges <- mapM toFillTextRange str 401 | 402 | case mayPath of 403 | Just (offset, tPath) -> 404 | return . pathPlacer offset tPath $ fillOrders textRanges 405 | Nothing -> return . linePlacer $ fillOrders textRanges 406 | where 407 | fillOrders = 408 | drawOrdersOfDrawing swidth sheight (_renderDpi ctxt) background 409 | . printTextRanges 0 410 | 411 | pixelToPt s = pixelSizeInPointAtDpi s $ _renderDpi ctxt 412 | (mini, maxi) = _renderViewBox ctxt 413 | V2 swidth sheight = floor <$> (maxi ^-^ mini) 414 | background = PixelRGBA8 0 0 0 0 415 | 416 | pathPlacer offset tPath = 417 | anchorStringRendering anchor 418 | . flip execState (initialLetterTransformerState str) 419 | . drawOrdersOnPath (transformPlaceGlyph ctxt) offset 0 tPath 420 | 421 | linePlacer = 422 | anchorStringRendering anchor 423 | . flip execState (initialLetterTransformerState str) 424 | . executePlacer (transformPlaceGlyph ctxt) 425 | 426 | toFillTextRange renderable = do 427 | mayTexture <- textureOf ctxt (_renderableAttributes renderable) 428 | _fillColor _fillOpacity 429 | return TextRange 430 | { _textFont = _renderableFont renderable 431 | , _textSize = pixelToPt $ _renderableSize renderable 432 | , _text = fst <$> _renderableString renderable 433 | , _textTexture = mayTexture 434 | } 435 | 436 | startOffsetOfPath :: RenderContext -> DrawAttributes -> R.Path -> Number 437 | -> Float 438 | startOffsetOfPath _ _ _ (Num i) = realToFrac i 439 | startOffsetOfPath _ attr _ (Em i) = emTransform attr $ realToFrac i 440 | startOffsetOfPath _ _ tPath (Percent p) = 441 | realToFrac p * RO.approximatePathLength tPath 442 | startOffsetOfPath ctxt attr tPath num = 443 | startOffsetOfPath ctxt attr tPath $ stripUnits ctxt num 444 | 445 | renderText :: RenderContext 446 | -> DrawAttributes 447 | -> Maybe TextPath 448 | -> Text 449 | -> IODraw (Drawing PixelRGBA8 ()) 450 | renderText ctxt attr ppath stext = 451 | prepareRenderableString ctxt attr stext >>= renderString ctxt pathInfo anchor 452 | where 453 | renderPath = 454 | svgPathToRasterificPath False . _textPathData <$> ppath 455 | 456 | offset = do 457 | rpath <- renderPath 458 | mayOffset <- _textPathStartOffset <$> ppath 459 | return $ startOffsetOfPath ctxt attr rpath mayOffset 460 | 461 | pathInfo = (,) <$> (offset <|> return 0) <*> renderPath 462 | 463 | anchor = fromMaybe TextAnchorStart 464 | . getLast 465 | . _textAnchor 466 | . mappend attr 467 | . _spanDrawAttributes $ _textRoot stext 468 | 469 | --------------------------------------------------------------------------------