├── 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 |
--------------------------------------------------------------------------------
/w3csvg/image01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
14 |
15 |
--------------------------------------------------------------------------------
/w3csvg/triangle01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/w3csvg/Use01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
14 |
15 |
--------------------------------------------------------------------------------
/w3csvg/circle01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/w3csvg/rect01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/w3csvg/rtl-complex.svg:
--------------------------------------------------------------------------------
1 |
2 |
18 |
--------------------------------------------------------------------------------
/w3csvg/text01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
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 |
15 |
16 |
--------------------------------------------------------------------------------
/w3csvg/OrigCoordSys.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/w3csvg/tspan04.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/w3csvg/tspan01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
20 |
21 |
--------------------------------------------------------------------------------
/w3csvg/rect02.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
20 |
--------------------------------------------------------------------------------
/w3csvg/ellipse01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
22 |
23 |
--------------------------------------------------------------------------------
/w3csvg/inheritance.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
23 |
24 |
--------------------------------------------------------------------------------
/w3csvg/marker.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
24 |
25 |
--------------------------------------------------------------------------------
/w3csvg/polygon01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
20 |
21 |
--------------------------------------------------------------------------------
/w3csvg/tspan03.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
24 |
25 |
--------------------------------------------------------------------------------
/w3csvg/InitialCoords.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
23 |
24 |
--------------------------------------------------------------------------------
/w3csvg/tref01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
24 |
25 |
--------------------------------------------------------------------------------
/w3csvg/tspan02.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
25 |
26 |
--------------------------------------------------------------------------------
/w3csvg/Use02.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
21 |
22 |
--------------------------------------------------------------------------------
/w3csvg/polyline01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
21 |
22 |
--------------------------------------------------------------------------------
/w3csvg/pattern01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Rasterific-svg
2 | ==============
3 |
4 | [](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 |
25 |
26 |
--------------------------------------------------------------------------------
/w3csvg/line01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
25 |
26 |
--------------------------------------------------------------------------------
/w3csvg/toap01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
26 |
27 |
--------------------------------------------------------------------------------
/w3csvg/toap03.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
26 |
27 |
--------------------------------------------------------------------------------
/w3csvg/arcs01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
24 |
25 |
--------------------------------------------------------------------------------
/w3csvg/radgrad01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
27 |
28 |
--------------------------------------------------------------------------------
/w3csvg/textdecoration01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/w3csvg/toap02.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
33 |
34 |
--------------------------------------------------------------------------------
/w3csvg/ViewBox.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
27 |
28 |
--------------------------------------------------------------------------------
/w3csvg/quad01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
30 |
31 |
--------------------------------------------------------------------------------
/w3csvg/tspan05.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
42 |
--------------------------------------------------------------------------------
/w3csvg/NewCoordSys.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
32 |
33 |
--------------------------------------------------------------------------------
/w3csvg/Use04.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
33 |
--------------------------------------------------------------------------------
/w3csvg/mask01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
32 |
33 |
--------------------------------------------------------------------------------
/w3csvg/feImage-01.svg:
--------------------------------------------------------------------------------
1 |
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 |
39 |
40 |
--------------------------------------------------------------------------------
/w3csvg/Skew.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
39 |
40 |
--------------------------------------------------------------------------------
/w3csvg/linejoin.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
37 |
38 |
--------------------------------------------------------------------------------
/w3csvg/feMorphology.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
37 |
--------------------------------------------------------------------------------
/w3csvg/Nested.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
43 |
44 |
--------------------------------------------------------------------------------
/w3csvg/primitive-subregion-01.svg:
--------------------------------------------------------------------------------
1 |
44 |
--------------------------------------------------------------------------------
/w3csvg/linecap.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
40 |
41 |
--------------------------------------------------------------------------------
/w3csvg/Units.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
45 |
46 |
--------------------------------------------------------------------------------
/w3csvg/cubic01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
40 |
--------------------------------------------------------------------------------
/w3csvg/filters01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/w3csvg/feBlend.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
46 |
47 |
--------------------------------------------------------------------------------
/w3csvg/opacity01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
47 |
48 |
--------------------------------------------------------------------------------
/w3csvg/filters00.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
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 |
59 |
60 |
--------------------------------------------------------------------------------
/w3csvg/feColorMatrix.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
55 |
56 |
--------------------------------------------------------------------------------
/w3csvg/fillrule-evenodd.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
38 |
39 |
--------------------------------------------------------------------------------
/w3csvg/fillrule-nonzero.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
38 |
39 |
--------------------------------------------------------------------------------
/w3csvg/feComponentTransfer.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
65 |
66 |
--------------------------------------------------------------------------------
/w3csvg/feTurbulence.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
71 |
72 |
--------------------------------------------------------------------------------
/w3csvg/enable-background-01.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
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 |
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 |
--------------------------------------------------------------------------------