packages feed

diagrams-pgf (empty) → 0.1.0.0

raw patch · 11 files changed

+2359/−0 lines, 11 filesdep +JuicyPixelsdep +basedep +bytestringsetup-changed

Dependencies added: JuicyPixels, base, bytestring, colour, containers, diagrams-core, diagrams-lib, directory, filepath, hashable, mtl, optparse-applicative, process, split, texrunner, time, vector, zlib

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Christopher Chalmers++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Christopher Chalmers nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,105 @@+## PGF diagrams backend++`diagrams-pgf` is a [PGF] backend for [diagrams]. Diagrams is a powerful, flexible, declarative domain-specific language for creating vector graphics, using the [Haskell programming language][haskell].++[PGF]: http://sourceforge.net/projects/pgf/+[diagrams]: http://projects.haskell.org/diagrams/+[haskell]: http://www.haskell.org/haskellwiki/Haskell++PGF is a TeX macro package for generating graphics. It is platform- and format-independent and works together with the most important TeX backend drivers, including pdftex and dvips.++`diagrams-pgf` is a work in progress, it supports the basic features of diagrams with the following features:++- LaTeX, ConTeXt and plain TeX support+- direct PDF generation using a TeX distribution (e.g. [texlive](https://www.tug.org/texlive/)) via [texrunner].++### Usage++A simple example that uses diagrams-pgf to draw a square.++```haskell+import Diagrams.Prelude+import Diagrams.Backend.PGF.CmdLine++b1 = square 20 # lwG 0.05++main = defaultMain (pad 1.1 b1)+```++Save this to file named `square.hs` and compile this program:++```+ghc --make square.hs+```++This will generate an executable which, when run produces a TeX (or PDF) file. Run the+executable with the `--help` option to find out more about how to call it.++```+$ ./square --help+square++Usage: square [-?|--help] [-w|--width WIDTH] [-h|--height HEIGHT]+              [-o|--output OUTPUT] [-f|--format FORMAT] [-a|--standalone]+              [-r|--readable] [-l|--loop] [-s|--src ARG]+              [-i|--interval INTERVAL]+  Command-line diagram generation.++Available options:+  -?,--help                Show this help text+  -w,--width WIDTH         Desired WIDTH of the output image+  -h,--height HEIGHT       Desired HEIGHT of the output image+  -o,--output OUTPUT       OUTPUT file+  -f,--format FORMAT       l for LaTeX, c for ConTeXt, p for plain+                           TeX (default: LaTeX)+  -a,--standalone          Produce standalone .tex output+  -r,--readable            Indent lines+  -l,--loop                Run in a self-recompiling loop+  -s,--src ARG             Source file to watch+  -i,--interval INTERVAL   When running in a loop, check for changes every+                           INTERVAL seconds.+```++If no output file is given, output is send to `stdout`. Supported outputs are `.tex` and `.pdf`. PDF generation is done using [texrunner].++[texrunner]: http://www.github.com/cchalmers/texrunner++```+$ ./Square -o square.tex+$ cat ./square.tex+\begin{pgfpicture}+  \pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{22.0000px}{22.0000px}}+  \pgfusepath{use as bounding box}+  \begin{pgfscope}+    \begin{pgfscope}+      \pgfpathmoveto{\pgfqpoint{21.0000px}{1.0000px}}+      \pgfpathlineto{\pgfqpoint{21.0000px}{21.0000px}}+      \pgfpathlineto{\pgfqpoint{1.0000px}{21.0000px}}+      \pgfpathlineto{\pgfqpoint{1.0000px}{1.0000px}}+      \pgfpathlineto{\pgfqpoint{21.0000px}{1.0000px}}+      \pgfpathclose+      \definecolor{sc}{rgb}{0.0000,0.0000,0.0000}+      \pgfsetstrokecolor{sc}+      \pgfsetlinewidth{0.0132mm}+      \pgfusepath{stroke}+    \end{pgfscope}+  \end{pgfscope}+\end{pgfpicture}+```++### Typesetting++`pgf-diagrams` allows typesetting TeX commands and can calculate the corresponding envelope. See the [hbox example] to see how get envelopes of text:++[hbox example]: examples/hbox.hs++![hbox](https://rawgit.com/cchalmers/texrunner/master/diagrams/hbox.svg)+++### Missing features++The following features are not currently supported:++- selecting fonts (italic and bold work)+- gradients+
+ Setup.hs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ diagrams-pgf.cabal view
@@ -0,0 +1,56 @@+name:                diagrams-pgf+version:             0.1.0.0+synopsis:            PGF backend for diagrams drawing EDSL.+license:             BSD3+license-file:        LICENSE+author:              Christopher Chalmers+maintainer:          c.chalmers@me.com+homepage:            http://github.com/cchalmers/diagrams-pgf+bug-reports:         http://github.com/cchalmers/diagrams-pgf/issues+stability:           Experimental+category:            Graphics+build-type:          Simple+cabal-version:       >=1.10+extra-source-files:  README.md, diagrams/*.svg+extra-doc-files:     diagrams/*.svg+description:+  This package provides a modular backend for rendering diagrams created+  with the diagrams EDSL using the TeX library PGF.+  .+  Support for rendering LaTeX, ConTeXt and plain TeX files.+source-repository head+  type:     git+  location: http://github.com/cchalmers/diagrams-pgf++library+  exposed-modules:+    Diagrams.Backend.PGF+    Diagrams.Backend.PGF.CmdLine+    Diagrams.Backend.PGF.Surface+    Diagrams.Backend.PGF.Render+    Diagrams.Backend.PGF.Hbox+    Graphics.Rendering.PGF+  hs-source-dirs:      src+  build-depends:+    base                 >= 4.4   && < 4.9,+    bytestring           >= 0.9   && < 1.1,+    containers           >= 0.3   && < 0.6,+    colour,+    diagrams-core        >= 1.3   && < 1.4,+    diagrams-lib         >= 1.3   && < 1.4,+    directory            >= 1.0   && < 1.4,+    filepath             >= 1.2   && < 1.5,+    hashable             >= 1.1   && < 1.3,+    mtl                  >= 2.1   && < 3.0,+    optparse-applicative >= 0.11  && < 0.12,+    process              >= 1.0   && < 1.3,+    split                >= 0.1.2 && < 0.3,+    texrunner            <= 0.0.2,+    time                 >= 1.2   && < 1.6,+    JuicyPixels,+    vector,+    zlib++  ghc-options:        -Wall+  default-language:    Haskell2010+
+ diagrams/hbox.svg view
@@ -0,0 +1,98 @@+<?xml version="1.0" encoding="UTF-8"?>+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400pt" height="197.048pt" viewBox="0 0 400 197.048" version="1.1">+<defs>+<g>+<symbol overflow="visible" id="glyph0-0">+<path style="stroke:none;" d=""/>+</symbol>+<symbol overflow="visible" id="glyph0-1">+<path style="stroke:none;" d="M 10.796875 -5.859375 C 11.125 -6.734375 11.65625 -6.96875 12.265625 -6.984375 L 12.265625 -7.515625 C 11.890625 -7.484375 11.375 -7.46875 11 -7.46875 C 10.484375 -7.46875 9.703125 -7.5 9.375 -7.515625 L 9.375 -6.984375 C 10 -6.96875 10.390625 -6.65625 10.390625 -6.140625 C 10.390625 -6.03125 10.390625 -6 10.296875 -5.78125 L 8.703125 -1.3125 L 6.984375 -6.171875 C 6.90625 -6.390625 6.890625 -6.421875 6.890625 -6.515625 C 6.890625 -6.984375 7.578125 -6.984375 7.921875 -6.984375 L 7.921875 -7.515625 C 7.421875 -7.5 6.53125 -7.46875 6.109375 -7.46875 C 5.578125 -7.46875 5.078125 -7.484375 4.5625 -7.515625 L 4.5625 -6.984375 C 5.203125 -6.984375 5.484375 -6.953125 5.65625 -6.71875 C 5.734375 -6.609375 5.9375 -6.09375 6.0625 -5.765625 L 4.5625 -1.53125 L 2.890625 -6.203125 C 2.8125 -6.40625 2.8125 -6.4375 2.8125 -6.515625 C 2.8125 -6.984375 3.484375 -6.984375 3.84375 -6.984375 L 3.84375 -7.515625 C 3.3125 -7.5 2.34375 -7.46875 1.9375 -7.46875 C 1.875 -7.46875 0.9375 -7.484375 0.3125 -7.515625 L 0.3125 -6.984375 C 1.1875 -6.984375 1.390625 -6.921875 1.609375 -6.375 L 3.8125 -0.1875 C 3.890625 0.046875 3.9375 0.1875 4.171875 0.1875 C 4.390625 0.1875 4.4375 0.09375 4.515625 -0.15625 L 6.28125 -5.09375 L 8.0625 -0.140625 C 8.140625 0.046875 8.1875 0.1875 8.40625 0.1875 C 8.640625 0.1875 8.6875 0.03125 8.765625 -0.140625 Z "/>+</symbol>+<symbol overflow="visible" id="glyph0-2">+<path style="stroke:none;" d="M 3.09375 -7.71875 L 0.640625 -7.515625 L 0.640625 -6.984375 C 1.78125 -6.984375 1.9375 -6.875 1.9375 -6.015625 L 1.9375 -1.328125 C 1.9375 -0.546875 1.75 -0.546875 0.578125 -0.546875 L 0.578125 0 C 1.140625 -0.015625 2.078125 -0.046875 2.5 -0.046875 C 3.109375 -0.046875 3.71875 -0.015625 4.3125 0 L 4.3125 -0.546875 C 3.15625 -0.546875 3.09375 -0.625 3.09375 -1.3125 Z M 3.15625 -10.75 C 3.15625 -11.3125 2.71875 -11.671875 2.234375 -11.671875 C 1.6875 -11.671875 1.3125 -11.203125 1.3125 -10.75 C 1.3125 -10.28125 1.6875 -9.828125 2.234375 -9.828125 C 2.71875 -9.828125 3.15625 -10.1875 3.15625 -10.75 Z "/>+</symbol>+<symbol overflow="visible" id="glyph0-3">+<path style="stroke:none;" d="M 6.625 -0.953125 L 6.625 0.1875 L 9.203125 0 L 9.203125 -0.546875 C 7.96875 -0.546875 7.84375 -0.65625 7.84375 -1.515625 L 7.84375 -12.109375 L 5.328125 -11.921875 L 5.328125 -11.375 C 6.546875 -11.375 6.6875 -11.25 6.6875 -10.40625 L 6.6875 -6.625 C 6.171875 -7.265625 5.421875 -7.71875 4.484375 -7.71875 C 2.421875 -7.71875 0.59375 -6 0.59375 -3.75 C 0.59375 -1.53125 2.296875 0.1875 4.296875 0.1875 C 5.40625 0.1875 6.203125 -0.40625 6.625 -0.953125 Z M 6.625 -5.640625 L 6.625 -2.0625 C 6.625 -1.75 6.625 -1.703125 6.4375 -1.40625 C 5.921875 -0.578125 5.125 -0.1875 4.375 -0.1875 C 3.59375 -0.1875 2.96875 -0.640625 2.546875 -1.3125 C 2.09375 -2.03125 2.046875 -3.015625 2.046875 -3.734375 C 2.046875 -4.375 2.078125 -5.421875 2.578125 -6.21875 C 2.953125 -6.75 3.609375 -7.328125 4.5625 -7.328125 C 5.171875 -7.328125 5.90625 -7.0625 6.4375 -6.28125 C 6.625 -5.984375 6.625 -5.953125 6.625 -5.640625 Z "/>+</symbol>+<symbol overflow="visible" id="glyph0-4">+<path style="stroke:none;" d="M 3.015625 -6.984375 L 5.515625 -6.984375 L 5.515625 -7.515625 L 3.015625 -7.515625 L 3.015625 -10.734375 L 2.578125 -10.734375 C 2.5625 -9.296875 2.046875 -7.4375 0.328125 -7.359375 L 0.328125 -6.984375 L 1.8125 -6.984375 L 1.8125 -2.171875 C 1.8125 -0.015625 3.4375 0.1875 4.0625 0.1875 C 5.3125 0.1875 5.796875 -1.046875 5.796875 -2.171875 L 5.796875 -3.15625 L 5.359375 -3.15625 L 5.359375 -2.203125 C 5.359375 -0.90625 4.828125 -0.25 4.1875 -0.25 C 3.015625 -0.25 3.015625 -1.828125 3.015625 -2.125 Z "/>+</symbol>+<symbol overflow="visible" id="glyph0-5">+<path style="stroke:none;" d="M 1.921875 -1.328125 C 1.921875 -0.546875 1.734375 -0.546875 0.5625 -0.546875 L 0.5625 0 C 1.171875 -0.015625 2.0625 -0.046875 2.53125 -0.046875 C 2.984375 -0.046875 3.890625 -0.015625 4.484375 0 L 4.484375 -0.546875 C 3.3125 -0.546875 3.125 -0.546875 3.125 -1.328125 L 3.125 -4.53125 C 3.125 -6.359375 4.359375 -7.328125 5.484375 -7.328125 C 6.578125 -7.328125 6.765625 -6.390625 6.765625 -5.390625 L 6.765625 -1.328125 C 6.765625 -0.546875 6.578125 -0.546875 5.40625 -0.546875 L 5.40625 0 C 6.015625 -0.015625 6.90625 -0.046875 7.375 -0.046875 C 7.84375 -0.046875 8.75 -0.015625 9.34375 0 L 9.34375 -0.546875 C 8.4375 -0.546875 8 -0.546875 7.96875 -1.0625 L 7.96875 -4.390625 C 7.96875 -5.90625 7.96875 -6.4375 7.4375 -7.0625 C 7.1875 -7.359375 6.609375 -7.71875 5.609375 -7.71875 C 4.140625 -7.71875 3.375 -6.671875 3.078125 -6 L 3.078125 -12.109375 L 0.5625 -11.921875 L 0.5625 -11.375 C 1.78125 -11.375 1.921875 -11.25 1.921875 -10.40625 Z "/>+</symbol>+<symbol overflow="visible" id="glyph0-6">+<path style="stroke:none;" d="M 1.953125 -4.390625 C 2.0625 -7 3.53125 -7.4375 4.125 -7.4375 C 5.921875 -7.4375 6.09375 -5.078125 6.09375 -4.390625 Z M 1.9375 -4.03125 L 6.8125 -4.03125 C 7.1875 -4.03125 7.25 -4.03125 7.25 -4.390625 C 7.25 -6.125 6.296875 -7.8125 4.125 -7.8125 C 2.09375 -7.8125 0.484375 -6.015625 0.484375 -3.84375 C 0.484375 -1.5 2.328125 0.1875 4.328125 0.1875 C 6.453125 0.1875 7.25 -1.75 7.25 -2.078125 C 7.25 -2.25 7.109375 -2.28125 7.015625 -2.28125 C 6.859375 -2.28125 6.828125 -2.1875 6.796875 -2.046875 C 6.171875 -0.25 4.609375 -0.25 4.4375 -0.25 C 3.5625 -0.25 2.859375 -0.765625 2.453125 -1.40625 C 1.9375 -2.25 1.9375 -3.40625 1.9375 -4.03125 Z "/>+</symbol>+<symbol overflow="visible" id="glyph0-7">+<path style="stroke:none;" d="M 3 -6.5625 L 3 -7.71875 L 0.484375 -7.515625 L 0.484375 -6.984375 C 1.734375 -6.984375 1.84375 -6.875 1.84375 -6.109375 L 1.84375 2.0625 C 1.84375 2.84375 1.65625 2.84375 0.484375 2.84375 L 0.484375 3.390625 C 1.078125 3.375 1.984375 3.328125 2.4375 3.328125 C 2.921875 3.328125 3.8125 3.375 4.421875 3.390625 L 4.421875 2.84375 C 3.25 2.84375 3.046875 2.84375 3.046875 2.0625 L 3.046875 -1.03125 C 3.140625 -0.75 3.875 0.1875 5.203125 0.1875 C 7.28125 0.1875 9.09375 -1.515625 9.09375 -3.765625 C 9.09375 -5.984375 7.40625 -7.71875 5.453125 -7.71875 C 4.078125 -7.71875 3.34375 -6.953125 3 -6.5625 Z M 3.046875 -1.984375 L 3.046875 -5.875 C 3.5625 -6.765625 4.421875 -7.28125 5.3125 -7.28125 C 6.578125 -7.28125 7.640625 -5.734375 7.640625 -3.765625 C 7.640625 -1.65625 6.421875 -0.1875 5.125 -0.1875 C 4.4375 -0.1875 3.765625 -0.546875 3.296875 -1.25 C 3.046875 -1.625 3.046875 -1.640625 3.046875 -1.984375 Z "/>+</symbol>+<symbol overflow="visible" id="glyph0-8">+<path style="stroke:none;" d="M 3.875 -3 C 2.359375 -3 2.359375 -4.75 2.359375 -5.15625 C 2.359375 -5.625 2.375 -6.171875 2.640625 -6.609375 C 2.78125 -6.828125 3.171875 -7.3125 3.875 -7.3125 C 5.390625 -7.3125 5.390625 -5.5625 5.390625 -5.171875 C 5.390625 -4.6875 5.375 -4.140625 5.109375 -3.703125 C 4.96875 -3.484375 4.578125 -3 3.875 -3 Z M 1.84375 -2.328125 C 1.84375 -2.390625 1.84375 -2.796875 2.140625 -3.140625 C 2.828125 -2.65625 3.546875 -2.59375 3.875 -2.59375 C 5.5 -2.59375 6.703125 -3.8125 6.703125 -5.15625 C 6.703125 -5.796875 6.421875 -6.4375 5.984375 -6.84375 C 6.609375 -7.4375 7.25 -7.515625 7.5625 -7.515625 C 7.59375 -7.515625 7.671875 -7.515625 7.734375 -7.5 C 7.546875 -7.4375 7.453125 -7.25 7.453125 -7.03125 C 7.453125 -6.734375 7.671875 -6.53125 7.953125 -6.53125 C 8.140625 -6.53125 8.46875 -6.65625 8.46875 -7.046875 C 8.46875 -7.34375 8.25 -7.90625 7.578125 -7.90625 C 7.21875 -7.90625 6.453125 -7.796875 5.71875 -7.09375 C 4.984375 -7.65625 4.265625 -7.71875 3.875 -7.71875 C 2.25 -7.71875 1.046875 -6.515625 1.046875 -5.171875 C 1.046875 -4.390625 1.4375 -3.734375 1.875 -3.375 C 1.640625 -3.109375 1.328125 -2.53125 1.328125 -1.921875 C 1.328125 -1.375 1.546875 -0.71875 2.09375 -0.359375 C 1.046875 -0.0625 0.484375 0.6875 0.484375 1.375 C 0.484375 2.640625 2.21875 3.59375 4.34375 3.59375 C 6.40625 3.59375 8.21875 2.703125 8.21875 1.34375 C 8.21875 0.734375 7.96875 -0.15625 7.09375 -0.640625 C 6.15625 -1.140625 5.15625 -1.140625 4.078125 -1.140625 C 3.640625 -1.140625 2.890625 -1.140625 2.78125 -1.15625 C 2.21875 -1.21875 1.84375 -1.765625 1.84375 -2.328125 Z M 4.359375 3.1875 C 2.59375 3.1875 1.390625 2.296875 1.390625 1.375 C 1.390625 0.578125 2.0625 -0.0625 2.828125 -0.125 L 3.859375 -0.125 C 5.359375 -0.125 7.3125 -0.125 7.3125 1.375 C 7.3125 2.328125 6.078125 3.1875 4.359375 3.1875 Z "/>+</symbol>+<symbol overflow="visible" id="glyph1-0">+<path style="stroke:none;" d=""/>+</symbol>+<symbol overflow="visible" id="glyph1-1">+<path style="stroke:none;" d="M 116.265625 -118.1875 L 9.609375 -118.1875 L 6.28125 -78.90625 L 10.65625 -78.90625 C 13.09375 -107.015625 15.71875 -112.78125 42.078125 -112.78125 C 45.21875 -112.78125 49.75 -112.78125 51.5 -112.421875 C 55.171875 -111.734375 55.171875 -109.8125 55.171875 -105.796875 L 55.171875 -13.796875 C 55.171875 -7.859375 55.171875 -5.40625 36.84375 -5.40625 L 29.859375 -5.40625 L 29.859375 0 C 37.015625 -0.53125 54.8125 -0.53125 62.84375 -0.53125 C 70.875 -0.53125 88.859375 -0.53125 96.015625 0 L 96.015625 -5.40625 L 89.03125 -5.40625 C 70.703125 -5.40625 70.703125 -7.859375 70.703125 -13.796875 L 70.703125 -105.796875 C 70.703125 -109.28125 70.703125 -111.734375 73.84375 -112.421875 C 75.765625 -112.78125 80.484375 -112.78125 83.796875 -112.78125 C 110.15625 -112.78125 112.78125 -107.015625 115.21875 -78.90625 L 119.59375 -78.90625 Z "/>+</symbol>+<symbol overflow="visible" id="glyph1-2">+<path style="stroke:none;" d="M 23.75 -13.609375 C 23.75 -7.328125 23.390625 -5.40625 9.953125 -5.40625 L 5.765625 -5.40625 L 5.765625 0 L 106.5 0 L 113.828125 -45.046875 L 109.453125 -45.046875 C 105.09375 -18.15625 101.078125 -5.40625 71.046875 -5.40625 L 47.828125 -5.40625 C 39.625 -5.40625 39.28125 -6.640625 39.28125 -12.390625 L 39.28125 -59 L 54.984375 -59 C 71.921875 -59 73.84375 -53.421875 73.84375 -38.578125 L 78.21875 -38.578125 L 78.21875 -84.84375 L 73.84375 -84.84375 C 73.84375 -69.828125 71.921875 -64.421875 54.984375 -64.421875 L 39.28125 -64.421875 L 39.28125 -106.3125 C 39.28125 -112.078125 39.625 -113.296875 47.828125 -113.296875 L 70.359375 -113.296875 C 97.0625 -113.296875 101.78125 -103.703125 104.578125 -79.4375 L 108.9375 -79.4375 L 104.046875 -118.71875 L 5.765625 -118.71875 L 5.765625 -113.296875 L 9.953125 -113.296875 C 23.390625 -113.296875 23.75 -111.375 23.75 -105.09375 Z "/>+</symbol>+<symbol overflow="visible" id="glyph1-3">+<path style="stroke:none;" d="M 70.1875 -67.390625 L 94.09375 -102.484375 C 97.9375 -108.0625 103.875 -113.65625 119.40625 -113.828125 L 119.40625 -119.234375 C 112.609375 -119.0625 104.390625 -118.71875 100.03125 -118.71875 C 93.046875 -118.71875 84.671875 -118.71875 77.6875 -119.234375 L 77.6875 -113.828125 C 84.671875 -113.65625 88.515625 -109.8125 88.515625 -105.796875 C 88.515625 -104.046875 88.15625 -103.703125 86.9375 -101.78125 L 67.03125 -72.28125 L 44.515625 -105.96875 C 44.171875 -106.5 43.296875 -107.890625 43.296875 -108.59375 C 43.296875 -110.6875 47.140625 -113.65625 54.640625 -113.828125 L 54.640625 -119.234375 C 48.53125 -118.71875 35.78125 -118.71875 29.15625 -118.71875 C 23.75 -118.71875 12.921875 -118.890625 6.453125 -119.234375 L 6.453125 -113.828125 L 9.78125 -113.828125 C 19.375 -113.828125 22.6875 -112.609375 26.015625 -107.71875 L 58.140625 -59.1875 L 29.5 -16.9375 C 27.0625 -13.4375 21.828125 -5.40625 4.1875 -5.40625 L 4.1875 0 C 10.46875 -0.171875 17.8125 -0.53125 23.5625 -0.53125 C 30.03125 -0.53125 39.625 -0.53125 45.921875 0 L 45.921875 -5.40625 C 37.890625 -5.59375 34.921875 -10.296875 34.921875 -13.4375 C 34.921875 -15.015625 35.4375 -15.71875 36.65625 -17.625 L 61.453125 -54.296875 L 89.03125 -12.5625 C 89.390625 -11.875 89.90625 -11.171875 89.90625 -10.65625 C 89.90625 -8.546875 86.0625 -5.59375 78.5625 -5.40625 L 78.5625 0 C 84.671875 -0.53125 97.421875 -0.53125 104.046875 -0.53125 C 111.375 -0.53125 119.40625 -0.34375 126.75 0 L 126.75 -5.40625 L 123.421875 -5.40625 C 114.34375 -5.40625 110.6875 -6.28125 107.015625 -11.703125 Z "/>+</symbol>+</g>+</defs>+<g id="surface1">+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">+  <use xlink:href="#glyph0-1" x="140.562" y="191.791"/>+  <use xlink:href="#glyph0-2" x="153.1705" y="191.791"/>+  <use xlink:href="#glyph0-3" x="158.02046" y="191.791"/>+  <use xlink:href="#glyph0-4" x="167.720381" y="191.791"/>+  <use xlink:href="#glyph0-5" x="174.509976" y="191.791"/>+</g>+<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4.914844 22.637844 L 320.0125 22.637844 " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4.914844 22.137844 C 4.864063 22.469875 4.864063 22.805813 4.914844 23.137844 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 0.00078125 22.637844 L 5.8875 25.075344 C 4.539844 23.731594 4.539844 21.548 5.8875 20.200344 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 324.922656 22.637844 L 319.035937 25.075344 C 320.383594 23.731594 320.383594 21.548 319.035937 20.200344 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 320.0125 22.137844 C 320.059375 22.469875 320.059375 22.805813 320.0125 23.137844 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">+  <use xlink:href="#glyph0-3" x="347.627" y="142.458"/>+  <use xlink:href="#glyph0-6" x="357.326921" y="142.458"/>+  <use xlink:href="#glyph0-7" x="365.08546" y="142.458"/>+  <use xlink:href="#glyph0-4" x="374.785381" y="142.458"/>+  <use xlink:href="#glyph0-5" x="381.574976" y="142.458"/>+</g>+<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.45 72.833156 L 342.45 45.075344 " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 341.95 72.833156 C 342.278125 72.883938 342.617969 72.883938 342.95 72.833156 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.45 77.747219 L 344.8875 71.8605 C 343.539844 73.208156 341.35625 73.208156 340.008594 71.8605 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.45 40.161281 L 344.8875 46.048 C 343.539844 44.70425 341.35625 44.70425 340.008594 46.048 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 341.95 45.075344 C 342.278125 45.028469 342.617969 45.028469 342.95 45.075344 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">+  <use xlink:href="#glyph0-5" x="347.627" y="64.015"/>+  <use xlink:href="#glyph0-6" x="357.326921" y="64.015"/>+  <use xlink:href="#glyph0-2" x="365.08546" y="64.015"/>+  <use xlink:href="#glyph0-8" x="369.93542" y="64.015"/>+  <use xlink:href="#glyph0-5" x="378.664651" y="64.015"/>+</g>+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">+  <use xlink:href="#glyph0-4" x="387.875734" y="64.015"/>+</g>+<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.45 82.661281 L 342.45 192.137844 " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.95 82.661281 C 342.617969 82.614406 342.278125 82.614406 341.95 82.661281 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.45 77.747219 L 340.008594 83.633938 C 341.35625 82.286281 343.539844 82.286281 344.8875 83.633938 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.45 197.051906 L 340.008594 191.165188 C 341.35625 192.508938 343.539844 192.508938 344.8875 191.165188 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 342.95 192.137844 C 342.617969 192.184719 342.278125 192.184719 341.95 192.137844 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 0.00078125 77.747219 L 324.922656 77.747219 " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 324.922656 40.161281 L 324.922656 197.051906 L 0.00078125 197.051906 L 0.00078125 40.161281 Z " transform="matrix(1,0,0,-1,-0.075,197.048)"/>+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">+  <use xlink:href="#glyph1-1" x="-0.075" y="119.301"/>+</g>+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">+  <use xlink:href="#glyph1-2" x="96.92034" y="156.88998"/>+</g>+<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">+  <use xlink:href="#glyph1-3" x="193.91568" y="119.301"/>+</g>+</g>+</svg>
+ src/Diagrams/Backend/PGF.hs view
@@ -0,0 +1,223 @@+{-# LANGUAGE TypeFamilies #-}++-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Backend.PGF+-- Copyright   :  (c) 2015 Christopher Chalmers+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- A full-featured PGF backend for diagrams producing PGF code+-- suitable for LaTeX, ConTeXt or plain TeX consumption.+--+-- To invoke the PGF backend, you have a number of options.+--+-- * You can use 'renderPGF' or 'renderPGF'' to render a 'Diagram' to a+--   ".tex" or ".pdf" file;+--+-- * You can use the most flexible 'renderDia' function to access the+--   resulting PGF code directly in memory as a bytestring 'Builder'.+--+-- * Use 'Diagrams.Backend.PGF.CmdLine.mainWith' or+--   'Diagrams.Backend.PGF.CmdLine.defaultMain' to make a command line+--   application. See 'Diagrams.Backend.PGF.CmdLine' for more info.+--+-- The 'Surface' provides the necessary information for rendering PGF code and+-- building a PDF using "texrunner". See 'Diagrams.Backend.PGF.Surface'+-- for more info.+--++module Diagrams.Backend.PGF+  ( -- * Rendering token & options+    PGF (..)+  , B++    -- * Rendering functions+  , renderPGF+  , renderPGF'+  , renderPGFSurf++    -- * Options+    -- | Options for changing how the diagram is rendered. 'Options'+    --   'PGF' is an instance of 'Default':+    --+    -- @+    -- def = PGFOptions {+    --   _surface    = 'latexSurface'+    --   _sizeSpec   = 'absolute'+    --   _readable   = 'True'+    --   _standalone = 'False'+    --   }+    -- @+    --+    --   You can edit the default options using lenses.+  , readable+  , sizeSpec+  , surface+  , standalone++    -- * Surfaces+    -- | These surfaces should be suitable for basic diagrams. For more+    --   complicated options see 'Diagrams.Backend.PGF.Surface'.+  , Surface+  , surfOnlineTex++    -- ** Predefined surfaces+  , latexSurface+  , contextSurface+  , plaintexSurface++    -- ** Lenses+  , command+  , arguments+  , preamble++    -- * Online TeX+    -- | By using 'OnlineTex', diagrams is able to query tex for sizes+    --   of hboxs and give them the corresponding envelopes. These can+    --   then be used as any other diagram with the correct size.+    --+    --   Online diagrams use the 'Surface' to run tex in online mode and+    --   get feedback for hbox sizes. To run it you can use+    --   'renderOnlinePGF', 'renderOnlinePGF'' or 'onlineMain' from+    --   'Diagrams.Backend.PGF.CmdLine'.+    --+    --   See+    --   <https://github.com/diagrams/diagrams-pgf/tree/master/examples>+    --   for examples.+  , OnlineTex+  , renderOnlinePGF+  , renderOnlinePGF'++    -- ** Hbox+  , Hbox+  , hboxOnline+  , hboxPoint+  , hboxSurf+  ) where++import           Data.ByteString.Builder+import           System.Directory             hiding (readable)+import           System.FilePath+import           System.IO+import           System.Texrunner+import           System.Texrunner.Online      hiding (hbox)++import qualified Data.ByteString.Char8        as B+import qualified Data.ByteString.Lazy.Char8   as LB++import           Diagrams.Backend.PGF.Hbox+import           Diagrams.Backend.PGF.Render+import           Diagrams.Backend.PGF.Surface+import           Diagrams.Size+import           Diagrams.Prelude             hiding (r2)++type B = PGF++type instance V PGF = V2+type instance N PGF = Double++-- | Render a pgf diagram and write it to the given filepath. Same as+--   'renderPGF'' but uses the default options.+renderPGF :: (TypeableFloat n, Monoid' m)+          => FilePath         -- ^ path to output+          -> SizeSpec V2 n    -- ^ size of output+          -> QDiagram PGF V2 n m -- ^ 'Diagram' to render+          -> IO ()+renderPGF outFile sizeSp = renderPGFSurf outFile sizeSp def++-- | Render a pgf diagram and write it to the given filepath. Same as+--   'renderPGF' but takes a 'Surface'.+renderPGFSurf :: (TypeableFloat n, Monoid' m)+          => FilePath         -- ^ path to output+          -> SizeSpec V2 n    -- ^ size of output+          -> Surface          -- ^ surface to render with+          -> QDiagram PGF V2 n m -- ^ diagram to render+          -> IO ()+renderPGFSurf outFile sizeSp surf =+  renderPGF' outFile $+    def & sizeSpec .~ sizeSp+        & surface  .~ surf++-- | Render a pgf diagram and write it to the given filepath. If the file has+--   the extension @.pdf@, a PDF is generated in a temporary directory using+--   options from the given surface, otherwise, the tex output is saved+--   using the surface's 'TexFormat'.+renderPGF' :: (TypeableFloat n, Monoid' m)+           => FilePath -> Options PGF V2 n -> QDiagram PGF V2 n m -> IO ()+renderPGF' outFile opts d = case takeExtension outFile of+  ".pdf" -> do+    let rendered = renderDia PGF (opts & standalone .~ True & readable .~ False) d++    currentDir <- getCurrentDirectory+    targetDir  <- canonicalizePath (takeDirectory outFile)++    let source = toLazyByteString rendered++    (_, texLog, mPDF) <- runTex (opts^.surface.command)+                                (opts^.surface.arguments)+                                [currentDir, targetDir]+                                source++    case mPDF of+      Nothing  -> putStrLn "Error, no PDF found:"+               >> B.putStrLn (prettyPrintLog texLog)+      Just pdf -> LB.writeFile outFile pdf++  -- tex output+  _      -> writeTexFile outFile opts d++-- | Render an online 'PGF' diagram and save it. Same as+--   'renderOnlinePGF'' using default options.+renderOnlinePGF :: (TypeableFloat n, Monoid' m)+                => FilePath+                -> SizeSpec V2 n+                -> OnlineTex (QDiagram PGF V2 n m)+                -> IO ()+renderOnlinePGF outFile sizeSp = renderOnlinePGF' outFile  (def & sizeSpec .~ sizeSp)++-- | Same as 'renderOnlinePDF' but takes 'Options' 'PGF'.+renderOnlinePGF' :: (TypeableFloat n, Monoid' m)+                 => FilePath+                 -> Options PGF V2 n+                 -> OnlineTex (QDiagram PGF V2 n m)+                 -> IO ()+renderOnlinePGF' outFile opts dOL = case takeExtension outFile of+  ".pdf" -> do++    ((), texLog, mPDF) <-+      runOnlineTex'+        (surf^.command)+        (surf^.arguments)+        (toByteString . stringUtf8 $ surf ^. (preamble <> beginDoc)) $ do++          d <- dOL++          -- we've already output the preamble so don't do it again+          let opts' = opts & surface    %~ set beginDoc "" . set preamble ""+                           & readable   .~ False+                           & standalone .~ True++              rendered = renderDia PGF opts' d++          texPutStrLn $ toByteString rendered++    case mPDF of+      Nothing  -> putStrLn "Error, no PDF found:"+               >> print texLog+      Just pdf -> LB.writeFile outFile pdf++  -- tex output+  _      ->  surfOnlineTexIO surf dOL >>= writeTexFile outFile opts+  where+    surf = opts ^. surface+    toByteString = LB.toStrict . toLazyByteString++-- | Write the rendered diagram to a text file, ignoring the extension.+writeTexFile :: (TypeableFloat n, Monoid' m)+             => FilePath -> Options PGF V2 n -> QDiagram PGF V2 n m -> IO ()+writeTexFile outFile opts d = do+  h <- openFile outFile WriteMode+  hPutBuilder h $ renderDia PGF opts d+  hClose h+
+ src/Diagrams/Backend/PGF/CmdLine.hs view
@@ -0,0 +1,274 @@+{-# LANGUAGE CPP                  #-}+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Backend.PGF.CmdLine+-- Copyright   :  (c) 2015 Diagrams team (see LICENSE)+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Convenient creation of command-line-driven executables for+-- rendering diagrams using the PGF backend.+--+-----------------------------------------------------------------------------++module Diagrams.Backend.PGF.CmdLine+  ( -- * General form of @main@+    -- $mainwith++    mainWith++    -- * Supported forms of @main@++  , defaultMain+  , mainWithSurf+  , onlineMain+  , onlineMainWithSurf+  , multiMain++  , module Diagrams.Backend.PGF+  ) where++import           Data.ByteString.Builder+import           Options.Applicative          as OP++import           System.IO                    (stdout)++import           Diagrams.Backend.CmdLine+import           Diagrams.Prelude             hiding (height, interval, output,+                                               width, (<>))++import           Diagrams.Backend.PGF+import           Diagrams.Backend.PGF.Surface++-- pgf specific stuff++data PGFCmdLineOpts = PGFCmdLineOpts+  { _cmdStandalone :: Bool+  , _cmdReadable   :: Bool+  }++makeLenses ''PGFCmdLineOpts++instance Parseable PGFCmdLineOpts where+  parser = PGFCmdLineOpts+        <$> switch+            ( long "standalone"+           <> short 'a'+           <> help "Produce standalone .tex output"+            )+        <*> switch+            ( long "readable"+           <> short 'r'+           <> help "Indent lines"+            )++-- not sure if this is of any use+instance ToResult d => ToResult (OnlineTex d) where+  type Args (OnlineTex d) = (Surface, Args d)+  type ResultOf (OnlineTex d) = IO (ResultOf d)++  toResult d (surf, args) = flip toResult args <$> surfOnlineTexIO surf d++-- $mainwith+-- The 'mainWith' method unifies all of the other forms of @main@ and is+-- now the recommended way to build a command-line diagrams program.  It+-- works as a direct replacement for 'defaultMain' or 'multiMain' as well+-- as allowing more general arguments.  For example, given a function that+-- produces a diagram when given an @Int@ and a @'Colour' Double@, 'mainWith'+-- will produce a program that looks for additional number and color arguments.+--+-- > ... definitions ...+-- > f :: Int -> Colour Double -> Diagram PGF+-- > f i c = ...+-- >+-- > main = mainWith f+--+-- We can run this program as follows:+--+-- > $ ghc --make mydiagram+-- >+-- > # output image.tex built by `f 20 red`+-- > $ ./MyDiagram -o image.tex -w 200 20 red+++-- | This is the simplest way to render diagrams, and is intended to+--   be used like so:+--+-- > ... definitions ...+-- >+-- > main = defaultMain myDiagram+--+--   Compiling this file will result in an executable which takes+--   various command-line options for setting the size, output file,+--   and so on, and renders @myDiagram@ with the specified options.+--+--   Pass @--help@ to the generated executable to see all available+--   options. Currently it looks something like+--+-- @+-- mydiagram+--+-- Usage: mydiagram [-?|--help] [-w|--width WIDTH] [-h|--height HEIGHT]+--                  [-o|--output OUTPUT] [-f|--format FORMAT] [-a|--standalone]+--                  [-r|--readable] [-l|--loop] [-s|--src ARG]+--                  [-i|--interval INTERVAL]+--   Command-line diagram generation.+--+-- Available options:+--   -?,--help                Show this help text+--   -w,--width WIDTH         Desired WIDTH of the output image+--   -h,--height HEIGHT       Desired HEIGHT of the output image+--   -o,--output OUTPUT       OUTPUT file+--   -f,--format FORMAT       l for LaTeX, c for ConTeXt, p for plain+--                            TeX (default: LaTeX)+--   -a,--standalone          Produce standalone .tex output+--   -r,--readable            Indent lines+--   -l,--loop                Run in a self-recompiling loop+--   -s,--src ARG             Source file to watch+--   -i,--interval INTERVAL   When running in a loop, check for changes every+--                            INTERVAL seconds.+-- @+--+--   For example, a common scenario is+--+-- @+-- $ ghc --make mydiagram+--+--   # output image.tex with a width of 400bp (and auto-determined height)+--   # (bp = big point = 1px at 72dpi)+-- $ ./mydiagram -o image.tex -w 400+-- @++defaultMain :: Diagram PGF -> IO ()+defaultMain = mainWith++-- | Allows you to pick a surface the diagram will be rendered with.+-- (This+mainWithSurf :: Surface -> Diagram PGF -> IO ()+mainWithSurf = curry mainWith++-- For online diagrams.++-- | Same as @defaultMain@ but takes an online pgf diagram.+onlineMain :: OnlineTex (Diagram PGF) -> IO ()+onlineMain = mainWith++-- | Same as @mainWithSurf@ but takes an online pgf diagram.+onlineMainWithSurf :: Surface -> OnlineTex (Diagram PGF) -> IO ()+onlineMainWithSurf = curry mainWith++-- Mainable instances++instance TypeableFloat n => Mainable (QDiagram PGF V2 n Any) where+  type MainOpts (QDiagram PGF V2 n Any) =+    (DiagramOpts, DiagramLoopOpts, PGFCmdLineOpts, TexFormat)+  mainRender (diaOpts, loopOpts, pgfOpts, format) d = do+    chooseRender diaOpts pgfOpts (formatToSurf format) d+    defaultLoopRender loopOpts++instance TypeableFloat n => Mainable (Surface, QDiagram PGF V2 n Any) where+  type MainOpts (Surface, QDiagram PGF V2 n Any) =+    (DiagramOpts, DiagramLoopOpts, PGFCmdLineOpts)+  mainRender (diaOpts, loopOpts, pgfOpts) (surf,d) = do+    chooseRender diaOpts pgfOpts surf d+    defaultLoopRender loopOpts++-- Online diagrams+instance TypeableFloat n => Mainable (OnlineTex (QDiagram PGF V2 n Any)) where+  type MainOpts (OnlineTex (QDiagram PGF V2 n Any))+    = (DiagramOpts, DiagramLoopOpts, PGFCmdLineOpts, TexFormat)+  mainRender (diaOpts, loopOpts, pgfOpts, format) d = do+    chooseOnlineRender diaOpts pgfOpts (formatToSurf format) d+    defaultLoopRender loopOpts++instance TypeableFloat n => Mainable (Surface, OnlineTex (QDiagram PGF V2 n Any)) where+  type MainOpts (Surface, OnlineTex (QDiagram PGF V2 n Any))+    = (DiagramOpts, DiagramLoopOpts, PGFCmdLineOpts)+  mainRender (diaOpts, loopOpts, pgfOpts) (surf, d) = do+    chooseOnlineRender diaOpts pgfOpts surf d+    defaultLoopRender loopOpts++formatToSurf :: TexFormat -> Surface+formatToSurf format = case format of+  LaTeX    -> latexSurface+  ConTeXt  -> contextSurface+  PlainTeX -> plaintexSurface++cmdLineOpts :: TypeableFloat n+   => DiagramOpts -> Surface -> PGFCmdLineOpts -> Options PGF V2 n+cmdLineOpts opts surf pgf+  = def & surface    .~ surf+        & sizeSpec   .~ sz+        & readable   .~ pgf^.cmdReadable+        & standalone .~ pgf^.cmdStandalone+  where+    sz = fromIntegral <$> mkSizeSpec2D (opts^.width) (opts^.height)++chooseRender :: TypeableFloat n+  => DiagramOpts -> PGFCmdLineOpts -> Surface -> QDiagram PGF V2 n Any -> IO ()+chooseRender diaOpts pgfOpts surf d =+  case diaOpts^.output of+    ""  -> hPutBuilder stdout $ renderDia PGF opts d+    out -> renderPGF' out opts d+  where+    opts = cmdLineOpts diaOpts surf pgfOpts++chooseOnlineRender :: TypeableFloat n+  => DiagramOpts -> PGFCmdLineOpts -> Surface -> OnlineTex (QDiagram PGF V2 n Any) -> IO ()+chooseOnlineRender diaOpts pgfOpts surf d =+    case diaOpts^.output of+      ""  -> surfOnlineTexIO surf d >>= hPutBuilder stdout . renderDia PGF opts+      out -> renderOnlinePGF' out opts d+  where+    opts = cmdLineOpts diaOpts surf pgfOpts+++-- | @multiMain@ is like 'defaultMain', except instead of a single+--   diagram it takes a list of diagrams paired with names as input.+--   The generated executable then takes a @--selection@ option+--   specifying the name of the diagram that should be rendered.  The+--   list of available diagrams may also be printed by passing the+--   option @--list@.+--+--   Example usage:+--+-- @+-- $ ghc --make MultiTest+-- [1 of 1] Compiling Main             ( MultiTest.hs, MultiTest.o )+-- Linking MultiTest ...+-- $ ./MultiTest --list+-- Available diagrams:+--   foo bar+-- $ ./MultiTest --selection bar -o Bar.tex -w 200+-- @++multiMain :: [(String, Diagram PGF)] -> IO ()+multiMain = mainWith++instance TypeableFloat n => Mainable [(String,QDiagram PGF V2 n Any)] where+  type MainOpts [(String,QDiagram PGF V2 n Any)]+      = (MainOpts (QDiagram PGF V2 n Any), DiagramMultiOpts)++  mainRender = defaultMultiMainRender++instance Parseable TexFormat where+  parser = OP.option (eitherReader parseFormat)+                      $ short   'f'+                     <> long    "format"+                     <> help    "l for LaTeX, c for ConTeXt, p for plain TeX"+                     <> metavar "FORMAT"+                     <> OP.value LaTeX+                     <> showDefault++parseFormat :: String -> Either String TexFormat+parseFormat ('l':_) = Right LaTeX+parseFormat ('c':_) = Right ConTeXt+parseFormat ('p':_) = Right PlainTeX+parseFormat ('t':_) = Right PlainTeX+parseFormat x       = Left $ "Unknown format" ++ x+
+ src/Diagrams/Backend/PGF/Hbox.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies          #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Backend.PGF+-- Copyright   :  (c) 2014 Christopher Chalmers+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- A hbox a primitive Tex box, typically used for holding text and+-- formulas but can hold anything. This module provides functions for+-- retrieving the dimensions of these boxes to give diagrams the correct+-- envelopes.+-----------------------------------------------------------------------------+module Diagrams.Backend.PGF.Hbox+  ( Hbox (..)++    -- * Enveloped diagrams+    -- | The dimensions of a hbox can be recovered by calling Tex. The+    --   resulting envelope has its origin at the baseline of the text.+    --+    --   <<diagrams/hbox.svg#width=200 hbox>>+  , hboxOnline++   -- ** Non-Online version+   -- | These versions bypass 'OnlineTex' by just running a whole tex+   --   program just to get the size of a single hbox. This is not+   --   recommended but because it is slow, but can be convientient if+   --   you only need one or two hbox sizes.+  , hboxSurf+  , hboxSurfIO++    -- * Point envelope diagrams+  , hboxPoint++  ) where++import           Data.ByteString.Char8        (pack)+import           Data.Monoid+import           Data.Typeable+import           System.IO.Unsafe+import           System.Texrunner.Online      hiding (hbox)+import qualified System.Texrunner.Online      as Online+import           System.Texrunner.Parse++import           Diagrams.Core.Envelope       (pointEnvelope)+import           Diagrams.Prelude             hiding (Box, (<>))++import           Diagrams.Backend.PGF.Surface++-- | Primitive for placing raw Tex commands in a hbox.+data Hbox n = Hbox (Transformation V2 n) String+  deriving Typeable++type instance V (Hbox n) = V2+type instance N (Hbox n) = n++instance Fractional n => Transformable (Hbox n) where+  transform t (Hbox tt str) = Hbox (t <> tt) str++instance Fractional n => Renderable (Hbox n) NullBackend where+  render _ _ = mempty++-- | Raw Tex commands in a hbox with no envelope. Transformations are+-- applied normally. This primitive ignores+-- 'Diagrams.TwoD.Text.FontSize'.+hboxPoint :: (OrderedField n, Typeable n, Renderable (Hbox n) b)+     => String -> QDiagram b V2 n Any+hboxPoint raw = mkQD (Prim (Hbox mempty raw))+                (pointEnvelope origin)+                mempty+                mempty+                mempty++-- | Hbox with bounding box envelope. Note that each box requires a call to+--   Tex. For multiple boxes consider using 'onlineHbox' to get multiple boxes+--   from a single call. (uses unsafePerformIO)+hboxSurf :: (TypeableFloat n, Renderable (Hbox n) b)+            => Surface -> String -> QDiagram b V2 n Any+hboxSurf surf txt = unsafePerformIO (hboxSurfIO surf txt)+{-# NOINLINE hboxSurf #-}++-- | Hbox with bounding box envelope. Note that each box requires a call to+--   Tex. For multiple boxes consider using 'onlineHbox' to get multiple boxes+--   from a single call.+hboxSurfIO :: (TypeableFloat n, Renderable (Hbox n) b)+       => Surface -> String -> IO (QDiagram b V2 n Any)+hboxSurfIO surf txt = surfOnlineTexIO surf (hboxOnline txt)++-- | Hbox with bounding box envelope.+hboxOnline :: (TypeableFloat n, Renderable (Hbox n) b)+           => String -> OnlineTex (QDiagram b V2 n Any)+hboxOnline txt = do+  Box h d w <- Online.hbox (pack txt)++  let bb = fromCorners (P $ V2 0 (-d))+                       (P $ V2 w h)++  return $ mkQD (Prim (Hbox mempty txt))+                (getEnvelope bb)+                (getTrace bb)+                mempty+                (boundingBoxQuery bb)+
+ src/Diagrams/Backend/PGF/Render.hs view
@@ -0,0 +1,236 @@+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE ViewPatterns          #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Backend.PGF.Render+-- Copyright   :  (c) 2015 Christopher Chalmers+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- This is an internal module exposeing internals for rendering a+-- diagram. This is for advanced use only. 'Diagrams.Backend.PGF'+-- has enought for general use.+--+module Diagrams.Backend.PGF.Render+  ( PGF (..)+  , Options (..)+  , Render (..)++  -- * Lenses+  , surface+  , sizeSpec+  , readable+  , standalone++  -- * Utilities+  , escapeString+  ) where++import           Control.Monad                (when)+import           Data.ByteString.Builder++import qualified Data.Foldable                as F (foldMap)+import           Data.Functor+import           Data.Hashable                (Hashable (..))+import           Data.Tree                    (Tree (Node))++import           Diagrams.Core.Types+import           Diagrams.Prelude hiding ((<~))++import           Diagrams.Backend.PGF.Hbox    (Hbox (..))+import           Diagrams.Backend.PGF.Surface (Surface)+import           Diagrams.TwoD.Adjust         (adjustDia2D)+import           Diagrams.TwoD.Path+import           Diagrams.TwoD.Text           (Text (..), TextAlignment (..), getFontSize,+                                               getFontSlant, getFontWeight)+import           Data.Typeable+import qualified Graphics.Rendering.PGF       as P++import Prelude++-- | This data declaration is simply used as a token to distinguish+--   this rendering engine.+data PGF = PGF+  deriving (Show, Typeable)++instance TypeableFloat n => Backend PGF V2 n where+  newtype Render  PGF V2 n = R (P.Render n)+  type    Result  PGF V2 n = Builder+  data    Options PGF V2 n = PGFOptions+    { _surface    :: Surface       -- ^ Surface you want to use.+    , _sizeSpec   :: SizeSpec V2 n -- ^ The requested size.+    , _readable   :: Bool          -- ^ Indented lines for @.tex@ output.+    , _standalone :: Bool          -- ^ Should @.tex@ output be standalone.+    }++  renderRTree _ ops (toRender -> R r) =+    P.renderWith (ops^.surface) (ops^.readable) (ops^.standalone) bounds r+      where+        bounds = specToSize 100 (ops^.sizeSpec)++  adjustDia = adjustDia2D sizeSpec++toRender :: TypeableFloat n => RTree PGF V2 n Annotation -> Render PGF V2 n+toRender (Node n rs) = case n of+  RPrim p                 -> render PGF p+  RStyle sty'             -> R $ do+    sty <- P.style <<<>= sty'        -- mappend old style+    clips <- use (P.style . _clip)+    clip clips r <* (P.style .= sty) -- render then revert to old style+  RAnnot (OpacityGroup x) -> R $ P.opacityGroup x r+  _                       -> R r+  where R r = F.foldMap toRender rs++instance Fractional n => Default (Options PGF V2 n) where+  def = PGFOptions+          { _surface    = def+          , _sizeSpec   = absolute+          , _readable   = True+          , _standalone = False+          }++instance Monoid (Render PGF V2 n) where+  mempty              = R $ return ()+  R ra `mappend` R rb = R $ ra >> rb++-- | Lens onto the surface used to render.+surface :: Lens' (Options PGF V2 n) Surface+surface = lens _surface (\o s -> o {_surface = s})++-- | Lens onto whether a standalone TeX document should be produced.+standalone :: Lens' (Options PGF V2 n) Bool+standalone = lens _standalone (\o s -> o {_standalone = s})++-- | Lens onto the 'SizeSpec2D'.+sizeSpec :: Lens' (Options PGF V2 n) (SizeSpec V2 n)+sizeSpec = lens _sizeSpec (\o s -> o {_sizeSpec = s})++-- | Lens onto whether the lines of the TeX output are indented.+readable :: Lens' (Options PGF V2 n) Bool+readable = lens _readable (\o b -> o {_readable = b})++-- helper function to easily get options and set them+(<~) :: AttributeClass a => (b -> P.Render n) -> (a -> b) -> P.Render n+renderF <~ getF = do+  s <- uses P.style (fmap getF . getAttr)+  maybe (return ()) renderF s++infixr 2 <~++-- | Fade a colour with the opacity from the style.+fade :: Color c => c -> P.RenderM n (AlphaColour Double)+fade c = flip dissolve (toAlphaColour c) <$> use (P.style . _opacity)++-- The Path is necessary so we can clip/workout gradients.+setFillTexture :: RealFloat n => Path V2 n -> Texture n -> P.Render n+setFillTexture p t = case t of+  SC (SomeColor c) -> fade c >>= P.setFillColor+  LG g             -> P.linearGradient p g+  RG g             -> P.radialGradient p g++setLineTexture :: RealFloat n => Texture n -> P.Render n+setLineTexture (SC (SomeColor c)) = fade c >>= P.setLineColor+setLineTexture _                  = return ()++clip :: TypeableFloat n => [Path V2 n] -> P.Render n -> P.Render n+clip paths r = go paths+  where+    go []     = r+    go (p:ps) = P.scope $ P.path p >> P.clip >> go ps++-- | Escapes some common characters in a string. Note that this does not+--   mean the string can't create an error, it mearly escapes common+--   characters.+escapeString :: String -> String+escapeString = concatMap escapeChar+  where+    escapeChar ch = case ch of+      '$' -> "\\$"+      '%' -> "\\letterpercent{}"+      '&' -> "\\&"+      '#' -> "\\#"+      '_' -> "\\_"+      '{' -> "$\\{$"+      '}' -> "$\\}$"+      '\\'-> "$\\backslash{}$"+      '~' -> "\\~{}"+      '^' -> "\\^{}"+      '[' -> "{[}"+      ']' -> "{]}"+      x   -> [x]++-- Renderable instances ------------------------------------------------++instance TypeableFloat n => Renderable (Path V2 n) PGF where+  render _ path = R . P.scope $ do+    -- lines and loops are separated when stroking so we only need to+    -- check the first one+    let canFill = noneOf (_head . located) isLine path+    -- solid colours need to be filled with usePath+    doFill <- if canFill+      then do+        mFillTexture <- preuse (P.style . _fillTexture)+        case mFillTexture of+          Nothing -> return False+          Just t  -> do+            setFillTexture path t+            P.setFillRule <~ getFillRule+            return (has _SC t)+      else return False+    --+    w <- use (P.style . _lineWidthU . non 0)+    let doStroke = w > 0.0001+    when doStroke $ do+      P.setLineWidth w+      setLineTexture <~ getLineTexture+      P.setLineJoin  <~ getLineJoin+      P.setLineCap   <~ getLineCap+      P.setDash      <~ getDashing+    --+    P.path path+    P.usePath doFill doStroke++-- | Does not support full alignment. Text is not escaped.+instance TypeableFloat n => Renderable (Text n) PGF where+  render _ (Text tt txtAlign str) = R . P.scope $ do+    setFillTexture mempty <~ getFillTexture+    --+    P.applyTransform tt+    (P.applyScale . (/8)) <~ getFontSize+        -- (/8) was obtained from trail and error+    --+    P.renderText (P.setTextAlign txtAlign) $ do+      P.setFontWeight <~ getFontWeight+      P.setFontSlant  <~ getFontSlant+      P.rawString str++instance TypeableFloat n => Renderable (Hbox n) PGF where+  render _ (Hbox tt str) = R . P.scope $ do+    P.applyTransform tt+    P.renderText (P.setTextAlign BaselineText) (P.rawString str)++-- | Supported: @.pdf@, @.jpg@, @.png@.+instance RealFloat n => Renderable (DImage n External) PGF where+  render _  = R . P.image++-- | Supported: 'ImageRGB8'. (Other types from 'DynamicImage' will+--   error)+instance RealFloat n => Renderable (DImage n Embedded) PGF where+  render _  = R . P.embeddedImage++------------------------------------------------------------------------+-- Hashable instances++instance Hashable n => Hashable (Options PGF V2 n) where+  hashWithSalt s (PGFOptions sf sz rd st)+    = s  `hashWithSalt`+      sf `hashWithSalt`+      sz `hashWithSalt`+      rd `hashWithSalt`+      st+
+ src/Diagrams/Backend/PGF/Surface.hs view
@@ -0,0 +1,301 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE TemplateHaskell    #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Diagrams.Backend.PGF.Surface+-- Copyright   :  (c) 2015 Christopher Chalmers+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- A 'Surface' defines how a pgfpicture should be placed and compiled. Surfaces+-- are used for rendering a @.tex@ or @.pdf@ using functions from+-- 'Diagrams.Backend.PGF'.+--+-- Surfaces are also used in 'Diagrams.Backend.PGF.Hbox' for querying+-- envelopes of text.+--+-- Surfaces for Latex, Context and plain Tex are provided and reexported by+-- Diagrams.Backend.PGF. Lenses here allow these to be adjusted as required.+-----------------------------------------------------------------------------++module Diagrams.Backend.PGF.Surface+  ( -- * Surface definition+    Surface(..)+  , TexFormat(..)++    -- * Online rendering with surfaces+  , surfOnlineTex+  , surfOnlineTexIO++    -- * Predefined surfaces+  , latexSurface+  , contextSurface+  , plaintexSurface+  , sampleSurfaceOutput++    -- * Lenses+  , texFormat+  , command+  , arguments+  , pageSize+  , preamble+  , beginDoc+  , endDoc+  ) where++import           Data.ByteString.Builder+import           Data.Hashable           (Hashable (..))+import           Data.Typeable           (Typeable)+import           System.IO.Unsafe+import           System.Texrunner.Online+++import           Diagrams.Prelude+import           Prelude++-- | The 'TexFormat' is used to choose the different PGF commands nessesary for+--   that format.+data TexFormat = LaTeX | ConTeXt | PlainTeX+  deriving (Show, Read, Eq, Typeable)+  -- These names are only captialised so Context doesn't conflict with+  -- lens's Context.++data Surface = Surface+  { _texFormat :: TexFormat -- ^ Format for the PGF commands+  , _command   :: String    -- ^ System command to be called.+  , _arguments :: [String]  -- ^ Auguments for command.+  , _pageSize  :: Maybe (V2 Int -> String)+                            -- ^ Command to change page size from dimensions of image.+                            --   (in bp)+  , _preamble  :: String    -- ^ Preamble for document, should import pgfcore.+  , _beginDoc  :: String    -- ^ Begin document.+  , _endDoc    :: String    -- ^ End document.+  }++makeLensesWith (lensRules & generateSignatures .~ False) ''Surface++-- | Format for the PGF commands.+texFormat :: Lens' Surface TexFormat++-- | System command to call for rendering PDFs for 'OnlineTex'.+command :: Lens' Surface String++-- | List of arguments for the 'command'.+arguments :: Lens' Surface [String]++-- | Preamble for the tex document. This should at least import+--   @pgfcore@.+preamble :: Lens' Surface String++-- | Specify the page size for the tex file.+pageSize :: Lens' Surface (Maybe (V2 Int -> String))++-- | Command to begin the document. (This normally doesn't need to+--   change)+beginDoc :: Lens' Surface String++-- | Command to end the document. (This normally doesn't need to+--   change)+endDoc :: Lens' Surface String++-- Predefined surfaces -------------------------------------------------++-- | Default surface for latex files by calling @pdflatex@.+--+-- ==== __Sample output__+--+-- @+-- 'command': pdflatex+--+-- % 'preamble'+-- \documentclass{article}+-- \usepackage{pgfcore}+-- \pagenumbering{gobble}+--+-- % 'pageSize'+-- \pdfpagewidth=100bp+-- \pdfpageheight=80bp+-- \textheight=80bp+-- \pdfhorigin=-76.6bp+-- \pdfvorigin=-52.8bp+--+-- % 'beginDoc'+-- \begin{document}+--+-- \<Latex pgf code\>+--+-- % 'endDoc'+-- \end{document}+-- @+--+latexSurface :: Surface+latexSurface = Surface+  { _texFormat = LaTeX+  , _command   = "pdflatex"+  , _arguments = []+  , _pageSize  = Just $ \(V2 w h) ->+                 "\\pdfpagewidth=" ++ show w ++ "bp\n"+              ++ "\\pdfpageheight=" ++ show h ++ "bp\n"+              ++ "\\textheight=" ++ show h ++ "bp\n"+              ++ "\\pdfhorigin=-76.6bp\n"+              ++ "\\pdfvorigin=-52.8bp"+  , _preamble  = "\\documentclass{article}\n"+              ++ "\\usepackage{pgfcore}\n"+              ++ "\\pagenumbering{gobble}"+  , _beginDoc  = "\\begin{document}"+  , _endDoc    = "\\end{document}"+  }++-- | Default surface for latex files by calling @pdflatex@.+--+-- ==== __Sample output__+--+-- @+-- 'command': context --pipe --once++-- % 'preamble'+-- \usemodule[pgf]+-- \setuppagenumbering[location=]+--+-- % 'pageSize'+-- \definepapersize[diagram][width=100bp,height=80bp]+-- \setuppapersize[diagram][diagram]+-- \setuplayout+--   [ topspace=0bp+--   , backspace=0bp+--   , header=0bp+--   , footer=0bp+--   , width=100bp+--   , height=80bp+--   ]+--+-- % 'beginDoc'+-- \starttext+--+-- \<Context pgf code\>+--+-- % 'endDoc'+-- \stoptext+-- @+--+contextSurface :: Surface+contextSurface = Surface+  { _texFormat = ConTeXt+  , _command   = "context"+  , _arguments = ["--pipe", "--once"]+  , _pageSize  = Just $ \(V2 w h) ->+                 "\\definepapersize[diagram][width="++ show w ++"bp,height="++ show h ++"bp]\n"+              ++ "\\setuppapersize[diagram][diagram]\n"+              ++ "\\setuplayout\n"+              ++ "  [ topspace=0bp\n"+              ++ "  , backspace=0bp\n"+              ++ "  , header=0bp\n"+              ++ "  , footer=0bp\n"+              ++ "  , width=" ++ show w ++ "bp\n"+              ++ "  , height=" ++ show h ++ "bp\n"+              ++ "  ]"+  , _preamble  = "\\usemodule[pgf]\n" -- pgfcore doesn't work+              ++ "\\setuppagenumbering[location=]"+  , _beginDoc  = "\\starttext"+  , _endDoc    = "\\stoptext"+  }++-- | Default surface for latex files by calling @pdflatex@.+--+-- ==== __Sample output__+--+-- @+-- 'command': pdftex+--+-- % 'preamble'+-- \input eplain+-- \beginpackages+-- \usepackage{color}+-- \endpackages+-- \input pgfcore+-- \def\frac#1#2{{\begingroup #1\endgroup\over #2}}\nopagenumbers+--+-- % 'pageSize'+-- \pdfpagewidth=100bp+-- \pdfpageheight=80bp+-- \pdfhorigin=-20bp+-- \pdfvorigin=0bp+--+-- % 'beginDoc'+--+--+-- <PlainTex pgf code>+--+-- % 'endDoc'+-- \bye+-- @+--+plaintexSurface :: Surface+plaintexSurface = Surface+  { _texFormat = PlainTeX+  , _command   = "pdftex"+  , _arguments = []+  , _pageSize  = Just $ \(V2 w h) ->+                 "\\pdfpagewidth=" ++ show w ++ "bp\n"+              ++ "\\pdfpageheight=" ++ show h ++ "bp\n"+              ++ "\\pdfhorigin=-20bp\n"+              ++ "\\pdfvorigin=0bp"+  , _preamble  = "\\input eplain\n"+              ++ "\\beginpackages\n\\usepackage{color}\n\\endpackages\n"+              ++ "\\input pgfcore\n"+              ++ "\\def\\frac#1#2{{\\begingroup #1\\endgroup\\over #2}}"+              ++ "\\nopagenumbers"+  , _beginDoc  = ""+  , _endDoc    = "\\bye"+  }++-- | Latex is the default surface.+instance Default Surface where+  def = latexSurface++sampleSurfaceOutput :: Surface -> String+sampleSurfaceOutput surf = unlines+  [ "command: " ++ surf ^. command ++ " " ++ unwords (surf ^. arguments)+  , "\n% preamble"+  , surf ^. preamble+  , "\n% pageSize"+  , view _Just $ surf ^. pageSize <*> Just (V2 100 80)+  , "\n% beginDoc"+  , surf ^. beginDoc+  , "\n<" ++ show (surf ^. texFormat) ++ " pgf code>"+  , "\n% endDoc"+  , surf ^. endDoc+  ]++-- OnlineTex functions -------------------------------------------------++-- | Get the result of an OnlineTex using the given surface.+surfOnlineTex :: Surface -> OnlineTex a -> a+surfOnlineTex surf a = unsafePerformIO (surfOnlineTexIO surf a)+{-# NOINLINE surfOnlineTex #-}++-- | Get the result of an OnlineTex using the given surface.+surfOnlineTexIO :: Surface -> OnlineTex a -> IO a+surfOnlineTexIO surf = runOnlineTex (surf^.command) (surf^.arguments) begin+  where+    begin = view strict . toLazyByteString . stringUtf8+          $ surf ^. (preamble <> beginDoc)++-- Hashable instances --------------------------------------------------++instance Hashable TexFormat where+  hashWithSalt s LaTeX    = s `hashWithSalt` (1::Int)+  hashWithSalt s ConTeXt  = s `hashWithSalt` (2::Int)+  hashWithSalt s PlainTeX = s `hashWithSalt` (3::Int)++instance Hashable Surface where+  hashWithSalt s (Surface tf cm ar ps p bd ed)+    = s                    `hashWithSalt`+      tf                   `hashWithSalt`+      cm                   `hashWithSalt`+      ar                   `hashWithSalt`+      ps <*> Just (V2 1 2) `hashWithSalt`+      p                    `hashWithSalt`+      bd                   `hashWithSalt`+      ed+
+ src/Graphics/Rendering/PGF.hs view
@@ -0,0 +1,927 @@+{-# LANGUAGE GADTs                      #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE Rank2Types                 #-}+{-# LANGUAGE TemplateHaskell            #-}+{-# LANGUAGE TupleSections              #-}+{-# LANGUAGE ViewPatterns               #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Graphics.Rendering.PGF+-- Copyright   :  (c) 2015 Christopher Chalmers+-- License     :  BSD-style (see LICENSE)+-- Maintainer  :  diagrams-discuss@googlegroups.com+--+-- Interface to PGF. See the manual http://www.ctan.org/pkg/pgf for details.+--+------------------------------------------------------------------------------+module Graphics.Rendering.PGF+  ( renderWith+  , RenderM+  , Render+  , initialState+  -- * Environments+  , scope+  -- , scopeHeader+  -- , resetState+  -- , scopeFooter+  , epsilon+  -- * Lenses+  -- , fillRule+  , style+  -- * units+  , bp+  , pt+  , mm+  , px+  -- * RenderM commands+  , ln+  , raw+  , rawString+  , pgf+  , bracers+  , brackets+  -- * Paths+  , path+  , trail+  , segment+  , usePath+  , lineTo+  , curveTo+  , moveTo+  , closePath+  , clip+  , stroke+  , fill+  , asBoundingBox+  -- , rectangleBoundingBox+  -- * Strokeing Options+  , setDash+  , setLineWidth+  , setLineCap+  , setLineJoin+  , setMiterLimit+  , setLineColor+  , setLineOpacity+  -- * Fill Options+  , setFillColor+  , setFillRule+  , setFillOpacity+  -- * Transformations+  , setTransform+  , applyTransform+  , baseTransform+  , applyScale+  , resetNonTranslations+  -- * Shading+  , linearGradient+  , radialGradient+  , colorSpec+  , shadePath+  , opacityGroup+  -- * images+  , image+  , embeddedImage+  , embeddedImage'+  -- * Text+  , renderText+  , setTextAlign+  , setTextRotation+  , setFontWeight+  , setFontSlant+  ) where++import           Codec.Compression.Zlib+import           Codec.Picture+import           Control.Monad.RWS+import           Data.ByteString.Builder+import           Data.ByteString.Char8        (ByteString)+import qualified Data.ByteString.Char8        as B (replicate)+import           Data.ByteString.Internal     (fromForeignPtr)+import qualified Data.ByteString.Lazy         as LB+import qualified Data.Foldable                as F (foldMap)+import           Data.List                    (intersperse)+import           Data.Maybe                   (catMaybes)+import           Data.Typeable+import qualified Data.Vector.Storable         as S+import           Numeric++import           Diagrams.Core.Transform+import           Diagrams.Prelude             hiding (Render, image, moveTo,+                                               opacity, opacityGroup, stroke,+                                               (<>))+import           Diagrams.TwoD.Text           (FontSlant (..), FontWeight (..),+                                               TextAlignment (..))++import           Diagrams.Backend.PGF.Surface+++-- * Types, lenses & runners++-- | Render state, mainly to be used for convenience when build, this module+--   only uses the indent properly.+data RenderState n = RenderState+  { _pos        :: P2 n -- ^ Current position+  , _indent     :: Int  -- ^ Current indentation+  , _style      :: Style V2 n+  }++makeLenses ''RenderState++data RenderInfo = RenderInfo+  { _format :: TexFormat+  , _pprint :: Bool+  }++makeLenses ''RenderInfo++-- | Type for render monad.+type RenderM n m = RWS RenderInfo Builder (RenderState n) m++-- | Convenient type for building.+type Render n = RenderM n ()++-- | Starting state for running the builder.+initialState :: (Typeable n, Floating n) => RenderState n+initialState = RenderState+  { _pos        = origin+  , _indent     = 0+  , _style      = lc black mempty -- Until I think of something better:+                                  -- (square 1 # opacity 0.5) doesn't work otherwise+  }++renderWith :: (RealFloat n, Typeable n)+  => Surface -> Bool -> Bool -> V2 n -> Render n -> Builder+renderWith s readable standalone bounds r = builder+  where+    bounds' = fmap (fromInteger . floor) bounds+    (_,builder) = evalRWS r'+                          (RenderInfo (s^.texFormat) readable)+                          initialState+    r' = do+      when standalone $ do+        ln . rawString $ s^.preamble+        maybe (return ())+              (ln . rawString . ($ fmap ceiling bounds'))+              (s^.pageSize)+        ln . rawString $ s^.beginDoc+      picture $ rectangleBoundingBox bounds' >> r+      when standalone $ rawString $ s^.endDoc++-- low level utilities -------------------------------------------------++-- builder functions+raw :: Builder -> Render n+raw = tell+{-# INLINE raw #-}++rawByteString :: ByteString -> Render n+rawByteString = tell . byteString+{-# INLINE rawByteString #-}++rawString :: String -> Render n+rawString = tell . string8+{-# INLINE rawString #-}++pgf :: Builder -> Render n+pgf c = raw $ "\\pgf" <> c+{-# INLINE pgf #-}++rawChar :: Char -> Render n+rawChar = tell . char8+{-# INLINE rawChar #-}++-- | Emit the indentation when 'pprint' is True.+emit :: Render n+emit = do+  pp <- view pprint+  when pp $ do+    tab <- use indent+    rawByteString $ B.replicate tab ' '+{-# INLINE emit #-}++ln :: Render n -> Render n+ln r = do+  emit+  r+  rawChar '\n'+{-# INLINE ln #-}++-- | Wrap a `Render n` in { .. }.+bracers :: Render n -> Render n+bracers r = do+  rawChar '{'+  r+  rawChar '}'+{-# INLINE bracers #-}++bracersBlock :: Render n -> Render n+bracersBlock rs = do+  raw "{\n"+  inBlock rs+  emit+  rawChar '}'++-- | Wrap a `Render n` in [ .. ].+brackets :: Render n -> Render n+brackets r = do+  rawChar '['+  r+  rawChar ']'+{-# INLINE brackets #-}++parens :: Render n -> Render n+parens r = do+  rawChar '('+  r+  rawChar ')'+{-# INLINE parens #-}++-- | Intersperse list of Render ns with commas.+commaIntersperce :: [Render n] -> Render n+commaIntersperce = sequence_ . intersperse (rawChar ',')++-- | Place a Render n in an indented block+inBlock :: Render n -> Render n+inBlock r = do+  indent += 2+  r+  indent -= 2++-- numbers and points --------------------------------------------------++-- | Render a point.+point :: RealFloat n => P2 n -> Render a+point = tuplePoint . unp2++bracerPoint :: RealFloat n => P2 n -> Render a+bracerPoint (P (V2 x y)) = do+  bracers (bp x)+  bracers (bp y)++-- | Render n a tuple as a point.+tuplePoint :: RealFloat n => (n,n) -> Render a+tuplePoint (x,y) = do+  pgf "qpoint"+  bracers (bp x)+  bracers (bp y)++-- | Render n a n to four decimal places.+n :: RealFloat a => a -> Render n+n x = rawString $ showFFloat (Just 4) x ""++-- | Render n length with bp (big point = 1 px at 72 dpi) units.+bp :: RealFloat a => a -> Render n+bp = (>> raw "bp") . n++-- | Render n length with px units.+px :: RealFloat a => a -> Render n+px = (>> raw "px") . n++-- | Render n length with mm units.+mm :: RealFloat a => a -> Render n+mm = (>> raw "mm") . n -- . (*0.35278)++-- | Render n length with pt units.+pt :: RealFloat a => a -> Render n+pt = (>> raw "pt") . n -- . (*1.00375)++-- | ε = 0.0001 is the limit at which lines are no longer stroked.+epsilon :: Fractional n => n+epsilon = 0.0001++-- environments --------------------------------------------------------++picture :: Render n -> Render n+picture r = do+  f <- view format+  ln . raw $ case f of+    LaTeX    -> "\\begin{pgfpicture}"+    ConTeXt  -> "\\startpgfpicture"+    PlainTeX -> "\\pgfpicture"+  inBlock r+  ln . raw $ case f of+    LaTeX    -> "\\end{pgfpicture}"+    ConTeXt  -> "\\stoppgfpicture"+    PlainTeX -> "\\endpgfpicture"++rectangleBoundingBox :: RealFloat n => V2 n -> Render n+rectangleBoundingBox bounds = do+  ln $ do+    pgf "pathrectangle"+    bracers $ pgf "pointorigin"+    bracers $ tuplePoint (unr2 bounds)+  ln $ do+    pgf "usepath"+    bracers $ raw "use as bounding box"++-- | Wrap the rendering in a scope.+scope :: Render n -> Render n+scope r = do+  f <- view format+  ln . raw $ case f of+    LaTeX    -> "\\begin{pgfscope}"+    ConTeXt  -> "\\startpgfscope"+    PlainTeX -> "\\pgfscope"+  inBlock r+  ln . raw $ case f of+    LaTeX    -> "\\end{pgfscope}"+    ConTeXt  -> "\\stoppgfscope"+    PlainTeX -> "\\endpgfscope"++-- opacity groups ------------------------------------------------------++transparencyGroup :: Render n -> Render n+transparencyGroup r = do+  f <- view format+  ln . raw $ case f of+    LaTeX    -> "\\begin{pgftransparencygroup}"+    ConTeXt  -> "\\startpgftransparencygroup"+    PlainTeX -> "\\pgftransparencygroup"+  inBlock r+  ln . raw $ case f of+    LaTeX    -> "\\end{pgftransparencygroup}"+    ConTeXt  -> "\\stoppgftransparencygroup"+    PlainTeX -> "\\endpgftransparencygroup"++opacityGroup :: RealFloat a => a -> Render n -> Render n+opacityGroup x r = scope $ do+  setFillOpacity x+  transparencyGroup r++-- colours -------------------------------------------------------------++texColor :: RealFloat a => a -> a -> a -> Render n+texColor r g b = do+  n r+  rawChar ','+  n g+  rawChar ','+  n b++contextColor :: RealFloat a => a -> a -> a -> Render n+contextColor r g b = do+  raw "r=" >> n r+  rawChar ','+  raw "g=" >> n g+  rawChar ','+  raw "b=" >> n b++-- | Defines an RGB colour with the given name, using the Tex format.+defineColour :: RealFloat a => ByteString -> a -> a -> a -> Render n+defineColour name r g b = do+  f <- view format+  ln $ case f of+    ConTeXt  -> do+      raw "\\definecolor"+      brackets $ rawByteString name+      brackets $ contextColor r g b+    _        -> do+      raw "\\definecolor"+      bracers $ rawByteString name+      bracers $ raw "rgb"+      bracers $ texColor r g b++parensColor :: Color c => c -> Render n+parensColor c = parens $ texColor r g b+  where (r,g,b,_) = colorToSRGBA c+++-- paths ---------------------------------------------------------------++-- | Close the current path.+closePath :: Render n+closePath = ln $ pgf "pathclose"++-- | Move path to point.+moveTo :: RealFloat n => P2 n -> Render n+moveTo v = ln $ do+  pos .= v+  pgf "pathqmoveto"+  bracerPoint v++-- | Move path by vector.+lineTo :: RealFloat n => V2 n -> Render n+lineTo v = ln $ do+  p <- use pos+  let v' = p .+^ v+  pos .= v'+  pgf "pathqlineto"+  bracerPoint v'++-- | Make curved path from vectors.+curveTo :: RealFloat n => V2 n -> V2 n -> V2 n -> Render n+curveTo v2 v3 v4 = ln $ do+  p <- use pos+  let [v2',v3',v4'] = map (p .+^) [v2,v3,v4]+  pos .= v4'+  pgf "pathqcurveto"+  mapM_ bracerPoint [v2', v3', v4']++-- | Stroke the defined path using parameters from current scope.+stroke :: Render n+stroke = ln $ pgf "usepathqstroke"++-- | Fill the defined path using parameters from current scope.+fill :: Render n+fill = ln $ pgf "usepathqfill"++-- | Use the defined path a clip for everything that follows in the current+--   scope. Stacks.+clip :: Render n+clip = ln $ pgf "usepathqclip"++path :: RealFloat n => Path V2 n -> Render n+path (Path trs) = do+  mapM_ renderTrail trs+  where+    renderTrail (viewLoc -> (p, tr)) = do+      moveTo p+      trail tr++trail :: RealFloat n => Trail V2 n -> Render n+trail t = withLine (render' . lineSegments) t+  where+    render' segs = do+      mapM_ segment segs+      when (isLoop t) closePath++segment ::  RealFloat n => Segment Closed V2 n -> Render n+segment (Linear (OffsetClosed v))       = lineTo v+segment (Cubic v1 v2 (OffsetClosed v3)) = curveTo v1 v2 v3++-- | @usePath fill stroke@ combined in one function.+usePath :: Bool -> Bool -> Render n+usePath False False     = return ()+usePath doFill doStroke = ln $ do+  pgf "usepathq"+  when doFill $ raw "fill"+  when doStroke $ raw "stroke"++-- | Uses the current path as the bounding box for whole picture.+asBoundingBox :: Render n+asBoundingBox = ln $ do+  pgf "usepath"+  bracers $ raw "use as bounding box"++-- rectangleBoundingBox :: (n,n) -> Render n+-- rectangleBoundingBox xy = do+--   ln $ do+--     pgf "pathrectangle"+--     bracers $ pgf "pointorigin"+--     bracers $ tuplePoint xy+--   asBoundingBox++-- stroke properties++-- | Sets the line width in current scope. Must be done before stroking.+setLineWidth :: RealFloat n => n -> Render n+setLineWidth w = ln $ do+  pgf "setlinewidth"+  bracers $ bp w++-- | Sets the line cap in current scope. Must be done before stroking.+setLineCap :: LineCap -> Render n+setLineCap cap = ln . pgf $ case cap of+   LineCapButt   -> "setbuttcap"+   LineCapRound  -> "setroundcap"+   LineCapSquare -> "setrectcap"++-- | Sets the line join in current scope. Must be done before stroking.+setLineJoin :: LineJoin -> Render n+setLineJoin lJoin = ln . pgf $ case lJoin of+   LineJoinBevel -> "setbeveljoin"+   LineJoinRound -> "setroundjoin"+   LineJoinMiter -> "setmiterjoin"++-- | Sets the miter limit in the current scope. Must be done before stroking.+setMiterLimit :: RealFloat n => n -> Render n+setMiterLimit l = do+  pgf "setmiterlimit"+  bracers $ bp l++-- stroke parameters ---------------------------------------------------++-- | Sets the dash for the current scope. Must be done before stroking.+setDash :: RealFloat n => Dashing n -> Render n+setDash (Dashing ds offs) = setDash' ds offs++-- \pgfsetdash{{0.5cm}{0.5cm}{0.1cm}{0.2cm}}{0cm}+-- | Takes the dash distances and offset, must be done before stroking.+setDash' :: RealFloat n => [n] -> n -> Render n+setDash' ds off = ln $ do+  pgf "setdash"+  bracers $ mapM_ (bracers . bp) ds+  bracers $ bp off++-- | Sets the stroke colour in current scope. If colour has opacity < 1, the+--   scope opacity is set accordingly. Must be done before stroking.+setLineColor :: (RealFloat a, Color c) => c -> Render a+setLineColor c = do+  defineColour "sc" r g b+  ln $ pgf "setstrokecolor{sc}"+  --+  when (a /= 1) $ setLineOpacity (realToFrac a)+  where+    (r,g,b,a) = colorToSRGBA c++-- | Sets the stroke opacity for the current scope. Should be a value between 0+--   and 1. Must be done  before stroking.+setLineOpacity :: RealFloat n => n -> Render n+setLineOpacity a = ln $ do+  pgf "setstrokeopacity"+  bracers $ n a++-- filling -------------------------------------------------------------++-- | Set the fill rule to winding or even-odd for current scope. Must be done+--   before filling.+setFillRule :: FillRule -> Render n+setFillRule rule = ln $ case rule of+  Winding -> pgf "setnonzerorule"+  EvenOdd -> pgf "seteorule"++-- | Sets the fill colour for current scope. If an alpha colour is used, the+--   fill opacity is set accordingly. Must be done before filling.+setFillColor :: (RealFloat n, Color c) => c -> Render n+setFillColor (colorToSRGBA -> (r,g,b,a)) = do+  defineColour "fc" r g b+  ln $ pgf "setfillcolor{fc}"+  --+  when (a /= 1) $ setFillOpacity (realToFrac a :: Double)++-- | Sets the stroke opacity for the current scope. Should be a value between 0+--   and 1. Must be done  before stroking.+setFillOpacity :: RealFloat a => a -> Render n+setFillOpacity a = ln $ do+  pgf "setfillopacity"+  bracers $ n a++-- transformations -----------------------------------------------------++getMatrix :: Num n => Transformation V2 n -> (n, n, n, n, n, n)+getMatrix t = (a1,a2,b1,b2,c1,c2)+ where+   [a1, a2, b1, b2, c1, c2] = concat $ matrixHomRep t++-- \pgftransformcm{⟨a⟩}{⟨b⟩}{⟨c⟩}{⟨d⟩}{⟨pointa}++-- | Applies a transformation to the current scope. This transformation only+--   effects coordinates and text, not line withs or dash spacing. (See+--   applyDeepTransform). Must be set before the path is used.+applyTransform :: RealFloat n => Transformation V2 n -> Render n+applyTransform t+  | isID      = return ()+  | shiftOnly = ln $ do+      pgf "transformshift"+      bracers p+  | otherwise = ln $ do+    pgf "transformcm"+    mapM_ (bracers . n) [a, b, c, d] >> bracers p+  where+    (a,b,c,d,e,f) = getMatrix t+    p             = tuplePoint (e,f)+    --+    shiftOnly = (a,b,c,d) == (1,0,0,1)+    isID      = shiftOnly && (e,f) == (0,0)++-- | Resets the transform and sets it. Must be set before the path is used.+setTransform :: RealFloat n => Transformation V2 n -> Render n+setTransform t = do+  pgf "settransformentries"+  mapM_ (bracers . n) [a, b, c, d] >> mapM_ (bracers . bp) [e, f]+  where+    (a,b,c,d,e,f) = getMatrix t++applyScale :: RealFloat n => n -> Render n+applyScale s = ln $ do+  pgf "transformscale"+  bracers $ n s++resetNonTranslations :: Render n+resetNonTranslations = ln $ pgf "transformresetnontranslations"++-- | Base transforms are applied by the document reader.+baseTransform :: RealFloat n => Transformation V2 n -> Render n+baseTransform t = ln $ do+  pgf "lowlevel"+  bracers $ setTransform t++-- setShadetransform :: Transformation V2 -> Render n+-- setShadetransform (dropTransl -> t) = do+--   pgf "setadditionalshadetransform"+--   bracersBlock $ applyTransform t++-- shading -------------------------------------------------------------++linearGradient :: RealFloat n => Path V2 n -> LGradient n -> Render n+linearGradient p lg = scope $ do+  path p+  let (stops', t) = calcLinearStops p lg+  ln $ do+    pgf "declarehorizontalshading"+    bracers $ raw "ft"    -- fill texture+    bracers $ raw "100bp" -- gradient is always 100 x 100 square+    bracersBlock $ colorSpec 1 stops'+  clip+  baseTransform t+  useShading $ raw "ft"++-- | Calculate the correct linear stops such that the path is completely+--   filled. PGF doesn't have spread methods so this has to be done+--   manually.+calcLinearStops :: RealFloat n+                => Path V2 n -> LGradient n -> ([GradientStop n], T2 n)+calcLinearStops (Path []) _ = ([], mempty)+calcLinearStops pth (LGradient stops p0 p1 gt sm)+  = (linearStops' x0 x1 stops sm, t <> ft)+  where+    -- Transform such that the transform t origin is start of the+    -- gradient, transform t unitX is the end.+    t = gt+        -- encorperate the start and end points+     <> translation (p0 ^. _Point)+     <> scaling (norm (p1 .-. p0))+     <> rotationTo (dirBetween p1 p0)++    -- Use the inverse transformed path and make the pre-transformed+    -- gradient fit to it. Then when we transform the gradient we know+    -- it'll fit the path.+    p' = transform (inv t) pth+    Just (x0,x1) = extentX p'+    Just (y0,y1) = extentY p'++    -- Final transform to fit the gradient to the path. The origin on+    -- the gradient is its centre so we translate by - V2 50 50 to get+    -- to the lower corner (because of this we set the size of the+    -- gradient to always be 100 x 100 for simplicity). Then scales up+    -- the gradient to cover the path and moves it into position.+    ft = translation (V2 x0 y0) <> scalingV ((*0.01) . abs <$> V2 (x0 - x1) (y0 - y1)) <> translation 50++scalingV :: (Additive v, Fractional n) => v n -> Transformation v n+scalingV v = fromSymmetric $ liftU2 (*) v <-> liftU2 (flip (/)) v++useShading :: Render n -> Render n+useShading nm = ln $ do+  pgf "useshading"+  bracers nm+++_translation :: Lens' (Transformation v n) (v n)+_translation f (Transformation a b v) = f v <&> \v' -> Transformation a b v'++linearStops' :: RealFloat n+             => n -> n -> [GradientStop n] -> SpreadMethod -> [GradientStop n]+linearStops' x0 x1 stops sm =+  GradientStop c1' 0 : filter (inRange . view stopFraction) stops' ++ [GradientStop c2' 100]+  where+    stops' = case sm of+      GradPad     -> over (each . stopFraction) normalise stops+      GradRepeat  -> flip F.foldMap [i0 .. i1] $ \i ->+                       increaseFirst $+                         over (each . stopFraction)+                              (normalise . (+ fromIntegral i))+                              stops+      GradReflect -> flip F.foldMap [i0 .. i1] $ \i ->+                       over (each . stopFraction)+                            (normalise . (+ fromIntegral i))+                            (reverseOdd i stops)++    -- for repeat it sometimes complains if two are exactly the same so+    -- increase the first by a little+    increaseFirst = over (_head . stopFraction) (+0.001)+    reverseOdd i+      | odd i     = reverse . over (each . stopFraction) (1 -)+      | otherwise = id+    i0 = floor x0 :: Int+    i1 = ceiling x1+    c1' = SomeColor $ colourInterp stops' 0+    c2' = SomeColor $ colourInterp stops' 100+    inRange x   = x > 0 && x < 100+    normalise x = 100 * (x - x0) / (x1 - x0)++colourInterp :: RealFloat n => [GradientStop n] -> n -> AlphaColour Double+colourInterp cs0 x = go cs0+  where+    go (GradientStop c1 a : c@(GradientStop c2 b) : cs)+      | x <= a         = toAlphaColour c1+      | x > a && x < b = blend y (toAlphaColour c2) (toAlphaColour c1)+      | otherwise      = go (c : cs)+      where+        y = realToFrac $ (x - a) / (b - a)+    go [GradientStop c2 _] = toAlphaColour c2+    go _ = transparent++radialGradient :: RealFloat n => Path V2 n -> RGradient n -> Render n+radialGradient p rg = scope $ do+  path p+  let (stops', t, p0) = calcRadialStops p rg+  ln $ do+    pgf "declareradialshading"+    bracers $ raw "ft"+    bracers $ point p0+    bracersBlock $ colorSpec 1 stops'+  clip+  baseTransform t+  useShading $ raw "ft"++-- | Calculate the correct linear stops such that the path is completely+--   filled. PGF doesn't have spread methods so this has to be done+--   manually.+calcRadialStops :: RealFloat n+                => Path V2 n -> RGradient n -> ([GradientStop n], T2 n, P2 n)+calcRadialStops (Path []) _ = ([], mempty, origin)+calcRadialStops pth (RGradient stops p0 r0 p1 r1 gt _sm)+  = (stops', t <> ft, P cv)+  where+    cv = tp0 .-. tp1+    tp0 = papply gt p0+    tp1 = papply gt p1+    -- Transform such that the transform t origin is start of the+    -- gradient, transform t unitX is the end.+    t = gt+     <> translation (p1 ^. _Point)+     <> scaling r1++    -- Similar to linear gradients but not so precise, d is a (bad and+    -- probably incorrect) lower bound for the required radius of the+    -- circle to cover the path.+    p' = transform (inv t) pth+    Just (x0,x1) = extentX p'+    Just (y0,y1) = extentY p'+    d = 2 * max (max (abs $ x0 - x1) (abs $ y0 - y1)) (lstop ^. stopFraction)++    -- Adjust for gradient size having radius 100+    ft = scaling 0.01++    -- Stops are scaled to start at r0 and end at r1. The gradient is+    -- extended to d to try to cover the path.+    --+    -- The problem is extending the size of the gradient in this way+    -- affects how the gradient scales if it is off-centre. This needs+    -- to be fixed.+    --+    -- Only the GradPad spread method is supported for now.+    stops' = head stops : over (each . stopFraction) refrac stops ++ [lstop & stopFraction .~ 100*d]+    refrac x = 100 * ((r0 + x * (r1 - r0)) / r1) -- start at r0, end at r1+    lstop = last stops++-- Dirty adjustments for spread methods (PGF doesn't seem to have them).+-- adjustStops :: RealFloat n => [GradientStop n] -> SpreadMethod -> [GradientStop n]+-- adjustStops stops method =+--   case method of+--     GradPad     -> (stopFraction .~ 0) (head stops) : map (stopFraction +~ 1) stops+--                 ++ [(stopFraction +~ 2) (last stops)]+--     GradReflect -> correct . concat . replicate 10+--                  $ [stops, zipWith (\a b -> a & (stopColor .§ b)) stops (reverse stops)]+--     GradRepeat  -> correct . replicate 10 $ stops+  -- where+  --   correct  = ifoldMap (\i -> map (stopFraction +~ (lastStop * fromIntegral i)) )+  --   lastStop = last stops ^. stopFraction++-- (.§) :: Lens s t b b -> s -> s -> t+-- (.§) l a b = b & l #~ (a ^# l)+-- {-# INLINE (.§) #-}++colorSpec :: RealFloat n => n -> [GradientStop n] -> Render n+colorSpec d = mapM_ ln+            . combinePairs+            . intersperse (rawChar ';')+            . map mkColor+  where+    mkColor (GradientStop c sf) = do+      raw "rgb"+      parens $ bp (d*sf)+      raw "="+      parensColor c++combinePairs :: Monad m => [m a] -> [m a]+combinePairs (x1:x2:xs) = (x1 >> x2) : combinePairs xs+combinePairs xs         = xs++shadePath :: RealFloat n => Angle n -> Render n -> Render n+shadePath (view deg -> θ) name = ln $ do+  pgf "shadepath"+  bracers name+  bracers $ n θ++-- external images -----------------------------------------------------++-- \pgfimage[⟨options ⟩]{⟨filename ⟩}++-- | Images are wraped in a \pgftext.+image :: RealFloat n => DImage n External -> Render n+image (DImage (ImageRef ref) w h t2) = scope $ do+  applyTransform t2+  ln $ do+    pgf "text"+    bracers $ do+      pgf "image"+      brackets $ do+        raw "width=" >> bp (fromIntegral w :: Double)+        rawChar ','+        raw "height=" >> bp (fromIntegral h :: Double)+      bracers $ rawString ref++-- embedded images -----------------------------------------------------++embeddedImage :: RealFloat n => DImage n Embedded -> Render n+embeddedImage (DImage (ImageRaster (ImageRGB8 img)) w h t) =+  embeddedImage' (hexImage img) w h t+  -- TODO: Support more formats (like grey scale and alpha channels)+embeddedImage _ = error "Unsupported embedded image. Only ImageRGB8 is currently supported."++-- | Convert an 'Image' to a zlib compressed lazy 'ByteString' of the+--   raw image data. This is a suitable format for an embedded PDF image+--   stream.+hexImage :: Image PixelRGB8 -> LB.ByteString+hexImage (imageData -> v) = compress $ LB.fromStrict bs+  where+    bs         = fromForeignPtr p i nn+    (p, i, nn) = S.unsafeToForeignPtr v++embeddedImage' :: RealFloat n => LB.ByteString -> Int -> Int -> T2 n -> Render n+embeddedImage' img w h t = scope $ do+  baseTransform t+  ln $ raw "\\immediate\\pdfliteral{"+  rawLn "q" -- save state++  -- Scale the image to it's actual size and translate so the origin is+  -- at the centre.+  rawLn $ s w <> " 0 0 " <> s h <> " -" <> half w <> " -" <> half h <> " cm"+  rawLn "BI"           -- begin image+  rawLn $ "/W " <> s w -- width in pixels+  rawLn $ "/H " <> s h -- height in pixels+  rawLn "/CS /RGB"     -- RGB colour space+  rawLn "/BPC 8"       -- 8 bits per component++  -- Filters for the encoded image:+  --   ASCIIHexDecode -- decode from hexadecimal to binary+  --   FlateDecode    -- decompress using zlib deflate compression+  rawLn "/F [/AHx /Fl]"++  -- We use hex format for the image data so tex can output it without+  -- any problems. Base85 might be possible and would be 2-3x smaller+  -- but there's some problem chars tex complains about. Base64 would+  -- be ideal but the pdf spec doesn't seem to support it.+  --+  -- This is an inline image which is only really suitable for small+  -- images. An XObject might be more appropriate. See+  -- http://partners.adobe.com/public/developer/en/pdf/PDFReference.pdf+  -- for more information.+  rawLn "ID" -- image data+  rawLn $ hexChunk img <> char8 '>'+  rawLn "EI" -- end image+  rawLn "Q"  -- restore state+  rawLn "}"+    where+      rawLn r = raw r >> rawChar '\n'+      s       = intDec+      half x  = s (x `div` 2) <> if odd x then ".5" else mempty++-- | Insert hex encode and add a newline every 80 chars. This is useful for+--   readable output and stopping tex from choking when streaming. Note+--   that new-lines and spaces are ignored with the hex decode filter.+hexChunk :: LB.ByteString -> Builder+hexChunk (LB.splitAt 40 -> (a,b))+  | LB.null b = lazyByteStringHex a+  | otherwise = lazyByteStringHex a <> char8 '\n' <> hexChunk b++-- text ----------------------------------------------------------------++renderText :: [Render n] -> Render n -> Render n+renderText ops txt = ln $ do+  pgf "text"+  brackets . commaIntersperce $ ops+  bracers txt++-- | Returns a list of values to be put in square brackets like+--   @\pgftext[left,top]{txt}@.+setTextAlign :: RealFloat n => TextAlignment n -> [Render n]+setTextAlign a = case a of+  BaselineText         -> [raw "base", raw "left"]+  BoxAlignedText xt yt -> catMaybes [xt', yt']+    where+      xt' | xt > 0.75 = Just $ raw "right"+          | xt < 0.25 = Just $ raw "left"+          | otherwise = Nothing+      yt' | yt > 0.75 = Just $ raw "top"+          | yt < 0.25 = Just $ raw "bottom"+          | otherwise = Nothing++setTextRotation :: RealFloat n => Angle n -> [Render n]+setTextRotation a = case a^.deg of+  0 -> []+  θ -> [raw "rotate=" >> n θ]++-- | Set the font weight by rendering @\bf @. Nothing is done for normal+--   weight.+setFontWeight :: FontWeight -> Render n+setFontWeight FontWeightNormal = return ()+setFontWeight FontWeightBold   = raw "\\bf "++-- | Set the font slant by rendering @\bf @. Nothing is done for normal weight.+setFontSlant :: FontSlant -> Render n+setFontSlant FontSlantNormal  = return ()+setFontSlant FontSlantItalic  = raw "\\it "+setFontSlant FontSlantOblique = raw "\\sl "