[
  {
    "path": ".github/workflows/CI.yml",
    "content": "name: CI\n\non:\n  pull_request:\n    branches:\n      - master\n  push:\n    branches:\n      - master\n    tags: '*'\n\njobs:\n  test:\n    name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        version:\n          - '1.10' \n          - '1'\n        os:\n          - ubuntu-latest\n          - macOS-latest\n          - windows-latest\n        arch:\n          - x64\n    steps:\n      - uses: actions/checkout@v6\n      - uses: julia-actions/setup-julia@v2\n        with:\n          version: ${{ matrix.version }}\n          arch: ${{ matrix.arch }}\n      - uses: julia-actions/cache@v2\n      - uses: julia-actions/julia-buildpkg@v1\n      - uses: julia-actions/julia-runtest@v1\n      - uses: julia-actions/julia-processcoverage@v1\n      - uses: codecov/codecov-action@v5\n        with:\n          files: lcov.info\n          token: ${{ secrets.CODECOV_TOKEN }}\n\n  docs:\n    name: Documentation\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: julia-actions/setup-julia@v2\n        with:\n          version: '1'\n      - run: |\n          julia --project=docs -e '\n            using Pkg\n            Pkg.develop(PackageSpec(path=pwd()))\n            Pkg.instantiate()'\n      - run: julia --project=docs docs/make.jl\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}\n\n"
  },
  {
    "path": ".github/workflows/CompatHelper.yml",
    "content": "name: CompatHelper\n\non:\n  schedule:\n    - cron: '0 0 * * 0'\n  issues:\n    types: [opened, reopened]\n\njobs:\n  build:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        julia-version: [1.2.0]\n        julia-arch: [x86]\n        os: [ubuntu-latest]\n    steps:\n      - uses: julia-actions/setup-julia@latest\n        with:\n          version: ${{ matrix.julia-version }}\n      - name: Pkg.add(\"CompatHelper\")\n        run: julia -e 'using Pkg; Pkg.add(\"CompatHelper\")'\n      - name: CompatHelper.main()\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: julia -e 'using CompatHelper; CompatHelper.main()'\n"
  },
  {
    "path": ".github/workflows/TagBot.yml",
    "content": "name: TagBot\non:\n  issue_comment:\n    types:\n      - created\n  workflow_dispatch:\njobs:\n  TagBot:\n    if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: JuliaRegistries/TagBot@v1\n        with:\n          token: ${{ secrets.TAGBOT_PAT }}\n          ssh: ${{ secrets.DOCUMENTER_KEY }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n*swp\n\n# System-specific files and directories generated by the BinaryProvider and BinDeps packages\n# They contain absolute paths specific to the host computer, and so should not be committed\ndeps/deps.jl\ndeps/build.log\ndeps/downloads/\ndeps/usr/\ndeps/src/\n\n# Build artifacts for creating documentation generated by the Documenter package\ndocs/build/\ndocs/site/\n\ntest/output\n\n# File generated by Pkg, the package manager, based on a corresponding Project.toml\n# It records a fixed state of all packages used by the project. As such, it should not be\n# committed for packages, but should be committed for applications that require a static\n# environment.\nManifest.toml\n\n# System files generated by MacOS.\n.DS_Store\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Compose is licensed under the MIT License:\n\n> Copyright (c) 2012--2015: Daniel C. Jones and other contributors\n>\n> Permission is hereby granted, free of charge, to any person obtaining\n> a copy of this software and associated documentation files (the\n> \"Software\"), to deal in the Software without restriction, including\n> without limitation the rights to use, copy, modify, merge, publish,\n> distribute, sublicense, and/or sell copies of the Software, and to\n> permit persons to whom the Software is furnished to do so, subject to\n> the following conditions:\n>\n> The above copyright notice and this permission notice shall be\n> included in all copies or substantial portions of the Software.\n>\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Project.toml",
    "content": "name = \"Compose\"\nuuid = \"a81c6b42-2e10-5240-aca2-a61377ecd94b\"\nversion = \"0.9.6\"\n\n[deps]\nBase64 = \"2a0f44e3-6c83-55bd-87e4-b1978d98bd5f\"\nColors = \"5ae59095-9a9b-59fe-a467-6f913c188581\"\nDataStructures = \"864edb3b-99cc-5e75-8d2d-829cb0a9cfe8\"\nDates = \"ade2ca70-3891-5945-98fb-dc099432e06a\"\nIterTools = \"c8e1da08-722c-5040-9ed9-7db0dc04731e\"\nJSON = \"682c06a0-de6a-54ab-a142-c8b1cf79cde6\"\nLinearAlgebra = \"37e2e46d-f89d-539d-b4ee-838fcccc9c8e\"\nMeasures = \"442fdcdd-2543-5da2-b0f3-8c86c306513e\"\nPrintf = \"de0858da-6303-5e67-8744-51eddeeeb8d7\"\nRandom = \"9a3f8284-a2c9-5f02-9a11-845980a1fd5c\"\nRequires = \"ae029012-a4dd-5104-9daa-d747884805df\"\nStatistics = \"10745b16-79ce-11e8-11f9-7d13ad32a3b2\"\nUUIDs = \"cf7118a7-6976-5b1a-9a39-7adc72f591a4\"\n\n[compat]\nColors = \"0.9, 0.10, 0.11, 0.12, 0.13\"\nDataStructures = \"0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19\"\nIterTools = \"1\"\nJSON = \"0.18, 0.19, 0.20, 0.21\"\nMeasures = \"0.3\"\nRequires = \"0.5, 1.0\"\njulia = \"1\"\n"
  },
  {
    "path": "README.md",
    "content": "# Compose!\n\n[![][docs-latest-img]][docs-latest-url] [![][travis-img]][travis-url] [![][codecov-img]][codecov-url]\n\nCompose is a vector graphics library for Julia.\nIt forms the basis for the statistical graphics system\n[Gadfly](https://github.com/GiovineItalia/Gadfly.jl).\n\n## Synopsis\n\nUnlike most vector graphics libraries, Compose is thoroughly declarative. Rather\nthan issue a sequence of drawing commands, graphics are formed by sticking\nvarious things together and then letting the library figure out how to draw it.\nThe \"things\" in this case fall one of three types: Property, Form, and Canvas.\n\"Sticking together\" is primary achieved with the `compose` function.\n\nThe semantics of composition are fairly simple, and once grasped provide a\nconsistent and powerful means of building vector graphics.\n\n## Documentation\n\n- [**LATEST**][docs-latest-url] &mdash; *in-development version of the documentation.*\n\n[docs-latest-img]: https://img.shields.io/badge/docs-latest-blue.svg\n[docs-latest-url]: https://giovineitalia.github.io/Compose.jl/latest\n\n[travis-img]: http://img.shields.io/travis/GiovineItalia/Compose.jl.svg\n[travis-url]: https://travis-ci.org/GiovineItalia/Compose.jl\n[codecov-img]: https://codecov.io/gh/GiovineItalia/Compose.jl/branch/master/graph/badge.svg\n[codecov-url]: https://codecov.io/gh/GiovineItalia/Compose.jl\n"
  },
  {
    "path": "TODO.md",
    "content": "\n* Coherent hstack and vstack functions.\n* Documentation!!!\n* Functions for arranging canvases in, e.g. grids.\n* Embedding fonts in SVGs.\n\n"
  },
  {
    "path": "deps/glyphsize.c",
    "content": "\n/* Use freetype to read a font and dump typeface name and glyph sizes for\n * printable ascii characters into an easily parsible JSON format. */\n\n#include <stdio.h>\n\n#include <ft2build.h>\n#include FT_FREETYPE_H\n\n\n/* Write a font's glyph extents to stdout. */\nvoid dumpfont(FT_Library library, const char* fn)\n{\n    FT_Error error;\n    FT_Face face;\n\n    int face_index;\n    int num_faces = 1;\n\n    for (face_index = 0; face_index < num_faces; ++face_index) {\n        error = FT_New_Face(library, fn, face_index, &face);\n\n        if (error) {\n            fprintf(stderr, \"Error reading font from %s. (%d)\\n\", fn, (int) error);\n            exit(1);\n        }\n\n        num_faces = face->num_faces;\n\n        /* set dpi to micrometer per inch, so sizes are reported in micrometers. */\n        const FT_UInt dpi = 25400;\n        error = FT_Set_Char_Size(face, 0, 12*64, dpi, dpi);\n        if (error) {\n            fprintf(stderr, \"Error setting font size.\\n\");\n            exit(1);\n        }\n\n        if (strcmp(face->style_name, \"Regular\") == 0) {\n            printf(\"\\\"%s\\\"\", face->family_name);\n        }\n        else {\n            printf(\"\\\"%s %s\\\"\", face->family_name, face->style_name);\n        }\n        printf(\": {\\n  \\\"widths\\\": {\\n  \");\n\n        char c;\n        FT_UInt i;\n        FT_Pos max_height = 0;\n        for (c = 0x20; c <= 0x7e; ++c) {\n            i = FT_Get_Char_Index(face, c);\n            error = FT_Load_Glyph(face, i, FT_LOAD_DEFAULT);\n            if (error) {\n                fprintf(stderr, \"Error loading glyph for '%c'.\", c);\n                exit(1);\n            }\n\n            if (face->glyph->metrics.height > max_height) {\n                max_height = face->glyph->metrics.height;\n            }\n\n            printf(\"  \\\"\");\n            if (c == '\"' || c == '\\\\') putchar('\\\\');\n            printf(\"%c\\\": \", c);\n            if (c != '\"' && c != '\\\\') putchar(' ');\n            printf(\"%5.2f\", (double) face->glyph->advance.x / 64.0 / 1000.0);\n            if (c != 0x7e) putchar(',');\n            if ((c - 0x20 + 1) % 5 == 0) {\n                printf(\"\\n  \");\n            }\n        }\n        printf(\"},\\n  \\\"height\\\": %0.2f\\n}\", (double) max_height / 64.0 / 1000.0);\n        FT_Done_Face(face);\n\n        if (face_index + 1 < num_faces) puts(\",\");\n    }\n}\n\n\nint main(int argc, char* argv[])\n{\n    if (argc < 2) {\n        fprintf(stderr, \"Usage: glyphsize fontfile [fontfile2...] > stuff.json\\n\");\n        return 1;\n    }\n\n    FT_Library library;\n    FT_Error error;\n\n    error = FT_Init_FreeType(&library);\n    if (error) {\n        fprintf(stderr, \"Error initalizing freetype.\\n\");\n        return 1;\n    }\n\n    puts(\"{\");\n    int i;\n    for (i = 1; i < argc; ++i) {\n        dumpfont(library, argv[i]);\n        if (i != argc - 1) puts(\",\");\n    }\n    puts(\"}\");\n\n    FT_Done_FreeType(library);\n\n    return 0;\n}\n"
  },
  {
    "path": "deps/glyphsize.json",
    "content": "{\n\"Helvetica Neue Bold\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.18,  \"\\\"\":  1.96,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   4.23,  \"&\":   2.90,  \"'\":   1.18,  \"(\":   1.25,  \")\":   1.25,\n    \"*\":   1.72,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.72,  \".\":   1.18,\n    \"/\":   1.57,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.35,  \"@\":   3.39,  \"A\":   2.90,  \"B\":   2.98,\n    \"C\":   3.14,  \"D\":   3.14,  \"E\":   2.74,  \"F\":   2.51,  \"G\":   3.21,\n    \"H\":   3.14,  \"I\":   1.25,  \"J\":   2.35,  \"K\":   3.06,  \"L\":   2.51,\n    \"M\":   3.84,  \"N\":   3.14,  \"O\":   3.29,  \"P\":   2.82,  \"Q\":   3.29,\n    \"R\":   3.06,  \"S\":   2.75,  \"T\":   2.59,  \"U\":   3.14,  \"V\":   2.67,\n    \"W\":   4.00,  \"X\":   2.82,  \"Y\":   2.82,  \"Z\":   2.74,  \"[\":   1.41,\n    \"\\\\\":  1.57,  \"]\":   1.41,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   1.10,\n    \"a\":   2.43,  \"b\":   2.59,  \"c\":   2.43,  \"d\":   2.59,  \"e\":   2.43,\n    \"f\":   1.41,  \"g\":   2.59,  \"h\":   2.51,  \"i\":   1.09,  \"j\":   1.18,\n    \"k\":   2.43,  \"l\":   1.09,  \"m\":   3.84,  \"n\":   2.51,  \"o\":   2.59,\n    \"p\":   2.59,  \"q\":   2.59,  \"r\":   1.65,  \"s\":   2.27,  \"t\":   1.49,\n    \"u\":   2.51,  \"v\":   2.20,  \"w\":   3.45,  \"x\":   2.27,  \"y\":   2.20,\n    \"z\":   2.20,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.10,  \"\\\"\":  1.80,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   4.23,  \"&\":   2.67,  \"'\":   1.18,  \"(\":   1.10,  \")\":   1.10,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.65,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.35,  \"@\":   3.39,  \"A\":   2.74,  \"B\":   2.90,\n    \"C\":   3.06,  \"D\":   2.98,  \"E\":   2.59,  \"F\":   2.43,  \"G\":   3.21,\n    \"H\":   3.06,  \"I\":   1.10,  \"J\":   2.20,  \"K\":   2.82,  \"L\":   2.35,\n    \"M\":   3.69,  \"N\":   3.06,  \"O\":   3.22,  \"P\":   2.74,  \"Q\":   3.22,\n    \"R\":   2.90,  \"S\":   2.74,  \"T\":   2.43,  \"U\":   3.06,  \"V\":   2.59,\n    \"W\":   3.92,  \"X\":   2.59,  \"Y\":   2.74,  \"Z\":   2.59,  \"[\":   1.10,\n    \"\\\\\":  1.41,  \"]\":   1.10,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.94,\n    \"a\":   2.27,  \"b\":   2.51,  \"c\":   2.27,  \"d\":   2.51,  \"e\":   2.27,\n    \"f\":   1.25,  \"g\":   2.43,  \"h\":   2.35,  \"i\":   0.94,  \"j\":   0.94,\n    \"k\":   2.20,  \"l\":   0.94,  \"m\":   3.61,  \"n\":   2.35,  \"o\":   2.43,\n    \"p\":   2.51,  \"q\":   2.51,  \"r\":   1.41,  \"s\":   2.12,  \"t\":   1.33,\n    \"u\":   2.35,  \"v\":   2.12,  \"w\":   3.21,  \"x\":   2.19,  \"y\":   2.12,\n    \"z\":   2.03,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue UltraLight\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   0.78,  \"\\\"\":  1.18,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   2.90,  \"&\":   2.43,  \"'\":   0.71,  \"(\":   0.94,  \")\":   0.94,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.12,  \"@\":   3.39,  \"A\":   2.43,  \"B\":   2.59,\n    \"C\":   2.90,  \"D\":   2.74,  \"E\":   2.27,  \"F\":   2.04,  \"G\":   3.06,\n    \"H\":   2.74,  \"I\":   0.54,  \"J\":   1.96,  \"K\":   2.51,  \"L\":   2.04,\n    \"M\":   3.30,  \"N\":   2.74,  \"O\":   3.05,  \"P\":   2.43,  \"Q\":   3.05,\n    \"R\":   2.59,  \"S\":   2.59,  \"T\":   2.12,  \"U\":   2.67,  \"V\":   2.28,\n    \"W\":   3.68,  \"X\":   2.20,  \"Y\":   2.28,  \"Z\":   2.12,  \"[\":   0.94,\n    \"\\\\\":  1.41,  \"]\":   0.94,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.55,\n    \"a\":   2.04,  \"b\":   2.27,  \"c\":   2.12,  \"d\":   2.27,  \"e\":   2.12,\n    \"f\":   0.86,  \"g\":   2.20,  \"h\":   2.12,  \"i\":   0.55,  \"j\":   0.55,\n    \"k\":   1.88,  \"l\":   0.55,  \"m\":   3.29,  \"n\":   2.12,  \"o\":   2.20,\n    \"p\":   2.27,  \"q\":   2.27,  \"r\":   1.10,  \"s\":   1.96,  \"t\":   1.02,\n    \"u\":   2.12,  \"v\":   1.72,  \"w\":   2.90,  \"x\":   1.72,  \"y\":   1.72,\n    \"z\":   1.72,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Italic\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.10,  \"\\\"\":  1.80,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.92,  \"&\":   2.67,  \"'\":   1.18,  \"(\":   1.10,  \")\":   1.10,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.65,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.35,  \"@\":   3.39,  \"A\":   2.82,  \"B\":   2.90,\n    \"C\":   3.06,  \"D\":   2.98,  \"E\":   2.59,  \"F\":   2.43,  \"G\":   3.21,\n    \"H\":   3.06,  \"I\":   1.10,  \"J\":   2.20,  \"K\":   2.82,  \"L\":   2.35,\n    \"M\":   3.68,  \"N\":   3.06,  \"O\":   3.21,  \"P\":   2.74,  \"Q\":   3.21,\n    \"R\":   2.90,  \"S\":   2.74,  \"T\":   2.43,  \"U\":   3.06,  \"V\":   2.59,\n    \"W\":   3.92,  \"X\":   2.59,  \"Y\":   2.59,  \"Z\":   2.59,  \"[\":   1.10,\n    \"\\\\\":  1.41,  \"]\":   1.10,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.94,\n    \"a\":   2.20,  \"b\":   2.51,  \"c\":   2.27,  \"d\":   2.51,  \"e\":   2.27,\n    \"f\":   1.25,  \"g\":   2.43,  \"h\":   2.35,  \"i\":   0.94,  \"j\":   0.94,\n    \"k\":   2.04,  \"l\":   0.94,  \"m\":   3.61,  \"n\":   2.35,  \"o\":   2.43,\n    \"p\":   2.51,  \"q\":   2.51,  \"r\":   1.41,  \"s\":   2.04,  \"t\":   1.33,\n    \"u\":   2.35,  \"v\":   2.04,  \"w\":   3.21,  \"x\":   2.04,  \"y\":   2.04,\n    \"z\":   1.88,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Light\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.02,  \"\\\"\":  1.57,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   2.59,  \"'\":   1.18,  \"(\":   1.02,  \")\":   1.02,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.57,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.27,  \"@\":   3.39,  \"A\":   2.67,  \"B\":   2.82,\n    \"C\":   2.98,  \"D\":   2.90,  \"E\":   2.51,  \"F\":   2.27,  \"G\":   3.14,\n    \"H\":   2.98,  \"I\":   0.94,  \"J\":   2.12,  \"K\":   2.74,  \"L\":   2.27,\n    \"M\":   3.48,  \"N\":   2.98,  \"O\":   3.14,  \"P\":   2.67,  \"Q\":   3.14,\n    \"R\":   2.82,  \"S\":   2.67,  \"T\":   2.35,  \"U\":   2.90,  \"V\":   2.51,\n    \"W\":   3.84,  \"X\":   2.43,  \"Y\":   2.59,  \"Z\":   2.43,  \"[\":   1.02,\n    \"\\\\\":  1.41,  \"]\":   1.02,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.78,\n    \"a\":   2.20,  \"b\":   2.43,  \"c\":   2.20,  \"d\":   2.43,  \"e\":   2.20,\n    \"f\":   1.10,  \"g\":   2.35,  \"h\":   2.27,  \"i\":   0.78,  \"j\":   0.78,\n    \"k\":   2.12,  \"l\":   0.78,  \"m\":   3.53,  \"n\":   2.27,  \"o\":   2.35,\n    \"p\":   2.43,  \"q\":   2.43,  \"r\":   1.33,  \"s\":   2.04,  \"t\":   1.25,\n    \"u\":   2.27,  \"v\":   1.96,  \"w\":   3.14,  \"x\":   2.04,  \"y\":   1.96,\n    \"z\":   1.96,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue UltraLight Italic\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   0.78,  \"\\\"\":  1.18,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   2.90,  \"&\":   2.43,  \"'\":   0.71,  \"(\":   0.94,  \")\":   0.94,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.12,  \"@\":   3.39,  \"A\":   2.43,  \"B\":   2.59,\n    \"C\":   2.90,  \"D\":   2.82,  \"E\":   2.20,  \"F\":   2.04,  \"G\":   3.06,\n    \"H\":   2.74,  \"I\":   0.63,  \"J\":   1.96,  \"K\":   2.51,  \"L\":   2.04,\n    \"M\":   3.37,  \"N\":   2.82,  \"O\":   3.06,  \"P\":   2.43,  \"Q\":   3.06,\n    \"R\":   2.51,  \"S\":   2.59,  \"T\":   2.12,  \"U\":   2.74,  \"V\":   2.35,\n    \"W\":   3.76,  \"X\":   2.20,  \"Y\":   2.20,  \"Z\":   2.12,  \"[\":   0.94,\n    \"\\\\\":  1.41,  \"]\":   0.94,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.55,\n    \"a\":   2.04,  \"b\":   2.27,  \"c\":   2.12,  \"d\":   2.27,  \"e\":   2.12,\n    \"f\":   0.86,  \"g\":   2.20,  \"h\":   2.12,  \"i\":   0.55,  \"j\":   0.55,\n    \"k\":   1.88,  \"l\":   0.55,  \"m\":   3.29,  \"n\":   2.12,  \"o\":   2.20,\n    \"p\":   2.27,  \"q\":   2.27,  \"r\":   1.10,  \"s\":   1.96,  \"t\":   1.02,\n    \"u\":   2.12,  \"v\":   1.72,  \"w\":   2.90,  \"x\":   1.72,  \"y\":   1.72,\n    \"z\":   1.72,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Condensed Black\": {\n  \"widths\": {\n    \" \":   1.10,  \"!\":   1.33,  \"\\\"\":  2.04,  \"#\":   2.20,  \"$\":   2.20,\n    \"%\":   3.29,  \"&\":   2.59,  \"'\":   1.10,  \"(\":   1.33,  \")\":   1.33,\n    \"*\":   1.72,  \"+\":   2.54,  \",\":   1.10,  \"-\":   1.56,  \".\":   1.10,\n    \"/\":   1.41,  \"0\":   2.20,  \"1\":   2.20,  \"2\":   2.20,  \"3\":   2.20,\n    \"4\":   2.20,  \"5\":   2.20,  \"6\":   2.20,  \"7\":   2.20,  \"8\":   2.20,\n    \"9\":   2.20,  \":\":   1.10,  \";\":   1.10,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.12,  \"@\":   3.39,  \"A\":   2.35,  \"B\":   2.43,\n    \"C\":   2.27,  \"D\":   2.43,  \"E\":   2.12,  \"F\":   2.04,  \"G\":   2.35,\n    \"H\":   2.43,  \"I\":   1.10,  \"J\":   2.04,  \"K\":   2.35,  \"L\":   1.96,\n    \"M\":   3.21,  \"N\":   2.51,  \"O\":   2.35,  \"P\":   2.27,  \"Q\":   2.35,\n    \"R\":   2.43,  \"S\":   2.27,  \"T\":   1.96,  \"U\":   2.35,  \"V\":   2.20,\n    \"W\":   3.29,  \"X\":   2.27,  \"Y\":   2.20,  \"Z\":   2.04,  \"[\":   1.33,\n    \"\\\\\":  1.41,  \"]\":   1.33,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   1.10,\n    \"a\":   2.12,  \"b\":   2.20,  \"c\":   2.04,  \"d\":   2.20,  \"e\":   2.04,\n    \"f\":   1.33,  \"g\":   2.20,  \"h\":   2.20,  \"i\":   1.09,  \"j\":   1.09,\n    \"k\":   2.12,  \"l\":   1.09,  \"m\":   3.29,  \"n\":   2.20,  \"o\":   2.12,\n    \"p\":   2.20,  \"q\":   2.20,  \"r\":   1.49,  \"s\":   1.96,  \"t\":   1.33,\n    \"u\":   2.20,  \"v\":   1.96,  \"w\":   3.13,  \"x\":   2.04,  \"y\":   1.96,\n    \"z\":   1.88,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 3.79\n},\n\"Helvetica Neue Condensed Bold\": {\n  \"widths\": {\n    \" \":   1.02,  \"!\":   1.25,  \"\\\"\":  1.96,  \"#\":   2.03,  \"$\":   2.03,\n    \"%\":   3.29,  \"&\":   2.51,  \"'\":   1.10,  \"(\":   1.25,  \")\":   1.25,\n    \"*\":   1.65,  \"+\":   2.54,  \",\":   1.02,  \"-\":   1.57,  \".\":   1.02,\n    \"/\":   1.41,  \"0\":   2.03,  \"1\":   2.03,  \"2\":   2.03,  \"3\":   2.03,\n    \"4\":   2.03,  \"5\":   2.03,  \"6\":   2.03,  \"7\":   2.03,  \"8\":   2.03,\n    \"9\":   2.03,  \":\":   1.02,  \";\":   1.02,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.04,  \"@\":   3.39,  \"A\":   2.35,  \"B\":   2.35,\n    \"C\":   2.27,  \"D\":   2.43,  \"E\":   2.04,  \"F\":   1.96,  \"G\":   2.35,\n    \"H\":   2.35,  \"I\":   1.09,  \"J\":   1.96,  \"K\":   2.27,  \"L\":   1.96,\n    \"M\":   3.13,  \"N\":   2.43,  \"O\":   2.35,  \"P\":   2.20,  \"Q\":   2.35,\n    \"R\":   2.35,  \"S\":   2.20,  \"T\":   2.03,  \"U\":   2.28,  \"V\":   2.20,\n    \"W\":   3.22,  \"X\":   2.27,  \"Y\":   2.20,  \"Z\":   2.04,  \"[\":   1.33,\n    \"\\\\\":  1.41,  \"]\":   1.33,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.94,\n    \"a\":   2.04,  \"b\":   2.12,  \"c\":   1.96,  \"d\":   2.12,  \"e\":   1.96,\n    \"f\":   1.25,  \"g\":   2.12,  \"h\":   2.12,  \"i\":   1.02,  \"j\":   1.02,\n    \"k\":   2.12,  \"l\":   1.02,  \"m\":   3.21,  \"n\":   2.12,  \"o\":   2.03,\n    \"p\":   2.12,  \"q\":   2.12,  \"r\":   1.41,  \"s\":   1.88,  \"t\":   1.25,\n    \"u\":   2.12,  \"v\":   1.88,  \"w\":   2.98,  \"x\":   1.96,  \"y\":   1.88,\n    \"z\":   1.80,  \"{\":   1.33,  \"|\":   0.94,  \"}\":   1.33,  \"~\":   2.54\n  },\n  \"height\": 3.79\n},\n\"Helvetica Neue Bold Italic\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.25,  \"\\\"\":  2.04,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   4.08,  \"&\":   2.90,  \"'\":   1.18,  \"(\":   1.25,  \")\":   1.25,\n    \"*\":   1.72,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.72,  \".\":   1.18,\n    \"/\":   1.65,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.43,  \"@\":   3.39,  \"A\":   2.90,  \"B\":   3.06,\n    \"C\":   3.14,  \"D\":   3.14,  \"E\":   2.82,  \"F\":   2.51,  \"G\":   3.21,\n    \"H\":   3.14,  \"I\":   1.25,  \"J\":   2.35,  \"K\":   3.06,  \"L\":   2.43,\n    \"M\":   3.84,  \"N\":   3.14,  \"O\":   3.29,  \"P\":   2.82,  \"Q\":   3.29,\n    \"R\":   3.06,  \"S\":   2.74,  \"T\":   2.59,  \"U\":   3.14,  \"V\":   2.67,\n    \"W\":   4.00,  \"X\":   2.82,  \"Y\":   2.74,  \"Z\":   2.74,  \"[\":   1.41,\n    \"\\\\\":  1.65,  \"]\":   1.41,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   1.10,\n    \"a\":   2.43,  \"b\":   2.59,  \"c\":   2.35,  \"d\":   2.59,  \"e\":   2.43,\n    \"f\":   1.49,  \"g\":   2.59,  \"h\":   2.59,  \"i\":   1.10,  \"j\":   1.10,\n    \"k\":   2.35,  \"l\":   1.10,  \"m\":   3.84,  \"n\":   2.59,  \"o\":   2.51,\n    \"p\":   2.59,  \"q\":   2.59,  \"r\":   1.65,  \"s\":   2.20,  \"t\":   1.57,\n    \"u\":   2.59,  \"v\":   2.20,  \"w\":   3.45,  \"x\":   2.20,  \"y\":   2.20,\n    \"z\":   2.12,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Light Italic\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.18,  \"\\\"\":  1.57,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.53,  \"&\":   2.59,  \"'\":   1.18,  \"(\":   1.10,  \")\":   1.10,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.57,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.27,  \"@\":   3.39,  \"A\":   2.67,  \"B\":   2.82,\n    \"C\":   2.98,  \"D\":   2.90,  \"E\":   2.43,  \"F\":   2.27,  \"G\":   3.14,\n    \"H\":   2.98,  \"I\":   0.94,  \"J\":   2.12,  \"K\":   2.74,  \"L\":   2.27,\n    \"M\":   3.61,  \"N\":   2.98,  \"O\":   3.14,  \"P\":   2.67,  \"Q\":   3.14,\n    \"R\":   2.74,  \"S\":   2.67,  \"T\":   2.35,  \"U\":   2.98,  \"V\":   2.51,\n    \"W\":   3.84,  \"X\":   2.43,  \"Y\":   2.43,  \"Z\":   2.43,  \"[\":   1.02,\n    \"\\\\\":  1.41,  \"]\":   1.02,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.78,\n    \"a\":   2.20,  \"b\":   2.43,  \"c\":   2.20,  \"d\":   2.43,  \"e\":   2.20,\n    \"f\":   1.10,  \"g\":   2.35,  \"h\":   2.27,  \"i\":   0.78,  \"j\":   0.78,\n    \"k\":   1.96,  \"l\":   0.78,  \"m\":   3.53,  \"n\":   2.27,  \"o\":   2.35,\n    \"p\":   2.43,  \"q\":   2.43,  \"r\":   1.33,  \"s\":   2.04,  \"t\":   1.25,\n    \"u\":   2.27,  \"v\":   1.96,  \"w\":   3.14,  \"x\":   1.96,  \"y\":   1.96,\n    \"z\":   1.80,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Medium\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.18,  \"\\\"\":  1.88,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   4.23,  \"&\":   2.74,  \"'\":   1.18,  \"(\":   1.18,  \")\":   1.18,\n    \"*\":   1.57,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.65,  \".\":   1.18,\n    \"/\":   1.49,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.35,  \"@\":   3.39,  \"A\":   2.82,  \"B\":   2.98,\n    \"C\":   3.06,  \"D\":   3.06,  \"E\":   2.67,  \"F\":   2.51,  \"G\":   3.21,\n    \"H\":   3.06,  \"I\":   1.18,  \"J\":   2.27,  \"K\":   2.90,  \"L\":   2.43,\n    \"M\":   3.76,  \"N\":   3.06,  \"O\":   3.22,  \"P\":   2.82,  \"Q\":   3.22,\n    \"R\":   2.98,  \"S\":   2.74,  \"T\":   2.51,  \"U\":   3.06,  \"V\":   2.59,\n    \"W\":   4.00,  \"X\":   2.74,  \"Y\":   2.74,  \"Z\":   2.67,  \"[\":   1.25,\n    \"\\\\\":  1.49,  \"]\":   1.25,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   1.02,\n    \"a\":   2.35,  \"b\":   2.59,  \"c\":   2.35,  \"d\":   2.59,  \"e\":   2.35,\n    \"f\":   1.33,  \"g\":   2.51,  \"h\":   2.43,  \"i\":   1.02,  \"j\":   1.02,\n    \"k\":   2.27,  \"l\":   1.02,  \"m\":   3.68,  \"n\":   2.43,  \"o\":   2.51,\n    \"p\":   2.59,  \"q\":   2.59,  \"r\":   1.49,  \"s\":   2.20,  \"t\":   1.41,\n    \"u\":   2.43,  \"v\":   2.20,  \"w\":   3.29,  \"x\":   2.27,  \"y\":   2.20,\n    \"z\":   2.12,  \"{\":   1.25,  \"|\":   0.94,  \"}\":   1.25,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Thin\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.10,  \"\\\"\":  1.33,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.29,  \"&\":   2.51,  \"'\":   0.86,  \"(\":   1.02,  \")\":   1.02,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.49,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.20,  \"@\":   3.39,  \"A\":   2.51,  \"B\":   2.67,\n    \"C\":   2.98,  \"D\":   2.82,  \"E\":   2.37,  \"F\":   2.18,  \"G\":   3.14,\n    \"H\":   2.84,  \"I\":   0.72,  \"J\":   2.04,  \"K\":   2.60,  \"L\":   2.12,\n    \"M\":   3.42,  \"N\":   2.87,  \"O\":   3.14,  \"P\":   2.50,  \"Q\":   3.14,\n    \"R\":   2.67,  \"S\":   2.67,  \"T\":   2.20,  \"U\":   2.85,  \"V\":   2.35,\n    \"W\":   3.76,  \"X\":   2.35,  \"Y\":   2.43,  \"Z\":   2.27,  \"[\":   1.02,\n    \"\\\\\":  1.41,  \"]\":   1.02,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.71,\n    \"a\":   2.12,  \"b\":   2.35,  \"c\":   2.20,  \"d\":   2.35,  \"e\":   2.20,\n    \"f\":   1.02,  \"g\":   2.27,  \"h\":   2.20,  \"i\":   0.71,  \"j\":   0.71,\n    \"k\":   1.96,  \"l\":   0.71,  \"m\":   3.37,  \"n\":   2.20,  \"o\":   2.27,\n    \"p\":   2.35,  \"q\":   2.35,  \"r\":   1.18,  \"s\":   2.04,  \"t\":   1.10,\n    \"u\":   2.20,  \"v\":   1.88,  \"w\":   2.98,  \"x\":   1.88,  \"y\":   1.88,\n    \"z\":   1.80,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Thin Italic\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   0.86,  \"\\\"\":  1.33,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.21,  \"&\":   2.51,  \"'\":   0.86,  \"(\":   1.02,  \")\":   1.02,\n    \"*\":   1.49,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.49,  \".\":   1.18,\n    \"/\":   1.41,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.20,  \"@\":   3.39,  \"A\":   2.59,  \"B\":   2.67,\n    \"C\":   2.98,  \"D\":   2.90,  \"E\":   2.35,  \"F\":   2.20,  \"G\":   3.14,\n    \"H\":   2.82,  \"I\":   0.78,  \"J\":   2.04,  \"K\":   2.59,  \"L\":   2.12,\n    \"M\":   3.45,  \"N\":   2.90,  \"O\":   3.14,  \"P\":   2.51,  \"Q\":   3.14,\n    \"R\":   2.67,  \"S\":   2.67,  \"T\":   2.20,  \"U\":   2.82,  \"V\":   2.43,\n    \"W\":   3.84,  \"X\":   2.35,  \"Y\":   2.35,  \"Z\":   2.27,  \"[\":   1.02,\n    \"\\\\\":  1.41,  \"]\":   1.02,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   0.71,\n    \"a\":   2.12,  \"b\":   2.35,  \"c\":   2.20,  \"d\":   2.35,  \"e\":   2.20,\n    \"f\":   1.02,  \"g\":   2.27,  \"h\":   2.20,  \"i\":   0.71,  \"j\":   0.71,\n    \"k\":   1.96,  \"l\":   0.71,  \"m\":   3.37,  \"n\":   2.20,  \"o\":   2.27,\n    \"p\":   2.35,  \"q\":   2.35,  \"r\":   1.18,  \"s\":   1.96,  \"t\":   1.10,\n    \"u\":   2.20,  \"v\":   1.80,  \"w\":   2.98,  \"x\":   1.80,  \"y\":   1.80,\n    \"z\":   1.80,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica Neue Medium Italic\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.25,  \"\\\"\":  1.88,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   4.00,  \"&\":   2.82,  \"'\":   1.18,  \"(\":   1.25,  \")\":   1.25,\n    \"*\":   1.65,  \"+\":   2.54,  \",\":   1.18,  \"-\":   1.65,  \".\":   1.18,\n    \"/\":   1.49,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.54,  \"=\":   2.54,\n    \">\":   2.54,  \"?\":   2.35,  \"@\":   3.39,  \"A\":   2.90,  \"B\":   2.98,\n    \"C\":   3.06,  \"D\":   3.06,  \"E\":   2.67,  \"F\":   2.51,  \"G\":   3.21,\n    \"H\":   3.06,  \"I\":   1.18,  \"J\":   2.27,  \"K\":   2.90,  \"L\":   2.43,\n    \"M\":   3.76,  \"N\":   3.06,  \"O\":   3.21,  \"P\":   2.82,  \"Q\":   3.21,\n    \"R\":   2.98,  \"S\":   2.74,  \"T\":   2.51,  \"U\":   3.14,  \"V\":   2.59,\n    \"W\":   3.92,  \"X\":   2.74,  \"Y\":   2.67,  \"Z\":   2.67,  \"[\":   1.25,\n    \"\\\\\":  1.49,  \"]\":   1.25,  \"^\":   2.54,  \"_\":   2.12,  \"`\":   1.02,\n    \"a\":   2.35,  \"b\":   2.59,  \"c\":   2.35,  \"d\":   2.59,  \"e\":   2.35,\n    \"f\":   1.33,  \"g\":   2.51,  \"h\":   2.43,  \"i\":   1.02,  \"j\":   1.02,\n    \"k\":   2.20,  \"l\":   1.02,  \"m\":   3.76,  \"n\":   2.43,  \"o\":   2.51,\n    \"p\":   2.59,  \"q\":   2.59,  \"r\":   1.57,  \"s\":   2.12,  \"t\":   1.41,\n    \"u\":   2.43,  \"v\":   2.12,  \"w\":   3.29,  \"x\":   2.12,  \"y\":   2.12,\n    \"z\":   2.04,  \"{\":   1.25,  \"|\":   0.94,  \"}\":   1.25,  \"~\":   2.54\n  },\n  \"height\": 4.23\n},\n\"Helvetica\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.18,  \"\\\"\":  1.49,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   2.82,  \"'\":   0.81,  \"(\":   1.41,  \")\":   1.41,\n    \"*\":   1.65,  \"+\":   2.47,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.18,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.44,  \"=\":   2.47,\n    \">\":   2.44,  \"?\":   2.38,  \"@\":   4.30,  \"A\":   2.82,  \"B\":   2.84,\n    \"C\":   3.03,  \"D\":   3.02,  \"E\":   2.81,  \"F\":   2.59,  \"G\":   3.25,\n    \"H\":   3.04,  \"I\":   1.18,  \"J\":   2.12,  \"K\":   2.87,  \"L\":   2.34,\n    \"M\":   3.53,  \"N\":   3.04,  \"O\":   3.29,  \"P\":   2.81,  \"Q\":   3.29,\n    \"R\":   3.01,  \"S\":   2.76,  \"T\":   2.59,  \"U\":   3.07,  \"V\":   2.82,\n    \"W\":   4.00,  \"X\":   2.79,  \"Y\":   2.83,  \"Z\":   2.60,  \"[\":   1.18,\n    \"\\\\\":  1.23,  \"]\":   1.18,  \"^\":   1.99,  \"_\":   2.35,  \"`\":   1.41,\n    \"a\":   2.35,  \"b\":   2.36,  \"c\":   2.12,  \"d\":   2.35,  \"e\":   2.34,\n    \"f\":   1.18,  \"g\":   2.35,  \"h\":   2.35,  \"i\":   0.94,  \"j\":   0.94,\n    \"k\":   2.12,  \"l\":   0.94,  \"m\":   3.53,  \"n\":   2.35,  \"o\":   2.34,\n    \"p\":   2.36,  \"q\":   2.35,  \"r\":   1.41,  \"s\":   2.14,  \"t\":   1.18,\n    \"u\":   2.33,  \"v\":   2.12,  \"w\":   3.06,  \"x\":   2.12,  \"y\":   2.12,\n    \"z\":   2.12,  \"{\":   1.41,  \"|\":   1.10,  \"}\":   1.41,  \"~\":   2.48\n  },\n  \"height\": 3.95\n},\n\"Helvetica Bold\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.41,  \"\\\"\":  2.01,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   3.08,  \"'\":   1.01,  \"(\":   1.41,  \")\":   1.41,\n    \"*\":   1.65,  \"+\":   2.47,  \",\":   1.18,  \"-\":   1.38,  \".\":   1.18,\n    \"/\":   1.18,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.41,  \";\":   1.41,  \"<\":   2.45,  \"=\":   2.47,\n    \">\":   2.45,  \"?\":   2.59,  \"@\":   4.09,  \"A\":   3.06,  \"B\":   3.04,\n    \"C\":   3.03,  \"D\":   3.06,  \"E\":   2.82,  \"F\":   2.60,  \"G\":   3.29,\n    \"H\":   3.05,  \"I\":   1.18,  \"J\":   2.31,  \"K\":   3.06,  \"L\":   2.60,\n    \"M\":   3.53,  \"N\":   3.05,  \"O\":   3.29,  \"P\":   2.79,  \"Q\":   3.29,\n    \"R\":   3.00,  \"S\":   2.85,  \"T\":   2.59,  \"U\":   3.05,  \"V\":   2.82,\n    \"W\":   4.00,  \"X\":   2.82,  \"Y\":   2.82,  \"Z\":   2.57,  \"[\":   1.41,\n    \"\\\\\":  1.18,  \"]\":   1.41,  \"^\":   2.47,  \"_\":   2.37,  \"`\":   1.41,\n    \"a\":   2.37,  \"b\":   2.60,  \"c\":   2.36,  \"d\":   2.59,  \"e\":   2.46,\n    \"f\":   1.41,  \"g\":   2.59,  \"h\":   2.58,  \"i\":   1.18,  \"j\":   1.18,\n    \"k\":   2.37,  \"l\":   1.18,  \"m\":   3.77,  \"n\":   2.58,  \"o\":   2.59,\n    \"p\":   2.60,  \"q\":   2.59,  \"r\":   1.65,  \"s\":   2.36,  \"t\":   1.41,\n    \"u\":   2.58,  \"v\":   2.35,  \"w\":   3.29,  \"x\":   2.35,  \"y\":   2.35,\n    \"z\":   2.07,  \"{\":   1.65,  \"|\":   1.19,  \"}\":   1.65,  \"~\":   2.49\n  },\n  \"height\": 3.98\n},\n\"Helvetica Oblique\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.18,  \"\\\"\":  1.50,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   2.82,  \"'\":   0.81,  \"(\":   1.41,  \")\":   1.41,\n    \"*\":   1.65,  \"+\":   2.47,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.18,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.47,  \"=\":   2.47,\n    \">\":   2.47,  \"?\":   2.35,  \"@\":   4.30,  \"A\":   2.82,  \"B\":   2.82,\n    \"C\":   3.06,  \"D\":   3.06,  \"E\":   2.82,  \"F\":   2.59,  \"G\":   3.29,\n    \"H\":   3.06,  \"I\":   1.18,  \"J\":   2.12,  \"K\":   2.82,  \"L\":   2.35,\n    \"M\":   3.53,  \"N\":   3.06,  \"O\":   3.29,  \"P\":   2.82,  \"Q\":   3.29,\n    \"R\":   3.06,  \"S\":   2.82,  \"T\":   2.59,  \"U\":   3.06,  \"V\":   2.82,\n    \"W\":   4.00,  \"X\":   2.82,  \"Y\":   2.82,  \"Z\":   2.59,  \"[\":   1.18,\n    \"\\\\\":  1.18,  \"]\":   1.18,  \"^\":   1.99,  \"_\":   2.35,  \"`\":   1.41,\n    \"a\":   2.35,  \"b\":   2.35,  \"c\":   2.12,  \"d\":   2.35,  \"e\":   2.35,\n    \"f\":   1.18,  \"g\":   2.35,  \"h\":   2.35,  \"i\":   0.94,  \"j\":   0.94,\n    \"k\":   2.12,  \"l\":   0.94,  \"m\":   3.53,  \"n\":   2.35,  \"o\":   2.35,\n    \"p\":   2.35,  \"q\":   2.35,  \"r\":   1.41,  \"s\":   2.12,  \"t\":   1.18,\n    \"u\":   2.35,  \"v\":   2.12,  \"w\":   3.06,  \"x\":   2.12,  \"y\":   2.12,\n    \"z\":   2.12,  \"{\":   1.41,  \"|\":   1.10,  \"}\":   1.41,  \"~\":   2.47\n  },\n  \"height\": 3.95\n},\n\"Helvetica Bold Oblique\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.41,  \"\\\"\":  2.01,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   3.06,  \"'\":   1.01,  \"(\":   1.41,  \")\":   1.41,\n    \"*\":   1.65,  \"+\":   2.47,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.18,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.41,  \";\":   1.41,  \"<\":   2.47,  \"=\":   2.47,\n    \">\":   2.47,  \"?\":   2.59,  \"@\":   4.13,  \"A\":   3.06,  \"B\":   3.06,\n    \"C\":   3.06,  \"D\":   3.06,  \"E\":   2.82,  \"F\":   2.59,  \"G\":   3.29,\n    \"H\":   3.06,  \"I\":   1.18,  \"J\":   2.35,  \"K\":   3.06,  \"L\":   2.59,\n    \"M\":   3.53,  \"N\":   3.06,  \"O\":   3.29,  \"P\":   2.82,  \"Q\":   3.29,\n    \"R\":   3.06,  \"S\":   2.82,  \"T\":   2.59,  \"U\":   3.06,  \"V\":   2.82,\n    \"W\":   4.00,  \"X\":   2.82,  \"Y\":   2.82,  \"Z\":   2.59,  \"[\":   1.41,\n    \"\\\\\":  1.18,  \"]\":   1.41,  \"^\":   2.47,  \"_\":   2.35,  \"`\":   1.41,\n    \"a\":   2.35,  \"b\":   2.59,  \"c\":   2.35,  \"d\":   2.59,  \"e\":   2.35,\n    \"f\":   1.41,  \"g\":   2.59,  \"h\":   2.59,  \"i\":   1.18,  \"j\":   1.18,\n    \"k\":   2.35,  \"l\":   1.18,  \"m\":   3.76,  \"n\":   2.59,  \"o\":   2.59,\n    \"p\":   2.59,  \"q\":   2.59,  \"r\":   1.65,  \"s\":   2.35,  \"t\":   1.41,\n    \"u\":   2.59,  \"v\":   2.35,  \"w\":   3.29,  \"x\":   2.35,  \"y\":   2.35,\n    \"z\":   2.12,  \"{\":   1.65,  \"|\":   1.18,  \"}\":   1.65,  \"~\":   2.47\n  },\n  \"height\": 3.98\n},\n\"Helvetica Light\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.41,  \"\\\"\":  1.18,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   2.82,  \"'\":   0.94,  \"(\":   1.41,  \")\":   1.41,\n    \"*\":   1.65,  \"+\":   2.79,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.18,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.79,  \"=\":   2.79,\n    \">\":   2.79,  \"?\":   2.12,  \"@\":   3.39,  \"A\":   2.82,  \"B\":   2.82,\n    \"C\":   3.06,  \"D\":   3.06,  \"E\":   2.59,  \"F\":   2.35,  \"G\":   3.29,\n    \"H\":   3.06,  \"I\":   1.18,  \"J\":   2.12,  \"K\":   2.82,  \"L\":   2.35,\n    \"M\":   3.53,  \"N\":   3.06,  \"O\":   3.29,  \"P\":   2.59,  \"Q\":   3.29,\n    \"R\":   2.82,  \"S\":   2.59,  \"T\":   2.35,  \"U\":   3.06,  \"V\":   2.59,\n    \"W\":   3.76,  \"X\":   2.59,  \"Y\":   2.59,  \"Z\":   2.59,  \"[\":   1.41,\n    \"\\\\\":  1.18,  \"]\":   1.41,  \"^\":   2.79,  \"_\":   2.12,  \"`\":   1.41,\n    \"a\":   2.35,  \"b\":   2.59,  \"c\":   2.35,  \"d\":   2.59,  \"e\":   2.35,\n    \"f\":   1.18,  \"g\":   2.59,  \"h\":   2.35,  \"i\":   0.94,  \"j\":   0.94,\n    \"k\":   2.12,  \"l\":   0.94,  \"m\":   3.53,  \"n\":   2.35,  \"o\":   2.35,\n    \"p\":   2.59,  \"q\":   2.59,  \"r\":   1.41,  \"s\":   2.12,  \"t\":   1.18,\n    \"u\":   2.35,  \"v\":   2.12,  \"w\":   3.06,  \"x\":   2.12,  \"y\":   2.12,\n    \"z\":   2.12,  \"{\":   1.41,  \"|\":   0.94,  \"}\":   1.41,  \"~\":   2.79\n  },\n  \"height\": 4.23\n},\n\"Helvetica Light Oblique\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.41,  \"\\\"\":  1.18,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   2.82,  \"'\":   0.94,  \"(\":   1.41,  \")\":   1.41,\n    \"*\":   1.65,  \"+\":   2.79,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.18,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.79,  \"=\":   2.79,\n    \">\":   2.79,  \"?\":   2.12,  \"@\":   3.39,  \"A\":   2.82,  \"B\":   2.82,\n    \"C\":   3.06,  \"D\":   3.06,  \"E\":   2.59,  \"F\":   2.35,  \"G\":   3.29,\n    \"H\":   3.06,  \"I\":   1.18,  \"J\":   2.12,  \"K\":   2.82,  \"L\":   2.35,\n    \"M\":   3.53,  \"N\":   3.06,  \"O\":   3.29,  \"P\":   2.59,  \"Q\":   3.29,\n    \"R\":   2.82,  \"S\":   2.59,  \"T\":   2.35,  \"U\":   3.06,  \"V\":   2.59,\n    \"W\":   3.76,  \"X\":   2.59,  \"Y\":   2.59,  \"Z\":   2.59,  \"[\":   1.41,\n    \"\\\\\":  1.18,  \"]\":   1.41,  \"^\":   2.79,  \"_\":   2.12,  \"`\":   1.41,\n    \"a\":   2.35,  \"b\":   2.59,  \"c\":   2.35,  \"d\":   2.59,  \"e\":   2.35,\n    \"f\":   1.18,  \"g\":   2.59,  \"h\":   2.35,  \"i\":   0.94,  \"j\":   0.94,\n    \"k\":   2.12,  \"l\":   0.94,  \"m\":   3.53,  \"n\":   2.35,  \"o\":   2.35,\n    \"p\":   2.59,  \"q\":   2.59,  \"r\":   1.41,  \"s\":   2.12,  \"t\":   1.18,\n    \"u\":   2.35,  \"v\":   2.12,  \"w\":   3.06,  \"x\":   2.12,  \"y\":   2.12,\n    \"z\":   2.12,  \"{\":   1.41,  \"|\":   1.70,  \"}\":   1.41,  \"~\":   2.79\n  },\n  \"height\": 4.63\n},\n\"Arial\": {\n  \"widths\": {\n    \" \":   1.18,  \"!\":   1.18,  \"\\\"\":  1.50,  \"#\":   2.35,  \"$\":   2.35,\n    \"%\":   3.76,  \"&\":   2.82,  \"'\":   0.81,  \"(\":   1.41,  \")\":   1.41,\n    \"*\":   1.65,  \"+\":   2.47,  \",\":   1.18,  \"-\":   1.41,  \".\":   1.18,\n    \"/\":   1.18,  \"0\":   2.35,  \"1\":   2.35,  \"2\":   2.35,  \"3\":   2.35,\n    \"4\":   2.35,  \"5\":   2.35,  \"6\":   2.35,  \"7\":   2.35,  \"8\":   2.35,\n    \"9\":   2.35,  \":\":   1.18,  \";\":   1.18,  \"<\":   2.47,  \"=\":   2.47,\n    \">\":   2.47,  \"?\":   2.35,  \"@\":   4.30,  \"A\":   2.82,  \"B\":   2.82,\n    \"C\":   3.06,  \"D\":   3.06,  \"E\":   2.82,  \"F\":   2.59,  \"G\":   3.29,\n    \"H\":   3.06,  \"I\":   1.18,  \"J\":   2.12,  \"K\":   2.82,  \"L\":   2.35,\n    \"M\":   3.53,  \"N\":   3.06,  \"O\":   3.29,  \"P\":   2.82,  \"Q\":   3.29,\n    \"R\":   3.06,  \"S\":   2.82,  \"T\":   2.59,  \"U\":   3.06,  \"V\":   2.82,\n    \"W\":   4.00,  \"X\":   2.82,  \"Y\":   2.82,  \"Z\":   2.59,  \"[\":   1.18,\n    \"\\\\\":  1.18,  \"]\":   1.18,  \"^\":   1.99,  \"_\":   2.35,  \"`\":   1.41,\n    \"a\":   2.35,  \"b\":   2.35,  \"c\":   2.12,  \"d\":   2.35,  \"e\":   2.35,\n    \"f\":   1.18,  \"g\":   2.35,  \"h\":   2.35,  \"i\":   0.94,  \"j\":   0.94,\n    \"k\":   2.12,  \"l\":   0.94,  \"m\":   3.53,  \"n\":   2.35,  \"o\":   2.35,\n    \"p\":   2.35,  \"q\":   2.35,  \"r\":   1.41,  \"s\":   2.12,  \"t\":   1.18,\n    \"u\":   2.35,  \"v\":   2.12,  \"w\":   3.06,  \"x\":   2.12,  \"y\":   2.12,\n    \"z\":   2.12,  \"{\":   1.41,  \"|\":   1.10,  \"}\":   1.41,  \"~\":   2.47\n  },\n  \"height\": 3.98\n},\n\"PT Sans\": {\n  \"widths\": {\n    \" \":   1.13,  \"!\":   1.29,  \"\\\"\":  1.42,  \"#\":   2.31,  \"$\":   2.31,\n    \"%\":   3.27,  \"&\":   3.45,  \"'\":   0.92,  \"(\":   1.19,  \")\":   1.19,\n    \"*\":   1.49,  \"+\":   2.14,  \",\":   0.82,  \"-\":   1.52,  \".\":   0.91,\n    \"/\":   1.50,  \"0\":   2.31,  \"1\":   2.31,  \"2\":   2.31,  \"3\":   2.31,\n    \"4\":   2.31,  \"5\":   2.31,  \"6\":   2.31,  \"7\":   2.31,  \"8\":   2.31,\n    \"9\":   2.31,  \":\":   0.93,  \";\":   1.08,  \"<\":   2.14,  \"=\":   2.14,\n    \">\":   2.14,  \"?\":   1.85,  \"@\":   4.50,  \"A\":   2.48,  \"B\":   2.47,\n    \"C\":   2.42,  \"D\":   2.77,  \"E\":   2.27,  \"F\":   2.19,  \"G\":   2.59,\n    \"H\":   2.85,  \"I\":   1.23,  \"J\":   1.23,  \"K\":   2.58,  \"L\":   2.19,\n    \"M\":   3.35,  \"N\":   2.85,  \"O\":   2.90,  \"P\":   2.37,  \"Q\":   2.90,\n    \"R\":   2.52,  \"S\":   2.25,  \"T\":   2.35,  \"U\":   2.76,  \"V\":   2.40,\n    \"W\":   3.50,  \"X\":   2.62,  \"Y\":   2.36,  \"Z\":   2.31,  \"[\":   1.29,\n    \"\\\\\":  1.61,  \"]\":   1.29,  \"^\":   2.12,  \"_\":   1.73,  \"`\":   1.19,\n    \"a\":   2.10,  \"b\":   2.29,  \"c\":   1.91,  \"d\":   2.28,  \"e\":   2.15,\n    \"f\":   1.35,  \"g\":   2.27,  \"h\":   2.31,  \"i\":   1.13,  \"j\":   1.13,\n    \"k\":   2.03,  \"l\":   1.24,  \"m\":   3.44,  \"n\":   2.31,  \"o\":   2.27,\n    \"p\":   2.29,  \"q\":   2.27,  \"r\":   1.44,  \"s\":   1.78,  \"t\":   1.44,\n    \"u\":   2.28,  \"v\":   2.04,  \"w\":   3.11,  \"x\":   2.18,  \"y\":   1.97,\n    \"z\":   1.89,  \"{\":   1.47,  \"|\":   1.01,  \"}\":   1.47,  \"~\":   2.14\n  },\n  \"height\": 3.94\n},\n\"PT Sans Italic\": {\n  \"widths\": {\n    \" \":   1.13,  \"!\":   1.16,  \"\\\"\":  1.28,  \"#\":   2.20,  \"$\":   2.20,\n    \"%\":   3.12,  \"&\":   3.27,  \"'\":   0.85,  \"(\":   1.13,  \")\":   1.13,\n    \"*\":   1.41,  \"+\":   2.03,  \",\":   0.79,  \"-\":   1.45,  \".\":   0.89,\n    \"/\":   1.42,  \"0\":   2.20,  \"1\":   2.20,  \"2\":   2.20,  \"3\":   2.20,\n    \"4\":   2.20,  \"5\":   2.20,  \"6\":   2.20,  \"7\":   2.20,  \"8\":   2.20,\n    \"9\":   2.20,  \":\":   1.13,  \";\":   1.16,  \"<\":   2.03,  \"=\":   2.03,\n    \">\":   2.03,  \"?\":   1.75,  \"@\":   4.50,  \"A\":   2.36,  \"B\":   2.35,\n    \"C\":   2.30,  \"D\":   2.63,  \"E\":   2.16,  \"F\":   2.08,  \"G\":   2.47,\n    \"H\":   2.71,  \"I\":   1.17,  \"J\":   1.18,  \"K\":   2.44,  \"L\":   2.08,\n    \"M\":   3.19,  \"N\":   2.72,  \"O\":   2.76,  \"P\":   2.25,  \"Q\":   2.76,\n    \"R\":   2.40,  \"S\":   2.14,  \"T\":   2.23,  \"U\":   2.62,  \"V\":   2.29,\n    \"W\":   3.34,  \"X\":   2.49,  \"Y\":   2.25,  \"Z\":   2.20,  \"[\":   1.23,\n    \"\\\\\":  1.61,  \"]\":   1.24,  \"^\":   2.12,  \"_\":   1.73,  \"`\":   1.01,\n    \"a\":   2.06,  \"b\":   2.21,  \"c\":   1.78,  \"d\":   2.15,  \"e\":   1.96,\n    \"f\":   1.18,  \"g\":   2.14,  \"h\":   2.24,  \"i\":   1.10,  \"j\":   1.05,\n    \"k\":   1.90,  \"l\":   1.14,  \"m\":   3.23,  \"n\":   2.21,  \"o\":   2.11,\n    \"p\":   2.15,  \"q\":   2.13,  \"r\":   1.38,  \"s\":   1.69,  \"t\":   1.32,\n    \"u\":   2.16,  \"v\":   1.89,  \"w\":   2.90,  \"x\":   2.05,  \"y\":   1.86,\n    \"z\":   1.82,  \"{\":   1.38,  \"|\":   1.01,  \"}\":   1.40,  \"~\":   2.03\n  },\n  \"height\": 3.94\n},\n\"PT Sans Narrow Bold\": {\n  \"widths\": {\n    \" \":   0.87,  \"!\":   1.06,  \"\\\"\":  1.49,  \"#\":   2.02,  \"$\":   2.02,\n    \"%\":   3.05,  \"&\":   2.84,  \"'\":   0.92,  \"(\":   1.18,  \")\":   1.18,\n    \"*\":   1.37,  \"+\":   1.88,  \",\":   0.83,  \"-\":   1.26,  \".\":   0.81,\n    \"/\":   1.48,  \"0\":   2.02,  \"1\":   2.02,  \"2\":   2.02,  \"3\":   2.02,\n    \"4\":   2.02,  \"5\":   2.02,  \"6\":   2.02,  \"7\":   2.02,  \"8\":   2.02,\n    \"9\":   2.02,  \":\":   1.06,  \";\":   1.06,  \"<\":   1.88,  \"=\":   1.88,\n    \">\":   1.88,  \"?\":   1.62,  \"@\":   3.73,  \"A\":   2.12,  \"B\":   2.07,\n    \"C\":   1.93,  \"D\":   2.27,  \"E\":   1.82,  \"F\":   1.77,  \"G\":   2.12,\n    \"H\":   2.26,  \"I\":   1.01,  \"J\":   1.22,  \"K\":   2.20,  \"L\":   1.79,\n    \"M\":   2.81,  \"N\":   2.29,  \"O\":   2.34,  \"P\":   2.02,  \"Q\":   2.34,\n    \"R\":   2.13,  \"S\":   1.86,  \"T\":   2.00,  \"U\":   2.18,  \"V\":   2.13,\n    \"W\":   3.04,  \"X\":   2.23,  \"Y\":   2.10,  \"Z\":   1.88,  \"[\":   1.17,\n    \"\\\\\":  1.53,  \"]\":   1.16,  \"^\":   1.81,  \"_\":   1.58,  \"`\":   1.23,\n    \"a\":   1.75,  \"b\":   1.88,  \"c\":   1.48,  \"d\":   1.88,  \"e\":   1.81,\n    \"f\":   1.13,  \"g\":   1.88,  \"h\":   1.90,  \"i\":   0.95,  \"j\":   0.95,\n    \"k\":   1.74,  \"l\":   1.03,  \"m\":   2.81,  \"n\":   1.90,  \"o\":   1.88,\n    \"p\":   1.89,  \"q\":   1.88,  \"r\":   1.24,  \"s\":   1.48,  \"t\":   1.23,\n    \"u\":   1.89,  \"v\":   1.69,  \"w\":   2.52,  \"x\":   1.86,  \"y\":   1.69,\n    \"z\":   1.61,  \"{\":   1.32,  \"|\":   0.80,  \"}\":   1.32,  \"~\":   1.88\n  },\n  \"height\": 3.94\n},\n\"PT Sans Narrow\": {\n  \"widths\": {\n    \" \":   0.90,  \"!\":   1.05,  \"\\\"\":  1.21,  \"#\":   1.91,  \"$\":   1.91,\n    \"%\":   2.70,  \"&\":   2.78,  \"'\":   0.77,  \"(\":   0.99,  \")\":   0.99,\n    \"*\":   1.23,  \"+\":   1.76,  \",\":   0.69,  \"-\":   1.23,  \".\":   0.66,\n    \"/\":   1.25,  \"0\":   1.91,  \"1\":   1.91,  \"2\":   1.91,  \"3\":   1.91,\n    \"4\":   1.91,  \"5\":   1.91,  \"6\":   1.91,  \"7\":   1.91,  \"8\":   1.91,\n    \"9\":   1.91,  \":\":   0.80,  \";\":   0.91,  \"<\":   1.76,  \"=\":   1.76,\n    \">\":   1.76,  \"?\":   1.51,  \"@\":   3.63,  \"A\":   2.00,  \"B\":   1.99,\n    \"C\":   1.94,  \"D\":   2.22,  \"E\":   1.82,  \"F\":   1.75,  \"G\":   2.08,\n    \"H\":   2.27,  \"I\":   0.99,  \"J\":   1.02,  \"K\":   2.09,  \"L\":   1.76,\n    \"M\":   2.70,  \"N\":   2.28,  \"O\":   2.32,  \"P\":   1.91,  \"Q\":   2.32,\n    \"R\":   2.03,  \"S\":   1.81,  \"T\":   1.89,  \"U\":   2.21,  \"V\":   1.96,\n    \"W\":   2.85,  \"X\":   2.12,  \"Y\":   1.93,  \"Z\":   1.86,  \"[\":   1.05,\n    \"\\\\\":  1.34,  \"]\":   1.05,  \"^\":   1.74,  \"_\":   1.41,  \"`\":   1.08,\n    \"a\":   1.69,  \"b\":   1.84,  \"c\":   1.51,  \"d\":   1.83,  \"e\":   1.75,\n    \"f\":   1.09,  \"g\":   1.83,  \"h\":   1.86,  \"i\":   0.92,  \"j\":   0.91,\n    \"k\":   1.64,  \"l\":   0.99,  \"m\":   2.76,  \"n\":   1.86,  \"o\":   1.83,\n    \"p\":   1.84,  \"q\":   1.83,  \"r\":   1.16,  \"s\":   1.43,  \"t\":   1.16,\n    \"u\":   1.84,  \"v\":   1.64,  \"w\":   2.50,  \"x\":   1.74,  \"y\":   1.59,\n    \"z\":   1.52,  \"{\":   1.19,  \"|\":   0.80,  \"}\":   1.19,  \"~\":   1.76\n  },\n  \"height\": 3.94\n},\n\"PT Sans Caption Bold\": {\n  \"widths\": {\n    \" \":   1.17,  \"!\":   1.33,  \"\\\"\":  1.85,  \"#\":   2.50,  \"$\":   2.50,\n    \"%\":   3.84,  \"&\":   3.75,  \"'\":   1.17,  \"(\":   1.51,  \")\":   1.50,\n    \"*\":   1.73,  \"+\":   2.41,  \",\":   1.05,  \"-\":   1.66,  \".\":   1.02,\n    \"/\":   1.88,  \"0\":   2.50,  \"1\":   2.50,  \"2\":   2.50,  \"3\":   2.50,\n    \"4\":   2.50,  \"5\":   2.50,  \"6\":   2.50,  \"7\":   2.50,  \"8\":   2.50,\n    \"9\":   2.50,  \":\":   1.24,  \";\":   1.19,  \"<\":   2.42,  \"=\":   2.42,\n    \">\":   2.42,  \"?\":   2.08,  \"@\":   4.86,  \"A\":   2.79,  \"B\":   2.71,\n    \"C\":   2.57,  \"D\":   2.97,  \"E\":   2.40,  \"F\":   2.33,  \"G\":   2.80,\n    \"H\":   2.99,  \"I\":   1.64,  \"J\":   1.53,  \"K\":   2.84,  \"L\":   2.36,\n    \"M\":   3.67,  \"N\":   3.02,  \"O\":   3.08,  \"P\":   2.62,  \"Q\":   3.08,\n    \"R\":   2.76,  \"S\":   2.43,  \"T\":   2.61,  \"U\":   2.87,  \"V\":   2.77,\n    \"W\":   3.97,  \"X\":   2.90,  \"Y\":   2.73,  \"Z\":   2.47,  \"[\":   1.48,\n    \"\\\\\":  1.95,  \"]\":   1.48,  \"^\":   2.27,  \"_\":   2.04,  \"`\":   1.48,\n    \"a\":   2.45,  \"b\":   2.62,  \"c\":   2.12,  \"d\":   2.66,  \"e\":   2.51,\n    \"f\":   1.57,  \"g\":   2.62,  \"h\":   2.70,  \"i\":   1.34,  \"j\":   1.34,\n    \"k\":   2.39,  \"l\":   1.43,  \"m\":   3.98,  \"n\":   2.70,  \"o\":   2.61,\n    \"p\":   2.64,  \"q\":   2.62,  \"r\":   1.72,  \"s\":   2.04,  \"t\":   1.71,\n    \"u\":   2.72,  \"v\":   2.31,  \"w\":   3.49,  \"x\":   2.48,  \"y\":   2.29,\n    \"z\":   2.23,  \"{\":   1.71,  \"|\":   1.04,  \"}\":   1.71,  \"~\":   2.42\n  },\n  \"height\": 3.99\n},\n\"PT Sans Caption\": {\n  \"widths\": {\n    \" \":   1.23,  \"!\":   1.40,  \"\\\"\":  1.59,  \"#\":   2.51,  \"$\":   2.51,\n    \"%\":   3.62,  \"&\":   3.77,  \"'\":   1.03,  \"(\":   1.33,  \")\":   1.32,\n    \"*\":   1.64,  \"+\":   2.35,  \",\":   0.93,  \"-\":   1.67,  \".\":   1.01,\n    \"/\":   1.68,  \"0\":   2.51,  \"1\":   2.51,  \"2\":   2.51,  \"3\":   2.51,\n    \"4\":   2.51,  \"5\":   2.51,  \"6\":   2.51,  \"7\":   2.51,  \"8\":   2.51,\n    \"9\":   2.51,  \":\":   1.16,  \";\":   1.17,  \"<\":   2.35,  \"=\":   2.35,\n    \">\":   2.35,  \"?\":   2.03,  \"@\":   4.92,  \"A\":   2.73,  \"B\":   2.71,\n    \"C\":   2.64,  \"D\":   3.03,  \"E\":   2.48,  \"F\":   2.39,  \"G\":   2.83,\n    \"H\":   3.10,  \"I\":   1.69,  \"J\":   1.38,  \"K\":   2.83,  \"L\":   2.40,\n    \"M\":   3.67,  \"N\":   3.11,  \"O\":   3.16,  \"P\":   2.60,  \"Q\":   3.16,\n    \"R\":   2.76,  \"S\":   2.46,  \"T\":   2.58,  \"U\":   3.00,  \"V\":   2.66,\n    \"W\":   3.87,  \"X\":   2.87,  \"Y\":   2.61,  \"Z\":   2.52,  \"[\":   1.42,\n    \"\\\\\":  1.78,  \"]\":   1.42,  \"^\":   2.35,  \"_\":   1.91,  \"`\":   1.27,\n    \"a\":   2.46,  \"b\":   2.66,  \"c\":   2.24,  \"d\":   2.66,  \"e\":   2.53,\n    \"f\":   1.54,  \"g\":   2.65,  \"h\":   2.71,  \"i\":   1.34,  \"j\":   1.32,\n    \"k\":   2.36,  \"l\":   1.42,  \"m\":   4.01,  \"n\":   2.71,  \"o\":   2.67,\n    \"p\":   2.67,  \"q\":   2.65,  \"r\":   1.61,  \"s\":   2.11,  \"t\":   1.71,\n    \"u\":   2.73,  \"v\":   2.27,  \"w\":   3.56,  \"x\":   2.51,  \"y\":   2.25,\n    \"z\":   2.18,  \"{\":   1.62,  \"|\":   1.09,  \"}\":   1.62,  \"~\":   2.35\n  },\n  \"height\": 4.01\n},\n\"PT Sans Bold Italic\": {\n  \"widths\": {\n    \" \":   1.07,  \"!\":   1.20,  \"\\\"\":  1.50,  \"#\":   2.31,  \"$\":   2.31,\n    \"%\":   3.40,  \"&\":   3.31,  \"'\":   0.92,  \"(\":   1.31,  \")\":   1.31,\n    \"*\":   1.51,  \"+\":   2.14,  \",\":   0.91,  \"-\":   1.45,  \".\":   1.06,\n    \"/\":   1.64,  \"0\":   2.31,  \"1\":   2.31,  \"2\":   2.31,  \"3\":   2.31,\n    \"4\":   2.31,  \"5\":   2.31,  \"6\":   2.31,  \"7\":   2.31,  \"8\":   2.31,\n    \"9\":   2.31,  \":\":   1.33,  \";\":   1.30,  \"<\":   2.14,  \"=\":   2.14,\n    \">\":   2.14,  \"?\":   1.82,  \"@\":   4.48,  \"A\":   2.46,  \"B\":   2.40,\n    \"C\":   2.27,  \"D\":   2.62,  \"E\":   2.13,  \"F\":   2.07,  \"G\":   2.48,\n    \"H\":   2.64,  \"I\":   1.18,  \"J\":   1.37,  \"K\":   2.51,  \"L\":   2.10,\n    \"M\":   3.25,  \"N\":   2.67,  \"O\":   2.73,  \"P\":   2.33,  \"Q\":   2.73,\n    \"R\":   2.47,  \"S\":   2.16,  \"T\":   2.31,  \"U\":   2.55,  \"V\":   2.45,\n    \"W\":   3.50,  \"X\":   2.57,  \"Y\":   2.41,  \"Z\":   2.19,  \"[\":   1.33,\n    \"\\\\\":  1.79,  \"]\":   1.32,  \"^\":   2.14,  \"_\":   1.88,  \"`\":   1.01,\n    \"a\":   2.13,  \"b\":   2.17,  \"c\":   1.74,  \"d\":   2.15,  \"e\":   1.97,\n    \"f\":   1.25,  \"g\":   2.14,  \"h\":   2.23,  \"i\":   1.08,  \"j\":   1.03,\n    \"k\":   1.97,  \"l\":   1.15,  \"m\":   3.25,  \"n\":   2.21,  \"o\":   2.10,\n    \"p\":   2.14,  \"q\":   2.12,  \"r\":   1.45,  \"s\":   1.68,  \"t\":   1.35,\n    \"u\":   2.17,  \"v\":   1.90,  \"w\":   2.87,  \"x\":   2.10,  \"y\":   1.91,\n    \"z\":   1.84,  \"{\":   1.51,  \"|\":   0.98,  \"}\":   1.52,  \"~\":   2.14\n  },\n  \"height\": 3.94\n},\n\"PT Sans Bold\": {\n  \"widths\": {\n    \" \":   1.07,  \"!\":   1.25,  \"\\\"\":  1.70,  \"#\":   2.40,  \"$\":   2.40,\n    \"%\":   3.56,  \"&\":   3.45,  \"'\":   1.07,  \"(\":   1.38,  \")\":   1.38,\n    \"*\":   1.60,  \"+\":   2.23,  \",\":   0.96,  \"-\":   1.52,  \".\":   1.04,\n    \"/\":   1.73,  \"0\":   2.40,  \"1\":   2.40,  \"2\":   2.40,  \"3\":   2.40,\n    \"4\":   2.40,  \"5\":   2.40,  \"6\":   2.40,  \"7\":   2.40,  \"8\":   2.40,\n    \"9\":   2.40,  \":\":   1.14,  \";\":   1.23,  \"<\":   2.23,  \"=\":   2.23,\n    \">\":   2.23,  \"?\":   1.92,  \"@\":   4.48,  \"A\":   2.56,  \"B\":   2.50,\n    \"C\":   2.37,  \"D\":   2.74,  \"E\":   2.21,  \"F\":   2.15,  \"G\":   2.58,\n    \"H\":   2.76,  \"I\":   1.21,  \"J\":   1.41,  \"K\":   2.62,  \"L\":   2.18,\n    \"M\":   3.39,  \"N\":   2.78,  \"O\":   2.84,  \"P\":   2.42,  \"Q\":   2.84,\n    \"R\":   2.55,  \"S\":   2.24,  \"T\":   2.40,  \"U\":   2.65,  \"V\":   2.55,\n    \"W\":   3.65,  \"X\":   2.68,  \"Y\":   2.51,  \"Z\":   2.28,  \"[\":   1.37,\n    \"\\\\\":  1.79,  \"]\":   1.37,  \"^\":   2.12,  \"_\":   1.88,  \"`\":   1.34,\n    \"a\":   2.10,  \"b\":   2.27,  \"c\":   1.83,  \"d\":   2.28,  \"e\":   2.15,\n    \"f\":   1.36,  \"g\":   2.27,  \"h\":   2.31,  \"i\":   1.14,  \"j\":   1.13,\n    \"k\":   2.08,  \"l\":   1.25,  \"m\":   3.41,  \"n\":   2.31,  \"o\":   2.27,\n    \"p\":   2.29,  \"q\":   2.27,  \"r\":   1.49,  \"s\":   1.79,  \"t\":   1.48,\n    \"u\":   2.28,  \"v\":   2.02,  \"w\":   3.05,  \"x\":   2.22,  \"y\":   2.02,\n    \"z\":   1.94,  \"{\":   1.57,  \"|\":   0.96,  \"}\":   1.57,  \"~\":   2.23\n  },\n  \"height\": 3.94\n},\n\"Source Sans Pro\": {\n  \"widths\": {\n    \" \":   0.85,  \"!\":   1.22,  \"\\\"\":  1.80,  \"#\":   2.10,  \"$\":   2.10,\n    \"%\":   3.49,  \"&\":   2.58,  \"'\":   1.05,  \"(\":   1.28,  \")\":   1.28,\n    \"*\":   1.77,  \"+\":   2.10,  \",\":   1.05,  \"-\":   1.32,  \".\":   1.05,\n    \"/\":   1.48,  \"0\":   2.10,  \"1\":   2.10,  \"2\":   2.10,  \"3\":   2.10,\n    \"4\":   2.10,  \"5\":   2.10,  \"6\":   2.10,  \"7\":   2.10,  \"8\":   2.10,\n    \"9\":   2.10,  \":\":   1.05,  \";\":   1.05,  \"<\":   2.10,  \"=\":   2.10,\n    \">\":   2.10,  \"?\":   1.80,  \"@\":   3.59,  \"A\":   2.28,  \"B\":   2.47,\n    \"C\":   2.40,  \"D\":   2.58,  \"E\":   2.21,  \"F\":   2.07,  \"G\":   2.59,\n    \"H\":   2.74,  \"I\":   1.09,  \"J\":   2.01,  \"K\":   2.43,  \"L\":   2.04,\n    \"M\":   3.06,  \"N\":   2.72,  \"O\":   2.79,  \"P\":   2.42,  \"Q\":   2.79,\n    \"R\":   2.44,  \"S\":   2.24,  \"T\":   2.25,  \"U\":   2.71,  \"V\":   2.16,\n    \"W\":   3.31,  \"X\":   2.15,  \"Y\":   1.99,  \"Z\":   2.26,  \"[\":   1.28,\n    \"\\\\\":  1.48,  \"]\":   1.28,  \"^\":   2.10,  \"_\":   2.12,  \"`\":   2.29,\n    \"a\":   2.17,  \"b\":   2.35,  \"c\":   1.93,  \"d\":   2.35,  \"e\":   2.11,\n    \"f\":   1.24,  \"g\":   2.13,  \"h\":   2.30,  \"i\":   1.04,  \"j\":   1.05,\n    \"k\":   2.10,  \"l\":   1.08,  \"m\":   3.51,  \"n\":   2.32,  \"o\":   2.29,\n    \"p\":   2.35,  \"q\":   2.33,  \"r\":   1.47,  \"s\":   1.77,  \"t\":   1.43,\n    \"u\":   2.32,  \"v\":   1.98,  \"w\":   3.04,  \"x\":   1.89,  \"y\":   1.98,\n    \"z\":   1.80,  \"{\":   1.28,  \"|\":   1.02,  \"}\":   1.28,  \"~\":   2.10\n  },\n  \"height\": 4.23\n}}\n"
  },
  {
    "path": "deps/mkfile",
    "content": "\n# Mkfile for updating the glyphsize table. This is not intended for general use.\n# If you really want to use it, you'll probably have to change FONTS to point to\n# the font files you want to serialize, then obtain a copy of mk, either from\n# plan9port, or from github.com/dcjones/mk. Then just type 'mk'.\n\nall:V: glyphsize.json\n\nFONTS=/System/Library/Fonts/HelveticaNeue.dfont \\\n      /System/Library/Fonts/Helvetica.dfont \\\n      /Library/Fonts/Microsoft/Arial.ttf \\\n      /Library/Fonts/PTSans.ttc \\\n      /Users/dcjones/Library/Fonts/SourceSansPro-Regular.otf\n\nglyphsize.json: glyphsize $FONTS\n    ./glyphsize $FONTS > $target\n\nCFLAGS=-Wall -g -I/usr/local/include/freetype2\n\nglyphsize: glyphsize.c\n    gcc $CFLAGS -o $target $prereq -lfreetype\n\nclean:V:\n    rm -f glyphsize\n\n\n# Uglifyjs outputs a bunch of non-ascii characters that cause problems\n# when embedded in an svg file, so use closure-complier.\nsnap.svg-min.js: /Users/dcjones/src/Snap.svg-0.3.0/dist/snap.svg.js\n    closure-compiler $prereq > $target\n\n\n\n\n\n"
  },
  {
    "path": "deps/snap.svg-min.js",
    "content": "(function(N){var k=/[\\.\\/]/,L=/\\s*,\\s*/,C=function(a,d){return a-d},a,v,y={n:{}},M=function(){for(var a=0,d=this.length;a<d;a++)if(\"undefined\"!=typeof this[a])return this[a]},A=function(){for(var a=this.length;--a;)if(\"undefined\"!=typeof this[a])return this[a]},w=function(k,d){k=String(k);var f=v,n=Array.prototype.slice.call(arguments,2),u=w.listeners(k),p=0,b,q=[],e={},l=[],r=a;l.firstDefined=M;l.lastDefined=A;a=k;for(var s=v=0,x=u.length;s<x;s++)\"zIndex\"in u[s]&&(q.push(u[s].zIndex),0>u[s].zIndex&&\n(e[u[s].zIndex]=u[s]));for(q.sort(C);0>q[p];)if(b=e[q[p++]],l.push(b.apply(d,n)),v)return v=f,l;for(s=0;s<x;s++)if(b=u[s],\"zIndex\"in b)if(b.zIndex==q[p]){l.push(b.apply(d,n));if(v)break;do if(p++,(b=e[q[p]])&&l.push(b.apply(d,n)),v)break;while(b)}else e[b.zIndex]=b;else if(l.push(b.apply(d,n)),v)break;v=f;a=r;return l};w._events=y;w.listeners=function(a){a=a.split(k);var d=y,f,n,u,p,b,q,e,l=[d],r=[];u=0;for(p=a.length;u<p;u++){e=[];b=0;for(q=l.length;b<q;b++)for(d=l[b].n,f=[d[a[u]],d[\"*\"]],n=2;n--;)if(d=\nf[n])e.push(d),r=r.concat(d.f||[]);l=e}return r};w.on=function(a,d){a=String(a);if(\"function\"!=typeof d)return function(){};for(var f=a.split(L),n=0,u=f.length;n<u;n++)(function(a){a=a.split(k);for(var b=y,f,e=0,l=a.length;e<l;e++)b=b.n,b=b.hasOwnProperty(a[e])&&b[a[e]]||(b[a[e]]={n:{}});b.f=b.f||[];e=0;for(l=b.f.length;e<l;e++)if(b.f[e]==d){f=!0;break}!f&&b.f.push(d)})(f[n]);return function(a){+a==+a&&(d.zIndex=+a)}};w.f=function(a){var d=[].slice.call(arguments,1);return function(){w.apply(null,\n[a,null].concat(d).concat([].slice.call(arguments,0)))}};w.stop=function(){v=1};w.nt=function(k){return k?(new RegExp(\"(?:\\\\.|\\\\/|^)\"+k+\"(?:\\\\.|\\\\/|$)\")).test(a):a};w.nts=function(){return a.split(k)};w.off=w.unbind=function(a,d){if(a){var f=a.split(L);if(1<f.length)for(var n=0,u=f.length;n<u;n++)w.off(f[n],d);else{for(var f=a.split(k),p,b,q,e,l=[y],n=0,u=f.length;n<u;n++)for(e=0;e<l.length;e+=q.length-2){q=[e,1];p=l[e].n;if(\"*\"!=f[n])p[f[n]]&&q.push(p[f[n]]);else for(b in p)p.hasOwnProperty(b)&&\nq.push(p[b]);l.splice.apply(l,q)}n=0;for(u=l.length;n<u;n++)for(p=l[n];p.n;){if(d){if(p.f){e=0;for(f=p.f.length;e<f;e++)if(p.f[e]==d){p.f.splice(e,1);break}!p.f.length&&delete p.f}for(b in p.n)if(p.n.hasOwnProperty(b)&&p.n[b].f){q=p.n[b].f;e=0;for(f=q.length;e<f;e++)if(q[e]==d){q.splice(e,1);break}!q.length&&delete p.n[b].f}}else for(b in delete p.f,p.n)p.n.hasOwnProperty(b)&&p.n[b].f&&delete p.n[b].f;p=p.n}}}else w._events=y={n:{}}};w.once=function(a,d){var f=function(){w.unbind(a,f);return d.apply(this,\narguments)};return w.on(a,f)};w.version=\"0.4.2\";w.toString=function(){return\"You are running Eve 0.4.2\"};\"undefined\"!=typeof module&&module.exports?module.exports=w:\"function\"===typeof define&&define.amd?define(\"eve\",[],function(){return w}):N.eve=w})(this);\n(function(N,k){\"function\"===typeof define&&define.amd?define(\"Snap.svg\",[\"eve\"],function(L){return k(N,L)}):k(N,N.eve)})(this,function(N,k){var L=function(a){var k={},y=N.requestAnimationFrame||N.webkitRequestAnimationFrame||N.mozRequestAnimationFrame||N.oRequestAnimationFrame||N.msRequestAnimationFrame||function(a){setTimeout(a,16)},M=Array.isArray||function(a){return a instanceof Array||\"[object Array]\"==Object.prototype.toString.call(a)},A=0,w=\"M\"+(+new Date).toString(36),z=function(a){if(null==\na)return this.s;var b=this.s-a;this.b+=this.dur*b;this.B+=this.dur*b;this.s=a},d=function(a){if(null==a)return this.spd;this.spd=a},f=function(a){if(null==a)return this.dur;this.s=this.s*a/this.dur;this.dur=a},n=function(){delete k[this.id];this.update();a(\"mina.stop.\"+this.id,this)},u=function(){this.pdif||(delete k[this.id],this.update(),this.pdif=this.get()-this.b)},p=function(){this.pdif&&(this.b=this.get()-this.pdif,delete this.pdif,k[this.id]=this)},b=function(){var a;if(M(this.start)){a=[];\nfor(var b=0,e=this.start.length;b<e;b++)a[b]=+this.start[b]+(this.end[b]-this.start[b])*this.easing(this.s)}else a=+this.start+(this.end-this.start)*this.easing(this.s);this.set(a)},q=function(){var l=0,b;for(b in k)if(k.hasOwnProperty(b)){var e=k[b],f=e.get();l++;e.s=(f-e.b)/(e.dur/e.spd);1<=e.s&&(delete k[b],e.s=1,l--,function(b){setTimeout(function(){a(\"mina.finish.\"+b.id,b)})}(e));e.update()}l&&y(q)},e=function(a,r,s,x,G,h,J){a={id:w+(A++).toString(36),start:a,end:r,b:s,s:0,dur:x-s,spd:1,get:G,\nset:h,easing:J||e.linear,status:z,speed:d,duration:f,stop:n,pause:u,resume:p,update:b};k[a.id]=a;r=0;for(var K in k)if(k.hasOwnProperty(K)&&(r++,2==r))break;1==r&&y(q);return a};e.time=Date.now||function(){return+new Date};e.getById=function(a){return k[a]||null};e.linear=function(a){return a};e.easeout=function(a){return Math.pow(a,1.7)};e.easein=function(a){return Math.pow(a,0.48)};e.easeinout=function(a){if(1==a)return 1;if(0==a)return 0;var b=0.48-a/1.04,e=Math.sqrt(0.1734+b*b);a=e-b;a=Math.pow(Math.abs(a),\n1/3)*(0>a?-1:1);b=-e-b;b=Math.pow(Math.abs(b),1/3)*(0>b?-1:1);a=a+b+0.5;return 3*(1-a)*a*a+a*a*a};e.backin=function(a){return 1==a?1:a*a*(2.70158*a-1.70158)};e.backout=function(a){if(0==a)return 0;a-=1;return a*a*(2.70158*a+1.70158)+1};e.elastic=function(a){return a==!!a?a:Math.pow(2,-10*a)*Math.sin(2*(a-0.075)*Math.PI/0.3)+1};e.bounce=function(a){a<1/2.75?a*=7.5625*a:a<2/2.75?(a-=1.5/2.75,a=7.5625*a*a+0.75):a<2.5/2.75?(a-=2.25/2.75,a=7.5625*a*a+0.9375):(a-=2.625/2.75,a=7.5625*a*a+0.984375);return a};\nreturn N.mina=e}(\"undefined\"==typeof k?function(){}:k),C=function(){function a(c,t){if(c){if(c.tagName)return x(c);if(y(c,\"array\")&&a.set)return a.set.apply(a,c);if(c instanceof e)return c;if(null==t)return c=G.doc.querySelector(c),x(c)}return new s(null==c?\"100%\":c,null==t?\"100%\":t)}function v(c,a){if(a){\"#text\"==c&&(c=G.doc.createTextNode(a.text||\"\"));\"string\"==typeof c&&(c=v(c));if(\"string\"==typeof a)return\"xlink:\"==a.substring(0,6)?c.getAttributeNS(m,a.substring(6)):\"xml:\"==a.substring(0,4)?c.getAttributeNS(la,\na.substring(4)):c.getAttribute(a);for(var da in a)if(a[h](da)){var b=J(a[da]);b?\"xlink:\"==da.substring(0,6)?c.setAttributeNS(m,da.substring(6),b):\"xml:\"==da.substring(0,4)?c.setAttributeNS(la,da.substring(4),b):c.setAttribute(da,b):c.removeAttribute(da)}}else c=G.doc.createElementNS(la,c);return c}function y(c,a){a=J.prototype.toLowerCase.call(a);return\"finite\"==a?isFinite(c):\"array\"==a&&(c instanceof Array||Array.isArray&&Array.isArray(c))?!0:\"null\"==a&&null===c||a==typeof c&&null!==c||\"object\"==\na&&c===Object(c)||$.call(c).slice(8,-1).toLowerCase()==a}function M(c){if(\"function\"==typeof c||Object(c)!==c)return c;var a=new c.constructor,b;for(b in c)c[h](b)&&(a[b]=M(c[b]));return a}function A(c,a,b){function m(){var e=Array.prototype.slice.call(arguments,0),f=e.join(\"\\u2400\"),d=m.cache=m.cache||{},l=m.count=m.count||[];if(d[h](f)){a:for(var e=l,l=f,B=0,H=e.length;B<H;B++)if(e[B]===l){e.push(e.splice(B,1)[0]);break a}return b?b(d[f]):d[f]}1E3<=l.length&&delete d[l.shift()];l.push(f);d[f]=c.apply(a,\ne);return b?b(d[f]):d[f]}return m}function w(c,a,b,m,e,f){return null==e?(c-=b,a-=m,c||a?(180*I.atan2(-a,-c)/C+540)%360:0):w(c,a,e,f)-w(b,m,e,f)}function z(c){return c%360*C/180}function d(c){var a=[];c=c.replace(/(?:^|\\s)(\\w+)\\(([^)]+)\\)/g,function(c,b,m){m=m.split(/\\s*,\\s*|\\s+/);\"rotate\"==b&&1==m.length&&m.push(0,0);\"scale\"==b&&(2<m.length?m=m.slice(0,2):2==m.length&&m.push(0,0),1==m.length&&m.push(m[0],0,0));\"skewX\"==b?a.push([\"m\",1,0,I.tan(z(m[0])),1,0,0]):\"skewY\"==b?a.push([\"m\",1,I.tan(z(m[0])),\n0,1,0,0]):a.push([b.charAt(0)].concat(m));return c});return a}function f(c,t){var b=O(c),m=new a.Matrix;if(b)for(var e=0,f=b.length;e<f;e++){var h=b[e],d=h.length,B=J(h[0]).toLowerCase(),H=h[0]!=B,l=H?m.invert():0,E;\"t\"==B&&2==d?m.translate(h[1],0):\"t\"==B&&3==d?H?(d=l.x(0,0),B=l.y(0,0),H=l.x(h[1],h[2]),l=l.y(h[1],h[2]),m.translate(H-d,l-B)):m.translate(h[1],h[2]):\"r\"==B?2==d?(E=E||t,m.rotate(h[1],E.x+E.width/2,E.y+E.height/2)):4==d&&(H?(H=l.x(h[2],h[3]),l=l.y(h[2],h[3]),m.rotate(h[1],H,l)):m.rotate(h[1],\nh[2],h[3])):\"s\"==B?2==d||3==d?(E=E||t,m.scale(h[1],h[d-1],E.x+E.width/2,E.y+E.height/2)):4==d?H?(H=l.x(h[2],h[3]),l=l.y(h[2],h[3]),m.scale(h[1],h[1],H,l)):m.scale(h[1],h[1],h[2],h[3]):5==d&&(H?(H=l.x(h[3],h[4]),l=l.y(h[3],h[4]),m.scale(h[1],h[2],H,l)):m.scale(h[1],h[2],h[3],h[4])):\"m\"==B&&7==d&&m.add(h[1],h[2],h[3],h[4],h[5],h[6])}return m}function n(c,t){if(null==t){var m=!0;t=\"linearGradient\"==c.type||\"radialGradient\"==c.type?c.node.getAttribute(\"gradientTransform\"):\"pattern\"==c.type?c.node.getAttribute(\"patternTransform\"):\nc.node.getAttribute(\"transform\");if(!t)return new a.Matrix;t=d(t)}else t=a._.rgTransform.test(t)?J(t).replace(/\\.{3}|\\u2026/g,c._.transform||aa):d(t),y(t,\"array\")&&(t=a.path?a.path.toString.call(t):J(t)),c._.transform=t;var b=f(t,c.getBBox(1));if(m)return b;c.matrix=b}function u(c){c=c.node.ownerSVGElement&&x(c.node.ownerSVGElement)||c.node.parentNode&&x(c.node.parentNode)||a.select(\"svg\")||a(0,0);var t=c.select(\"defs\"),t=null==t?!1:t.node;t||(t=r(\"defs\",c.node).node);return t}function p(c){return c.node.ownerSVGElement&&\nx(c.node.ownerSVGElement)||a.select(\"svg\")}function b(c,a,m){function b(c){if(null==c)return aa;if(c==+c)return c;v(B,{width:c});try{return B.getBBox().width}catch(a){return 0}}function h(c){if(null==c)return aa;if(c==+c)return c;v(B,{height:c});try{return B.getBBox().height}catch(a){return 0}}function e(b,B){null==a?d[b]=B(c.attr(b)||0):b==a&&(d=B(null==m?c.attr(b)||0:m))}var f=p(c).node,d={},B=f.querySelector(\".svg---mgr\");B||(B=v(\"rect\"),v(B,{x:-9E9,y:-9E9,width:10,height:10,\"class\":\"svg---mgr\",\nfill:\"none\"}),f.appendChild(B));switch(c.type){case \"rect\":e(\"rx\",b),e(\"ry\",h);case \"image\":e(\"width\",b),e(\"height\",h);case \"text\":e(\"x\",b);e(\"y\",h);break;case \"circle\":e(\"cx\",b);e(\"cy\",h);e(\"r\",b);break;case \"ellipse\":e(\"cx\",b);e(\"cy\",h);e(\"rx\",b);e(\"ry\",h);break;case \"line\":e(\"x1\",b);e(\"x2\",b);e(\"y1\",h);e(\"y2\",h);break;case \"marker\":e(\"refX\",b);e(\"markerWidth\",b);e(\"refY\",h);e(\"markerHeight\",h);break;case \"radialGradient\":e(\"fx\",b);e(\"fy\",h);break;case \"tspan\":e(\"dx\",b);e(\"dy\",h);break;default:e(a,\nb)}f.removeChild(B);return d}function q(c){y(c,\"array\")||(c=Array.prototype.slice.call(arguments,0));for(var a=0,b=0,m=this.node;this[a];)delete this[a++];for(a=0;a<c.length;a++)\"set\"==c[a].type?c[a].forEach(function(c){m.appendChild(c.node)}):m.appendChild(c[a].node);for(var h=m.childNodes,a=0;a<h.length;a++)this[b++]=x(h[a]);return this}function e(c){if(c.snap in E)return E[c.snap];var a=this.id=V(),b;try{b=c.ownerSVGElement}catch(m){}this.node=c;b&&(this.paper=new s(b));this.type=c.tagName;this.anims=\n{};this._={transform:[]};c.snap=a;E[a]=this;\"g\"==this.type&&(this.add=q);if(this.type in{g:1,mask:1,pattern:1})for(var e in s.prototype)s.prototype[h](e)&&(this[e]=s.prototype[e])}function l(c){this.node=c}function r(c,a){var b=v(c);a.appendChild(b);return x(b)}function s(c,a){var b,m,f,d=s.prototype;if(c&&\"svg\"==c.tagName){if(c.snap in E)return E[c.snap];var l=c.ownerDocument;b=new e(c);m=c.getElementsByTagName(\"desc\")[0];f=c.getElementsByTagName(\"defs\")[0];m||(m=v(\"desc\"),m.appendChild(l.createTextNode(\"Created with Snap\")),\nb.node.appendChild(m));f||(f=v(\"defs\"),b.node.appendChild(f));b.defs=f;for(var ca in d)d[h](ca)&&(b[ca]=d[ca]);b.paper=b.root=b}else b=r(\"svg\",G.doc.body),v(b.node,{height:a,version:1.1,width:c,xmlns:la});return b}function x(c){return!c||c instanceof e||c instanceof l?c:c.tagName&&\"svg\"==c.tagName.toLowerCase()?new s(c):c.tagName&&\"object\"==c.tagName.toLowerCase()&&\"image/svg+xml\"==c.type?new s(c.contentDocument.getElementsByTagName(\"svg\")[0]):new e(c)}a.version=\"0.3.0\";a.toString=function(){return\"Snap v\"+\nthis.version};a._={};var G={win:N,doc:N.document};a._.glob=G;var h=\"hasOwnProperty\",J=String,K=parseFloat,U=parseInt,I=Math,P=I.max,Q=I.min,Y=I.abs,C=I.PI,aa=\"\",$=Object.prototype.toString,F=/^\\s*((#[a-f\\d]{6})|(#[a-f\\d]{3})|rgba?\\(\\s*([\\d\\.]+%?\\s*,\\s*[\\d\\.]+%?\\s*,\\s*[\\d\\.]+%?(?:\\s*,\\s*[\\d\\.]+%?)?)\\s*\\)|hsba?\\(\\s*([\\d\\.]+(?:deg|\\xb0|%)?\\s*,\\s*[\\d\\.]+%?\\s*,\\s*[\\d\\.]+(?:%?\\s*,\\s*[\\d\\.]+)?%?)\\s*\\)|hsla?\\(\\s*([\\d\\.]+(?:deg|\\xb0|%)?\\s*,\\s*[\\d\\.]+%?\\s*,\\s*[\\d\\.]+(?:%?\\s*,\\s*[\\d\\.]+)?%?)\\s*\\))\\s*$/i;a._.separator=\nRegExp(\"[,\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]+\");var S=RegExp(\"[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*,[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*\"),X={hs:1,rg:1},W=RegExp(\"([a-z])[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029,]*((-?\\\\d*\\\\.?\\\\d*(?:e[\\\\-+]?\\\\d+)?[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*,?[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*)+)\",\n\"ig\"),ma=RegExp(\"([rstm])[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029,]*((-?\\\\d*\\\\.?\\\\d*(?:e[\\\\-+]?\\\\d+)?[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*,?[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*)+)\",\"ig\"),Z=RegExp(\"(-?\\\\d*\\\\.?\\\\d*(?:e[\\\\-+]?\\\\d+)?)[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*,?[\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*\",\n\"ig\"),na=0,ba=\"S\"+(+new Date).toString(36),V=function(){return ba+(na++).toString(36)},m=\"http://www.w3.org/1999/xlink\",la=\"http://www.w3.org/2000/svg\",E={},ca=a.url=function(c){return\"url('#\"+c+\"')\"};a._.$=v;a._.id=V;a.format=function(){var c=/\\{([^\\}]+)\\}/g,a=/(?:(?:^|\\.)(.+?)(?=\\[|\\.|$|\\()|\\[('|\")(.+?)\\2\\])(\\(\\))?/g,b=function(c,b,m){var h=m;b.replace(a,function(c,a,b,m,t){a=a||m;h&&(a in h&&(h=h[a]),\"function\"==typeof h&&t&&(h=h()))});return h=(null==h||h==m?c:h)+\"\"};return function(a,m){return J(a).replace(c,\nfunction(c,a){return b(c,a,m)})}}();a._.clone=M;a._.cacher=A;a.rad=z;a.deg=function(c){return 180*c/C%360};a.angle=w;a.is=y;a.snapTo=function(c,a,b){b=y(b,\"finite\")?b:10;if(y(c,\"array\"))for(var m=c.length;m--;){if(Y(c[m]-a)<=b)return c[m]}else{c=+c;m=a%c;if(m<b)return a-m;if(m>c-b)return a-m+c}return a};a.getRGB=A(function(c){if(!c||(c=J(c)).indexOf(\"-\")+1)return{r:-1,g:-1,b:-1,hex:\"none\",error:1,toString:ka};if(\"none\"==c)return{r:-1,g:-1,b:-1,hex:\"none\",toString:ka};!X[h](c.toLowerCase().substring(0,\n2))&&\"#\"!=c.charAt()&&(c=T(c));if(!c)return{r:-1,g:-1,b:-1,hex:\"none\",error:1,toString:ka};var b,m,e,f,d;if(c=c.match(F)){c[2]&&(e=U(c[2].substring(5),16),m=U(c[2].substring(3,5),16),b=U(c[2].substring(1,3),16));c[3]&&(e=U((d=c[3].charAt(3))+d,16),m=U((d=c[3].charAt(2))+d,16),b=U((d=c[3].charAt(1))+d,16));c[4]&&(d=c[4].split(S),b=K(d[0]),\"%\"==d[0].slice(-1)&&(b*=2.55),m=K(d[1]),\"%\"==d[1].slice(-1)&&(m*=2.55),e=K(d[2]),\"%\"==d[2].slice(-1)&&(e*=2.55),\"rgba\"==c[1].toLowerCase().slice(0,4)&&(f=K(d[3])),\nd[3]&&\"%\"==d[3].slice(-1)&&(f/=100));if(c[5])return d=c[5].split(S),b=K(d[0]),\"%\"==d[0].slice(-1)&&(b/=100),m=K(d[1]),\"%\"==d[1].slice(-1)&&(m/=100),e=K(d[2]),\"%\"==d[2].slice(-1)&&(e/=100),\"deg\"!=d[0].slice(-3)&&\"\\u00b0\"!=d[0].slice(-1)||(b/=360),\"hsba\"==c[1].toLowerCase().slice(0,4)&&(f=K(d[3])),d[3]&&\"%\"==d[3].slice(-1)&&(f/=100),a.hsb2rgb(b,m,e,f);if(c[6])return d=c[6].split(S),b=K(d[0]),\"%\"==d[0].slice(-1)&&(b/=100),m=K(d[1]),\"%\"==d[1].slice(-1)&&(m/=100),e=K(d[2]),\"%\"==d[2].slice(-1)&&(e/=100),\n\"deg\"!=d[0].slice(-3)&&\"\\u00b0\"!=d[0].slice(-1)||(b/=360),\"hsla\"==c[1].toLowerCase().slice(0,4)&&(f=K(d[3])),d[3]&&\"%\"==d[3].slice(-1)&&(f/=100),a.hsl2rgb(b,m,e,f);b=Q(I.round(b),255);m=Q(I.round(m),255);e=Q(I.round(e),255);f=Q(P(f,0),1);c={r:b,g:m,b:e,toString:ka};c.hex=\"#\"+(16777216|e|m<<8|b<<16).toString(16).slice(1);c.opacity=y(f,\"finite\")?f:1;return c}return{r:-1,g:-1,b:-1,hex:\"none\",error:1,toString:ka}},a);a.hsb=A(function(c,b,m){return a.hsb2rgb(c,b,m).hex});a.hsl=A(function(c,b,m){return a.hsl2rgb(c,\nb,m).hex});a.rgb=A(function(c,a,b,m){if(y(m,\"finite\")){var e=I.round;return\"rgba(\"+[e(c),e(a),e(b),+m.toFixed(2)]+\")\"}return\"#\"+(16777216|b|a<<8|c<<16).toString(16).slice(1)});var T=function(c){var a=G.doc.getElementsByTagName(\"head\")[0]||G.doc.getElementsByTagName(\"svg\")[0];T=A(function(c){if(\"red\"==c.toLowerCase())return\"rgb(255, 0, 0)\";a.style.color=\"rgb(255, 0, 0)\";a.style.color=c;c=G.doc.defaultView.getComputedStyle(a,aa).getPropertyValue(\"color\");return\"rgb(255, 0, 0)\"==c?null:c});return T(c)},\nqa=function(){return\"hsb(\"+[this.h,this.s,this.b]+\")\"},ra=function(){return\"hsl(\"+[this.h,this.s,this.l]+\")\"},ka=function(){return 1==this.opacity||null==this.opacity?this.hex:\"rgba(\"+[this.r,this.g,this.b,this.opacity]+\")\"},D=function(c,b,m){null==b&&y(c,\"object\")&&\"r\"in c&&\"g\"in c&&\"b\"in c&&(m=c.b,b=c.g,c=c.r);null==b&&y(c,string)&&(m=a.getRGB(c),c=m.r,b=m.g,m=m.b);if(1<c||1<b||1<m)c/=255,b/=255,m/=255;return[c,b,m]},oa=function(c,b,m,e){c=I.round(255*c);b=I.round(255*b);m=I.round(255*m);c={r:c,\ng:b,b:m,opacity:y(e,\"finite\")?e:1,hex:a.rgb(c,b,m),toString:ka};y(e,\"finite\")&&(c.opacity=e);return c};a.color=function(c){var b;y(c,\"object\")&&\"h\"in c&&\"s\"in c&&\"b\"in c?(b=a.hsb2rgb(c),c.r=b.r,c.g=b.g,c.b=b.b,c.opacity=1,c.hex=b.hex):y(c,\"object\")&&\"h\"in c&&\"s\"in c&&\"l\"in c?(b=a.hsl2rgb(c),c.r=b.r,c.g=b.g,c.b=b.b,c.opacity=1,c.hex=b.hex):(y(c,\"string\")&&(c=a.getRGB(c)),y(c,\"object\")&&\"r\"in c&&\"g\"in c&&\"b\"in c&&!(\"error\"in c)?(b=a.rgb2hsl(c),c.h=b.h,c.s=b.s,c.l=b.l,b=a.rgb2hsb(c),c.v=b.b):(c={hex:\"none\"},\nc.r=c.g=c.b=c.h=c.s=c.v=c.l=-1,c.error=1));c.toString=ka;return c};a.hsb2rgb=function(c,a,b,m){y(c,\"object\")&&\"h\"in c&&\"s\"in c&&\"b\"in c&&(b=c.b,a=c.s,c=c.h,m=c.o);var e,h,d;c=360*c%360/60;d=b*a;a=d*(1-Y(c%2-1));b=e=h=b-d;c=~~c;b+=[d,a,0,0,a,d][c];e+=[a,d,d,a,0,0][c];h+=[0,0,a,d,d,a][c];return oa(b,e,h,m)};a.hsl2rgb=function(c,a,b,m){y(c,\"object\")&&\"h\"in c&&\"s\"in c&&\"l\"in c&&(b=c.l,a=c.s,c=c.h);if(1<c||1<a||1<b)c/=360,a/=100,b/=100;var e,h,d;c=360*c%360/60;d=2*a*(0.5>b?b:1-b);a=d*(1-Y(c%2-1));b=e=\nh=b-d/2;c=~~c;b+=[d,a,0,0,a,d][c];e+=[a,d,d,a,0,0][c];h+=[0,0,a,d,d,a][c];return oa(b,e,h,m)};a.rgb2hsb=function(c,a,b){b=D(c,a,b);c=b[0];a=b[1];b=b[2];var m,e;m=P(c,a,b);e=m-Q(c,a,b);c=((0==e?0:m==c?(a-b)/e:m==a?(b-c)/e+2:(c-a)/e+4)+360)%6*60/360;return{h:c,s:0==e?0:e/m,b:m,toString:qa}};a.rgb2hsl=function(c,a,b){b=D(c,a,b);c=b[0];a=b[1];b=b[2];var m,e,h;m=P(c,a,b);e=Q(c,a,b);h=m-e;c=((0==h?0:m==c?(a-b)/h:m==a?(b-c)/h+2:(c-a)/h+4)+360)%6*60/360;m=(m+e)/2;return{h:c,s:0==h?0:0.5>m?h/(2*m):h/(2-2*\nm),l:m,toString:ra}};a.parsePathString=function(c){if(!c)return null;var b=a.path(c);if(b.arr)return a.path.clone(b.arr);var m={a:7,c:6,o:2,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,u:3,z:0},e=[];y(c,\"array\")&&y(c[0],\"array\")&&(e=a.path.clone(c));e.length||J(c).replace(W,function(c,a,b){var h=[];c=a.toLowerCase();b.replace(Z,function(c,a){a&&h.push(+a)});\"m\"==c&&2<h.length&&(e.push([a].concat(h.splice(0,2))),c=\"l\",a=\"m\"==a?\"l\":\"L\");\"o\"==c&&1==h.length&&e.push([a,h[0]]);if(\"r\"==c)e.push([a].concat(h));else for(;h.length>=\nm[c]&&(e.push([a].concat(h.splice(0,m[c]))),m[c]););});e.toString=a.path.toString;b.arr=a.path.clone(e);return e};var O=a.parseTransformString=function(c){if(!c)return null;var b=[];y(c,\"array\")&&y(c[0],\"array\")&&(b=a.path.clone(c));b.length||J(c).replace(ma,function(c,a,m){var e=[];a.toLowerCase();m.replace(Z,function(c,a){a&&e.push(+a)});b.push([a].concat(e))});b.toString=a.path.toString;return b};a._.svgTransform2string=d;a._.rgTransform=RegExp(\"^[a-z][\\t\\n\\x0B\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029]*-?\\\\.?\\\\d\",\n\"i\");a._.transform2matrix=f;a._unit2px=b;a._.getSomeDefs=u;a._.getSomeSVG=p;a.select=function(c){return x(G.doc.querySelector(c))};a.selectAll=function(c){c=G.doc.querySelectorAll(c);for(var b=(a.set||Array)(),m=0;m<c.length;m++)b.push(x(c[m]));return b};setInterval(function(){for(var c in E)if(E[h](c)){var a=E[c],b=a.node;(\"svg\"!=a.type&&!b.ownerSVGElement||\"svg\"==a.type&&(!b.parentNode||\"ownerSVGElement\"in b.parentNode&&!b.ownerSVGElement))&&delete E[c]}},1E4);(function(c){function m(c){function a(c,\nb){var m=v(c.node,b);(m=(m=m&&m.match(d))&&m[2])&&\"#\"==m.charAt()&&(m=m.substring(1))&&(f[m]=(f[m]||[]).concat(function(a){var m={};m[b]=ca(a);v(c.node,m)}))}function b(c){var a=v(c.node,\"xlink:href\");a&&\"#\"==a.charAt()&&(a=a.substring(1))&&(f[a]=(f[a]||[]).concat(function(a){c.attr(\"xlink:href\",\"#\"+a)}))}var e=c.selectAll(\"*\"),h,d=/^\\s*url\\((\"|'|)(.*)\\1\\)\\s*$/;c=[];for(var f={},l=0,E=e.length;l<E;l++){h=e[l];a(h,\"fill\");a(h,\"stroke\");a(h,\"filter\");a(h,\"mask\");a(h,\"clip-path\");b(h);var t=v(h.node,\n\"id\");t&&(v(h.node,{id:h.id}),c.push({old:t,id:h.id}))}l=0;for(E=c.length;l<E;l++)if(e=f[c[l].old])for(h=0,t=e.length;h<t;h++)e[h](c[l].id)}function e(c,a,b){return function(m){m=m.slice(c,a);1==m.length&&(m=m[0]);return b?b(m):m}}function d(c){return function(){var a=c?\"<\"+this.type:\"\",b=this.node.attributes,m=this.node.childNodes;if(c)for(var e=0,h=b.length;e<h;e++)a+=\" \"+b[e].name+'=\"'+b[e].value.replace(/\"/g,'\\\\\"')+'\"';if(m.length){c&&(a+=\">\");e=0;for(h=m.length;e<h;e++)3==m[e].nodeType?a+=m[e].nodeValue:\n1==m[e].nodeType&&(a+=x(m[e]).toString());c&&(a+=\"</\"+this.type+\">\")}else c&&(a+=\"/>\");return a}}c.attr=function(c,a){if(!c)return this;if(y(c,\"string\"))if(1<arguments.length){var b={};b[c]=a;c=b}else return k(\"snap.util.getattr.\"+c,this).firstDefined();for(var m in c)c[h](m)&&k(\"snap.util.attr.\"+m,this,c[m]);return this};c.getBBox=function(c){if(!a.Matrix||!a.path)return this.node.getBBox();var b=this,m=new a.Matrix;if(b.removed)return a._.box();for(;\"use\"==b.type;)if(c||(m=m.add(b.transform().localMatrix.translate(b.attr(\"x\")||\n0,b.attr(\"y\")||0))),b.original)b=b.original;else var e=b.attr(\"xlink:href\"),b=b.original=b.node.ownerDocument.getElementById(e.substring(e.indexOf(\"#\")+1));var e=b._,h=a.path.get[b.type]||a.path.get.deflt;try{if(c)return e.bboxwt=h?a.path.getBBox(b.realPath=h(b)):a._.box(b.node.getBBox()),a._.box(e.bboxwt);b.realPath=h(b);b.matrix=b.transform().localMatrix;e.bbox=a.path.getBBox(a.path.map(b.realPath,m.add(b.matrix)));return a._.box(e.bbox)}catch(d){return a._.box()}};var f=function(){return this.string};\nc.transform=function(c){var b=this._;if(null==c){var m=this;c=new a.Matrix(this.node.getCTM());for(var e=n(this),h=[e],d=new a.Matrix,l=e.toTransformString(),b=J(e)==J(this.matrix)?J(b.transform):l;\"svg\"!=m.type&&(m=m.parent());)h.push(n(m));for(m=h.length;m--;)d.add(h[m]);return{string:b,globalMatrix:c,totalMatrix:d,localMatrix:e,diffMatrix:c.clone().add(e.invert()),global:c.toTransformString(),total:d.toTransformString(),local:l,toString:f}}c instanceof a.Matrix?this.matrix=c:n(this,c);this.node&&\n(\"linearGradient\"==this.type||\"radialGradient\"==this.type?v(this.node,{gradientTransform:this.matrix}):\"pattern\"==this.type?v(this.node,{patternTransform:this.matrix}):v(this.node,{transform:this.matrix}));return this};c.parent=function(){return x(this.node.parentNode)};c.append=c.add=function(c){if(c){if(\"set\"==c.type){var a=this;c.forEach(function(c){a.add(c)});return this}c=x(c);this.node.appendChild(c.node);c.paper=this.paper}return this};c.appendTo=function(c){c&&(c=x(c),c.append(this));return this};\nc.prepend=function(c){if(c){if(\"set\"==c.type){var a=this,b;c.forEach(function(c){b?b.after(c):a.prepend(c);b=c});return this}c=x(c);var m=c.parent();this.node.insertBefore(c.node,this.node.firstChild);this.add&&this.add();c.paper=this.paper;this.parent()&&this.parent().add();m&&m.add()}return this};c.prependTo=function(c){c=x(c);c.prepend(this);return this};c.before=function(c){if(\"set\"==c.type){var a=this;c.forEach(function(c){var b=c.parent();a.node.parentNode.insertBefore(c.node,a.node);b&&b.add()});\nthis.parent().add();return this}c=x(c);var b=c.parent();this.node.parentNode.insertBefore(c.node,this.node);this.parent()&&this.parent().add();b&&b.add();c.paper=this.paper;return this};c.after=function(c){c=x(c);var a=c.parent();this.node.nextSibling?this.node.parentNode.insertBefore(c.node,this.node.nextSibling):this.node.parentNode.appendChild(c.node);this.parent()&&this.parent().add();a&&a.add();c.paper=this.paper;return this};c.insertBefore=function(c){c=x(c);var a=this.parent();c.node.parentNode.insertBefore(this.node,\nc.node);this.paper=c.paper;a&&a.add();c.parent()&&c.parent().add();return this};c.insertAfter=function(c){c=x(c);var a=this.parent();c.node.parentNode.insertBefore(this.node,c.node.nextSibling);this.paper=c.paper;a&&a.add();c.parent()&&c.parent().add();return this};c.remove=function(){var c=this.parent();this.node.parentNode&&this.node.parentNode.removeChild(this.node);delete this.paper;this.removed=!0;c&&c.add();return this};c.select=function(c){return x(this.node.querySelector(c))};c.selectAll=\nfunction(c){c=this.node.querySelectorAll(c);for(var b=(a.set||Array)(),m=0;m<c.length;m++)b.push(x(c[m]));return b};c.asPX=function(c,a){null==a&&(a=this.attr(c));return+b(this,c,a)};c.use=function(){var c,a=this.node.id;a||(a=this.id,v(this.node,{id:a}));c=\"linearGradient\"==this.type||\"radialGradient\"==this.type||\"pattern\"==this.type?r(this.type,this.node.parentNode):r(\"use\",this.node.parentNode);v(c.node,{\"xlink:href\":\"#\"+a});c.original=this;return c};var l=/\\S+/g;c.addClass=function(c){var a=(c||\n\"\").match(l)||[];c=this.node;var b=c.className.baseVal,m=b.match(l)||[],e,h,d;if(a.length){for(e=0;d=a[e++];)h=m.indexOf(d),~h||m.push(d);a=m.join(\" \");b!=a&&(c.className.baseVal=a)}return this};c.removeClass=function(c){var a=(c||\"\").match(l)||[];c=this.node;var b=c.className.baseVal,m=b.match(l)||[],e,h;if(m.length){for(e=0;h=a[e++];)h=m.indexOf(h),~h&&m.splice(h,1);a=m.join(\" \");b!=a&&(c.className.baseVal=a)}return this};c.hasClass=function(c){return!!~(this.node.className.baseVal.match(l)||[]).indexOf(c)};\nc.toggleClass=function(c,a){if(null!=a)return a?this.addClass(c):this.removeClass(c);var b=(c||\"\").match(l)||[],m=this.node,e=m.className.baseVal,h=e.match(l)||[],d,f,E;for(d=0;E=b[d++];)f=h.indexOf(E),~f?h.splice(f,1):h.push(E);b=h.join(\" \");e!=b&&(m.className.baseVal=b);return this};c.clone=function(){var c=x(this.node.cloneNode(!0));v(c.node,\"id\")&&v(c.node,{id:c.id});m(c);c.insertAfter(this);return c};c.toDefs=function(){u(this).appendChild(this.node);return this};c.pattern=c.toPattern=function(c,\na,b,m){var e=r(\"pattern\",u(this));null==c&&(c=this.getBBox());y(c,\"object\")&&\"x\"in c&&(a=c.y,b=c.width,m=c.height,c=c.x);v(e.node,{x:c,y:a,width:b,height:m,patternUnits:\"userSpaceOnUse\",id:e.id,viewBox:[c,a,b,m].join(\" \")});e.node.appendChild(this.node);return e};c.marker=function(c,a,b,m,e,h){var d=r(\"marker\",u(this));null==c&&(c=this.getBBox());y(c,\"object\")&&\"x\"in c&&(a=c.y,b=c.width,m=c.height,e=c.refX||c.cx,h=c.refY||c.cy,c=c.x);v(d.node,{viewBox:[c,a,b,m].join(\" \"),markerWidth:b,markerHeight:m,\norient:\"auto\",refX:e||0,refY:h||0,id:d.id});d.node.appendChild(this.node);return d};var E=function(c,a,b,m){\"function\"!=typeof b||b.length||(m=b,b=L.linear);this.attr=c;this.dur=a;b&&(this.easing=b);m&&(this.callback=m)};a._.Animation=E;a.animation=function(c,a,b,m){return new E(c,a,b,m)};c.inAnim=function(){var c=[],a;for(a in this.anims)this.anims[h](a)&&function(a){c.push({anim:new E(a._attrs,a.dur,a.easing,a._callback),mina:a,curStatus:a.status(),status:function(c){return a.status(c)},stop:function(){a.stop()}})}(this.anims[a]);\nreturn c};a.animate=function(c,a,b,m,e,h){\"function\"!=typeof e||e.length||(h=e,e=L.linear);var d=L.time();c=L(c,a,d,d+m,L.time,b,e);h&&k.once(\"mina.finish.\"+c.id,h);return c};c.stop=function(){for(var c=this.inAnim(),a=0,b=c.length;a<b;a++)c[a].stop();return this};c.animate=function(c,a,b,m){\"function\"!=typeof b||b.length||(m=b,b=L.linear);c instanceof E&&(m=c.callback,b=c.easing,a=b.dur,c=c.attr);var d=[],f=[],l={},t,ca,n,T=this,q;for(q in c)if(c[h](q)){T.equal?(n=T.equal(q,J(c[q])),t=n.from,ca=\nn.to,n=n.f):(t=+T.attr(q),ca=+c[q]);var la=y(t,\"array\")?t.length:1;l[q]=e(d.length,d.length+la,n);d=d.concat(t);f=f.concat(ca)}t=L.time();var p=L(d,f,t,t+a,L.time,function(c){var a={},b;for(b in l)l[h](b)&&(a[b]=l[b](c));T.attr(a)},b);T.anims[p.id]=p;p._attrs=c;p._callback=m;k(\"snap.animcreated.\"+T.id,p);k.once(\"mina.finish.\"+p.id,function(){delete T.anims[p.id];m&&m.call(T)});k.once(\"mina.stop.\"+p.id,function(){delete T.anims[p.id]});return T};var T={};c.data=function(c,b){var m=T[this.id]=T[this.id]||\n{};if(0==arguments.length)return k(\"snap.data.get.\"+this.id,this,m,null),m;if(1==arguments.length){if(a.is(c,\"object\")){for(var e in c)c[h](e)&&this.data(e,c[e]);return this}k(\"snap.data.get.\"+this.id,this,m[c],c);return m[c]}m[c]=b;k(\"snap.data.set.\"+this.id,this,b,c);return this};c.removeData=function(c){null==c?T[this.id]={}:T[this.id]&&delete T[this.id][c];return this};c.outerSVG=c.toString=d(1);c.innerSVG=d()})(e.prototype);a.parse=function(c){var a=G.doc.createDocumentFragment(),b=!0,m=G.doc.createElement(\"div\");\nc=J(c);c.match(/^\\s*<\\s*svg(?:\\s|>)/)||(c=\"<svg>\"+c+\"</svg>\",b=!1);m.innerHTML=c;if(c=m.getElementsByTagName(\"svg\")[0])if(b)a=c;else for(;c.firstChild;)a.appendChild(c.firstChild);m.innerHTML=aa;return new l(a)};l.prototype.select=e.prototype.select;l.prototype.selectAll=e.prototype.selectAll;a.fragment=function(){for(var c=Array.prototype.slice.call(arguments,0),b=G.doc.createDocumentFragment(),m=0,e=c.length;m<e;m++){var h=c[m];h.node&&h.node.nodeType&&b.appendChild(h.node);h.nodeType&&b.appendChild(h);\n\"string\"==typeof h&&b.appendChild(a.parse(h).node)}return new l(b)};a._.make=r;a._.wrap=x;s.prototype.el=function(c,a){var b=r(c,this.node);a&&b.attr(a);return b};k.on(\"snap.util.getattr\",function(){var c=k.nt(),c=c.substring(c.lastIndexOf(\".\")+1),a=c.replace(/[A-Z]/g,function(c){return\"-\"+c.toLowerCase()});return pa[h](a)?this.node.ownerDocument.defaultView.getComputedStyle(this.node,null).getPropertyValue(a):v(this.node,c)});var pa={\"alignment-baseline\":0,\"baseline-shift\":0,clip:0,\"clip-path\":0,\n\"clip-rule\":0,color:0,\"color-interpolation\":0,\"color-interpolation-filters\":0,\"color-profile\":0,\"color-rendering\":0,cursor:0,direction:0,display:0,\"dominant-baseline\":0,\"enable-background\":0,fill:0,\"fill-opacity\":0,\"fill-rule\":0,filter:0,\"flood-color\":0,\"flood-opacity\":0,font:0,\"font-family\":0,\"font-size\":0,\"font-size-adjust\":0,\"font-stretch\":0,\"font-style\":0,\"font-variant\":0,\"font-weight\":0,\"glyph-orientation-horizontal\":0,\"glyph-orientation-vertical\":0,\"image-rendering\":0,kerning:0,\"letter-spacing\":0,\n\"lighting-color\":0,marker:0,\"marker-end\":0,\"marker-mid\":0,\"marker-start\":0,mask:0,opacity:0,overflow:0,\"pointer-events\":0,\"shape-rendering\":0,\"stop-color\":0,\"stop-opacity\":0,stroke:0,\"stroke-dasharray\":0,\"stroke-dashoffset\":0,\"stroke-linecap\":0,\"stroke-linejoin\":0,\"stroke-miterlimit\":0,\"stroke-opacity\":0,\"stroke-width\":0,\"text-anchor\":0,\"text-decoration\":0,\"text-rendering\":0,\"unicode-bidi\":0,visibility:0,\"word-spacing\":0,\"writing-mode\":0};k.on(\"snap.util.attr\",function(c){var a=k.nt(),b={},a=a.substring(a.lastIndexOf(\".\")+\n1);b[a]=c;var m=a.replace(/-(\\w)/gi,function(c,a){return a.toUpperCase()}),a=a.replace(/[A-Z]/g,function(c){return\"-\"+c.toLowerCase()});pa[h](a)?this.node.style[m]=null==c?aa:c:v(this.node,b)});a.ajax=function(c,a,b,m){var e=new XMLHttpRequest,h=V();if(e){if(y(a,\"function\"))m=b,b=a,a=null;else if(y(a,\"object\")){var d=[],f;for(f in a)a.hasOwnProperty(f)&&d.push(encodeURIComponent(f)+\"=\"+encodeURIComponent(a[f]));a=d.join(\"&\")}e.open(a?\"POST\":\"GET\",c,!0);a&&(e.setRequestHeader(\"X-Requested-With\",\"XMLHttpRequest\"),\ne.setRequestHeader(\"Content-type\",\"application/x-www-form-urlencoded\"));b&&(k.once(\"snap.ajax.\"+h+\".0\",b),k.once(\"snap.ajax.\"+h+\".200\",b),k.once(\"snap.ajax.\"+h+\".304\",b));e.onreadystatechange=function(){4==e.readyState&&k(\"snap.ajax.\"+h+\".\"+e.status,m,e)};if(4==e.readyState)return e;e.send(a);return e}};a.load=function(c,b,m){a.ajax(c,function(c){c=a.parse(c.responseText);m?b.call(m,c):b(c)})};a.getElementByPoint=function(c,a){var b,m,e=G.doc.elementFromPoint(c,a);if(G.win.opera&&\"svg\"==e.tagName){b=\ne;m=b.getBoundingClientRect();b=b.ownerDocument;var h=b.body,d=b.documentElement;b=m.top+(g.win.pageYOffset||d.scrollTop||h.scrollTop)-(d.clientTop||h.clientTop||0);m=m.left+(g.win.pageXOffset||d.scrollLeft||h.scrollLeft)-(d.clientLeft||h.clientLeft||0);h=e.createSVGRect();h.x=c-m;h.y=a-b;h.width=h.height=1;b=e.getIntersectionList(h,null);b.length&&(e=b[b.length-1])}return e?x(e):null};a.plugin=function(c){c(a,e,s,G,l)};return G.win.Snap=a}();C.plugin(function(a,k,y,M,A){function w(a,d,f,b,q,e){null==\nd&&\"[object SVGMatrix]\"==z.call(a)?(this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,this.f=a.f):null!=a?(this.a=+a,this.b=+d,this.c=+f,this.d=+b,this.e=+q,this.f=+e):(this.a=1,this.c=this.b=0,this.d=1,this.f=this.e=0)}var z=Object.prototype.toString,d=String,f=Math;(function(n){function k(a){return a[0]*a[0]+a[1]*a[1]}function p(a){var d=f.sqrt(k(a));a[0]&&(a[0]/=d);a[1]&&(a[1]/=d)}n.add=function(a,d,e,f,n,p){var k=[[],[],[]],u=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]];d=[[a,\ne,n],[d,f,p],[0,0,1]];a&&a instanceof w&&(d=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]);for(a=0;3>a;a++)for(e=0;3>e;e++){for(f=n=0;3>f;f++)n+=u[a][f]*d[f][e];k[a][e]=n}this.a=k[0][0];this.b=k[1][0];this.c=k[0][1];this.d=k[1][1];this.e=k[0][2];this.f=k[1][2];return this};n.invert=function(){var a=this.a*this.d-this.b*this.c;return new w(this.d/a,-this.b/a,-this.c/a,this.a/a,(this.c*this.f-this.d*this.e)/a,(this.b*this.e-this.a*this.f)/a)};n.clone=function(){return new w(this.a,this.b,this.c,this.d,this.e,\nthis.f)};n.translate=function(a,d){return this.add(1,0,0,1,a,d)};n.scale=function(a,d,e,f){null==d&&(d=a);(e||f)&&this.add(1,0,0,1,e,f);this.add(a,0,0,d,0,0);(e||f)&&this.add(1,0,0,1,-e,-f);return this};n.rotate=function(b,d,e){b=a.rad(b);d=d||0;e=e||0;var l=+f.cos(b).toFixed(9);b=+f.sin(b).toFixed(9);this.add(l,b,-b,l,d,e);return this.add(1,0,0,1,-d,-e)};n.x=function(a,d){return a*this.a+d*this.c+this.e};n.y=function(a,d){return a*this.b+d*this.d+this.f};n.get=function(a){return+this[d.fromCharCode(97+\na)].toFixed(4)};n.toString=function(){return\"matrix(\"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+\")\"};n.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]};n.determinant=function(){return this.a*this.d-this.b*this.c};n.split=function(){var b={};b.dx=this.e;b.dy=this.f;var d=[[this.a,this.c],[this.b,this.d]];b.scalex=f.sqrt(k(d[0]));p(d[0]);b.shear=d[0][0]*d[1][0]+d[0][1]*d[1][1];d[1]=[d[1][0]-d[0][0]*b.shear,d[1][1]-d[0][1]*b.shear];b.scaley=f.sqrt(k(d[1]));\np(d[1]);b.shear/=b.scaley;0>this.determinant()&&(b.scalex=-b.scalex);var e=-d[0][1],d=d[1][1];0>d?(b.rotate=a.deg(f.acos(d)),0>e&&(b.rotate=360-b.rotate)):b.rotate=a.deg(f.asin(e));b.isSimple=!+b.shear.toFixed(9)&&(b.scalex.toFixed(9)==b.scaley.toFixed(9)||!b.rotate);b.isSuperSimple=!+b.shear.toFixed(9)&&b.scalex.toFixed(9)==b.scaley.toFixed(9)&&!b.rotate;b.noRotation=!+b.shear.toFixed(9)&&!b.rotate;return b};n.toTransformString=function(a){a=a||this.split();if(+a.shear.toFixed(9))return\"m\"+[this.get(0),\nthis.get(1),this.get(2),this.get(3),this.get(4),this.get(5)];a.scalex=+a.scalex.toFixed(4);a.scaley=+a.scaley.toFixed(4);a.rotate=+a.rotate.toFixed(4);return(a.dx||a.dy?\"t\"+[+a.dx.toFixed(4),+a.dy.toFixed(4)]:\"\")+(1!=a.scalex||1!=a.scaley?\"s\"+[a.scalex,a.scaley,0,0]:\"\")+(a.rotate?\"r\"+[+a.rotate.toFixed(4),0,0]:\"\")}})(w.prototype);a.Matrix=w;a.matrix=function(a,d,f,b,k,e){return new w(a,d,f,b,k,e)}});C.plugin(function(a,v,y,M,A){function w(h){return function(d){k.stop();d instanceof A&&1==d.node.childNodes.length&&\n(\"radialGradient\"==d.node.firstChild.tagName||\"linearGradient\"==d.node.firstChild.tagName||\"pattern\"==d.node.firstChild.tagName)&&(d=d.node.firstChild,b(this).appendChild(d),d=u(d));if(d instanceof v)if(\"radialGradient\"==d.type||\"linearGradient\"==d.type||\"pattern\"==d.type){d.node.id||e(d.node,{id:d.id});var f=l(d.node.id)}else f=d.attr(h);else f=a.color(d),f.error?(f=a(b(this).ownerSVGElement).gradient(d))?(f.node.id||e(f.node,{id:f.id}),f=l(f.node.id)):f=d:f=r(f);d={};d[h]=f;e(this.node,d);this.node.style[h]=\nx}}function z(a){k.stop();a==+a&&(a+=\"px\");this.node.style.fontSize=a}function d(a){var b=[];a=a.childNodes;for(var e=0,f=a.length;e<f;e++){var l=a[e];3==l.nodeType&&b.push(l.nodeValue);\"tspan\"==l.tagName&&(1==l.childNodes.length&&3==l.firstChild.nodeType?b.push(l.firstChild.nodeValue):b.push(d(l)))}return b}function f(){k.stop();return this.node.style.fontSize}var n=a._.make,u=a._.wrap,p=a.is,b=a._.getSomeDefs,q=/^url\\(#?([^)]+)\\)$/,e=a._.$,l=a.url,r=String,s=a._.separator,x=\"\";k.on(\"snap.util.attr.mask\",\nfunction(a){if(a instanceof v||a instanceof A){k.stop();a instanceof A&&1==a.node.childNodes.length&&(a=a.node.firstChild,b(this).appendChild(a),a=u(a));if(\"mask\"==a.type)var d=a;else d=n(\"mask\",b(this)),d.node.appendChild(a.node);!d.node.id&&e(d.node,{id:d.id});e(this.node,{mask:l(d.id)})}});(function(a){k.on(\"snap.util.attr.clip\",a);k.on(\"snap.util.attr.clip-path\",a);k.on(\"snap.util.attr.clipPath\",a)})(function(a){if(a instanceof v||a instanceof A){k.stop();if(\"clipPath\"==a.type)var d=a;else d=\nn(\"clipPath\",b(this)),d.node.appendChild(a.node),!d.node.id&&e(d.node,{id:d.id});e(this.node,{\"clip-path\":l(d.id)})}});k.on(\"snap.util.attr.fill\",w(\"fill\"));k.on(\"snap.util.attr.stroke\",w(\"stroke\"));var G=/^([lr])(?:\\(([^)]*)\\))?(.*)$/i;k.on(\"snap.util.grad.parse\",function(a){a=r(a);var b=a.match(G);if(!b)return null;a=b[1];var e=b[2],b=b[3],e=e.split(/\\s*,\\s*/).map(function(a){return+a==a?+a:a});1==e.length&&0==e[0]&&(e=[]);b=b.split(\"-\");b=b.map(function(a){a=a.split(\":\");var b={color:a[0]};a[1]&&\n(b.offset=parseFloat(a[1]));return b});return{type:a,params:e,stops:b}});k.on(\"snap.util.attr.d\",function(b){k.stop();p(b,\"array\")&&p(b[0],\"array\")&&(b=a.path.toString.call(b));b=r(b);b.match(/[ruo]/i)&&(b=a.path.toAbsolute(b));e(this.node,{d:b})})(-1);k.on(\"snap.util.attr.#text\",function(a){k.stop();a=r(a);for(a=M.doc.createTextNode(a);this.node.firstChild;)this.node.removeChild(this.node.firstChild);this.node.appendChild(a)})(-1);k.on(\"snap.util.attr.path\",function(a){k.stop();this.attr({d:a})})(-1);\nk.on(\"snap.util.attr.class\",function(a){k.stop();this.node.className.baseVal=a})(-1);k.on(\"snap.util.attr.viewBox\",function(a){a=p(a,\"object\")&&\"x\"in a?[a.x,a.y,a.width,a.height].join(\" \"):p(a,\"array\")?a.join(\" \"):a;e(this.node,{viewBox:a});k.stop()})(-1);k.on(\"snap.util.attr.transform\",function(a){this.transform(a);k.stop()})(-1);k.on(\"snap.util.attr.r\",function(a){\"rect\"==this.type&&(k.stop(),e(this.node,{rx:a,ry:a}))})(-1);k.on(\"snap.util.attr.textpath\",function(a){k.stop();if(\"text\"==this.type){var d,\nf;if(!a&&this.textPath){for(a=this.textPath;a.node.firstChild;)this.node.appendChild(a.node.firstChild);a.remove();delete this.textPath}else if(p(a,\"string\")?(d=b(this),a=u(d.parentNode).path(a),d.appendChild(a.node),d=a.id,a.attr({id:d})):(a=u(a),a instanceof v&&(d=a.attr(\"id\"),d||(d=a.id,a.attr({id:d})))),d)if(a=this.textPath,f=this.node,a)a.attr({\"xlink:href\":\"#\"+d});else{for(a=e(\"textPath\",{\"xlink:href\":\"#\"+d});f.firstChild;)a.appendChild(f.firstChild);f.appendChild(a);this.textPath=u(a)}}})(-1);\nk.on(\"snap.util.attr.text\",function(a){if(\"text\"==this.type){for(var b=this.node,d=function(a){var b=e(\"tspan\");if(p(a,\"array\"))for(var f=0;f<a.length;f++)b.appendChild(d(a[f]));else b.appendChild(M.doc.createTextNode(a));b.normalize&&b.normalize();return b};b.firstChild;)b.removeChild(b.firstChild);for(a=d(a);a.firstChild;)b.appendChild(a.firstChild)}k.stop()})(-1);k.on(\"snap.util.attr.fontSize\",z)(-1);k.on(\"snap.util.attr.font-size\",z)(-1);k.on(\"snap.util.getattr.transform\",function(){k.stop();\nreturn this.transform()})(-1);k.on(\"snap.util.getattr.textpath\",function(){k.stop();return this.textPath})(-1);(function(){function b(d){return function(){k.stop();var b=M.doc.defaultView.getComputedStyle(this.node,null).getPropertyValue(\"marker-\"+d);return\"none\"==b?b:a(M.doc.getElementById(b.match(q)[1]))}}function d(a){return function(b){k.stop();var d=\"marker\"+a.charAt(0).toUpperCase()+a.substring(1);if(\"\"==b||!b)this.node.style[d]=\"none\";else if(\"marker\"==b.type){var f=b.node.id;f||e(b.node,{id:b.id});\nthis.node.style[d]=l(f)}}}k.on(\"snap.util.getattr.marker-end\",b(\"end\"))(-1);k.on(\"snap.util.getattr.markerEnd\",b(\"end\"))(-1);k.on(\"snap.util.getattr.marker-start\",b(\"start\"))(-1);k.on(\"snap.util.getattr.markerStart\",b(\"start\"))(-1);k.on(\"snap.util.getattr.marker-mid\",b(\"mid\"))(-1);k.on(\"snap.util.getattr.markerMid\",b(\"mid\"))(-1);k.on(\"snap.util.attr.marker-end\",d(\"end\"))(-1);k.on(\"snap.util.attr.markerEnd\",d(\"end\"))(-1);k.on(\"snap.util.attr.marker-start\",d(\"start\"))(-1);k.on(\"snap.util.attr.markerStart\",\nd(\"start\"))(-1);k.on(\"snap.util.attr.marker-mid\",d(\"mid\"))(-1);k.on(\"snap.util.attr.markerMid\",d(\"mid\"))(-1)})();k.on(\"snap.util.getattr.r\",function(){if(\"rect\"==this.type&&e(this.node,\"rx\")==e(this.node,\"ry\"))return k.stop(),e(this.node,\"rx\")})(-1);k.on(\"snap.util.getattr.text\",function(){if(\"text\"==this.type||\"tspan\"==this.type){k.stop();var a=d(this.node);return 1==a.length?a[0]:a}})(-1);k.on(\"snap.util.getattr.#text\",function(){return this.node.textContent})(-1);k.on(\"snap.util.getattr.viewBox\",\nfunction(){k.stop();var b=e(this.node,\"viewBox\");if(b)return b=b.split(s),a._.box(+b[0],+b[1],+b[2],+b[3])})(-1);k.on(\"snap.util.getattr.points\",function(){var a=e(this.node,\"points\");k.stop();if(a)return a.split(s)})(-1);k.on(\"snap.util.getattr.path\",function(){var a=e(this.node,\"d\");k.stop();return a})(-1);k.on(\"snap.util.getattr.class\",function(){return this.node.className.baseVal})(-1);k.on(\"snap.util.getattr.fontSize\",f)(-1);k.on(\"snap.util.getattr.font-size\",f)(-1)});C.plugin(function(a,v,y,\nM,A){function w(a){return a}function z(a){return function(b){return+b.toFixed(3)+a}}var d={\"+\":function(a,b){return a+b},\"-\":function(a,b){return a-b},\"/\":function(a,b){return a/b},\"*\":function(a,b){return a*b}},f=String,n=/[a-z]+$/i,u=/^\\s*([+\\-\\/*])\\s*=\\s*([\\d.eE+\\-]+)\\s*([^\\d\\s]+)?\\s*$/;k.on(\"snap.util.attr\",function(a){if(a=f(a).match(u)){var b=k.nt(),b=b.substring(b.lastIndexOf(\".\")+1),q=this.attr(b),e={};k.stop();var l=a[3]||\"\",r=q.match(n),s=d[a[1]];r&&r==l?a=s(parseFloat(q),+a[2]):(q=this.asPX(b),\na=s(this.asPX(b),this.asPX(b,a[2]+l)));isNaN(q)||isNaN(a)||(e[b]=a,this.attr(e))}})(-10);k.on(\"snap.util.equal\",function(a,b){var q=f(this.attr(a)||\"\"),e=f(b).match(u);if(e){k.stop();var l=e[3]||\"\",r=q.match(n),s=d[e[1]];if(r&&r==l)return{from:parseFloat(q),to:s(parseFloat(q),+e[2]),f:z(r)};q=this.asPX(a);return{from:q,to:s(q,this.asPX(a,e[2]+l)),f:w}}})(-10)});C.plugin(function(a,v,y,M,A){var w=y.prototype,z=a.is;w.rect=function(a,d,k,p,b,q){var e;null==q&&(q=b);z(a,\"object\")&&\"[object Object]\"==\na?e=a:null!=a&&(e={x:a,y:d,width:k,height:p},null!=b&&(e.rx=b,e.ry=q));return this.el(\"rect\",e)};w.circle=function(a,d,k){var p;z(a,\"object\")&&\"[object Object]\"==a?p=a:null!=a&&(p={cx:a,cy:d,r:k});return this.el(\"circle\",p)};var d=function(){function a(){this.parentNode.removeChild(this)}return function(d,k){var p=M.doc.createElement(\"img\"),b=M.doc.body;p.style.cssText=\"position:absolute;left:-9999em;top:-9999em\";p.onload=function(){k.call(p);p.onload=p.onerror=null;b.removeChild(p)};p.onerror=a;\nb.appendChild(p);p.src=d}}();w.image=function(f,n,k,p,b){var q=this.el(\"image\");if(z(f,\"object\")&&\"src\"in f)q.attr(f);else if(null!=f){var e={\"xlink:href\":f,preserveAspectRatio:\"none\"};null!=n&&null!=k&&(e.x=n,e.y=k);null!=p&&null!=b?(e.width=p,e.height=b):d(f,function(){a._.$(q.node,{width:this.offsetWidth,height:this.offsetHeight})});a._.$(q.node,e)}return q};w.ellipse=function(a,d,k,p){var b;z(a,\"object\")&&\"[object Object]\"==a?b=a:null!=a&&(b={cx:a,cy:d,rx:k,ry:p});return this.el(\"ellipse\",b)};\nw.path=function(a){var d;z(a,\"object\")&&!z(a,\"array\")?d=a:a&&(d={d:a});return this.el(\"path\",d)};w.group=w.g=function(a){var d=this.el(\"g\");1==arguments.length&&a&&!a.type?d.attr(a):arguments.length&&d.add(Array.prototype.slice.call(arguments,0));return d};w.svg=function(a,d,k,p,b,q,e,l){var r={};z(a,\"object\")&&null==d?r=a:(null!=a&&(r.x=a),null!=d&&(r.y=d),null!=k&&(r.width=k),null!=p&&(r.height=p),null!=b&&null!=q&&null!=e&&null!=l&&(r.viewBox=[b,q,e,l]));return this.el(\"svg\",r)};w.mask=function(a){var d=\nthis.el(\"mask\");1==arguments.length&&a&&!a.type?d.attr(a):arguments.length&&d.add(Array.prototype.slice.call(arguments,0));return d};w.ptrn=function(a,d,k,p,b,q,e,l){if(z(a,\"object\"))var r=a;else arguments.length?(r={},null!=a&&(r.x=a),null!=d&&(r.y=d),null!=k&&(r.width=k),null!=p&&(r.height=p),null!=b&&null!=q&&null!=e&&null!=l&&(r.viewBox=[b,q,e,l])):r={patternUnits:\"userSpaceOnUse\"};return this.el(\"pattern\",r)};w.use=function(a){return null!=a?(make(\"use\",this.node),a instanceof v&&(a.attr(\"id\")||\na.attr({id:ID()}),a=a.attr(\"id\")),this.el(\"use\",{\"xlink:href\":a})):v.prototype.use.call(this)};w.text=function(a,d,k){var p={};z(a,\"object\")?p=a:null!=a&&(p={x:a,y:d,text:k||\"\"});return this.el(\"text\",p)};w.line=function(a,d,k,p){var b={};z(a,\"object\")?b=a:null!=a&&(b={x1:a,x2:k,y1:d,y2:p});return this.el(\"line\",b)};w.polyline=function(a){1<arguments.length&&(a=Array.prototype.slice.call(arguments,0));var d={};z(a,\"object\")&&!z(a,\"array\")?d=a:null!=a&&(d={points:a});return this.el(\"polyline\",d)};\nw.polygon=function(a){1<arguments.length&&(a=Array.prototype.slice.call(arguments,0));var d={};z(a,\"object\")&&!z(a,\"array\")?d=a:null!=a&&(d={points:a});return this.el(\"polygon\",d)};(function(){function d(){return this.selectAll(\"stop\")}function n(b,d){var f=e(\"stop\"),k={offset:+d+\"%\"};b=a.color(b);k[\"stop-color\"]=b.hex;1>b.opacity&&(k[\"stop-opacity\"]=b.opacity);e(f,k);this.node.appendChild(f);return this}function u(){if(\"linearGradient\"==this.type){var b=e(this.node,\"x1\")||0,d=e(this.node,\"x2\")||\n1,f=e(this.node,\"y1\")||0,k=e(this.node,\"y2\")||0;return a._.box(b,f,math.abs(d-b),math.abs(k-f))}b=this.node.r||0;return a._.box((this.node.cx||0.5)-b,(this.node.cy||0.5)-b,2*b,2*b)}function p(a,d){function f(a,b){for(var d=(b-u)/(a-w),e=w;e<a;e++)h[e].offset=+(+u+d*(e-w)).toFixed(2);w=a;u=b}var n=k(\"snap.util.grad.parse\",null,d).firstDefined(),p;if(!n)return null;n.params.unshift(a);p=\"l\"==n.type.toLowerCase()?b.apply(0,n.params):q.apply(0,n.params);n.type!=n.type.toLowerCase()&&e(p.node,{gradientUnits:\"userSpaceOnUse\"});\nvar h=n.stops,n=h.length,u=0,w=0;n--;for(var v=0;v<n;v++)\"offset\"in h[v]&&f(v,h[v].offset);h[n].offset=h[n].offset||100;f(n,h[n].offset);for(v=0;v<=n;v++){var y=h[v];p.addStop(y.color,y.offset)}return p}function b(b,k,p,q,w){b=a._.make(\"linearGradient\",b);b.stops=d;b.addStop=n;b.getBBox=u;null!=k&&e(b.node,{x1:k,y1:p,x2:q,y2:w});return b}function q(b,k,p,q,w,h){b=a._.make(\"radialGradient\",b);b.stops=d;b.addStop=n;b.getBBox=u;null!=k&&e(b.node,{cx:k,cy:p,r:q});null!=w&&null!=h&&e(b.node,{fx:w,fy:h});\nreturn b}var e=a._.$;w.gradient=function(a){return p(this.defs,a)};w.gradientLinear=function(a,d,e,f){return b(this.defs,a,d,e,f)};w.gradientRadial=function(a,b,d,e,f){return q(this.defs,a,b,d,e,f)};w.toString=function(){var b=this.node.ownerDocument,d=b.createDocumentFragment(),b=b.createElement(\"div\"),e=this.node.cloneNode(!0);d.appendChild(b);b.appendChild(e);a._.$(e,{xmlns:\"http://www.w3.org/2000/svg\"});b=b.innerHTML;d.removeChild(d.firstChild);return b};w.clear=function(){for(var a=this.node.firstChild,\nb;a;)b=a.nextSibling,\"defs\"!=a.tagName?a.parentNode.removeChild(a):w.clear.call({node:a}),a=b}})()});C.plugin(function(a,k,y,M){function A(a){var b=A.ps=A.ps||{};b[a]?b[a].sleep=100:b[a]={sleep:100};setTimeout(function(){for(var d in b)b[L](d)&&d!=a&&(b[d].sleep--,!b[d].sleep&&delete b[d])});return b[a]}function w(a,b,d,e){null==a&&(a=b=d=e=0);null==b&&(b=a.y,d=a.width,e=a.height,a=a.x);return{x:a,y:b,width:d,w:d,height:e,h:e,x2:a+d,y2:b+e,cx:a+d/2,cy:b+e/2,r1:F.min(d,e)/2,r2:F.max(d,e)/2,r0:F.sqrt(d*\nd+e*e)/2,path:s(a,b,d,e),vb:[a,b,d,e].join(\" \")}}function z(){return this.join(\",\").replace(N,\"$1\")}function d(a){a=C(a);a.toString=z;return a}function f(a,b,d,h,f,k,l,n,p){if(null==p)return e(a,b,d,h,f,k,l,n);if(0>p||e(a,b,d,h,f,k,l,n)<p)p=void 0;else{var q=0.5,O=1-q,s;for(s=e(a,b,d,h,f,k,l,n,O);0.01<Z(s-p);)q/=2,O+=(s<p?1:-1)*q,s=e(a,b,d,h,f,k,l,n,O);p=O}return u(a,b,d,h,f,k,l,n,p)}function n(b,d){function e(a){return+(+a).toFixed(3)}return a._.cacher(function(a,h,l){a instanceof k&&(a=a.attr(\"d\"));\na=I(a);for(var n,p,D,q,O=\"\",s={},c=0,t=0,r=a.length;t<r;t++){D=a[t];if(\"M\"==D[0])n=+D[1],p=+D[2];else{q=f(n,p,D[1],D[2],D[3],D[4],D[5],D[6]);if(c+q>h){if(d&&!s.start){n=f(n,p,D[1],D[2],D[3],D[4],D[5],D[6],h-c);O+=[\"C\"+e(n.start.x),e(n.start.y),e(n.m.x),e(n.m.y),e(n.x),e(n.y)];if(l)return O;s.start=O;O=[\"M\"+e(n.x),e(n.y)+\"C\"+e(n.n.x),e(n.n.y),e(n.end.x),e(n.end.y),e(D[5]),e(D[6])].join();c+=q;n=+D[5];p=+D[6];continue}if(!b&&!d)return n=f(n,p,D[1],D[2],D[3],D[4],D[5],D[6],h-c)}c+=q;n=+D[5];p=+D[6]}O+=\nD.shift()+D}s.end=O;return n=b?c:d?s:u(n,p,D[0],D[1],D[2],D[3],D[4],D[5],1)},null,a._.clone)}function u(a,b,d,e,h,f,k,l,n){var p=1-n,q=ma(p,3),s=ma(p,2),c=n*n,t=c*n,r=q*a+3*s*n*d+3*p*n*n*h+t*k,q=q*b+3*s*n*e+3*p*n*n*f+t*l,s=a+2*n*(d-a)+c*(h-2*d+a),t=b+2*n*(e-b)+c*(f-2*e+b),x=d+2*n*(h-d)+c*(k-2*h+d),c=e+2*n*(f-e)+c*(l-2*f+e);a=p*a+n*d;b=p*b+n*e;h=p*h+n*k;f=p*f+n*l;l=90-180*F.atan2(s-x,t-c)/S;return{x:r,y:q,m:{x:s,y:t},n:{x:x,y:c},start:{x:a,y:b},end:{x:h,y:f},alpha:l}}function p(b,d,e,h,f,n,k,l){a.is(b,\n\"array\")||(b=[b,d,e,h,f,n,k,l]);b=U.apply(null,b);return w(b.min.x,b.min.y,b.max.x-b.min.x,b.max.y-b.min.y)}function b(a,b,d){return b>=a.x&&b<=a.x+a.width&&d>=a.y&&d<=a.y+a.height}function q(a,d){a=w(a);d=w(d);return b(d,a.x,a.y)||b(d,a.x2,a.y)||b(d,a.x,a.y2)||b(d,a.x2,a.y2)||b(a,d.x,d.y)||b(a,d.x2,d.y)||b(a,d.x,d.y2)||b(a,d.x2,d.y2)||(a.x<d.x2&&a.x>d.x||d.x<a.x2&&d.x>a.x)&&(a.y<d.y2&&a.y>d.y||d.y<a.y2&&d.y>a.y)}function e(a,b,d,e,h,f,n,k,l){null==l&&(l=1);l=(1<l?1:0>l?0:l)/2;for(var p=[-0.1252,\n0.1252,-0.3678,0.3678,-0.5873,0.5873,-0.7699,0.7699,-0.9041,0.9041,-0.9816,0.9816],q=[0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472],s=0,c=0;12>c;c++)var t=l*p[c]+l,r=t*(t*(-3*a+9*d-9*h+3*n)+6*a-12*d+6*h)-3*a+3*d,t=t*(t*(-3*b+9*e-9*f+3*k)+6*b-12*e+6*f)-3*b+3*e,s=s+q[c]*F.sqrt(r*r+t*t);return l*s}function l(a,b,d){a=I(a);b=I(b);for(var h,f,l,n,k,s,r,O,x,c,t=d?0:[],w=0,v=a.length;w<v;w++)if(x=a[w],\"M\"==x[0])h=k=x[1],f=s=x[2];else{\"C\"==x[0]?(x=[h,f].concat(x.slice(1)),\nh=x[6],f=x[7]):(x=[h,f,h,f,k,s,k,s],h=k,f=s);for(var G=0,y=b.length;G<y;G++)if(c=b[G],\"M\"==c[0])l=r=c[1],n=O=c[2];else{\"C\"==c[0]?(c=[l,n].concat(c.slice(1)),l=c[6],n=c[7]):(c=[l,n,l,n,r,O,r,O],l=r,n=O);var z;var K=x,B=c;z=d;var H=p(K),J=p(B);if(q(H,J)){for(var H=e.apply(0,K),J=e.apply(0,B),H=~~(H/8),J=~~(J/8),U=[],A=[],F={},M=z?0:[],P=0;P<H+1;P++){var C=u.apply(0,K.concat(P/H));U.push({x:C.x,y:C.y,t:P/H})}for(P=0;P<J+1;P++)C=u.apply(0,B.concat(P/J)),A.push({x:C.x,y:C.y,t:P/J});for(P=0;P<H;P++)for(K=\n0;K<J;K++){var Q=U[P],L=U[P+1],B=A[K],C=A[K+1],N=0.001>Z(L.x-Q.x)?\"y\":\"x\",S=0.001>Z(C.x-B.x)?\"y\":\"x\",R;R=Q.x;var Y=Q.y,V=L.x,ea=L.y,fa=B.x,ga=B.y,ha=C.x,ia=C.y;if(W(R,V)<X(fa,ha)||X(R,V)>W(fa,ha)||W(Y,ea)<X(ga,ia)||X(Y,ea)>W(ga,ia))R=void 0;else{var $=(R*ea-Y*V)*(fa-ha)-(R-V)*(fa*ia-ga*ha),aa=(R*ea-Y*V)*(ga-ia)-(Y-ea)*(fa*ia-ga*ha),ja=(R-V)*(ga-ia)-(Y-ea)*(fa-ha);if(ja){var $=$/ja,aa=aa/ja,ja=+$.toFixed(2),ba=+aa.toFixed(2);R=ja<+X(R,V).toFixed(2)||ja>+W(R,V).toFixed(2)||ja<+X(fa,ha).toFixed(2)||\nja>+W(fa,ha).toFixed(2)||ba<+X(Y,ea).toFixed(2)||ba>+W(Y,ea).toFixed(2)||ba<+X(ga,ia).toFixed(2)||ba>+W(ga,ia).toFixed(2)?void 0:{x:$,y:aa}}else R=void 0}R&&F[R.x.toFixed(4)]!=R.y.toFixed(4)&&(F[R.x.toFixed(4)]=R.y.toFixed(4),Q=Q.t+Z((R[N]-Q[N])/(L[N]-Q[N]))*(L.t-Q.t),B=B.t+Z((R[S]-B[S])/(C[S]-B[S]))*(C.t-B.t),0<=Q&&1>=Q&&0<=B&&1>=B&&(z?M++:M.push({x:R.x,y:R.y,t1:Q,t2:B})))}z=M}else z=z?0:[];if(d)t+=z;else{H=0;for(J=z.length;H<J;H++)z[H].segment1=w,z[H].segment2=G,z[H].bez1=x,z[H].bez2=c;t=t.concat(z)}}}return t}\nfunction r(a){var b=A(a);if(b.bbox)return C(b.bbox);if(!a)return w();a=I(a);for(var d=0,e=0,h=[],f=[],l,n=0,k=a.length;n<k;n++)l=a[n],\"M\"==l[0]?(d=l[1],e=l[2],h.push(d),f.push(e)):(d=U(d,e,l[1],l[2],l[3],l[4],l[5],l[6]),h=h.concat(d.min.x,d.max.x),f=f.concat(d.min.y,d.max.y),d=l[5],e=l[6]);a=X.apply(0,h);l=X.apply(0,f);h=W.apply(0,h);f=W.apply(0,f);f=w(a,l,h-a,f-l);b.bbox=C(f);return f}function s(a,b,d,e,h){if(h)return[[\"M\",+a+ +h,b],[\"l\",d-2*h,0],[\"a\",h,h,0,0,1,h,h],[\"l\",0,e-2*h],[\"a\",h,h,0,0,1,\n-h,h],[\"l\",2*h-d,0],[\"a\",h,h,0,0,1,-h,-h],[\"l\",0,2*h-e],[\"a\",h,h,0,0,1,h,-h],[\"z\"]];a=[[\"M\",a,b],[\"l\",d,0],[\"l\",0,e],[\"l\",-d,0],[\"z\"]];a.toString=z;return a}function x(a,b,d,e,h){null==h&&null==e&&(e=d);a=+a;b=+b;d=+d;e=+e;if(null!=h){var f=Math.PI/180,l=a+d*Math.cos(-e*f);a+=d*Math.cos(-h*f);var n=b+d*Math.sin(-e*f);b+=d*Math.sin(-h*f);d=[[\"M\",l,n],[\"A\",d,d,0,+(180<h-e),0,a,b]]}else d=[[\"M\",a,b],[\"m\",0,-e],[\"a\",d,e,0,1,1,0,2*e],[\"a\",d,e,0,1,1,0,-2*e],[\"z\"]];d.toString=z;return d}function G(b){var e=\nA(b);if(e.abs)return d(e.abs);Q(b,\"array\")&&Q(b&&b[0],\"array\")||(b=a.parsePathString(b));if(!b||!b.length)return[[\"M\",0,0]];var h=[],f=0,l=0,n=0,k=0,p=0;\"M\"==b[0][0]&&(f=+b[0][1],l=+b[0][2],n=f,k=l,p++,h[0]=[\"M\",f,l]);for(var q=3==b.length&&\"M\"==b[0][0]&&\"R\"==b[1][0].toUpperCase()&&\"Z\"==b[2][0].toUpperCase(),s,r,w=p,c=b.length;w<c;w++){h.push(s=[]);r=b[w];p=r[0];if(p!=p.toUpperCase())switch(s[0]=p.toUpperCase(),s[0]){case \"A\":s[1]=r[1];s[2]=r[2];s[3]=r[3];s[4]=r[4];s[5]=r[5];s[6]=+r[6]+f;s[7]=+r[7]+\nl;break;case \"V\":s[1]=+r[1]+l;break;case \"H\":s[1]=+r[1]+f;break;case \"R\":for(var t=[f,l].concat(r.slice(1)),u=2,v=t.length;u<v;u++)t[u]=+t[u]+f,t[++u]=+t[u]+l;h.pop();h=h.concat(P(t,q));break;case \"O\":h.pop();t=x(f,l,r[1],r[2]);t.push(t[0]);h=h.concat(t);break;case \"U\":h.pop();h=h.concat(x(f,l,r[1],r[2],r[3]));s=[\"U\"].concat(h[h.length-1].slice(-2));break;case \"M\":n=+r[1]+f,k=+r[2]+l;default:for(u=1,v=r.length;u<v;u++)s[u]=+r[u]+(u%2?f:l)}else if(\"R\"==p)t=[f,l].concat(r.slice(1)),h.pop(),h=h.concat(P(t,\nq)),s=[\"R\"].concat(r.slice(-2));else if(\"O\"==p)h.pop(),t=x(f,l,r[1],r[2]),t.push(t[0]),h=h.concat(t);else if(\"U\"==p)h.pop(),h=h.concat(x(f,l,r[1],r[2],r[3])),s=[\"U\"].concat(h[h.length-1].slice(-2));else for(t=0,u=r.length;t<u;t++)s[t]=r[t];p=p.toUpperCase();if(\"O\"!=p)switch(s[0]){case \"Z\":f=+n;l=+k;break;case \"H\":f=s[1];break;case \"V\":l=s[1];break;case \"M\":n=s[s.length-2],k=s[s.length-1];default:f=s[s.length-2],l=s[s.length-1]}}h.toString=z;e.abs=d(h);return h}function h(a,b,d,e){return[a,b,d,e,d,\ne]}function J(a,b,d,e,h,f){var l=1/3,n=2/3;return[l*a+n*d,l*b+n*e,l*h+n*d,l*f+n*e,h,f]}function K(b,d,e,h,f,l,n,k,p,s){var r=120*S/180,q=S/180*(+f||0),c=[],t,x=a._.cacher(function(a,b,c){var d=a*F.cos(c)-b*F.sin(c);a=a*F.sin(c)+b*F.cos(c);return{x:d,y:a}});if(s)v=s[0],t=s[1],l=s[2],u=s[3];else{t=x(b,d,-q);b=t.x;d=t.y;t=x(k,p,-q);k=t.x;p=t.y;F.cos(S/180*f);F.sin(S/180*f);t=(b-k)/2;v=(d-p)/2;u=t*t/(e*e)+v*v/(h*h);1<u&&(u=F.sqrt(u),e*=u,h*=u);var u=e*e,w=h*h,u=(l==n?-1:1)*F.sqrt(Z((u*w-u*v*v-w*t*t)/\n(u*v*v+w*t*t)));l=u*e*v/h+(b+k)/2;var u=u*-h*t/e+(d+p)/2,v=F.asin(((d-u)/h).toFixed(9));t=F.asin(((p-u)/h).toFixed(9));v=b<l?S-v:v;t=k<l?S-t:t;0>v&&(v=2*S+v);0>t&&(t=2*S+t);n&&v>t&&(v-=2*S);!n&&t>v&&(t-=2*S)}if(Z(t-v)>r){var c=t,w=k,G=p;t=v+r*(n&&t>v?1:-1);k=l+e*F.cos(t);p=u+h*F.sin(t);c=K(k,p,e,h,f,0,n,w,G,[t,c,l,u])}l=t-v;f=F.cos(v);r=F.sin(v);n=F.cos(t);t=F.sin(t);l=F.tan(l/4);e=4/3*e*l;l*=4/3*h;h=[b,d];b=[b+e*r,d-l*f];d=[k+e*t,p-l*n];k=[k,p];b[0]=2*h[0]-b[0];b[1]=2*h[1]-b[1];if(s)return[b,d,k].concat(c);\nc=[b,d,k].concat(c).join().split(\",\");s=[];k=0;for(p=c.length;k<p;k++)s[k]=k%2?x(c[k-1],c[k],q).y:x(c[k],c[k+1],q).x;return s}function U(a,b,d,e,h,f,l,k){for(var n=[],p=[[],[]],s,r,c,t,q=0;2>q;++q)0==q?(r=6*a-12*d+6*h,s=-3*a+9*d-9*h+3*l,c=3*d-3*a):(r=6*b-12*e+6*f,s=-3*b+9*e-9*f+3*k,c=3*e-3*b),1E-12>Z(s)?1E-12>Z(r)||(s=-c/r,0<s&&1>s&&n.push(s)):(t=r*r-4*c*s,c=F.sqrt(t),0>t||(t=(-r+c)/(2*s),0<t&&1>t&&n.push(t),s=(-r-c)/(2*s),0<s&&1>s&&n.push(s)));for(r=q=n.length;q--;)s=n[q],c=1-s,p[0][q]=c*c*c*a+3*\nc*c*s*d+3*c*s*s*h+s*s*s*l,p[1][q]=c*c*c*b+3*c*c*s*e+3*c*s*s*f+s*s*s*k;p[0][r]=a;p[1][r]=b;p[0][r+1]=l;p[1][r+1]=k;p[0].length=p[1].length=r+2;return{min:{x:X.apply(0,p[0]),y:X.apply(0,p[1])},max:{x:W.apply(0,p[0]),y:W.apply(0,p[1])}}}function I(a,b){var e=!b&&A(a);if(!b&&e.curve)return d(e.curve);var f=G(a),l=b&&G(b),n={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},k={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},p=function(a,b,c){if(!a)return[\"C\",b.x,b.y,b.x,b.y,b.x,b.y];a[0]in{T:1,Q:1}||(b.qx=b.qy=null);\nswitch(a[0]){case \"M\":b.X=a[1];b.Y=a[2];break;case \"A\":a=[\"C\"].concat(K.apply(0,[b.x,b.y].concat(a.slice(1))));break;case \"S\":\"C\"==c||\"S\"==c?(c=2*b.x-b.bx,b=2*b.y-b.by):(c=b.x,b=b.y);a=[\"C\",c,b].concat(a.slice(1));break;case \"T\":\"Q\"==c||\"T\"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y);a=[\"C\"].concat(J(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case \"Q\":b.qx=a[1];b.qy=a[2];a=[\"C\"].concat(J(b.x,b.y,a[1],a[2],a[3],a[4]));break;case \"L\":a=[\"C\"].concat(h(b.x,b.y,a[1],a[2]));break;case \"H\":a=[\"C\"].concat(h(b.x,\nb.y,a[1],b.y));break;case \"V\":a=[\"C\"].concat(h(b.x,b.y,b.x,a[1]));break;case \"Z\":a=[\"C\"].concat(h(b.x,b.y,b.X,b.Y))}return a},s=function(a,b){if(7<a[b].length){a[b].shift();for(var c=a[b];c.length;)q[b]=\"A\",l&&(u[b]=\"A\"),a.splice(b++,0,[\"C\"].concat(c.splice(0,6)));a.splice(b,1);v=W(f.length,l&&l.length||0)}},r=function(a,b,c,d,e){a&&b&&\"M\"==a[e][0]&&\"M\"!=b[e][0]&&(b.splice(e,0,[\"M\",d.x,d.y]),c.bx=0,c.by=0,c.x=a[e][1],c.y=a[e][2],v=W(f.length,l&&l.length||0))},q=[],u=[],c=\"\",t=\"\",x=0,v=W(f.length,\nl&&l.length||0);for(;x<v;x++){f[x]&&(c=f[x][0]);\"C\"!=c&&(q[x]=c,x&&(t=q[x-1]));f[x]=p(f[x],n,t);\"A\"!=q[x]&&\"C\"==c&&(q[x]=\"C\");s(f,x);l&&(l[x]&&(c=l[x][0]),\"C\"!=c&&(u[x]=c,x&&(t=u[x-1])),l[x]=p(l[x],k,t),\"A\"!=u[x]&&\"C\"==c&&(u[x]=\"C\"),s(l,x));r(f,l,n,k,x);r(l,f,k,n,x);var w=f[x],z=l&&l[x],y=w.length,U=l&&z.length;n.x=w[y-2];n.y=w[y-1];n.bx=$(w[y-4])||n.x;n.by=$(w[y-3])||n.y;k.bx=l&&($(z[U-4])||k.x);k.by=l&&($(z[U-3])||k.y);k.x=l&&z[U-2];k.y=l&&z[U-1]}l||(e.curve=d(f));return l?[f,l]:f}function P(a,\nb){for(var d=[],e=0,h=a.length;h-2*!b>e;e+=2){var f=[{x:+a[e-2],y:+a[e-1]},{x:+a[e],y:+a[e+1]},{x:+a[e+2],y:+a[e+3]},{x:+a[e+4],y:+a[e+5]}];b?e?h-4==e?f[3]={x:+a[0],y:+a[1]}:h-2==e&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[h-2],y:+a[h-1]}:h-4==e?f[3]=f[2]:e||(f[0]={x:+a[e],y:+a[e+1]});d.push([\"C\",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return d}y=k.prototype;var Q=a.is,C=a._.clone,L=\"hasOwnProperty\",\nN=/,?([a-z]),?/gi,$=parseFloat,F=Math,S=F.PI,X=F.min,W=F.max,ma=F.pow,Z=F.abs;M=n(1);var na=n(),ba=n(0,1),V=a._unit2px;a.path=A;a.path.getTotalLength=M;a.path.getPointAtLength=na;a.path.getSubpath=function(a,b,d){if(1E-6>this.getTotalLength(a)-d)return ba(a,b).end;a=ba(a,d,1);return b?ba(a,b).end:a};y.getTotalLength=function(){if(this.node.getTotalLength)return this.node.getTotalLength()};y.getPointAtLength=function(a){return na(this.attr(\"d\"),a)};y.getSubpath=function(b,d){return a.path.getSubpath(this.attr(\"d\"),\nb,d)};a._.box=w;a.path.findDotsAtSegment=u;a.path.bezierBBox=p;a.path.isPointInsideBBox=b;a.path.isBBoxIntersect=q;a.path.intersection=function(a,b){return l(a,b)};a.path.intersectionNumber=function(a,b){return l(a,b,1)};a.path.isPointInside=function(a,d,e){var h=r(a);return b(h,d,e)&&1==l(a,[[\"M\",d,e],[\"H\",h.x2+10]],1)%2};a.path.getBBox=r;a.path.get={path:function(a){return a.attr(\"path\")},circle:function(a){a=V(a);return x(a.cx,a.cy,a.r)},ellipse:function(a){a=V(a);return x(a.cx||0,a.cy||0,a.rx,\na.ry)},rect:function(a){a=V(a);return s(a.x||0,a.y||0,a.width,a.height,a.rx,a.ry)},image:function(a){a=V(a);return s(a.x||0,a.y||0,a.width,a.height)},line:function(a){return\"M\"+[a.attr(\"x1\")||0,a.attr(\"y1\")||0,a.attr(\"x2\"),a.attr(\"y2\")]},polyline:function(a){return\"M\"+a.attr(\"points\")},polygon:function(a){return\"M\"+a.attr(\"points\")+\"z\"},deflt:function(a){a=a.node.getBBox();return s(a.x,a.y,a.width,a.height)}};a.path.toRelative=function(b){var e=A(b),h=String.prototype.toLowerCase;if(e.rel)return d(e.rel);\na.is(b,\"array\")&&a.is(b&&b[0],\"array\")||(b=a.parsePathString(b));var f=[],l=0,n=0,k=0,p=0,s=0;\"M\"==b[0][0]&&(l=b[0][1],n=b[0][2],k=l,p=n,s++,f.push([\"M\",l,n]));for(var r=b.length;s<r;s++){var q=f[s]=[],x=b[s];if(x[0]!=h.call(x[0]))switch(q[0]=h.call(x[0]),q[0]){case \"a\":q[1]=x[1];q[2]=x[2];q[3]=x[3];q[4]=x[4];q[5]=x[5];q[6]=+(x[6]-l).toFixed(3);q[7]=+(x[7]-n).toFixed(3);break;case \"v\":q[1]=+(x[1]-n).toFixed(3);break;case \"m\":k=x[1],p=x[2];default:for(var c=1,t=x.length;c<t;c++)q[c]=+(x[c]-(c%2?l:\nn)).toFixed(3)}else for(f[s]=[],\"m\"==x[0]&&(k=x[1]+l,p=x[2]+n),q=0,c=x.length;q<c;q++)f[s][q]=x[q];x=f[s].length;switch(f[s][0]){case \"z\":l=k;n=p;break;case \"h\":l+=+f[s][x-1];break;case \"v\":n+=+f[s][x-1];break;default:l+=+f[s][x-2],n+=+f[s][x-1]}}f.toString=z;e.rel=d(f);return f};a.path.toAbsolute=G;a.path.toCubic=I;a.path.map=function(a,b){if(!b)return a;var d,e,h,f,l,n,k;a=I(a);h=0;for(l=a.length;h<l;h++)for(k=a[h],f=1,n=k.length;f<n;f+=2)d=b.x(k[f],k[f+1]),e=b.y(k[f],k[f+1]),k[f]=d,k[f+1]=e;return a};\na.path.toString=z;a.path.clone=d});C.plugin(function(a,v,y,C){var A=Math.max,w=Math.min,z=function(a){this.items=[];this.bindings={};this.length=0;this.type=\"set\";if(a)for(var f=0,n=a.length;f<n;f++)a[f]&&(this[this.items.length]=this.items[this.items.length]=a[f],this.length++)};v=z.prototype;v.push=function(){for(var a,f,n=0,k=arguments.length;n<k;n++)if(a=arguments[n])f=this.items.length,this[f]=this.items[f]=a,this.length++;return this};v.pop=function(){this.length&&delete this[this.length--];\nreturn this.items.pop()};v.forEach=function(a,f){for(var n=0,k=this.items.length;n<k&&!1!==a.call(f,this.items[n],n);n++);return this};v.animate=function(d,f,n,u){\"function\"!=typeof n||n.length||(u=n,n=L.linear);d instanceof a._.Animation&&(u=d.callback,n=d.easing,f=n.dur,d=d.attr);var p=arguments;if(a.is(d,\"array\")&&a.is(p[p.length-1],\"array\"))var b=!0;var q,e=function(){q?this.b=q:q=this.b},l=0,r=u&&function(){l++==this.length&&u.call(this)};return this.forEach(function(a,l){k.once(\"snap.animcreated.\"+\na.id,e);b?p[l]&&a.animate.apply(a,p[l]):a.animate(d,f,n,r)})};v.remove=function(){for(;this.length;)this.pop().remove();return this};v.bind=function(a,f,k){var u={};if(\"function\"==typeof f)this.bindings[a]=f;else{var p=k||a;this.bindings[a]=function(a){u[p]=a;f.attr(u)}}return this};v.attr=function(a){var f={},k;for(k in a)if(this.bindings[k])this.bindings[k](a[k]);else f[k]=a[k];a=0;for(k=this.items.length;a<k;a++)this.items[a].attr(f);return this};v.clear=function(){for(;this.length;)this.pop()};\nv.splice=function(a,f,k){a=0>a?A(this.length+a,0):a;f=A(0,w(this.length-a,f));var u=[],p=[],b=[],q;for(q=2;q<arguments.length;q++)b.push(arguments[q]);for(q=0;q<f;q++)p.push(this[a+q]);for(;q<this.length-a;q++)u.push(this[a+q]);var e=b.length;for(q=0;q<e+u.length;q++)this.items[a+q]=this[a+q]=q<e?b[q]:u[q-e];for(q=this.items.length=this.length-=f-e;this[q];)delete this[q++];return new z(p)};v.exclude=function(a){for(var f=0,k=this.length;f<k;f++)if(this[f]==a)return this.splice(f,1),!0;return!1};\nv.insertAfter=function(a){for(var f=this.items.length;f--;)this.items[f].insertAfter(a);return this};v.getBBox=function(){for(var a=[],f=[],k=[],u=[],p=this.items.length;p--;)if(!this.items[p].removed){var b=this.items[p].getBBox();a.push(b.x);f.push(b.y);k.push(b.x+b.width);u.push(b.y+b.height)}a=w.apply(0,a);f=w.apply(0,f);k=A.apply(0,k);u=A.apply(0,u);return{x:a,y:f,x2:k,y2:u,width:k-a,height:u-f,cx:a+(k-a)/2,cy:f+(u-f)/2}};v.clone=function(a){a=new z;for(var f=0,k=this.items.length;f<k;f++)a.push(this.items[f].clone());\nreturn a};v.toString=function(){return\"Snap\\u2018s set\"};v.type=\"set\";a.set=function(){var a=new z;arguments.length&&a.push.apply(a,Array.prototype.slice.call(arguments,0));return a}});C.plugin(function(a,v,y,C){function A(a){var b=a[0];switch(b.toLowerCase()){case \"t\":return[b,0,0];case \"m\":return[b,1,0,0,1,0,0];case \"r\":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case \"s\":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}}function w(b,d,f){d=q(d).replace(/\\.{3}|\\u2026/g,b);b=a.parseTransformString(b)||\n[];d=a.parseTransformString(d)||[];for(var k=Math.max(b.length,d.length),p=[],v=[],h=0,w,z,y,I;h<k;h++){y=b[h]||A(d[h]);I=d[h]||A(y);if(y[0]!=I[0]||\"r\"==y[0].toLowerCase()&&(y[2]!=I[2]||y[3]!=I[3])||\"s\"==y[0].toLowerCase()&&(y[3]!=I[3]||y[4]!=I[4])){b=a._.transform2matrix(b,f());d=a._.transform2matrix(d,f());p=[[\"m\",b.a,b.b,b.c,b.d,b.e,b.f]];v=[[\"m\",d.a,d.b,d.c,d.d,d.e,d.f]];break}p[h]=[];v[h]=[];w=0;for(z=Math.max(y.length,I.length);w<z;w++)w in y&&(p[h][w]=y[w]),w in I&&(v[h][w]=I[w])}return{from:u(p),\nto:u(v),f:n(p)}}function z(a){return a}function d(a){return function(b){return+b.toFixed(3)+a}}function f(b){return a.rgb(b[0],b[1],b[2])}function n(a){var b=0,d,f,k,n,h,p,q=[];d=0;for(f=a.length;d<f;d++){h=\"[\";p=['\"'+a[d][0]+'\"'];k=1;for(n=a[d].length;k<n;k++)p[k]=\"val[\"+b++ +\"]\";h+=p+\"]\";q[d]=h}return Function(\"val\",\"return Snap.path.toString.call([\"+q+\"])\")}function u(a){for(var b=[],d=0,f=a.length;d<f;d++)for(var k=1,n=a[d].length;k<n;k++)b.push(a[d][k]);return b}var p={},b=/[a-z]+$/i,q=String;\np.stroke=p.fill=\"colour\";v.prototype.equal=function(a,b){return k(\"snap.util.equal\",this,a,b).firstDefined()};k.on(\"snap.util.equal\",function(e,k){var r,s;r=q(this.attr(e)||\"\");var x=this;if(r==+r&&k==+k)return{from:+r,to:+k,f:z};if(\"colour\"==p[e])return r=a.color(r),s=a.color(k),{from:[r.r,r.g,r.b,r.opacity],to:[s.r,s.g,s.b,s.opacity],f:f};if(\"transform\"==e||\"gradientTransform\"==e||\"patternTransform\"==e)return k instanceof a.Matrix&&(k=k.toTransformString()),a._.rgTransform.test(k)||(k=a._.svgTransform2string(k)),\nw(r,k,function(){return x.getBBox(1)});if(\"d\"==e||\"path\"==e)return r=a.path.toCubic(r,k),{from:u(r[0]),to:u(r[1]),f:n(r[0])};if(\"points\"==e)return r=q(r).split(a._.separator),s=q(k).split(a._.separator),{from:r,to:s,f:function(a){return a}};aUnit=r.match(b);s=q(k).match(b);return aUnit&&aUnit==s?{from:parseFloat(r),to:parseFloat(k),f:d(aUnit)}:{from:this.asPX(e),to:this.asPX(e,k),f:z}})});C.plugin(function(a,v,y,C){var A=v.prototype,w=\"createTouch\"in C.doc;v=\"click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel\".split(\" \");\nvar z={mousedown:\"touchstart\",mousemove:\"touchmove\",mouseup:\"touchend\"},d=function(a,b){var d=\"y\"==a?\"scrollTop\":\"scrollLeft\",e=b&&b.node?b.node.ownerDocument:C.doc;return e[d in e.documentElement?\"documentElement\":\"body\"][d]},f=function(){this.returnValue=!1},n=function(){return this.originalEvent.preventDefault()},u=function(){this.cancelBubble=!0},p=function(){return this.originalEvent.stopPropagation()},b=function(){if(C.doc.addEventListener)return function(a,b,e,f){var k=w&&z[b]?z[b]:b,l=function(k){var l=\nd(\"y\",f),q=d(\"x\",f);if(w&&z.hasOwnProperty(b))for(var r=0,u=k.targetTouches&&k.targetTouches.length;r<u;r++)if(k.targetTouches[r].target==a||a.contains(k.targetTouches[r].target)){u=k;k=k.targetTouches[r];k.originalEvent=u;k.preventDefault=n;k.stopPropagation=p;break}return e.call(f,k,k.clientX+q,k.clientY+l)};b!==k&&a.addEventListener(b,l,!1);a.addEventListener(k,l,!1);return function(){b!==k&&a.removeEventListener(b,l,!1);a.removeEventListener(k,l,!1);return!0}};if(C.doc.attachEvent)return function(a,\nb,e,h){var k=function(a){a=a||h.node.ownerDocument.window.event;var b=d(\"y\",h),k=d(\"x\",h),k=a.clientX+k,b=a.clientY+b;a.preventDefault=a.preventDefault||f;a.stopPropagation=a.stopPropagation||u;return e.call(h,a,k,b)};a.attachEvent(\"on\"+b,k);return function(){a.detachEvent(\"on\"+b,k);return!0}}}(),q=[],e=function(a){for(var b=a.clientX,e=a.clientY,f=d(\"y\"),l=d(\"x\"),n,p=q.length;p--;){n=q[p];if(w)for(var r=a.touches&&a.touches.length,u;r--;){if(u=a.touches[r],u.identifier==n.el._drag.id||n.el.node.contains(u.target)){b=\nu.clientX;e=u.clientY;(a.originalEvent?a.originalEvent:a).preventDefault();break}}else a.preventDefault();b+=l;e+=f;k(\"snap.drag.move.\"+n.el.id,n.move_scope||n.el,b-n.el._drag.x,e-n.el._drag.y,b,e,a)}},l=function(b){a.unmousemove(e).unmouseup(l);for(var d=q.length,f;d--;)f=q[d],f.el._drag={},k(\"snap.drag.end.\"+f.el.id,f.end_scope||f.start_scope||f.move_scope||f.el,b);q=[]};for(y=v.length;y--;)(function(d){a[d]=A[d]=function(e,f){a.is(e,\"function\")&&(this.events=this.events||[],this.events.push({name:d,\nf:e,unbind:b(this.node||document,d,e,f||this)}));return this};a[\"un\"+d]=A[\"un\"+d]=function(a){for(var b=this.events||[],e=b.length;e--;)if(b[e].name==d&&(b[e].f==a||!a)){b[e].unbind();b.splice(e,1);!b.length&&delete this.events;break}return this}})(v[y]);A.hover=function(a,b,d,e){return this.mouseover(a,d).mouseout(b,e||d)};A.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var r=[];A.drag=function(b,d,f,h,n,p){function u(r,v,w){(r.originalEvent||r).preventDefault();this._drag.x=v;\nthis._drag.y=w;this._drag.id=r.identifier;!q.length&&a.mousemove(e).mouseup(l);q.push({el:this,move_scope:h,start_scope:n,end_scope:p});d&&k.on(\"snap.drag.start.\"+this.id,d);b&&k.on(\"snap.drag.move.\"+this.id,b);f&&k.on(\"snap.drag.end.\"+this.id,f);k(\"snap.drag.start.\"+this.id,n||h||this,v,w,r)}if(!arguments.length){var v;return this.drag(function(a,b){this.attr({transform:v+(v?\"T\":\"t\")+[a,b]})},function(){v=this.transform().local})}this._drag={};r.push({el:this,start:u});this.mousedown(u);return this};\nA.undrag=function(){for(var b=r.length;b--;)r[b].el==this&&(this.unmousedown(r[b].start),r.splice(b,1),k.unbind(\"snap.drag.*.\"+this.id));!r.length&&a.unmousemove(e).unmouseup(l);return this}});C.plugin(function(a,v,y,C){y=y.prototype;var A=/^\\s*url\\((.+)\\)/,w=String,z=a._.$;a.filter={};y.filter=function(d){var f=this;\"svg\"!=f.type&&(f=f.paper);d=a.parse(w(d));var k=a._.id(),u=z(\"filter\");z(u,{id:k,filterUnits:\"userSpaceOnUse\"});u.appendChild(d.node);f.defs.appendChild(u);return new v(u)};k.on(\"snap.util.getattr.filter\",\nfunction(){k.stop();var d=z(this.node,\"filter\");if(d)return(d=w(d).match(A))&&a.select(d[1])});k.on(\"snap.util.attr.filter\",function(d){if(d instanceof v&&\"filter\"==d.type){k.stop();var f=d.node.id;f||(z(d.node,{id:d.id}),f=d.id);z(this.node,{filter:a.url(f)})}d&&\"none\"!=d||(k.stop(),this.node.removeAttribute(\"filter\"))});a.filter.blur=function(d,f){null==d&&(d=2);return a.format('<feGaussianBlur stdDeviation=\"{def}\"/>',{def:null==f?d:[d,f]})};a.filter.blur.toString=function(){return this()};a.filter.shadow=\nfunction(d,f,k,u,p){\"string\"==typeof k&&(p=u=k,k=4);\"string\"!=typeof u&&(p=u,u=\"#000\");null==k&&(k=4);null==p&&(p=1);null==d&&(d=0,f=2);null==f&&(f=d);u=a.color(u||\"#000\");return a.format('<feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"{blur}\"/><feOffset dx=\"{dx}\" dy=\"{dy}\" result=\"offsetblur\"/><feFlood flood-color=\"{color}\"/><feComposite in2=\"offsetblur\" operator=\"in\"/><feComponentTransfer><feFuncA type=\"linear\" slope=\"{opacity}\"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in=\"SourceGraphic\"/></feMerge>',\n{color:u,dx:d,dy:f,blur:k,opacity:p})};a.filter.shadow.toString=function(){return this()};a.filter.grayscale=function(d){null==d&&(d=1);return a.format('<feColorMatrix type=\"matrix\" values=\"{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {b} {h} 0 0 0 0 0 1 0\"/>',{a:0.2126+0.7874*(1-d),b:0.7152-0.7152*(1-d),c:0.0722-0.0722*(1-d),d:0.2126-0.2126*(1-d),e:0.7152+0.2848*(1-d),f:0.0722-0.0722*(1-d),g:0.2126-0.2126*(1-d),h:0.0722+0.9278*(1-d)})};a.filter.grayscale.toString=function(){return this()};a.filter.sepia=\nfunction(d){null==d&&(d=1);return a.format('<feColorMatrix type=\"matrix\" values=\"{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {h} {i} 0 0 0 0 0 1 0\"/>',{a:0.393+0.607*(1-d),b:0.769-0.769*(1-d),c:0.189-0.189*(1-d),d:0.349-0.349*(1-d),e:0.686+0.314*(1-d),f:0.168-0.168*(1-d),g:0.272-0.272*(1-d),h:0.534-0.534*(1-d),i:0.131+0.869*(1-d)})};a.filter.sepia.toString=function(){return this()};a.filter.saturate=function(d){null==d&&(d=1);return a.format('<feColorMatrix type=\"saturate\" values=\"{amount}\"/>',{amount:1-\nd})};a.filter.saturate.toString=function(){return this()};a.filter.hueRotate=function(d){return a.format('<feColorMatrix type=\"hueRotate\" values=\"{angle}\"/>',{angle:d||0})};a.filter.hueRotate.toString=function(){return this()};a.filter.invert=function(d){null==d&&(d=1);return a.format('<feComponentTransfer><feFuncR type=\"table\" tableValues=\"{amount} {amount2}\"/><feFuncG type=\"table\" tableValues=\"{amount} {amount2}\"/><feFuncB type=\"table\" tableValues=\"{amount} {amount2}\"/></feComponentTransfer>',{amount:d,\namount2:1-d})};a.filter.invert.toString=function(){return this()};a.filter.brightness=function(d){null==d&&(d=1);return a.format('<feComponentTransfer><feFuncR type=\"linear\" slope=\"{amount}\"/><feFuncG type=\"linear\" slope=\"{amount}\"/><feFuncB type=\"linear\" slope=\"{amount}\"/></feComponentTransfer>',{amount:d})};a.filter.brightness.toString=function(){return this()};a.filter.contrast=function(d){null==d&&(d=1);return a.format('<feComponentTransfer><feFuncR type=\"linear\" slope=\"{amount}\" intercept=\"{amount2}\"/><feFuncG type=\"linear\" slope=\"{amount}\" intercept=\"{amount2}\"/><feFuncB type=\"linear\" slope=\"{amount}\" intercept=\"{amount2}\"/></feComponentTransfer>',\n{amount:d,amount2:0.5-d/2})};a.filter.contrast.toString=function(){return this()}});return C});\n"
  },
  {
    "path": "docs/Project.toml",
    "content": "[deps]\nCairo = \"159f3aea-2a34-519c-b102-8c37f9878175\"\nColors = \"5ae59095-9a9b-59fe-a467-6f913c188581\"\nCompose = \"a81c6b42-2e10-5240-aca2-a61377ecd94b\"\nDocumenter = \"e30172f5-a6a5-5a46-863b-614d45cd2de4\"\nFontconfig = \"186bb1d3-e1f7-5a2c-a377-96d770f13627\"\nMeasures = \"442fdcdd-2543-5da2-b0f3-8c86c306513e\"\n"
  },
  {
    "path": "docs/make.jl",
    "content": "using Documenter, Compose\nimport Cairo\n\nstruct SVGJSWritable{T}\n    x :: T\nend\nBase.show(io::IO, m::MIME\"text/html\", x::SVGJSWritable) = show(io, m, x.x)\n\nmakedocs(\n    modules = [Compose],\n    clean = false,\n    sitename = \"Compose.jl\",\n    pages = Any[\n        \"Home\" => \"index.md\",\n        \"Tutorial\" => \"tutorial.md\",\n        \"Gallery\" => Any[\n            \"Forms\" => \"gallery/forms.md\",\n            \"Properties\"=> \"gallery/properties.md\",\n            \"Transformations\"=> \"gallery/transforms.md\",\n            ],\n        \"Library\" => \"library.md\"\n    ]\n)\n\ndeploydocs(\n    repo   = \"github.com/GiovineItalia/Compose.jl.git\",\n)\n"
  },
  {
    "path": "docs/src/gallery/forms.md",
    "content": "```@meta\nAuthor = [\"Mattriks\"]\n```\n\n\n# [Forms](@id forms_gallery)\n\n## [`arc`](@ref)\n \n```@example\nusing Compose\nset_default_graphic_size(8cm,4cm)\ncolv = [\"red\",\"orange\",\"green\",\"blue\", \"purple\"]\na = range(-π/4, stop=7π/4, length=6)+ 0.2*randn(6)\na[6] = a[1]\n\nimg1 = compose(context(),\n    (context(), arc([0.5], [0.5], [0.3], [4.5π/4,π/4] , [7.5π/4,3π/4], [true,false]),\n        stroke(\"black\"), fill([\"red\",\"white\"])) )\nimg2 = compose(context(),\n    (context(), arc([0.5], [0.5], [0.3], a[1:5], a[2:6]), \n        stroke(colv), fill(\"transparent\"), linewidth(2mm)) )\nhstack(img1, img2)\n```\n\n## [`bezigon`](@ref)\n \n```@example\nusing Colors, Compose\nset_default_graphic_size(14cm,10cm)\n\npetal = [[(0.4, 0.4), (0.4, 0.2), (0.5, 0.0)],  [(0.6, 0.2), (0.6, 0.4), (0.5, 0.5)]]\npetalf(θ::Float64) = (context(rotation=Rotation(θ, 0.5,0.5)),\n    bezigon((0.5, 0.5), petal), fill(LCHuvA(70.,50., 360*θ/2π, 0.4)))\n\ntheta = range(π/20, 2π, step=2π/10).-π\nimg = compose(context(), petalf.(theta)...)\n```\n\n## [`bitmap`](@ref)\n```@example\nusing Main: SVGJSWritable #hide\nusing Compose\nset_default_graphic_size(14cm,4cm)\nrawimg = read(joinpath(@__DIR__,\"..\",\"assets/smiley.png\"));\nX = 0.9*rand(10,2)\n\nimg = compose(context(), \n    (context(), rectangle(), fill(\"transparent\"), stroke(\"orange\")),\n    (context(), bitmap([\"image/png\"], [rawimg], X[:,1], X[:,2], [0.1], [0.1]))\n)\nSVGJSWritable(ans) #hide\n```\n\n## [`circle`](@ref)\n\n```@example\nusing Colors, Compose\nset_default_graphic_size(14cm,4cm)\ncolv = HSVA.([0:30:179;], 1, 1, 0.5)\nimg = compose(context(units=UnitBox(0,0,40,8)),\n    (context(), circle([5.0:6:35;], [4], [4]), fill(colv), stroke(\"black\"))\n)\n```\n\n## [`curve`](@ref)\n```@example\nusing Colors, Compose\nset_default_graphic_size(14cm, 4cm)\nepoint(x) = [(x,y) for y in rand(10)]\ncpoint(t=0) = [(t+x,y) for (x,y) in zip(rand(10), 0.5*rand(10))]\ncolv = range(colorant\"blue\",stop=colorant\"orange\", length=10)\n\nimg = compose(context(units=UnitBox(0,0,2,1)),\n    (context(), curve([(0.5,1.0)], cpoint(), cpoint(), epoint(0.5)), stroke(colv)),\n    (context(), curve([(1.5,1.0)], cpoint(1), cpoint(1), epoint(1.5)), stroke(colv)) \n)\n```\n## [`ellipse`](@ref)\n```@example\nusing Colors, Compose\nset_default_graphic_size(14cm, 4cm)\ncolv1 = HSVA.([0:30:179;], 1, 1, 0.5)\nr = 2*[1:6;]/24\n\ncolv2 = HSVA.([0:15:179;], 1, 1, 0.3)\nθ = collect(range(0, stop=1.9π, length=10))\nrl = 0.5*rand(10)\nrw = 0.3*rand(10)\nrot = Rotation.(θ, 1.5, 0.5)\nellipsef(i::Int) = ellipse(1.5, 0.5, rl[i], rw[i])\n\nimg = compose(context(units=UnitBox(0,0,2,1)),\n        (context(), ellipse(r,[0.5],r,reverse(r)), stroke(\"black\"), fill(colv1)),\n[(context(rotation=rot[i]), ellipsef(i), fill(colv2[i]), stroke(\"black\")) for i in 1:10]...\n)\n```\n\n## [`line`](@ref)\n```@example\nusing Compose\nset_default_graphic_size(10cm, 10cm)\nθ = collect(range(0, stop=2π, length=60))\npoint_array = [[(0,0.75), (x,y)] for (x,y) in zip(cos.(θ), sin.(θ))]\n\nimg = compose(context(), \n    (context(), rectangle(), fill(\"salmon\"), fillopacity(0.3)),\n    (context(0.12, 0.12, 0.76, 0.76, units=UnitBox(-1,-1,2,2)),  \n        line(point_array), stroke(\"gold\"), linewidth(1mm))\n)\n```\n\n## [`ngon`](@ref), [`star`](@ref), [`xgon`](@ref) \n```@example\nusing Compose\nset_default_graphic_size(14cm, 5cm)\nrainbow = [\"orange\",\"green\",\"indigo\",\n    \"darkviolet\",\"indigo\",\"blue\",\"green\",\"yellow\",\"orange\",\"red\"]\nproperties = [fillopacity(0.5), fill(rainbow), stroke(\"black\")]\nnpoints = [7,5,3,2,3,4,5,6,7,8]\nX = range(0.06, stop=0.94, length=10)\nradii = 0.035*[-ones(3); ones(7)]\np = compose(context(),\n    (context(), ngon(X, [0.16], radii, npoints),\n        star(X, [0.5], radii, npoints),\n        xgon(X, [0.84], radii, npoints), properties...))\n```\n\n## [`polygon`](@ref)\n```@example\nusing Statistics, Compose\nset_default_graphic_size(10cm,10cm)\nX = randn(50,2)\nX = 0.3*(X .- mean(X,dims=1))./std(X,dims=1)\nhp = hypot.(X[:,1],X[:,2])\ni = hp .> Statistics.quantile(hp, 0.82)\nZ = X[i,:]\nθ = atan.(Z[:,1], Z[:,2])\nord = sortperm(θ)\npolypoints = [(x,y) for (x,y) in zip(Z[ord,1],Z[ord,2])]  \n\nimg = compose(context(units=UnitBox(-1,-1, 2,2)),\n  (context(), line([(-1,-1), (-1,1), (1,1)]), stroke(\"black\")),\n  (context(), circle(0,0,0.02), fill(\"red\")),\n  (context(), circle(X[:,1],X[:,2],[0.02]), fill(\"transparent\"),stroke(\"deepskyblue\")),\n  (context(), polygon(polypoints), fill(\"red\"), fillopacity(0.1))\n)\n```\n\n## [`rectangle`](@ref)\n```@example\nusing Colors, Compose\nset_default_graphic_size(14cm,4cm)\ncolv = HSVA.([0:15:179;], 1, 1, 0.3)\nX = 0.9*rand(10,2)\nrl = 0.3*rand(10).+0.03\nrw = 0.3*rand(10).+0.03\n\nimg = compose(context(), \n    (context(), rectangle(), fill(\"transparent\"), stroke(\"orange\")),\n     (context(), rectangle(X[:,1], X[:,2], rl, rw), fill(colv), stroke(\"black\")) \n)\n```\n\n\n## [`sector`](@ref)\n```@example\nusing Compose\nset_default_graphic_size(14cm, 4cm)\ncolv = [\"red\",\"orange\",\"green\",\"blue\", \"purple\"]\na = range(-π/4, stop=7π/4, length=6)+ 0.2*randn(6)\na[6] = a[1]\n\nsectorobj = sector([0.5], [0.5], [0.3], a[1:5], a[2:6])\nimg1 = compose(context(),\n    (context(), sectorobj, fill(colv)) )\nimg2 = compose(context(),\n    (context(), sectorobj, stroke(\"white\"), fill(colv), linewidth(1.4mm)) )\nimg3 = compose(context(),\n    (context(), sectorobj, stroke(colv), fill(\"transparent\"), linewidth(1.4mm)) )\nhstack(img1, img2, img3)\n```\n\n## [`text`](@ref)\n```@example\nusing Colors, Compose\nset_default_graphic_size(10cm,10cm)\nlabels=rand(string.(names(Base)[280:end]), 30)\nθ = collect(range(0, stop=58π/30, length=30))\nX = 1 .+ 0.7*[cos.(θ) sin.(θ)]\ncolv = range(colorant\"blue\",stop=colorant\"orange\", length=30)\nrot = Rotation.(θ, X[:,1], X[:,2])\n\nimg = compose(context(units=UnitBox(0,0,2,2)),\n  (context(), text(1, 1, \"Julia\", hcenter, vcenter), stroke(\"red\"), fontsize(30pt)),\n  (context(), text(X[:,1], X[:,2], labels, [hcenter], [vcenter], rot), stroke(colv))\n)\n```\n\n```@example\nusing Compose\nset_default_graphic_size(10cm,8cm)\n\n# This graphic illustrates text alignment\ntxt = [x*\"\\n\"*y for  x in [\"hleft\", \"hcenter\",\"hright\"], \n        y in [\"vtop\",\"vcenter\",\"vbottom\"] ]\nx = repeat(0.1w.*[1,5,9], outer=3)\ny = repeat(0.1h.*[1,5,9], inner=3)\nxp = repeat([hleft,hcenter,hright], outer=3)\nyp = repeat([vtop,vcenter,vbottom], inner=3)\n\nimg = compose(context(),\n        (context(), circle(x, y, [0.01]), fill(\"red\")),\n        text(x, y, txt, xp, yp), fontsize(14pt)\n)\n```\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "docs/src/gallery/properties.md",
    "content": "```@meta\nAuthor = [\"Mattriks\"]\n```\n\n\n# [Properties](@id properties_gallery)\n\n\n## [`arrow`](@ref)\n \n```@example\nusing Compose\nset_default_graphic_size(14cm,5cm)\nθ, r = 2π*rand(3),  0.1.+0.08*rand(3)\nc, s = r.*cos.(θ), r.*sin.(θ)\npoint_array = [[(0.5,0.5), 0.5.+(x,y)] for (x,y) in zip(c,s) ]\nimg = compose(context(), arrow(), stroke(\"black\"), fill(nothing),\n        (context(), arc(0.18, 0.5, 0.08, -π/4, 1π)),\n        (context(), line(point_array), stroke([\"red\",\"green\",\"deepskyblue\"])),\n        (context(), curve((0.7,0.5), (0.8,-0.5), (0.8,1.5), (0.9,0.5)))\n)\n```\n\n## [`clip`](@ref)\n\n```@example\nusing Colors, Compose\nset_default_graphic_size(14cm,7cm)\nX = rand(10,2)\ncolv = HSVA.(range(0,stop=180,length=10), 1, 1, 0.5)\nimg = compose(context(units=UnitBox(0,0,2,1)), stroke(\"black\"),\n    (context(), rectangle(), fill(nothing)),\n  (context(), ngon([0.5,1.5],[0.5], [0.4], [10]), fill(nothing), stroke(\"lightgray\")),\n    (context(), xgon(X[:,1],X[:,2],[0.2],[4]), fill(colv)),\n    (context(), xgon(X[:,1].+1,X[:,2],[0.2],[4]), fill(colv),\n        clip(points(ngon(1.5,0.5, 0.4, 10))) )\n)\n```\n\n\n\n## [`fill`](@ref), [`fillopacity`](@ref)\n \n```@example\nusing Compose\nset_default_graphic_size(14cm,4cm)\nimg = compose(context(),\n  (context(), circle(0.5, 0.5, 0.08), fillopacity(0.3), fill(\"orange\")),\n  (context(), circle([0.1, 0.26], [0.5], [0.1]), fillopacity(0.3), fill(\"blue\")),\n  (context(), circle([0.42, 0.58], [0.5], [0.1]), fillopacity(0.3), fill([\"yellow\",\"green\"])),\n  (context(), circle([0.74, 0.90], [0.5], [0.1]), fillopacity([0.5,0.3]), fill([\"yellow\",\"red\"]) )     \n)\n```\n\n\n"
  },
  {
    "path": "docs/src/gallery/transforms.md",
    "content": "```@meta\nAuthor = [\"Mattriks\"]\n```\n\n\n# [Transformations](@id transforms_gallery)\n\n## [`Mirror`](@ref)\n\n```@example\nusing Compose\nset_default_graphic_size(15cm,5cm)\n\nf_points = [(.1, .1), (.9, .1), (.9, .2), (.2, .2), (.2, .4), (.6, .4), (.6, .5),\n    (.2, .5), (.2, .9), (.1, .9), (.1, .1)]\nf_points = (x->0.4.*x.+0.1).(f_points)\nfpoly(ϕ::Float64) = (context(rotation=Rotation(ϕ,0.3,0.46)), polygon(f_points))\n\nimgfa(θ, ϕ=0.0, x=0.5,y=0.5) = compose(context(), \n  fill(\"salmon\"), fillopacity(1.0), fpoly(ϕ),\n  (context(rotation=Rotation(θ,x,y)), line([(x-0.5,y),(x+0.5,y)]), circle(x,y, 0.02)),\n  (context(mirror=Mirror(θ, x, y)), fpoly(ϕ))\n)\n\nFmir = hstack(imgfa(-π/4), imgfa(-π/2.2), imgfa(π/4, 1π))\nimg = compose(context(), rectangle(), fill(nothing), stroke(\"black\"), Fmir)\n```\n\n## [`Rotation`](@ref)\n \n```@example\nusing Compose\nset_default_graphic_size(15cm,5cm)\n\n# This example also illustrates nested contexts\nf_points = [(.1, .1), (.9, .1), (.9, .2), (.2, .2), (.2, .4),\n    (.6, .4), (.6, .5), (.2, .5), (.2, .9), (.1, .9), (.1, .1)]\nrect(c::String) = (context(), rectangle(), stroke(c))\ncirc(c::String, s::Float64=0.4) =  (context(), circle([x],[y],[0.03,s]), stroke(c))\nfpoly(c::String) = (context(), polygon(f_points), fill(c), fillopacity(1.0))\ncontextC(θ::Float64) = (context(0.5,0.5,1.5,1.5, units=UnitBox(0,0,1,1),\n        rotation=Rotation(θ,x,y)), fpoly(\"steelblue\"), circ(\"orange\"))\nimgf(θ::Float64) =  compose(context(),\n        (context(0.15, 0.15, 0.7, 0.7, units=UnitBox(0,0,2,2)), rect(\"red\"),\n        contextC(θ)) # context C in context B in context A\n    )\n\nx, y, θ = 0.5, 0.25, π/3\nFrot =  hstack(imgf(-θ), imgf(0.), imgf(θ))\nimg = compose(context(), rectangle(), fill(nothing), stroke(\"black\"), Frot)\n```\n\n## [`Shear`](@ref)\n \n```@example\nusing Compose\nset_default_graphic_size(15cm, 5cm)\n\nf_points = [(.1, .1), (.9, .1), (.9, .2), (.2, .2), (.2, .4), (.6, .4),\n    (.6, .5), (.2, .5), (.2, .9), (.1, .9), (.1, .1)]\nf_points = (x->0.5.*x.+0.3).(f_points)\nctxl(θ, x, y) = (context(rotation=Rotation(θ, x, y)), circle(x,y, 0.01), \n    line([(x-0.5,y),(x+0.5,y)]))\nfpoly(c::String) = (context(),  polygon(f_points), fill(c) )\nctxf(θ, ϕ, s, x, y,c) = (context(rotation=Rotation(-θ, x, y), \n        shear=Shear(s, ϕ, x, y)), fpoly(c))\n\nx, y, θ  = 0.5, 0.5, -π/6\nimg1 = compose(context(), stroke(\"black\"), ctxl(θ,x,y),  ctxf(0,0,0,x,y, \"yellow\"),\n    (context(), arc(x,y,0.3,π+θ,π-0.15), arrow()) )\nimg2 = compose(context(), stroke(\"black\"), ctxl(0,x,y),\n    ctxf(θ,0,1.8,x,y,\"transparent\"), ctxf(θ,0,0,x,y,\"yellow\"),\n    text(0.5, 0.1, \"x' = x+y*shear\", hcenter, vcenter) )\nimg3 = compose(context(), stroke(\"black\"), \n    ctxl(θ, x, y), ctxf(0,θ,1.8,x,y,\"yellow\") )\n\nhstack(img1, img2, img3)\n```\n\n"
  },
  {
    "path": "docs/src/index.md",
    "content": "```@meta\nAuthor = [\"Daniel C. Jones\", \"Gio Borje\", \"Tamas Nagy\"]\n```\n\n# Compose\n\nCompose is a declarative vector graphics system written in Julia. It's designed\nto simplify the creation of complex graphics and serves as the basis of the\n[Gadfly](https://github.com/GiovineItalia/Gadfly.jl) data visualization package.\n\n\n## Package features\n\n- Renders publication quality graphics to SVG, PNG, Postscript, PDF and PGF\n- Intuitive and consistent interface\n- Works with [Jupyter](http://jupyter.org/) notebooks via [IJulia](https://github.com/JuliaLang/IJulia.jl) out of the box\n\n## Installation\n\nThe latest release of **Compose** can be installed from the Julia REPL prompt with\n\n```julia\njulia> Pkg.add(\"Compose\")\n```\n\nThis installs the package and any missing dependencies.  From there, the\nsimplest of graphics can be rendered to your default internet browser with\n\n```julia\njulia> using Compose\njulia> compose(context(), circle(), fill(\"gold\"))\n```\n\nNow that you have it installed, check out the [Tutorial](@ref) and the [Forms](@ref forms_gallery) gallery.\n\n\n## Influences\n\nCompose is intended as a futuristic version of the R library\n[grid](http://www.stat.auckland.ac.nz/~paul/grid/grid.html), and so takes a few\nideas from grid. The Compose canvas is roughly equivalent to a viewport in grid,\nfor example. Compose was also inspired by the admirable Haskell library\n[Diagrams](http://projects.haskell.org/diagrams/).\n"
  },
  {
    "path": "docs/src/library.md",
    "content": "```@autodocs\nModules = [Compose]\n```\n"
  },
  {
    "path": "docs/src/tutorial.md",
    "content": "```@meta\nAuthor = [\"Daniel C. Jones\", \"Gio Borje\", \"Tamas Nagy\"]\n```\n\n# Tutorial\n\n\n## Compose is declarative\n\nIn a declarative graphics system, a figure is built without specifying the\nprecise sequence of drawing commands but by arranging shapes and attaching\nproperties. This makes it easy to break a complex graphic into manageable parts\nand then figure out how to combine the parts.\n\n## Everything is a tree\n\nGraphics in Compose are defined using a tree structure. It's not unlike SVG in\nthis regard, but has simpler semantics. There are three important types that\nmake up the nodes of the tree:\n\n* `Context`: An internal node.\n* `Form`: A leaf node that defines some geometry, like a line or a polygon.\n* `Property`: A leaf node that modifies how its parent's subtree is drawn,\n  like fill color, font family, or line width.\n\nThe all-important function in Compose, is called, not surprisingly, `compose`.\nCalling `compose(a, b)` will return a new tree rooted at `a` and with `b`\nattached as a child.\n\nThat's enough to start drawing some simple shapes.\n\n```@setup 1\nusing Compose\nset_default_graphic_size(4cm, 4cm)\nset_default_jsmode(:exclude)\n```\n\n```@example 1\nusing Compose\n\ncomposition = compose(compose(context(), rectangle()), fill(\"tomato\"))\ndraw(SVG(\"tomato.svg\", 4cm, 4cm), composition)\nnothing # hide\n```\n\n![](tomato.svg)\n\nThe last line renders the composition to specificied backend, here the SVG\nbackend. This can also be written like\n`composition |> SVG(\"tomato.svg\", 4cm, 4cm)`.\nAlternatively, if multiple compositions of the same size are to be\ngenerated, this can be abbreviated even further to\n\n```\nset_default_graphic_size(4cm, 4cm)\ncomposition |> SVG(\"tomato.svg\")\ncomposition2 |> SVG(\"celery.svg\")\ncomposition3 |> SVG(\"rutabaga.svg\")  # etc...\n```\n\n\n## The compose function accepts S-expressions\n\nIn the first example, we had to call `compose` twice just to draw a lousy red\nsquare. Fortunately `compose` has a few tricks up its sleeve. As everyone from\nlisp hackers and [phylogeneticists](http://en.wikipedia.org/wiki/Newick_format)\nknows, trees can be defined most tersely using S-expressions. We can rewrite our\nfirst example like:\n\n```julia\n# equivalent to compose(compose(context(), rectangle()), fill(\"tomato\")))\ncompose(context(), rectangle(), fill(\"tomato\"))\n```\n\nFurthermore, more complex trees can be formed by grouping subtrees with\nparenthesis or brackets.\n\n```@example 1\ncomposition = compose(context(),\n        (context(), circle(), fill(\"bisque\")),\n        (context(), rectangle(), fill(\"tomato\")))\ncomposition |> SVG(\"tomato_bisque.svg\")\nnothing # hide\n```\n\n![](tomato_bisque.svg)\n\n## Trees can be visualized with introspect\n\nA useful function for visualizing the graphic that you've constructed is\n`introspect`. It takes a `Context` defining a graphic and returns a new graphic\nwith a schematic of the tree.\n\n```@example 2\nusing Compose # hide\nset_default_graphic_size(6cm, 6cm) # hide\n\ntomato_bisque =\n    compose(context(),\n            (context(), circle(), fill(\"bisque\")),\n            (context(), rectangle(), fill(\"tomato\")))\n\nintrospect(tomato_bisque)\n```\n\nThis is a little cryptic, but you can use this limited edition decoder ring:\n\n```@example 2\nusing Compose, Colors, Measures\nset_default_graphic_size(6cm, 4cm)\n\nfigsize = 6mm\nt = table(3, 2, 1:3, 2:2, y_prop=[1.0, 1.0, 1.0])\nt[1,1] = [compose(context(minwidth=figsize + 2mm, minheight=figsize),\n                  circle(0.5, 0.5, figsize/2), fill(LCHab(92, 10, 77)))]\nt[2,1] = [compose(context(minwidth=figsize + 2mm, minheight=figsize),\n                  rectangle(0.5cx - figsize/2, 0.5cy - figsize/2, figsize, figsize),\n                  fill(LCHab(68, 74, 192)))]\nt[3,1] = [compose(context(minwidth=figsize + 2mm, minheight=figsize),\n                  polygon([(0.5cx - figsize/2, 0.5cy - figsize/2),\n                           (0.5cx + figsize/2, 0.5cy - figsize/2),\n                           (0.5cx, 0.5cy + figsize/2)]),\n                  fill(LCHab(68, 74, 29)))]\nt[1,2] = [compose(context(), text(0, 0.5, \"Context\", hleft, vcenter))]\nt[2,2] = [compose(context(), text(0, 0.5, \"Form\", hleft, vcenter))]\nt[3,2] = [compose(context(), text(0, 0.5, \"Property\", hleft, vcenter))]\ncompose(context(), t, fill(LCHab(92, 10, 77)), fontsize(10pt))\n```\n\n## Contexts specify a coordinate system for their children\n\nIn addition to forming internal nodes to group `Form` and `Property` children, a\n`Context` can define a coordinate system using the `context(x0, y0, width, height)`\nform. Here we'll reposition some circles by composing them with contexts using\ndifferent coordinate systems.\n\n```@setup 3\nusing Compose\nset_default_graphic_size(4cm, 4cm)\n```\n\n```@example 3\ncomposition = compose(context(), fill(\"tomato\"),\n        (context(0.0, 0.0, 0.5, 0.5), circle()),\n        (context(0.5, 0.5, 0.5, 0.5), circle()))\ncomposition |> SVG(\"tomatos.svg\")\nnothing # hide\n```\n\n![](tomatos.svg)\n\nThe context's box (i.e. `(x0, y0, width, height)`) is given in terms of its\nparent's coordinate system and defaults to `(0, 0, 1, 1)`. All the children of a\ncontext will use coordinates relative to that box.\n\nThis is an easy mechanism to translate the coordinates of a subtree in the\ngraphic, but coordinates can be scaled and shifted as well by passing a\n`UnitBox` to the `units` attribute.\n\n```@example 3\ncomposition = compose(context(),\n        (context(units=UnitBox(0, 0, 1000, 1000)),\n         polygon([(0, 1000), (500, 1000), (500, 0)]),\n         fill(\"tomato\")),\n        (context(),\n         polygon([(1, 1), (0.5, 1), (0.5, 0)]),\n         fill(\"bisque\")))\ncomposition |> SVG(\"tomato_bisque_triangle.svg\")\nnothing # hide\n```\n\n![](tomato_bisque_triangle.svg)\n\n\n## Measures can be a combination of absolute and relative units\n\nComplex visualizations often are defined using a combination of relative and\nabsolute units. Compose makes these easy. In fact there are four sorts of units\nused in Compose:\n\n* **Context (position) units**: If no unit is explicitly attached to a position, it is\n  assumed to be in “context units”, which are relative to the parent Context's\n  box and coordinate system. (Constants: `cx`, `cy`)\n* **Context (relative position) units**: The radius of a form can be expressed in size units \n  with respect to the context units (Constants: `sx`, `sy`). If no unit is explicitly attached to\n  a size (e.g. radius) unit, it is assumed to be wrt the context `x` units. More info below.  \n* **Width/Height units**: Sometimes you'll want place geometry in relative\n  coordinates, but bypassing the parent context's coordinate system.\n  Width/height work so that `(0w, 0h)` is always the top-left corner of the\n  context, and `(1w, 1h)` is always the bottom-right. (Constants: `w`, `h`)\n* **Absolute units**: Absolute units are inches, centimeters, points, etc.\n  (Constants: `inch`, `cm`, `mm`, `pt`)\n\nAny linear combination of these types of units is allowed. For example, `1w - 10mm` is a well formed expression, giving the width of the parent canvas minus ten millimeters. An example of the difference between\ncontext position units (`cx`, `cy`) and size units (`sx`, `sy`), is when the coordinate system (defined by `UnitBox`) does not start at `(0,0)`, then the size `0sx` (or `0sy`) will always equal `0mm`, but the position `(0cx, 0cy)` will be dependent on the coordinates (and may refer to a point outside the context's `UnitBox`).\n\n\n## Forms and Properties can be vectorized\n\nOften one needs to produce many copies of a similar shape. Most of the forms an\nproperties have a scalar and vector forms to simplify this sort of mass\nproduction.\n\nWe'll use `circle` as an example, which has two constructors:\n\n```julia\ncircle(x=0.5w, y=0.5h, r=0.5w)\ncircle(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)\n```\n\nThe first of these creates only circle centered at `(x, y)` with radius `r`. The\nsecond form can succinctly create many circles (using the [Colors](https://github.com/JuliaLang/Colors.jl) package to specify the `LHCab` colorspace):\n\n```@setup 4\nusing Compose, Colors\nset_default_graphic_size(4cm, 4cm)\n```\n\n```@example 4\ncomposition = compose(context(),\n        circle([0.25, 0.5, 0.75], [0.25, 0.5, 0.75], [0.1, 0.1, 0.1]),\n        fill(LCHab(92, 10, 77)))\ncomposition |> SVG(\"circles.svg\")\nnothing # hide\n```\n\n![](circles.svg)\n\nThe arrays in passed to `xs`, `ys`, and `rs` need not be the same length.\nShorter arrays will be cycled. This let's us shorten this last example by only\nspecifying the radius just once.\n\n```@example 4\ncomposition = compose(context(),\n        circle([0.25, 0.5, 0.75], [0.25, 0.5, 0.75], [0.1]),\n        fill(LCHab(92, 10, 77)))\ncomposition |> SVG(\"cycled_circles.svg\")\nnothing # hide\n```\n\n![](cycled_circles.svg)\n\nThe `fill` is a property can also be vectorized here to quickly assign different\ncolors to each circle.\n\n\n```@example 4\ncircles_fill_vectorized = compose(context(),\n        circle([0.25, 0.5, 0.75], [0.25, 0.5, 0.75], [0.1]),\n        fill([LCHab(92, 10, 77), LCHab(68, 74, 192), LCHab(78, 84, 29)]))\ncircles_fill_vectorized |> SVG(\"circles_fill_vectorized.svg\")\nnothing # hide\n```\n\n![](circles_fill_vectorized.svg)\n\nIf vector properties are used with vector forms, they must be of equal length.\n\n\n## Compose can produce arbitrary directed graphs\n\nThough we've so far explained `compose` as producing trees, there's nothing\nstopping one from producing an arbitrary directed graph. This can be quite\nuseful in some cases.\n\nIn this example, only one triangle object is ever initialized, despite many\ntriangles being drawn, which is possible because the graph produced by\n`siepinski` is not a tree. The triangle polygon has many parent nodes than\n“re-contextualize” that triangle by repositioning it.\n\n```@example 5\nusing Compose, Colors # hide\nset_default_graphic_size(8cm, 8*(sqrt(3)/2)*cm) # hide\n\nfunction sierpinski(n)\n    if n == 0\n        compose(context(), polygon([(1,1), (0,1), (1/2, 0)]))\n    else\n        t = sierpinski(n - 1)\n        compose(context(),\n                (context(1/4,   0, 1/2, 1/2), t),\n                (context(  0, 1/2, 1/2, 1/2), t),\n                (context(1/2, 1/2, 1/2, 1/2), t))\n    end\nend\n\ncomposition = compose(sierpinski(6), fill(LCHab(92, 10, 77)))\ncomposition |> SVG(\"sierpinski.svg\", 8cm, 8*(sqrt(3)/2)*cm)\nnothing # hide\n```\n\n![](sierpinski.svg)\n\nThere are no safeguards to check for cycles. You can produce a graph with a\ncycle and Compose will run in an infinite loop trying to draw it. In most\napplications, this isn't a concern.\n\n## Fancier compositions\n\nThere are fancier forms of the `compose` function, in particular, variadic\n`compose`, which is roughly defined as:\n\n```julia\ncompose(a, b, cs...) = compose(compose(a, b), cs...)\n```\n\nCompose over tuples or arrays:\n```julia\ncompose((as...)) = compose(as...)\n```\n\nIn effect, this lets one write a complex series of compose operations as an\nS-expression. For example:\n\n```julia\ncompose(a, b, ((c, d), (e, f), g))\n```\n\nSince all we are doing is building trees, this syntax tends to be pretty\nconvenient.\n\n## [Forms](@ref forms_gallery)\n\nThese are basic constructors for the in-built forms - see the [Forms gallery](@ref forms_gallery) for examples.\n\n* `polygon(points)`\n* `rectangle(x0, y0, width, height)`\n* `circle(x, y, r)`\n* `ellipse(x, y, x_radius, y_radius)`\n* `text(x, y, value)`\n* `line(points)`\n* `curve(anchor0, ctrl0, ctrl1, anchor1)`\n* `bitmap(mime, data, x0, y0, width, height)`\n* `arc(x, y, r, angle1, angle2, sector)`\n* `sector(x, y, r, angle1, angle2)`\n* `bezigon(anchor0, sides)`\n\n## [Properties](@ref properties_gallery)\n\nProperties include `arrow`, `fill`, `fillopacity`, etc. See the [Properties gallery](@ref properties_gallery) for examples.\nFor colors, Compose supports the colors available in [Colors.jl](https://github.com/JuliaGraphics/Colors.jl), which includes many [color spaces](http://juliagraphics.github.io/Colors.jl/stable/), hex strings, and [named colors](http://juliagraphics.github.io/Colors.jl/stable/namedcolors/).\n\n\n## Text\n\nSymbols can be used in text strings by inserting [HTML\ncodes](http://www.ascii.cl/htmlcodes.htm).  More general formatting for the SVG\nbackend is [documented here](https://www.w3.org/TR/SVG/text.html),\nwhereas the Cairo backend uses a [Pango markup\nlanguage](https://developer.gnome.org/pango/unstable/PangoMarkupFormat.html).\n\n```@example 6\nusing Compose # hide\ncents_ina_dollar = compose(context(), text(0.5, 0.5,\"100&#162; in a &#36;\"))\ncents_ina_dollar |> SVG(\"dollar.svg\",5cm,1cm)\nnothing # hide\n```\n\n![](dollar.svg)\n\nUse the `font` and `fontsize` properties to change the appearance of type:\n\n```@example 7\nusing Compose # hide\ncompose(context(),\n       (context(), text(0.2,0.5,\"big\"), fontsize(18pt)),\n       (context(), text(0.4,0.5,\"small\"), fontsize(6pt)),\n       (context(), text(0.6,0.5,\"bold\"), font(\"Helvetica-Bold\")),\n       (context(), text(0.8,0.5,\"oblique\"), font(\"Helvetica-Oblique\"))) |>\n    SVG(\"font_fontsize.svg\",15cm,1cm)\nnothing # hide\n```\n\n![](font_fontsize.svg)\n\n\n"
  },
  {
    "path": "src/Compose.jl",
    "content": "module Compose\n\nusing Colors\nusing IterTools\nusing DataStructures\nusing Measures\nusing Requires\nusing Dates\nusing Printf\nusing Base.Iterators\nusing Statistics\nimport JSON\n\nimport Base: length, isempty, getindex, setindex!,\n    display, show, convert, zero, isless, max, fill, size, copy,\n    min, max, abs, +, -, *, /, ==\nimport Measures: resolve, w, h\n\nexport compose, compose!, Context, UnitBox, AbsoluteBoundingBox,\n        Rotation, Mirror, Shear,\n       ParentDrawContext, context, ctxpromise, table, set_units!, minwidth, minheight,\n       text_extents, max_text_extents, polygon, ngon, star, xgon, bezigon,\n       line, rectangle, circle, arc, sector, ellipse, text, curve, bitmap, \n       stroke, fill, strokedash, strokelinecap, arrow, strokelinejoin,\n       linewidth, visible, fillopacity, strokeopacity, clip, points,\n       font, fontsize, svgid, svgclass, svgattribute, jsinclude, jscall, Measure,\n       inch, mm, cm, pt, px, cx, cy, sx, sy, w, h, hleft, hcenter, hright, vtop, vcenter,\n       vbottom, SVG, SVGJS, PGF, PNG, PS, PDF, draw, pad, pad_inner, pad_outer,\n       hstack, vstack, gridstack, LineCapButt, LineCapSquare, LineCapRound,\n       CAIROSURFACE, introspect, set_default_graphic_size, set_default_jsmode,\n       boundingbox, Patchable\n\nabstract type Backend end\n\n\"\"\"\nSome backends can more efficiently draw forms by batching. If so, they\nshuld define a similar method that returns true.\n\"\"\"\ncanbatch(::Backend) = false\n\n# Allow users to supply strings without deprecation warnings\nparse_colorant(c::Colorant) = c\nparse_colorant(str::AbstractString) = parse(Colorant, str)\nparse_colorant(c::Union{Tuple,Vector}) = [parse_colorant(x) for x in c]\nparse_colorant(c...) = parse_colorant(c)\n@deprecate parse_colorant_vec(c...) parse_colorant(c)\n\ninclude(\"misc.jl\")\ninclude(\"measure.jl\")\ninclude(\"list.jl\")\n\n# Every graphic in Compose consists of a tree.\nabstract type ComposeNode end\n\n# Used to mark null child pointers\nstruct NullNode <: ComposeNode end\nnullnode = NullNode()\n\ninclude(\"form.jl\")\ninclude(\"property.jl\")\ninclude(\"container.jl\")\ninclude(\"batch.jl\")\ninclude(\"table.jl\")\ninclude(\"stack.jl\")\n\n# How large to draw graphics when not explicitly drawing to a backend\ndefault_graphic_width = sqrt(2)*10*cm\ndefault_graphic_height = 10cm\n\nfunction set_default_graphic_size(width::MeasureOrNumber,\n                                  height::MeasureOrNumber)\n    global default_graphic_width\n    global default_graphic_height\n    default_graphic_width = x_measure(width)\n    default_graphic_height = y_measure(height)\n    nothing\nend\n\ndefault_graphic_format = :html\n\nfunction set_default_graphic_format(fmt::Symbol)\n    fmt in [:html, :png, :svg, :pdf, :ps, :pgf] || error(\"$(fmt) is not a supported plot format\")\n    global default_graphic_format\n    default_graphic_format = fmt\n    nothing\nend\n\n# Default means to include javascript dependencies in the SVGJS backend.\ndefault_jsmode = :embed\n\nfunction set_default_jsmode(mode::Symbol)\n    global default_jsmode\n    if mode in [:none, :exclude, :embed, :linkabs, :linkrel]\n        default_jsmode = mode\n    else\n        error(\"$(mode) is not a valid jsmode\")\n    end\n    nothing\nend\n\nfunction default_mime()\n    if default_graphic_format == :png\n        \"image/png\"\n    elseif default_graphic_format == :svg\n        \"image/svg+xml\"\n    elseif default_graphic_format == :html\n        \"text/html\"\n    elseif default_graphic_format == :ps\n        \"application/postscript\"\n    elseif default_graphic_format == :pdf\n        \"application/pdf\"\n    elseif default_graphic_format == :pgf\n        \"application/x-tex\"\n    else\n        \"\"\n    end\nend\n\n# Default property values\ndefault_font_family = \"Helvetica Neue,Helvetica,Arial,sans\"\ndefault_font_size = 11pt\ndefault_line_width = 0.3mm\ndefault_stroke_color = nothing\ndefault_fill_color = colorant\"black\"\n\n# If Cairo is not available, throw an error when trying to save with a Cairo backend\nmissing_cairo_error(backend::String, invocation::String=backend) =\n    \"\"\"\n    The Cairo and Fontconfig packages are necessary for saving as $backend.\n    Add them with the package manager if necessary, then run `import Cairo,\n    Fontconfig` before invoking `$invocation`.\n    \"\"\"\nmissing_cairo_error(m::MIME) =\n    missing_cairo_error(string(m), \"show(::IO, ::MIME\\\"$m\\\", ::Context)\")\n\nfor backend in [:PNG, :PS, :PDF]\n  docstr = missing_cairo_error(string(backend))\n  @eval @doc $docstr $backend(args...; kwargs...) = error(missing_cairo_error(string($backend)))\nend\n\nCairoMIME = Union{MIME\"image/png\", MIME\"application/ps\", MIME\"application/pdf\"}\nshow(io::IO, m::CairoMIME, ctx::Context) = error(missing_cairo_error(m))\n\ninclude(\"svg.jl\")\ninclude(\"pgf_backend.jl\")\n\n# If available, pango and fontconfig are used to compute text extents and match\n# fonts. Otherwise a simplistic pure-julia fallback is used.\n\ninclude(\"fontfallback.jl\")\n\nconst pango_cairo_ctx = Ref{Ptr}(C_NULL)\nconst pango_cairo_fm = Ref{Ptr}(C_NULL)\nconst pangolayout = Ref{Any}(nothing)\n\nfunction link_fontconfig()\n    @debug \"Loading Fontconfig backend into Compose.jl\"\n    pango_cairo_ctx[] = C_NULL\n\n    ccall((:g_type_init, libgobject), Cvoid, ())\n    pango_cairo_fm[]  = ccall((:pango_cairo_font_map_new, libpangocairo),\n                               Ptr{Cvoid}, ())\n    pango_cairo_ctx[] = ccall((:pango_font_map_create_context, libpango),\n                               Ptr{Cvoid}, (Ptr{Cvoid},), pango_cairo_fm[])\n    pangolayout[] = PangoLayout()\nend\n\nfunction link_cairo()\n    @debug \"Loading Cairo backend into Compose.jl\"\n    include(joinpath(@__DIR__,\"cairo_backends.jl\"))\n    include(joinpath(@__DIR__,\"immerse_backend.jl\"))\nend\n\nfunction __init__()\n    @require Cairo=\"159f3aea-2a34-519c-b102-8c37f9878175\" link_cairo()\n    @require Fontconfig=\"186bb1d3-e1f7-5a2c-a377-96d770f13627\" begin\n      include(\"pango.jl\")\n      link_fontconfig()\n    end\nend\n\nshow(io::IO, m::MIME\"text/html\", ctx::Context) =\n    draw(SVGJS(io, default_graphic_width, default_graphic_height, false,\n               jsmode=default_jsmode), ctx)\n\nshow(io::IO, m::MIME\"image/svg+xml\", ctx::Context) =\n    draw(SVG(io, default_graphic_width, default_graphic_height, false), ctx)\n\nfunction pad_outer(c::Context,\n                   left_padding::MeasureOrNumber,\n                   right_padding::MeasureOrNumber,\n                   top_padding::MeasureOrNumber,\n                   bottom_padding::MeasureOrNumber)\n\n    left_padding   = size_measure(left_padding)\n    right_padding  = size_measure(right_padding)\n    top_padding    = size_measure(top_padding)\n    bottom_padding = size_measure(bottom_padding)\n\n    root = context(c.box.x0[1], c.box.x0[1],\n                   c.box.a[1] + left_padding + right_padding,\n                   c.box.a[2] + top_padding + bottom_padding,\n                   minwidth=c.minwidth,\n                   minheight=c.minheight)\n    c = copy(c)\n    c.box = BoundingBox(left_padding, top_padding,\n                        1w - left_padding - right_padding,\n                        1h - top_padding - bottom_padding)\n    return compose!(root, c)\nend\n\npad_outer(c::Context, padding::MeasureOrNumber) =\n        pad_outer(c, padding, padding, padding, padding)\n\npad_outer(cs::Vector{Context},\n                   left_padding::MeasureOrNumber,\n                   right_padding::MeasureOrNumber,\n                   top_padding::MeasureOrNumber,\n                   bottom_padding::MeasureOrNumber) =\n        map(c -> pad_outer(c, left_padding, right_padding, top_padding, bottom_padding), cs)\n\npad_outer(cs::Vector{Context}, padding::MeasureOrNumber) =\n        pad_outer(cs, padding, padding, padding, padding)\n\nfunction pad_inner(c::Context,\n                   left_padding::MeasureOrNumber,\n                   right_padding::MeasureOrNumber,\n                   top_padding::MeasureOrNumber,\n                   bottom_padding::MeasureOrNumber)\n\n    left_padding   = size_measure(left_padding)\n    right_padding  = size_measure(right_padding)\n    top_padding    = size_measure(top_padding)\n    bottom_padding = size_measure(bottom_padding)\n\n    root = context(c.box.x0[1], c.box.x0[2],\n                   c.box.a[1], c.box.a[2],\n                   minwidth=c.minwidth,\n                   minheight=c.minheight)\n    c = copy(c)\n    c.box = BoundingBox(left_padding, top_padding,\n                        1w - left_padding - right_padding,\n                        1h - top_padding - bottom_padding)\n    return compose!(root, c)\nend\n\npad_inner(c::Context, padding::MeasureOrNumber) =\n        pad_inner(c, padding, padding, padding, padding)\n\npad_inner(cs::Vector{Context},\n                   left_padding::MeasureOrNumber,\n                   right_padding::MeasureOrNumber,\n                   top_padding::MeasureOrNumber,\n                   bottom_padding::MeasureOrNumber) =\n        map(c -> pad_inner(c, left_padding, right_padding, top_padding, bottom_padding), cs)\n\npad_inner(cs::Vector{Context}, padding::MeasureOrNumber) =\n        pad_inner(cs, padding, padding, padding, padding)\n\nfunction gridstack(cs::Matrix{Context})\n    m, n = size(cs)\n    t = Table(m, n, 1:m, 1:n, x_prop=ones(n), y_prop=ones(m))\n    for i in 1:m, j in 1:n\n        t[i, j] = [cs[i, j]]\n    end\n    return compose!(context(), t)\nend\n\nconst pad = pad_outer\n\nend # module Compose\n"
  },
  {
    "path": "src/abandoned.jl",
    "content": "\n# Path Primitives\n\n\n# From form.jl:\n# Path\n# ----\n\n# An implementation of the SVG path mini-language.\n\nabstract type PathOp end\n\nstruct MoveAbsPathOp <: PathOp\n    to::Vec\nend\n\nfunction assert_pathop_tokens_len(op_type, tokens, i, needed)\n    provided = length(tokens) - i + 1\n    provided < needed &&\n            error(\"In path $(op_type) requires $(needed) argumens but only $(provided) provided.\")\nend\n\nfunction parsepathop(::Type{MoveAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(MoveAbsPathOp, tokens, i, 2)\n    op = MoveAbsPathOp((x_measure(tokens[i]), y_measure(tokens[i + 1])))\n    return (op, i + 2)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::MoveAbsPathOp) =\n        MoveAbsPathOp(resolve(box, units, t, p.to))\n\nstruct MoveRelPathOp <: PathOp\n    to::Vec\nend\n\nfunction parsepathop(::Type{MoveRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(MoveRelPathOp, tokens, i, 2)\n    op = MoveRelPathOp((tokens[i], tokens[i + 1]))\n    return (op, i + 2)\nend\n\nfunction resolve_offset(box::AbsoluteBox, units::UnitBox, t::Transform, p::Vec)\n    absp = resolve(box, units, t, p)\n    zer0 = resolve(box, units, t, (0w, 0h))\n    return (absp[1] - zer0[1], absp[2] - zer0[2])\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::MoveRelPathOp) =\n        MoveRelPathOp(resolve_offset(box, units, t, p.to))\n\nstruct ClosePathOp <: PathOp\nend\n\nparsepathop(::Type{ClosePathOp}, tokens::AbstractArray, i) = (ClosePathOp(), i)\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::ClosePathOp) = p\n\nstruct LineAbsPathOp <: PathOp\n    to::Vec\nend\n\nfunction parsepathop(::Type{LineAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(LineAbsPathOp, tokens, i, 2)\n    op = LineAbsPathOp((x_measure(tokens[i]), y_measure(tokens[i + 1])))\n    return (op, i + 2)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::LineAbsPathOp) =\n        LineAbsPathOp(resolve(box, units, t, p.to))\n\nstruct LineRelPathOp <: PathOp\n    to::Vec\nend\n\nfunction parsepathop(::Type{LineRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(LineRelPathOp, tokens, i, 2)\n    op = LineRelPathOp((x_measure(tokens[i]), y_measure(tokens[i + 1])))\n    return (op, i + 2)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::LineRelPathOp) =\n        LineRelPathOp(resolve(box, units, t, p.to))\n\nstruct HorLineAbsPathOp <: PathOp\n    x::Measure\nend\n\nfunction parsepathop(::Type{HorLineAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(HorLineAbsPathOp, tokens, i, 1)\n    op = HorLineAbsPathOp(x_measure(tokens[i]))\n    return (op, i + 1)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::HorLineAbsPathOp) =\n        HorLineAbsPathOp(resolve(box, units, t, (p.x, 0mm))[1])\n\nstruct HorLineRelPathOp <: PathOp\n    Δx::Measure\nend\n\nfunction parsepathop(::Type{HorLineRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(HorLineRelPathOp, tokens, i, 1)\n    op = HorLineRelPathOp(x_measure(tokens[i]))\n    return (op, i + 1)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::HorLineRelPathOp) =\n        HorLineRelPathOp(resolve(box, units, t, p.Δx))\n\nstruct VertLineAbsPathOp <: PathOp\n    y::Measure\nend\n\nfunction parsepathop(::Type{VertLineAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(VertLineAbsPathOp, tokens, i, 1)\n    op = VertLineAbsPathOp(y_measure(tokens[i]))\n    return (op, i + 1)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::VertLineAbsPathOp) =\n        VertLineAbsPathOp(resolve(box, units, t, (0mm, p.y))[2])\n\nstruct VertLineRelPathOp <: PathOp\n    Δy::Measure\nend\n\nfunction parsepathop(::Type{VertLineRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(VertLineRelPathOp, tokens, i, 1)\n    op = VertLineRelPathOp(y_measure(tokens[i]))\n    return (op, i + 1)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::VertLineRelPathOp) =\n        VertLineAbsPathOp(resolve(box, units, t, (0mmm, p.Δy))[2])\n\nstruct CubicCurveAbsPathOp <: PathOp\n    ctrl1::Vec\n    ctrl2::Vec\n    to::Vec\nend\n\nfunction parsepathop(::Type{CubicCurveAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(CubicCurveAbsPathOp, tokens, i, 6)\n    op = CubicCurveAbsPathOp((tokens[i],     tokens[i + 1]),\n                             (tokens[i + 2], tokens[i + 3]),\n                             (tokens[i + 4], tokens[i + 5]))\n    return (op, i + 6)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::CubicCurveAbsPathOp) =\n        CubicCurveAbsPathOp(\n            resolve(box, units, t, p.ctrl1),\n            resolve(box, units, t, p.ctrl2),\n            resolve(box, units, t, p.to))\n\nstruct CubicCurveRelPathOp <: PathOp\n    ctrl1::Vec\n    ctrl2::Vec\n    to::Vec\nend\n\nfunction parsepathop(::Type{CubicCurveRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(CubicCurveRelPathOp, tokens, i, 6)\n    op = CubicCurveRelPathOp((tokens[i],     tokens[i + 1]),\n                             (tokens[i + 2], tokens[i + 3]),\n                             (tokens[i + 4], tokens[i + 5]))\n    return (op, i + 6)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::CubicCurveRelPathOp) =\n        CubicCurveRelPathOp(\n            resolve(box, units, t, p.ctrl1),\n            resolve(box, units, t, p.ctrl2),\n            resolve(box, units, t, p.to))\n\nstruct CubicCurveShortAbsPathOp <: PathOp\n    ctrl2::Vec\n    to::Vec\nend\n\nfunction parsepathop(::Type{CubicCurveShortAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(CubicCurveShortAbsPathOp, tokens, i, 4)\n    op = CubicCurveShortAbsPathOp((x_measure(tokens[i]),     y_measure(tokens[i + 1])),\n                                  (x_measure(tokens[i + 2]), y_measure(tokens[i + 3])))\n    return (op, i + 4)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::CubicCurveShortAbsPathOp) =\n        CubicCurveShortAbsPathOp(\n            resolve_offset(box, units, t, p.ctrl2),\n            resolve_offset(box, units, t, p.to))\n\nstruct CubicCurveShortRelPathOp <: PathOp\n    ctrl2::Vec\n    to::Vec\nend\n\nfunction parsepathop(::Type{CubicCurveShortRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(CubicCurveShortRelPathOp, tokens, i, 4)\n    op = CubicCurveShortRelPathOp((x_measure(tokens[i]),     y_measure(tokens[i + 1])),\n                                  (x_measure(tokens[i + 2]), y_measure(tokens[i + 3])))\n    return (op, i + 4)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::CubicCurveShortRelPathOp) =\n        CubicCurveShortRelPathOp(\n            resolve(box, units, t, p.ctrl2),\n            resolve(box, units, t, p.to))\n\nstruct QuadCurveAbsPathOp <: PathOp\n    ctrl1::Vec\n    to::Vec\nend\n\nfunction parsepathop(::Type{QuadCurveAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(QuadCurveAbsPathOp, tokens, i, 4)\n    op = QuadCurveAbsPathOp((tokens[i],     tokens[i + 1]),\n                            (tokens[i + 2], tokens[i + 3]))\n    return (op, i + 4)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::QuadCurveAbsPathOp) =\n        QuadCurveAbsPathOp(\n            resolve(box, units, t, p.ctrl1),\n            resolve(box, units, t, p.to))\n\nstruct QuadCurveRelPathOp <: PathOp\n    ctrl1::Vec\n    to::Vec\nend\n\nfunction parsepathop(::Type{QuadCurveRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(QuadCurveRelPathOp, tokens, i, 4)\n    op = QuadCurveRelPathOp((tokens[i],     tokens[i + 1]),\n                            (tokens[i + 2], tokens[i + 3]))\n    return (op, i + 4)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::QuadCurveRelPathOp) =\n        QuadCurveRelPathOp(\n            (resolve(box, units, t, p.ctrl1[1]),\n             resolve(box, units, t, p.ctrl1[2])),\n            (resolve(box, units, t, p.to[1]),\n             resolve(box, units, t, p.to[2])))\n\nstruct QuadCurveShortAbsPathOp <: PathOp\n    to::Vec\nend\n\nfunction parsepathop(::Type{QuadCurveShortAbsPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(QuadCurveShortAbsPathOp, tokens, i, 2)\n    op = QuadCurveShortAbsPathOp((tokens[i], tokens[i + 1]))\n    return (op, i + 2)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::QuadCurveShortAbsPathOp) =\n        QuadCurveShortAbsPathOp(resolve(box, units, t, p.to))\n\nstruct QuadCurveShortRelPathOp <: PathOp\n    to::Vec\nend\n\nfunction parsepathop(::Type{QuadCurveShortRelPathOp}, tokens::AbstractArray, i)\n    assert_pathop_tokens_len(QuadCurveShortRelPathOp, tokens, i, 2)\n    op = QuadCurveShortRelPathOp((tokens[i], tokens[i + 1]))\n    return (op, i + 2)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::QuadCurveShortRelPathOp) =\n        QuadCurveShortRelPathOp(\n            (resolve(box, units, t, p.to[1]),\n             resolve(box, units, t, p.to[2])))\n\nstruct ArcAbsPathOp <: PathOp\n    rx::Measure\n    ry::Measure\n    rotation::Float64\n    largearc::Bool\n    sweep::Bool\n    to::Vec\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::ArcAbsPathOp) =\n        ArcAbsPathOp(\n            resolve(box, units, t, p.rx),\n            resolve(box, units, t, p.ry),\n            p.rotation,\n            p.largearc,\n            p.sweep,\n            resolve(box, units, t, p.to))\n\nstruct ArcRelPathOp <: PathOp\n    rx::Measure\n    ry::Measure\n    rotation::Float64\n    largearc::Bool\n    sweep::Bool\n    to::Vec\nend\n\nfunction parsepathop(::Type{T}, tokens::AbstractArray, i) where T <: Union{ArcAbsPathOp, ArcRelPathOp}\n    assert_pathop_tokens_len(T, tokens, i, 7)\n\n    if isa(tokens[i + 3], Bool)\n        largearc = tokens[i + 3]\n    elseif tokens[i + 3] == 0\n        largearc = false\n    elseif tokens[i + 3] == 1\n        largearc = true\n    else\n        error(\"largearc argument to the arc path operation must be boolean\")\n    end\n\n    if isa(tokens[i + 4], Bool)\n        sweep = tokens[i + 4]\n    elseif tokens[i + 4] == 0\n        sweep = false\n    elseif tokens[i + 4] == 1\n        sweep = true\n    else\n        error(\"sweep argument to the arc path operation must be boolean\")\n    end\n\n    isa(tokens[i + 2], Number) || error(\"path arc operation requires a numerical rotation\")\n\n    op = T(x_measure(tokens[i]),\n           y_measure(tokens[i + 1]),\n           convert(Float64, tokens[i + 2]),\n           largearc, sweep,\n           (x_measure(tokens[i + 5]), y_measure(tokens[i + 6])))\n\n    return (op, i + 7)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::ArcRelPathOp) =\n        ArcRelPathOp(\n            resolve(box, units, t, p.rx),\n            resolve(box, units, t, p.ry),\n            p.rotation,\n            p.largearc,\n            p.sweep,\n            (resolve(box, units, t, p.to[1]),\n             resolve(box, units, t, p.to[2])))\n\nconst path_ops = Dict(\n     :M => MoveAbsPathOp,\n     :m => MoveRelPathOp,\n     :Z => ClosePathOp,\n     :z => ClosePathOp,\n     :L => LineAbsPathOp,\n     :l => LineRelPathOp,\n     :H => HorLineAbsPathOp,\n     :h => HorLineRelPathOp,\n     :V => VertLineAbsPathOp,\n     :v => VertLineRelPathOp,\n     :C => CubicCurveAbsPathOp,\n     :c => CubicCurveRelPathOp,\n     :S => CubicCurveShortAbsPathOp,\n     :s => CubicCurveShortRelPathOp,\n     :Q => QuadCurveAbsPathOp,\n     :q => QuadCurveRelPathOp,\n     :T => QuadCurveShortAbsPathOp,\n     :t => QuadCurveShortRelPathOp,\n     :A => ArcAbsPathOp,\n     :a => ArcRelPathOp\n)\n\n# A path is an array of symbols, numbers, and measures following SVGs path\n# mini-language.\nfunction parsepath(tokens::AbstractArray)\n    ops = PathOp[]\n    last_op_type = nothing\n    i = 1\n    while i <= length(tokens)\n        tok = tokens[i]\n        strt = i\n        if isa(tok, Symbol)\n            if !haskey(path_ops, tok)\n                error(\"$(tok) is not a valid path operation\")\n            else\n                op_type = path_ops[tok]\n                i += 1\n                op, i = parsepathop(op_type, tokens, i)\n                push!(ops, op)\n                last_op_type = op_type\n            end\n        else\n            op, i = parsepathop(last_op_type, tokens, i)\n            push!(ops, op)\n        end\n    end\n\n    return ops\nend\n\nstruct PathPrimitive <: FormPrimitive\n    ops::Vector{PathOp}\nend\n\nconst Path = Form{PathPrimitive}\n\npath(tokens::AbstractArray, tag=empty_tag) = Path([PathPrimitive(parsepath(tokens))], tag)\n\npath(tokens::AbstractArray{T}, tag=empty_tag) where T <: AbstractArray =\n        Path([PathPrimitive(parsepath(ts)) for ts in tokens], tag)\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::PathPrimitive) =\n        PathPrimitive([resolve(box, units, t, op) for op in p.ops])\n\n# TODO: boundingbox\n\n\n\n# From cairo_backends.jl:\n\nfunction draw(img::Image, prim::PathPrimitive)\n    for op in prim.ops\n        draw_path_op(img, op)\n    end\n    fillstroke(img)\nend\n\ndraw_path_op(img::Image, op::MoveAbsPathOp) = move_to(img, op.to)\ndraw_path_op(img::Image, op::MoveRelPathOp) = rel_move_to(img, op.to)\ndraw_path_op(img::Image, op::ClosePathOp)   = close_path(img)\ndraw_path_op(img::Image, op::LineAbsPathOp) = line_to(img, op.to)\ndraw_path_op(img::Image, op::LineRelPathOp) = rel_line_to(img, op.to)\n\nfunction draw_path_op(img::Image, op::HorLineAbsPathOp)\n    pos = current_point(img)\n    line_to(img, (op.x, pos.y))\nend\n\ndraw_path_op(img::Image, op::HorLineRelPathOp) = rel_line_to(img, (op.Δx, 0.0mm))\n\nfunction draw_path_op(img::Image, op::VertLineAbsPathOp)\n    pos = current_point(img)\n    line_to(img, (pos.x, op.y))\nend\n\ndraw_path_op(img::Image, op::VertLineRelPathOp) = rel_line_to(img, (0.0mm, op.Δy))\n\nfunction draw_path_op(img::Image, op::CubicCurveAbsPathOp)\n    curve_to(img, op.ctrl1, op.ctrl2, op.to)\n    img.last_ctrl2_point = op.ctrl2\nend\n\nfunction draw_path_op(img::Image, op::CubicCurveRelPathOp)\n    xy = current_point(img)\n    rel_curve_to(img, op.ctrl1, op.ctrl2, op.to)\n    img.last_ctrl2_point = (op.ctrl2[1] + xy[1], op.ctrl2[2] + xy[2])\nend\n\nfunction draw_path_op(img::Image, op::CubicCurveShortAbsPathOp)\n    xy = current_point(img)\n    ctrl1 = img.last_ctrl2_point\n    if ctrl1 === nothing\n        ctrl1 = xy\n    else\n        ctrl1 = (2*xy[1] - ctrl1[1], 2*xy[2] - ctrl1[2])\n    end\n    curve_to(img, ctrl1, op.ctrl2, op.to)\n    img.last_ctrl2_point = op.ctrl2\nend\n\nfunction draw_path_op(img::Image, op::CubicCurveShortRelPathOp)\n    xy = current_point(img)\n    x1, y1 = xy[1].value, xy[2].value\n    x2, y2 = op.to[1].value, op.to[2].value\n\n    ctrl1 = img.last_ctrl2_point\n    if ctrl1 === nothing\n        ctrl1 = xy\n    else\n        ctrl1 = (Measure(abs=(2*x1 - ctrl1[1].value) - x1),\n                 Measure(abs=(2*y1 - ctrl1[2].value) - y1))\n    end\n    cx, cy = ctrl1[1].value, ctrl1[2].value\n\n    rel_curve_to(img, ctrl1, op.ctrl2, op.to)\n    img.last_ctrl2_point =\n        (Measure(abs=op.ctrl2[1].value + xy.x.abs),\n         Measure(abs=op.ctrl2[2].value + xy.y.abs))\nend\n\nfunction draw_path_op(img::Image, op::QuadCurveAbsPathOp)\n    xy = current_point(img)\n    x1, y1 = xy[1].value, xy[2].value\n    x2, y2 = op.to[1].value, op.to[2].value\n    cx, cy = op.ctrl1[1].value, op.ctrl1[2].value\n    curve_to(img,\n             (Measure(abs=(x1 + 2*cx)/3),\n              Measure(abs=(y1 + 2*cy)/3)),\n             (Measure(abs=(x2 + 2*cx)/3),\n              Measure(abs=(y2 + 2*cy)/3)),\n             op.to)\n    img.last_ctrl1_point = op.ctrl1\nend\n\nfunction draw_path_op(img::Image, op::QuadCurveRelPathOp)\n    xy = current_point(img)\n    x1, y1 = xy[1].value, xy[2].value\n    x2, y2 = op.to[1].value, op.to[2].value\n    cx, cy = op.ctrl1[1].value, op.ctrl1[2].value\n    rel_curve_to(img,\n                 (Measure(abs=(x1 + 2*cx)/3),\n                  Measure(abs=(y1 + 2*cy)/3)),\n                 (Measure(abs=(x2 + 2*cx)/3),\n                  Measure(abs=(y2 + 2*cy)/3)),\n             op.to)\n    img.last_ctrl1_point =\n        (Measure(abs=op.ctrl1[1].value + xy.x.abs),\n         Measure(abs=op.ctrl1[2].value + xy.y.abs))\nend\n\nfunction draw_path_op(img::Image, op::QuadCurveShortAbsPathOp)\n    xy = current_point(img)\n    x1, y1 = xy[1].value, xy[2].value\n    x2, y2 = op.to[1].value, op.to[2].value\n\n    ctrl1 = img.last_ctrl1_point\n    if img.last_ctrl1_point === nothing\n        ctrl1 = xy\n    else\n        ctrl1 = (Measure(abs=2*x1 - ctrl1[1].value),\n                 Measure(abs=2*y1 - ctrl1[2].value))\n    end\n    cx, cy = ctrl1[1].value, ctrl1[2].value\n\n    curve_to(img,\n             (Measure(abs=(x1 + 2*cx)/3),\n              Measure(abs=(y1 + 2*cy)/3)),\n             (Measure(abs=(x2 + 2*cx)/3),\n              Measure(abs=(y2 + 2*cy)/3)),\n             (Measure(abs=x2), Measure(abs=y2)))\n    img.last_ctrl1_point = ctrl1\nend\n\nfunction draw_path_op(img::Image, op::QuadCurveShortRelPathOp)\n    xy = current_point(img)\n    x1, y1 = xy[1].value, xy[2].value\n    x2, y2 = x1 + op.to[1].value, y1 + op.to[2].value\n\n    ctrl1 = img.last_ctrl1_point\n    if ctrl1 === nothing\n        ctrl1 = xy\n    else\n        ctrl1 = (Measure(abs=(2*x1 - ctrl1[1].value) - x1),\n                 Measure(abs=(2*y1 - ctrl1[2].value) - y1))\n    end\n    cx, cy = ctrl1[1].value, ctrl1[2].value\n\n    rel_curve_to(img,\n                 (Measure(abs=(x1 + 2*cx)/3),\n                  Measure(abs=(y1 + 2*cy)/3)),\n                 (Measure(abs=(x2 + 2*cx)/3),\n                  Measure(abs=(y2 + 2*cy)/3)),\n                 (Measure(abs=x2), Measure(abs=y2)))\n    img.last_ctrl1_point =\n        (Measure(abs=op.ctrl1[1].value + x1),\n         Measure(abs=op.ctrl1[2].value + y1))\nend\n\nfunction draw_path_op(img::Image, op::ArcAbsPathOp)\n    xy = current_point(img)\n    x1, y1 = xy[1].value, xy[2].value\n    x2, y2 = op.to[1].value, op.to[2].value\n    rx, ry = op.rx.abs, op.ry.abs\n    φ = deg2rad(op.rotation)\n    draw_endpoint_arc(img, rx, ry, φ, op.largearc, op.sweep, x1, y1, x2, y2)\nend\n\nfunction draw_path_op(img::Image, op::ArcRelPathOp)\n    xy = current_point(img)\n    x1, y1 = xy[1].value, xy[2].value\n    x2, y2 = x1 + op.to[1].value, y1 + op.to[2].value\n    rx, ry = op.rx.abs, op.ry.abs\n    φ = deg2rad(op.rotation)\n    draw_endpoint_arc(img, rx, ry, φ, op.largearc, op.sweep, x1, y1, x2, y2)\nend\n\n# Draw an SVG style elliptical arc\nfunction draw_endpoint_arc(img::Image, rx::Float64, ry::Float64, φ::Float64,\n                           largearc::Bool, sweep::Bool,\n                           x1::Float64, y1::Float64,\n                           x2::Float64, y2::Float64)\n    function uvangle(ux, uy, vx, vy)\n        t = (ux * vx + uy * vy) / (sqrt(ux^2 + uy^2) * sqrt(vx^2 + vy^2))\n        t = max(min(t, 1.0), -1.0)\n        return (ux * vy - uy * vx < 0.0 ? -1 : 1.0) * acos(t)\n    end\n\n    # From: http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter\n    xm, ym = (x1 - x2)/2, (y1 - y2)/2\n    x1p =  cos(φ) * xm + sin(φ) * ym\n    y1p = -sin(φ) * xm + cos(φ) * ym\n\n    u = (rx^2 * ry^2 - rx^2 * y1p^2 - ry^2 * x1p^2) / (rx^2 * y1p^2 + ry^2 * x1p^2)\n    u = u >= 0.0 ? sqrt(u) : 0.0\n\n    cxp =  u * (rx * y1p) / ry\n    cyp = -u * (ry * x1p) / rx\n    if sweep == largearc\n        cxp = -cxp\n        cyp = -cyp\n    end\n    cx = (x1 + x2)/2 + cos(φ) * cxp - sin(φ) * cyp\n    cy = (y1 + y2)/2 + sin(φ) * cxp + cos(φ) * cyp\n\n    θ1 = uvangle(1.0, 0.0, (x1p - cxp) / rx, (y1p - cyp) / ry)\n    Δθ = uvangle((x1p - cxp) / rx, (y1p - cyp) / ry,\n                 (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (2.0*π)\n    if Δθ > 0.0 && !sweep\n        Δθ -= 2*π\n    elseif Δθ < 0.0 && sweep\n        Δθ += 2*π\n    end\n\n    Cairo.save(img.ctx)\n    Cairo.translate(img.ctx,\n                    absolute_native_units(img, cx),\n                    absolute_native_units(img, cy))\n    Cairo.rotate(img.ctx, φ)\n    Cairo.scale(img.ctx, rx, ry)\n    if sweep\n        arc(img, 0.0, 0.0, 1.0, θ1, θ1 + Δθ)\n    else\n        arc_negative(img, 0.0, 0.0, 1.0, θ1, θ1 + Δθ)\n    end\n    Cairo.restore(img.ctx)\nend\n\n\n# From svg.jl:\n\nfunction svg_print_path_op(io::IO, op::MoveAbsPathOp)\n    print(io, 'M')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::MoveRelPathOp)\n    print(io, 'm')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nsvg_print_path_op(io::IO, op::ClosePathOp) = print(io, 'z')\n\nfunction svg_print_path_op(io::IO, op::LineAbsPathOp)\n    print(io, 'L')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::LineRelPathOp)\n    print(io, 'l')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::HorLineAbsPathOp)\n    print(io, 'H')\n    svg_print_float(io, op.x.value)\nend\n\nfunction svg_print_path_op(io::IO, op::HorLineRelPathOp)\n    print(io, 'h')\n    svg_print_float(io, op.Δx.value)\nend\n\nfunction svg_print_path_op(io::IO, op::VertLineAbsPathOp)\n    print(io, 'V')\n    svg_print_float(io, op.y.value)\nend\n\nfunction svg_print_path_op(io::IO, op::VertLineRelPathOp)\n    print(io, 'v')\n    svg_print_float(io, op.Δy.value)\nend\n\nfunction svg_print_path_op(io::IO, op::CubicCurveAbsPathOp)\n    print(io, 'C')\n    svg_print_float(io, op.ctrl1[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl1[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl2[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl2[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::CubicCurveRelPathOp)\n    print(io, 'c')\n    svg_print_float(io, op.ctrl1[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl1[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl2[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl2[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::CubicCurveShortAbsPathOp)\n    print(io, 'S')\n    svg_print_float(io, op.ctrl2[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl2[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::CubicCurveShortRelPathOp)\n    print(io, 's')\n    svg_print_float(io, op.ctrl2[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl2[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::QuadCurveAbsPathOp)\n    print(io, 'Q')\n    svg_print_float(io, op.ctrl1[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl1[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::QuadCurveRelPathOp)\n    print(io, 'q')\n    svg_print_float(io, op.ctrl1[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.ctrl1[2].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::QuadCurveShortAbsPathOp)\n    print(io, 'T')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::QuadCurveShortRelPathOp)\n    print(io, 't')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::ArcAbsPathOp)\n    print(io, 'A')\n    svg_print_float(io, op.rx.value)\n    print(io, ' ')\n    svg_print_float(io, op.ry.value)\n    print(io, ' ')\n    svg_print_float(io, op.rotation)\n    print(io, ' ',\n          op.largearc ? 1 : 0, ' ',\n          op.sweep ? 1 : 0, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction svg_print_path_op(io::IO, op::ArcRelPathOp)\n    print(io, 'a')\n    svg_print_float(io, op.rx.value)\n    print(io, ' ')\n    svg_print_float(io, op.ry.value)\n    print(io, ' ')\n    svg_print_float(io, op.rotation)\n    print(io, ' ',\n          op.largearc ? 1 : 0, ' ',\n          op.sweep ? 1 : 0, ' ')\n    svg_print_float(io, op.to[1].value)\n    print(io, ' ')\n    svg_print_float(io, op.to[2].value)\nend\n\nfunction draw(img::SVG, prim::PathPrimitive, idx::Int)\n    indent(img)\n    print(img.out, \"<path d=\\\"\")\n    for op in prim.ops\n        svg_print_path_op(img.out, op)\n    end\n    print(img.out, '\"')\n    print_vector_properties(img, idx)\n    print(img.out, \"/>\\n\")\nend\n\n\n\n"
  },
  {
    "path": "src/batch.jl",
    "content": "\"\"\"\nA form batch is a vectorized form with n primitives transformed into a simpler\nrepresentation: one primitive repositioned n times.\n\nOn certain backends this leads to more efficient drawing. For example, SVG can\nbe shortened by using <def> and <use> tags, and raster graphics can render the\nform primitive to a back buffer and blit it into place for faster drawing.\n\nBatching is an optimization transform that happens at draw time. There's\ncurrently no mechanism to manually batch. E.g. contexts cannot have FormBatch\nchildren.\n\"\"\"\nstruct FormBatch{P <: FormPrimitive}\n    primitive::P\n    offsets::Vector{AbsoluteVec2}\nend\n\n\"\"\"\nAttempt to batch a form. Return a Nothing singleton if the Form could not be\nbatched, and FormBatch object if the original form can be replaced.\n\"\"\"\nbatch(form::Form{P}) where P = nothing\n\n# Note: in tests using random data, this optimization wasn't worth it. I'm\n# keeping it around out of hopes I find a more clever version that is\n# worthwhile, or benchmarks using real data show different results.\n\n# maximum distance between offsets to be considered redundand in mm\nconst offset_redundancy_threshold = 0.05\n\n\"\"\"\nProduce a new array of offsets in which near duplicate values have been removed.\n\"\"\"\nfunction filter_redundant_offsets!(offsets::Vector{AbsoluteVec2})\n    isempty(offsets) && return offsets\n\n    sort!(offsets)\n    nonredundant_offsets = AbsoluteVec2[offsets[1]]\n    for i in 2:length(offsets)\n        # use l1 distance for perf\n        d = abs(offsets[i-1][1].value - offsets[i][1].value) +\n            abs(offsets[i-1][2].value - offsets[i][2].value)\n        d > offset_redundancy_threshold && push!(nonredundant_offsets, offsets[i])\n    end\n    @show (length(offsets), length(nonredundant_offsets))\n\n    return nonredundant_offsets\nend\n\n#=\nfunction batch{T <: CirclePrimitive}(form::Form{T})\n    # circles can be batched if they all have the same radius.\n    r = form.primitives[1].radius\n    n = length(form.primitives)\n    for i in 2:n\n        form.primitives[i].radius == r || return Nothing\n    end\n\n    prim = CirclePrimitive((0mm, 0mm), r)\n    offsets = Array{AbsoluteVec2}(n)\n    for i in 1:n\n        offsets[i] = form.primitives[i].center\n    end\n\n    return FormBatch(prim, offsets)\nend\n=#\n\n# TODO: same for polygon, rectangle, ellipse\n\n# TODO: batch needs to be exposed as something that users can construct and\n# insert into a context. It doesn't make sense to make the same polygon over and\n# over and then try to convert it to FormBach.\n\n\n# Don't attempt to optimize for batching if the form is smaller than this.\nconst batch_length_threshold = 100\n\n\"\"\"\nCount the number of unique primitives in a property, stopping when max_count is\nexceeded.\n\"\"\"\nfunction count_unique_primitives(property::Property, max_count::Int)\n    unique_primitives = Set{eltype(property.primitives)}()\n    for primitive in property.primitives\n        push!(unique_primitives, primitive)\n        length(unique_primitives) > max_count && break\n    end\n\n    return length(unique_primitives)\nend\n\n\"\"\"\nRemove and return vector forms and vector properties from the Context.\n\"\"\"\nfunction excise_vector_children!(ctx::Context)\n    # excise vector forms\n    prev_form_child = form_child = ctx.form_children\n    forms = Form[]\n    while !isa(form_child, ListNull)\n        if length(form_child.head.primitives) > 1\n            push!(forms, form_child.head)\n            if prev_form_child == form_child\n                prev_form_child = ctx.form_children = form_child.tail\n            else\n                prev_form_child.tail = form_child.tail\n            end\n        else\n            prev_form_child = form_child\n        end\n\n        form_child = form_child.tail\n    end\n\n    # excise vector properties\n    prev_property_child = property_child = ctx.property_children\n    properties = Property[]\n    while !isa(property_child, ListNull)\n        if length(property_child.head.primitives) > 1\n            push!(properties, property_child.head)\n            if prev_property_child == property_child\n                prev_property_child = ctx.property_children = property_child.tail\n            else\n                prev_property_child.tail = property_child.tail\n            end\n        else\n            prev_property_child = property_child\n        end\n\n        property_child = property_child.tail\n    end\n\n    return (forms, properties)\nend\n\n\"\"\"\nAttempt to transform a tree into an equivalent tree that can more easily be\nbatched.\n\nWhat this does is look for patterns in which a long vector form is accompanied\nby a large vector property that has a relatively small number of unique values.\nIf there are n unique values, we can split it into n contexts, each with a\nshorter vector form and only scalar properties.\n\"\"\"\nfunction optimize_batching(ctx::Context)\n    # condition 1: has a 1 or more long vector forms\n    max_form_length = 0\n    form_child = ctx.form_children\n    while !isa(form_child, ListNull)\n        max_form_length = max(max_form_length, length(form_child.head.primitives))\n        form_child = form_child.tail\n    end\n\n    max_form_length < batch_length_threshold && return ctx\n\n    # condition 2: has a 1 or more long vector properties each with a smaller\n    # number of unique values\n    max_count = div(max_form_length, batch_length_threshold) + 1\n    max_unique_primitives = 0\n    prop_child = ctx.property_children\n    while !isa(prop_child, ListNull)\n        if length(prop_child.head.primitives) > 1\n            max_unique_primitives = max(max_unique_primitives,\n                    count_unique_primitives(prop_child.head, max_count))\n        end\n        prop_child = prop_child.tail\n    end\n\n    # don't batch when there are not many forms per unique property primitive\n    if max_unique_primitives == 0 ||\n        div(max_form_length, max_unique_primitives) + 1 < batch_length_threshold\n        return ctx\n    end\n\n    # non-destructive since this happens at draw time and draw should not modify\n    # the context.\n    ctx = copy(ctx)\n\n    # step 1: remove vector form and vector properties\n    forms, properties = excise_vector_children!(ctx)\n\n    # step 2: split primitives into groups on the cross product of property\n    # primives\n    n = length(forms[1].primitives)\n    grouped_forms = Dict{UInt, Vector{Form}}()\n    grouped_properties = Dict{UInt, Vector{Property}}()\n    for i in 1:n\n        h = UInt(0)\n        for property in properties\n            h = hash(property.primitives[i], h)\n        end\n\n        if !haskey(grouped_forms, h)\n            grouped_forms[h] = Form[similar(form) for form in forms]\n            group_prop = Array{Property}(undef, length(properties))\n            for j in 1:length(properties)\n                group_prop[j] = Property([properties[j].primitives[i]])\n            end\n            grouped_properties[h] = group_prop\n        end\n\n        for j in 1:length(forms)\n            push!(grouped_forms[h][j].primitives, forms[j].primitives[i])\n        end\n    end\n\n    # step 3: put forms in new contexts and insert into the ctx\n    for (h, fs) in grouped_forms\n        subctx = context()\n        compose!(subctx, fs...)\n        compose!(subctx, grouped_properties[h]...)\n        compose!(ctx, subctx)\n    end\n\n    return ctx\nend\n"
  },
  {
    "path": "src/cairo_backends.jl",
    "content": "import .Cairo: CairoContext, CairoSurface, CairoARGBSurface, CairoEPSSurface,\nCairoPDFSurface, CairoSVGSurface, CairoImageSurface\n\nabstract type ImageBackend end\nabstract type PNGBackend <: ImageBackend end\n\nabstract type VectorImageBackend <: ImageBackend end\nabstract type SVGBackend <: VectorImageBackend end\nabstract type PDFBackend <: VectorImageBackend end\nabstract type PSBackend  <: VectorImageBackend end\nabstract type CairoBackend <: VectorImageBackend end\n\nmutable struct ImagePropertyState\n    stroke::RGBA{Float64}\n    fill::RGBA{Float64}\n    stroke_dash::Array{Float64,1}\n    stroke_linecap::LineCap\n    stroke_linejoin::LineJoin\n    visible::Bool\n    linewidth::AbsoluteLength\n    fontsize::AbsoluteLength\n    font::AbstractString\n    clip::Union{ClipPrimitive, Nothing}\n    arrow::Bool\nend\n\nmutable struct ImagePropertyFrame\n    # Vector properties in this frame.\n    vector_properties::Dict{Type, Property}\n\n    # True if this property frame has scalar properties. Scalar properties are\n    # emitted as a group (<g> tag) that must be closed when the frame is popped.\n    has_scalar_properties::Bool\nend\nImagePropertyFrame() = ImagePropertyFrame(Dict{Type, Property}(), false)\n\nmutable struct Image{B<:ImageBackend} <: Backend\n    out::IO\n    surface::CairoSurface\n    ctx::CairoContext\n\n    width::Float64\n    height::Float64\n\n    # Current state\n    stroke::RGBA{Float64}\n    fill::RGBA{Float64}\n    stroke_dash::Array{Float64,1}\n    stroke_linecap::LineCap\n    stroke_linejoin::LineJoin\n    visible::Bool\n    linewidth::AbsoluteLength\n    fontsize::AbsoluteLength\n    font::AbstractString\n    clip::Union{ClipPrimitive, Nothing}\n    arrow::Bool\n\n    # Keep track of property\n    state_stack::Vector{ImagePropertyState}\n    property_stack::Vector{ImagePropertyFrame}\n    vector_properties::Dict{Type, Union{Property, Nothing}}\n\n    # Close the surface when finished\n    owns_surface::Bool\n\n    # Backend is responsible for opening/closing the file\n    ownedfile::Bool\n\n    # Filename when ownedfile is true\n    filename::Union{AbstractString, Nothing}\n\n    # True when finish has been called and no more drawing should occur\n    finished::Bool\n\n    # Emit on finish\n    emit_on_finish::Bool\n\n    # Points (or pixels for PNG) per mm\n    ppmm::Float64\n\n    # For use with the t/T and s/S commands in SVG-style paths\n    last_ctrl1_point::Union{AbsoluteVec2, Nothing}\n    last_ctrl2_point::Union{AbsoluteVec2, Nothing}\nend\n\nfunction Image{B}(surface::CairoSurface,\n               ctx::CairoContext,\n               out::IO;\n\n               width = 0,\n               height = 0,\n               stroke = default_stroke_color == nothing ?\n                   RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_stroke_color),\n               fill = default_fill_color == nothing ?\n                   RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_fill_color),\n               stroke_dash = [],\n               stroke_linecap = LineCapButt(),\n               stroke_linejoin = LineJoinMiter(),\n               visible = true,\n               linewidth = default_line_width,\n               fontsize = default_font_size,\n               font = default_font_family,\n               clip = nothing,\n               arrow = false,\n               state_stack = Array{ImagePropertyState}(undef, 0),\n               property_stack = Array{ImagePropertyFrame}(undef, 0),\n               vector_properties = Dict{Type, Union{Property, Nothing}}(),\n               owns_surface = false,\n               ownedfile = false,\n               filename = nothing,\n               finished = false,\n               emit_on_finish = false,\n               ppmm = 72 / 25.4,\n               last_ctrl1_point = nothing,\n               last_ctrl2_point = nothing) where B<:ImageBackend\n\n    Image{B}(out,\n          surface,\n          ctx,\n          width,\n          height,\n          stroke,\n          fill,\n          stroke_dash,\n          stroke_linecap,\n          stroke_linejoin,\n          visible,\n          linewidth,\n          fontsize,\n          font,\n          clip,\n          arrow,\n          state_stack,\n          property_stack,\n          vector_properties,\n          owns_surface,\n          ownedfile,\n          filename,\n          finished,\n          emit_on_finish,\n          ppmm,\n          last_ctrl1_point,\n          last_ctrl2_point)\nend\n\nImage{B}(surface::CairoSurface, ctx::CairoContext) where {B<:ImageBackend} =\n        Image{B}(surface, ctx, IOBuffer())\nImage{B}(surface::CairoSurface) where {B<:ImageBackend} =\n        Image{B}(surface, CairoContext(surface))\n\nfunction Image{B}(out::IO,\n            width::MeasureOrNumber=default_graphic_width,\n            height::MeasureOrNumber=default_graphic_height,\n            emit_on_finish::Bool=true;\n            dpi = (B==PNGBackend ? 96 : 72),\n            kwargs...) where B<:ImageBackend\n    width = size_measure(width)\n    height = size_measure(height)\n\n    (!isa(width, AbsoluteLength) || !isa(height, AbsoluteLength)) &&\n                error(\"Image size must be specificed in absolute units.\")\n\n    ppmm = dpi / 25.4\n    width = width.value * ppmm\n    height = height.value * ppmm\n\n    surface = newsurface(B, out, width, height)\n    Image{B}(surface, CairoContext(surface), out;\n                width=width, height=height,\n                owns_surface=true, emit_on_finish=emit_on_finish, ppmm=ppmm,\n                kwargs...)\nend\n\nImage{B}(filename::AbstractString,\n            width::MeasureOrNumber=default_graphic_width,\n            height::MeasureOrNumber=default_graphic_height;\n            dpi = (B==PNGBackend ? 96 : 72)) where {B<:ImageBackend} =\n        Image{B}(open(filename, \"w\"), width, height, dpi=dpi; ownedfile=true, filename=filename)\n\nImage{B}(width::MeasureOrNumber=default_graphic_width,\n            height::MeasureOrNumber=default_graphic_height,\n            emit_on_finish::Bool=true;\n            dpi = (B==PNGBackend ? 96 : 72)) where {B<:ImageBackend} =\n        Image{B}(IOBuffer(), width, height, emit_on_finish, dpi=dpi)\n\n\n\ndocfunc(func,abbr) = \"\"\"\n    $func([output::Union{AbstractString, IO}], width=√200cm, height=10cm; dpi=$(func==:PNG ? 96 : 72)) -> Backend\n\nCreate a $abbr backend. The output is normally passed to [`draw`](@ref).\nSpecify a filename using a string as the first argument.  Depends on `Cairo.jl`.\n\n# Examples\n```\nusing Cairo\nc = compose(context(), circle())\ndraw($(func)(\"myplot.$(lowercase(String(func)))\", 10cm, 5cm, dpi=250), c)\n```\n\"\"\"\n\n\nfor (func,abbr) in [(:PNG,\"Portable Network Graphics\"),\n                    (:PDF,\"Portable Document Format\"),\n                    (:PS,\"Postscript\")]\n    backend = Symbol(func, \"Backend\")\n    docstr = docfunc(func,abbr)\n    @eval $func(args...; kwargs...) = Image{$backend}(args...; kwargs...)\n    @eval @doc $docstr $func\n\nend\n\n\n\nconst CAIROSURFACE = Image{CairoBackend}\n\nfunction (img::Image)(x)\n    draw(img, x)\nend\n\nfunction canbatch(img::Image)\n    for vp in values(img.vector_properties)\n        (vp === nothing) || return false\n    end\n    return true\nend\n\n# convert compose absolute units (millimeters) to the absolute units used by the\n# cairo surface (pixels for PNG, points for all others)\n\nabsolute_native_units(img::Image, u::Float64) = img.ppmm * u\n\nsurface(img::Image) = img.surface\n\nMeasures.width(img::Image) = (Cairo.width(img.surface) / img.ppmm) * mm\nMeasures.height(img::Image) = (Cairo.height(img.surface) / img.ppmm) * mm\n\niswithjs(img::Image) = false\niswithousjs(img::Image) = true\n\nfinish(::Type{B}, img::Image) where {B<:ImageBackend} = nothing\nfinish(::Type{PNGBackend}, img::Image) = Cairo.write_to_png(img.surface, img.out)\n\nfunction finish(img::Image{B}) where B<:ImageBackend\n    img.finished && return\n\n    img.owns_surface && Cairo.destroy(img.ctx)\n    finish(B, img)\n    img.owns_surface && Cairo.destroy(img.surface)\n\n    img.finished = true\n    img.emit_on_finish && typeof(img.out) == IOBuffer && display(img)\n    hasmethod(flush, (typeof(img.out),)) && flush(img.out)\n    img.ownedfile && close(img.out)\nend\n\nfunction newsurface(::Type{B}, out, width, height) where B\n    local surface::CairoSurface\n    if B == SVGBackend\n        surface = CairoSVGSurface(out, width, height)\n    elseif B == PNGBackend\n        surface = CairoARGBSurface(round(Integer, width), round(Integer, height))\n    elseif B == PDFBackend\n        surface = CairoPDFSurface(out, width, height)\n    elseif B == PSBackend\n        surface = CairoEPSSurface(out, width, height)\n    elseif B == CairoBackend\n        surface = out\n    else\n        error(\"Unkown Cairo backend.\")\n    end\n\n    Cairo.status(surface) == Cairo.STATUS_SUCCESS || error(\"Unable to create cairo surface.\")\n\n    surface\nend\n\nfunction reset(img::Image{B}) where B\n    img.owns_surface ||\n            error(\"Backend can't be reused since an external cairo surface is being used.\")\n\n    if img.ownedfile\n        img.out = open(img.filename, \"w\")\n    else\n        try\n            seekstart(img.out)\n        catch\n            error(\"Backend can't be reused, since the output stream is not seekable.\")\n        end\n    end\n\n    img.surface = newsurface(B, img.out, img.width, img.height)\n    img.ctx = CairoContext(img.surface)\n    img.finished = false\nend\n\nisfinished(img::Image) = img.finished\n\nroot_box(img::Image) = BoundingBox(width(img), height(img))\n\nshow(io::IO, ::MIME\"image/png\", img::Image{PNGBackend}) = write(io, String(take!(img.out)))\nshow(io::IO, ::MIME\"application/pdf\", img::Image{PDFBackend}) = write(io, String(take!(img.out)))\nshow(io::IO, ::MIME\"application/postscript\", img::Image{PSBackend}) = write(io, String(take!(img.out)))\n\n\n# Applying Properties\n# -------------------\n\nfunction push_property_frame(img::Image, properties::Vector{Property})\n    isempty(properties) && return\n\n    frame = ImagePropertyFrame()\n    isp = isscalar.(properties)\n    scalar_properties = Dict{Type, Property}(typeof(x)=>x for x in properties[isp])\n    vector_properties = Dict{Type, Property}(typeof(x)=>x for x in properties[.!isp])\n\n    kt = [Property{FillOpacityPrimitive}, Property{FillPrimitive}]\n    if haskey(scalar_properties, kt[1]) && haskey(vector_properties, kt[2])\n        alpha = scalar_properties[kt[1]].primitives[1].value\n        vector_properties[kt[1]] = fillopacity(fill(alpha, length(vector_properties[kt[2]].primitives)))\n        pop!(scalar_properties, kt[1])\n    end\n\n    frame.has_scalar_properties = !isempty(scalar_properties)\n    img.vector_properties = vector_properties\n    frame.vector_properties = vector_properties\n    push!(img.property_stack, frame)\n    isempty(scalar_properties) && return\n\n    save_property_state(img)\n    for (_, property) in scalar_properties\n        apply_property(img, property.primitives[1])\n    end\n    haskey(scalar_properties, kt[1]) && apply_property(img, scalar_properties[kt[1]].primitives[1])\nend\n\n\nfunction pop_property_frame(img::Image)\n    @assert !isempty(img.property_stack)\n    frame = pop!(img.property_stack)\n\n    frame.has_scalar_properties && restore_property_state(img)\n\n    for (propertytype, property) in frame.vector_properties\n        img.vector_properties[propertytype] = nothing\n        for i in length(img.property_stack):-1:1\n            if haskey(img.property_stack[i].vector_properties, propertytype)\n                img.vector_properties[propertytype] =\n                    img.property_stack[i].vector_properties[propertytype]\n            end\n        end\n    end\nend\n\nfunction save_property_state(img::Image)\n    push!(img.state_stack,\n        ImagePropertyState(\n            img.stroke,\n            img.fill,\n            img.stroke_dash,\n            img.stroke_linecap,\n            img.stroke_linejoin,\n            img.visible,\n            img.linewidth,\n            img.fontsize,\n            img.font,\n            img.clip,\n            img.arrow))\n    Cairo.save(img.ctx)\nend\n\nfunction restore_property_state(img::Image)\n    state = pop!(img.state_stack)\n    img.stroke = state.stroke\n    img.fill = state.fill\n    img.stroke_dash = state.stroke_dash\n    img.stroke_linecap = state.stroke_linecap\n    img.stroke_linejoin = state.stroke_linejoin\n    img.visible = state.visible\n    img.linewidth = state.linewidth\n    img.fontsize = state.fontsize\n    img.font = state.font\n    img.clip = state.clip\n    img.arrow = state.arrow\n    Cairo.restore(img.ctx)\nend\n\n# Return true if the vector properties need to be pushed and popped, rather\n# than simply applied.\nfunction vector_properties_require_push_pop(img::Image)\n    for (propertytype, property) in img.vector_properties\n        in(propertytype, [Property{FontPrimitive},\n                          Property{FontSizePrimitive},\n                          Property{ClipPrimitive}]) && return true\n    end\n    return false\nend\n\nfunction push_vector_properties(img::Image, idx::Int)\n    save_property_state(img)\n    for (propertytype, property) in img.vector_properties\n        (property === nothing) && continue\n        primitives = property.primitives\n        idx > length(primitives) &&\n                error(\"Vector form and vector property differ in length. Can't distribute.\")\n        apply_property(img, primitives[idx])\n    end\nend\n\npop_vector_properties(img::Image) = restore_property_state(img)\napply_property(img::Image, p::StrokePrimitive) =\n        img.stroke = p.color\napply_property(img::Image, p::FillPrimitive) =\n        img.fill = p.color\napply_property(img::Image, p::FillOpacityPrimitive) =\n        img.fill = RGBA{Float64}(color(img.fill), p.value)\napply_property(img::Image, p::StrokeOpacityPrimitive) =\n        img.stroke = RGBA{Float64}(color(img.stroke), p.value)\napply_property(img::Image, p::StrokeDashPrimitive) =\n        img.stroke_dash = map(v -> absolute_native_units(img, v.value), p.value)\napply_property(img::Image, p::StrokeLineCapPrimitive) =\n        img.stroke_linecap = p.value\napply_property(img::Image, p::StrokeLineJoinPrimitive) =\n        img.stroke_linejoin = p.value\napply_property(img::Image, p::VisiblePrimitive) =\n        img.visible = p.value\n\nfunction apply_property(img::Image, property::LineWidthPrimitive)\n    img.linewidth = property.value\n    Cairo.set_line_width(img.ctx,\n        absolute_native_units(img, property.value.value))\nend\n\nfunction apply_property(img::Image, property::FontPrimitive)\n    img.font = property.family\n\n    font_desc = ccall((:pango_layout_get_font_description, Cairo.libpango),\n                      Ptr{Cvoid}, (Ptr{Cvoid},), img.ctx.layout)\n\n    if font_desc == C_NULL\n        size = absolute_native_units(img, default_font_size.value)\n    else\n        size = ccall((:pango_font_description_get_size, Cairo.libpango),\n                     Cint, (Ptr{Cvoid},), font_desc)\n    end\n\n    Cairo.set_font_face(img.ctx,\n        @sprintf(\"%s %0.2fpx\", property.family, size / PANGO_SCALE))\nend\n\nfunction apply_property(img::Image, property::FontSizePrimitive)\n    img.fontsize = property.value\n\n    font_desc = ccall((:pango_layout_get_font_description, Cairo.libpango),\n                      Ptr{Cvoid}, (Ptr{Cvoid},), img.ctx.layout)\n\n    if font_desc == C_NULL\n        family = \"sans\"\n    else\n        family = ccall((:pango_font_description_get_family, Cairo.libpango),\n                       Ptr{UInt8}, (Ptr{Cvoid},), font_desc)\n        family = unsafe_string(family)\n    end\n\n    Cairo.set_font_face(img.ctx,\n        @sprintf(\"%s %.2fpx\",\n            family, absolute_native_units(img, property.value.value)))\nend\n\nfunction apply_property(img::Image, property::ClipPrimitive)\n    if isempty(property.points); return; end\n    move_to(img, property.points[1])\n    for point in property.points[2:end]\n        line_to(img, point)\n    end\n    close_path(img)\n    Cairo.clip(img.ctx)\n    img.clip = property\nend\n\n# No-op SVG+JS only properties\napply_property(img::Image, property::JSIncludePrimitive) = nothing\napply_property(img::Image, property::JSCallPrimitive) = nothing\napply_property(img::Image, property::SVGIDPrimitive) = nothing\napply_property(img::Image, property::SVGClassPrimitive) = nothing\napply_property(img::Image, property::SVGAttributePrimitive) = nothing\n\n\n# Cairo Wrappers\n# --------------\n\nfunction current_point(img::Image)\n    x = Array{Float64}(undef, 1)\n    y = Array{Float64}(undef, 1)\n    ccall((:cairo_get_current_point, Cairo.libcairo), Cvoid,\n          (Ptr{Cvoid}, Ptr{Float64}, Ptr{Float64}), img.ctx.ptr, x, y)\n    return ((x[1] / img.ppmm)*mm, (x[2] / img.ppmm)*mm)\nend\n\nmove_to(img::Image, point::AbsoluteVec2) = Cairo.move_to(img.ctx,\n        absolute_native_units(img, point[1].value),\n        absolute_native_units(img, point[2].value))\n\nrel_move_to(img::Image, point::AbsoluteVec2) = Cairo.rel_move_to(img.ctx,\n        absolute_native_units(img, point[1].value),\n        absolute_native_units(img, point[2].value))\n\nline_to(img::Image, point::AbsoluteVec2) = Cairo.line_to(img.ctx,\n        absolute_native_units(img, point[1].value),\n        absolute_native_units(img, point[2].value))\n\nrel_line_to(img::Image, point::AbsoluteVec2) = Cairo.rel_line_to(img.ctx,\n        absolute_native_units(img, point[1].value),\n        absolute_native_units(img, point[2].value))\n\nrel_curve_to(img::Image, ctrl1::AbsoluteVec2, ctrl2::AbsoluteVec2, to::AbsoluteVec2) =\n        Cairo.rel_curve_to(img.ctx,\n            absolute_native_units(img, ctrl1[1].value),\n            absolute_native_units(img, ctrl1[2].value),\n            absolute_native_units(img, ctrl2[1].value),\n            absolute_native_units(img, ctrl2[2].value),\n            absolute_native_units(img, to[1].value),\n            absolute_native_units(img, to[2].value),)\n\nrectangle(img::Image, corner::AbsoluteVec2, width::AbsoluteLength, height::AbsoluteLength) =\n        Cairo.rectangle(img.ctx,\n            absolute_native_units(img, corner[1].value),\n            absolute_native_units(img, corner[2].value),\n            absolute_native_units(img, width.value),\n            absolute_native_units(img, height.value))\n\ncircle(img::Image, center::AbsoluteVec2, radius::AbsoluteLength) =\n        Cairo.circle(img.ctx,\n            absolute_native_units(img, center[1].value),\n            absolute_native_units(img, center[2].value),\n            absolute_native_units(img, radius.value))\n\ncurve_to(img::Image, ctrl1::AbsoluteVec2, ctrl2::AbsoluteVec2, anchor::AbsoluteVec2) =\n        Cairo.curve_to(img.ctx,\n            absolute_native_units(img, ctrl1[1].value),\n            absolute_native_units(img, ctrl1[2].value),\n            absolute_native_units(img, ctrl2[1].value),\n            absolute_native_units(img, ctrl2[2].value),\n            absolute_native_units(img, anchor[1].value),\n            absolute_native_units(img, anchor[2].value))\n\nclose_path(img::Image) = Cairo.close_path(img.ctx)\nnew_sub_path(img::Image) = Cairo.new_sub_path(img.ctx)\n\narc(img::Image, x::Float64, y::Float64, radius::Float64, angle1::Float64, angle2::Float64) =\n        Cairo.arc(img.ctx,\n            absolute_native_units(img, x),\n            absolute_native_units(img, y),\n            absolute_native_units(img, radius),\n            angle1, angle2)\n\narc_negative(img::Image, x::Float64, y::Float64, radius::Float64,\n                      angle1::Float64, angle2::Float64) =\n        Cairo.arc_negative(img.ctx,\n            absolute_native_units(img, x),\n            absolute_native_units(img, y),\n            absolute_native_units(img, radius),\n            angle1, angle2)\n\ntranslate(img::Image, tx::Float64, ty::Float64) = Cairo.translate(img.ctx,\n        absolute_native_units(img, tx),\n        absolute_native_units(img, ty))\n\nscale(img::Image, sx::Float64, sy::Float64) = Cairo.scale(img.ctx, sx, sy)\nrotate(img::Image, theta::Float64) = Cairo.rotate(img.ctx, theta)\n\nfunction rotate(img::Image, theta::Float64, x::Float64, y::Float64)\n    ct = cos(theta)\n    st = sin(theta)\n\n    x′ = x - (ct * x - st * y)\n    y′ = y - (st * x + ct * y)\n\n    translate(img, x′, y′)\n    rotate(img, theta)\nend\n\n# Convert native linecap/linejoin enums to the Cairo values.\ncairo_linecap(::LineCapButt) = Cairo.CAIRO_LINE_CAP_BUTT\ncairo_linecap(::LineCapRound) = Cairo.CAIRO_LINE_CAP_ROUND\ncairo_linecap(::LineCapSquare) = Cairo.CAIRO_LINE_CAP_SQUARE\ncairo_linejoin(::LineJoinMiter) = Cairo.CAIRO_LINE_JOIN_MITER\ncairo_linejoin(::LineJoinBevel) = Cairo.CAIRO_LINE_JOIN_BEVEL\ncairo_linejoin(::LineJoinRound) = Cairo.CAIRO_LINE_JOIN_ROUND\n\nfunction fillstroke(img::Image, strokeonly::Bool=false)\n    img.visible || return\n\n    if img.fill.alpha > 0.0 && !strokeonly\n        Cairo.set_source_rgba(img.ctx, img.fill.r, img.fill.g, img.fill.b, img.fill.alpha)\n\n        if img.stroke.alpha > 0.0\n            Cairo.fill_preserve(img.ctx)\n        else\n            Cairo.fill(img.ctx)\n        end\n    end\n\n    if img.stroke.alpha > 0.0\n        Cairo.set_source_rgba(img.ctx, img.stroke.r, img.stroke.g, img.stroke.b, img.stroke.alpha)\n        Cairo.set_dash(img.ctx, img.stroke_dash)\n        Cairo.set_line_cap(img.ctx, cairo_linecap(img.stroke_linecap))\n        Cairo.set_line_join(img.ctx, cairo_linejoin(img.stroke_linejoin))\n\n        Cairo.stroke(img.ctx)\n    end\n\n    # if the path wasn't stroked or filled, we should still clear it\n    Cairo.new_path(img.ctx)\nend\n\n# Form Drawing\n# ------------\n\nfunction draw(img::Image, form::Form)\n    if vector_properties_require_push_pop(img)\n        for (idx, primitive) in enumerate(form.primitives)\n            push_vector_properties(img, idx)\n            draw(img, primitive)\n            pop_vector_properties(img)\n        end\n    else\n        for (idx, primitive) in enumerate(form.primitives)\n            for (propertytype, property) in img.vector_properties\n                (property === nothing) && continue\n                primitives = property.primitives\n                idx > length(primitives) &&\n                        error(\"Vector form and vector property differ in length. Can't distribute.\")\n                apply_property(img, primitives[idx])\n            end\n            kt = [Property{FillOpacityPrimitive}]\n            haskey(img.vector_properties, kt[1]) && apply_property(img, img.vector_properties[kt[1]].primitives[idx])\n            draw(img, primitive)\n        end\n    end\nend\n\nfunction draw(img::Image, prim::RectanglePrimitive)\n    rectangle(img, prim.corner, prim.width, prim.height)\n    fillstroke(img)\nend\n\nfunction draw(img::Image, prim::PolygonPrimitive)\n    length(prim.points) <= 1 && return\n    prev_ok = false\n    for (i,p) in enumerate(prim.points)\n        ok = isfinite(p[1].value) && isfinite(p[2].value)\n        if ok && prev_ok\n            line_to(img, p)\n        elseif !ok && prev_ok\n            close_path(img)\n            fillstroke(img)\n        else\n            move_to(img, p)\n        end\n        prev_ok = ok\n    end\n    close_path(img)\n    fillstroke(img)\nend\n\nfunction draw(img::Image, prim::Compose.ComplexPolygonPrimitive)\n    isempty(prim.rings) && return\n\n    for ring in prim.rings\n        move_to(img, ring[1])\n        for point in ring[2:end]\n            line_to(img, point)\n        end\n        close_path(img)\n    end\n    fillstroke(img)\nend\n\nfunction draw(img::Image, prim::CirclePrimitive)\n    new_sub_path(img)\n    circle(img, prim.center, prim.radius)\n    fillstroke(img)\nend\n\nfunction draw(img::Image, prim::EllipsePrimitive)\n    new_sub_path(img)\n    cx = prim.center[1].value\n    cy = prim.center[2].value\n    rx = sqrt((prim.x_point[1].value - cx)^2 +\n              (prim.x_point[2].value - cy)^2)\n    ry = sqrt((prim.y_point[1].value - cx)^2 +\n              (prim.y_point[2].value - cy)^2)\n    theta = atan(prim.x_point[2].value - cy,\n                 prim.x_point[1].value - cx)\n\n    all(isfinite,[cx, cy, rx, ry, theta]) || return\n\n    save_property_state(img)\n    translate(img, cx, cy)\n    rotate(img, theta)\n    translate(img, -rx, -ry)\n    scale(img, 2rx, 2ry)\n    arc(img, 0.5, 0.5, 0.5, 0.0, 2pi)\n    restore_property_state(img)\n    fillstroke(img)\nend\n\nfunction draw(img::Image, prim::LinePrimitive)\n    length(prim.points) <= 1 && return\n    prev_ok = false\n    for (i,p) in enumerate(prim.points)\n        ok = isfinite(p[1].value) && isfinite(p[2].value)\n        if ok && prev_ok\n            line_to(img, p)\n        else\n            move_to(img, p)\n        end\n        prev_ok = ok\n    end\n    img.arrow && arrow(img, prim.points)\n    fillstroke(img, true)\nend\n\nfunction get_layout_size(img::Image)\n    width, height = Cairo.get_layout_size(img.ctx)\n    return ((width / img.ppmm) * mm, (height / img.ppmm) * mm)\nend\n\nshow(io::IO, ::MIME\"image/png\", ctx::Context) =\n    draw(PNG(io, default_graphic_width, default_graphic_height), ctx)\n\nfunction draw(img::Image, prim::TextPrimitive)\n    (!img.visible || (img.fill.alpha == 0.0 && img.stroke.alpha == 0.0)) && return\n\n    Cairo.set_text(img.ctx, prim.value, true)\n    width, height = get_layout_size(img)\n    pos = (prim.position[1]+prim.offset[1], prim.position[2]+prim.offset[2] - height)\n\n    if prim.halign != hleft || prim.valign != vbottom\n        if prim.halign == hcenter\n            pos = (pos[1] - width/2, pos[2])\n        elseif prim.halign == hright\n            pos = (pos[1] - width, pos[2])\n        end\n\n        if prim.valign == vcenter\n            pos = (pos[1], pos[2] + height/2)\n        elseif prim.valign == vtop\n            pos = (pos[1], pos[2] + height)\n        end\n    end\n\n    Cairo.set_source_rgba(img.ctx, img.fill.r, img.fill.g, img.fill.b, img.fill.alpha)\n\n    if prim.rot.theta != 0.0\n        save_property_state(img)\n        rotate(img, prim.rot.theta, prim.rot.offset[1].value, prim.rot.offset[2].value)\n    end\n\n    move_to(img, pos)\n    Cairo.show_layout(img.ctx)\n\n    prim.rot.theta == 0.0 || restore_property_state(img)\nend\n\nfunction draw(img::Image, prim::CurvePrimitive)\n    move_to(img, prim.anchor0)\n    curve_to(img, prim.ctrl0, prim.ctrl1, prim.anchor1)\n    fillstroke(img, true)\nend\n\ndraw(img::Image, prim::BitmapPrimitive) =\n        error(\"Embedding bitmaps in Cairo backends (i.e. PNG, PDF, PS) is not supported.\")\n\n\n\nfunction draw(img::Image, batch::FormBatch)\n    bounds = boundingbox(batch.primitive, img.linewidth, img.font, img.fontsize)\n    width = bounds.a[1].value * img.ppmm\n    height = bounds.a[2].value * img.ppmm\n\n    xt = -bounds.x0[1].value * img.ppmm\n    yt = -bounds.x0[2].value * img.ppmm\n\n    Cairo.save(img.ctx)\n    Cairo.reset_clip(img.ctx)\n    Cairo.translate(img.ctx, xt, yt)\n    Cairo.push_group(img.ctx)\n    draw(img, batch.primitive)\n    pattern = Cairo.pop_group(img.ctx)\n    surface = Cairo.pattern_get_surface(pattern)\n    Cairo.restore(img.ctx)\n\n    # reapply the clipping region we just reset\n    if img.clip !== nothing\n        apply_property(img, img.clip)\n    end\n\n    Cairo.set_antialias(img.ctx, Cairo.ANTIALIAS_NONE)\n    for offset in batch.offsets\n        x = offset[1].value * img.ppmm - xt\n        y = offset[2].value * img.ppmm - yt\n        Cairo.set_source_surface(img.ctx, surface, x, y)\n        Cairo.rectangle(img.ctx, x, y, width, height)\n        Cairo.fill(img.ctx)\n    end\n    Cairo.set_antialias(img.ctx, Cairo.ANTIALIAS_DEFAULT)\nend\n\n\nfunction draw(img::Compose.Image, prim::ArcPrimitive)\n    new_sub_path(img)\n    xc = prim.center[1]\n    yc = prim.center[2]\n    prim.sector && move_to(img, (xc, yc))\n    arc(img, xc.value, yc.value, prim.radius.value, prim.angle1, prim.angle2)\n    prim.sector && line_to(img, (xc, yc))\n    img.arrow && arrow(img, prim)\n    fillstroke(img)\nend\n\n\n# Arrows\n\napply_property(img::Image, p::ArrowPrimitive) = \n        img.arrow = p.value\n\n\nfunction arrow(img::Image, points::Vector{<:Vec})\n    xmax, ymax = points[end]\n    dxy = points[end] .- points[end-1]\n    dx, dy = dxy[1].value,  dxy[2].value \n    vl, θ = 0.225*hypot(dy,dx), atan(dy, dx) \n    arrowhead(img, (xmax,ymax), vl, θ)\nend\n\nfunction arrow(img::Image, prim::ArcPrimitive)\n    xy = prim.center .+ prim.radius.*(cos(prim.angle2), sin(prim.angle2))\n    θ = prim.angle2 + 0.45π\n   arrowhead(img, xy, 0.5*prim.radius.value, θ)\nend\n\n\nfunction arrowhead(img::Image, point::Vec, vl::Float64, θ::Float64)\n    xmax, ymax = point\n    ϕ  =  pi/15\n    xr = -vl*[cos(θ+ϕ), cos(θ-ϕ)].*1mm\n    yr = -vl*[sin(θ+ϕ), sin(θ-ϕ)].*1mm\n    move_to(img, (xmax+xr[1], ymax+yr[1]))\n    line_to(img, (xmax, ymax))\n    line_to(img, (xmax+xr[2], ymax+yr[2]))\nend\n\n\n# Bezigon\n\n\n\nfunction draw(img::Image, prim::BezierPolygonPrimitive)\n    move_to(img, prim.anchor)\n    currentpoint = prim.anchor\n    for side in prim.sides\n        if length(side)==1\n            line_to(img, side[1])\n        elseif length(side)==2\n            cp1 = controlpnt(currentpoint, side[1])\n            cp2 = controlpnt(side[2], side[1])\n            curve_to(img, cp1, cp2, side[2])\n        elseif length(side)==3\n            curve_to(img, side[1], side[2], side[3])\n        end\n        currentpoint = side[end]\n    end\n    close_path(img)\n    fillstroke(img)\nend\n\n\n\n\n"
  },
  {
    "path": "src/container.jl",
    "content": "# A container is a node in the tree that can have Forms, Properties, or other\n# Containers as children.\nabstract type Container <: ComposeNode end\n\n\n# The basic Container which defines a coordinate transform for its children.\nmutable struct Context <: Container\n    # Bounding box relative to the parent's coordinates\n    box::BoundingBox\n\n    units::Union{UnitBox, Nothing}\n    rot::Union{Rotation, Nothing}\n    mir::Union{Mirror, Nothing}\n    shear::Union{Shear, Nothing}\n\n    # Container children\n    container_children::List{Container}\n    form_children::List{Form}\n    property_children::List{Property}\n\n    order::Int\n    clip::Bool\n\n    withjs::Bool\n    withoutjs::Bool\n    raster::Bool\n\n    minwidth::Maybe(Float64)\n    minheight::Maybe(Float64)\n\n    # A field that can be used by layouts to indicate that one configuration is\n    # preferable to another.\n    penalty::Float64\n\n    tag::Symbol\n\n    function Context(box, units, rotation, mirror, shear,\n                     container_children, form_children, property_children,\n                     order, clip, withjs, withoutjs, raster, minwidth, minheight, penalty, tag)\n        if isa(minwidth, AbsoluteLength)\n            minwidth = minwidth.value\n        end\n\n        if isa(minheight, AbsoluteLength)\n            minheight = minheight.value\n        end\n\n        return new(box, units, rotation, mirror, shear,\n                   container_children, form_children, property_children,\n                   order, clip, withjs, withoutjs, raster, minwidth, minheight, penalty, tag)\n    end\nend\n\nContext(x0=0.0w, y0=0.0h, width=1.0w, height=1.0h;\n            units=nothing,\n            rotation=nothing,\n            mirror=nothing,\n            shear=nothing,\n            order=0,\n            clip=false,\n            withjs=false,\n            withoutjs=false,\n            raster=false,\n            minwidth=nothing,\n            minheight=nothing,\n            penalty=0.0,\n            tag=empty_tag) =\n        Context(BoundingBox(x0, y0, width, height), units, rotation, mirror,\n            ListNull{ComposeNode}(),\n            order, clip, withjs, withoutjs, raster, minwidth, minheight, penalty, tag)\n\nContext(ctx::Context) = Context(ctx.box, ctx.units, ctx.rot, ctx.mir, ctx.shear,\n            ctx.container_children, ctx.form_children, ctx.property_children,\n            ctx.order, ctx.clip, ctx.withjs, ctx.withoutjs, ctx.raster,\n            ctx.minwidth, ctx.minheight, ctx.penalty, ctx.tag)\n\n\"\"\"\n    context(x0=0.0w, y0=0.0h, width=1.0w, height=1.0h;\n            units=nothing,\n            rotation=nothing, mirror=nothing, shear=nothing,\n            withjs=false, withoutjs=false,\n            minwidth=nothing, minheight=nothing,\n            order=0, clip=false, raster=false) -> Context\n\nCreate a node in the tree structure that defines a graphic.  Use\n[`compose`](@ref) to connect child nodes to a parent node.  See also\n[`Rotation`](@ref), [`Mirror`](@ref), and [`Shear`](@ref).\n\n# Arguments\n- `units`: the coordinate system for the context, defined by a [`UnitBox`](@ref).\n- `order`: the Z-order of this context relative to its siblings.\n- `clip`:  clip children of the canvas by its bounding box if true.\n- `withjs`: ignore this context and everything under it if we are not drawing to the SVGJS backend.\n- `withoutjs`: ignore this context if we *are* drawing on the SVGJS backend.\n- `raster`: if possible, render this subtree as a bitmap. This requires the Cairo. If Cairo isn't available, the default rendering is used.\n- `minwidth` and `minheight`: the minimum size needed to be drawn correctly, in millimeters.\n\"\"\"\ncontext(x0=0.0w, y0=0.0h, width=1.0w, height=1.0h;\n            units=nothing,\n            rotation=nothing,\n            mirror=nothing,\n            shear=nothing,\n            order=0,\n            clip=false,\n            withjs=false,\n            withoutjs=false,\n            raster=false,\n            minwidth=nothing,\n            minheight=nothing,\n            penalty=0.0,\n            tag=empty_tag) =\n        Context(BoundingBox(x_measure(x0), y_measure(y0), x_measure(width), y_measure(height)),\n            units, rotation, mirror, shear,\n            ListNull{Container}(), ListNull{Form}(), ListNull{Property}(),\n            order, clip, withjs, withoutjs, raster, minwidth, minheight, penalty, tag)\n\nchildren(ctx::Context) = Iterators.flatten((ctx.container_children, ctx.form_children, ctx.property_children))\n\n# Updating context fields\n\nset_units!(ctx::Context, units::UnitBox) =\n    ctx.units = units\n\ncopy(ctx::Context) = Context(ctx)\niswithjs(ctx::Container) = ctx.withjs\niswithoutjs(ctx::Container) = ctx.withoutjs\norder(cont::Container) = cont.order\nminwidth(cont::Container) = cont.minwidth\nminheight(cont::Container) = cont.minheight\n\n# Normalize cx or cy coordinate to a number [0,1] according to the given\n# unit box.\nfunction cx_proportion(from::Measure,units::UnitBox)\n    cux0 = units.x0\n    cuw = units.width\n    if cux0 == nothing\n        cux0 = 0.0\n        cuw = 1.0\n    end\n    ret_cx_proportion = from.cx != measure_nil ? (from.cx-cux0)/cuw : 0.0\n    if !isfinite(ret_cx_proportion)\n        ret_cx_proportion = 0.0\n    end\n    ret_cx_proportion\nend\n\nfunction cy_proportion(from::Measure,units::UnitBox)\n    cuy0 = units.x0\n    cuh = units.width\n    if cuy0 == nothing\n        cuy0 = 0.0\n        cuh = 1.0\n    end\n    ret_cy_proportion  = from.cy != measure_nil ? (from.cy-cuy0)/cuh : 0.0\n    if !isfinite(ret_cy_proportion)\n        ret_cy_proportion = 0.0\n    end\n    ret_cy_proportion\nend\n\nfunction transformcoordinates(from::Measure, ctx::Context)\n    Measure(;abs = from.abs) +\n            (from.cw + cx_proportion(from,ctx.units))*ctx.box.width +\n            (from.ch + cy_proportion(from,ctx.units))*ctx.box.height\nend\n\nfunction boundingbox(c::Context,linewidth::Measure=default_line_width,\n                     font::AbstractString=default_font_family,\n                     fontsize::Measure=default_font_size,\n                     parent_abs_width = nothing,\n                     parent_abs_height = nothing)\n    for child in c.property_children\n        for p in child.primitives\n            if isa(p, LineWidthPrimitive)\n                linewidth = p.value\n            elseif isa(p, FontSizePrimitive)\n                fontsize = p.value\n            elseif isa(p, FontPrimitive)\n                font = p.family\n            end\n        end\n    end\n\n    c_abs_width = c.box.width.abs\n    if !iszero(c.box.width.cx) || !iszero(c.box.width.cy) ||\n       !iszero(c.box.width.cw) || !iszero(c.box.width.ch)\n        if parent_abs_width == nothing || parent_abs_height == nothing\n            c_abs_width = nothing\n        else\n            c_abs_width = parent_abs_width*(c.box.width.cw + cx_proportion(c.box.width,c.units)) +\n                parent_abs_height*(c.box.width.ch + cy_proportion(c.box.width,c.units))\n        end\n    end\n\n    c_abs_height = c.box.height.abs\n    if !iszero(c.box.height.cx) || !iszero(c.box.height.cy) ||\n       !iszero(c.box.height.cw) || !iszero(c.box.height.ch)\n        if parent_abs_width == nothing || parent_abs_height == nothing\n            c_abs_height = nothing\n        else\n            c_abs_height = parent_abs_width*(c.box.height.cw + cx_proportion(c.box.height,c.units)) +\n                parent_abs_height*(c.box.height.ch + cy_proportion(c.box.height,c.units))\n        end\n    end\n\n    bb = BoundingBox(Measure(),Measure(),Measure(),Measure())\n    for child in c.container_children\n        if !isa(child, Context)\n            error(\"Can not compute boundingbox for graphics with non-Context containers\")\n        end\n        cbb = boundingbox(child, linewidth, font, fontsize, c_abs_width, c_abs_height)\n        width′  = transformcoordinates(cbb.width,child)\n        height′ = transformcoordinates(cbb.height,child)\n        x0′     = transformcoordinates(cbb.x0,child)\n        y0′     = transformcoordinates(cbb.y0,child)\n        bb′ = BoundingBox(child.box.x0+x0′, child.box.y0+y0′, width′, height′)\n        bb = union(bb, bb′, c.units, c_abs_width, c_abs_height)\n    end\n\n    for child in c.form_children\n        for prim in child.primitives\n            newbb = boundingbox(prim, linewidth, font, fontsize)\n            nextbb = union(bb, newbb, c.units, c_abs_height, c_abs_height)\n            bb = nextbb\n        end\n    end\n\n    return bb\nend\n\n# Frequently we can't compute the contents of a container without knowing its\n# absolute size, or it is one many possible layout that we want to decide\n# between before rendering. A ContainerPromise lets us defer computing a subtree\n# until the graphic is actually being rendered.\nabstract type ContainerPromise <: Container end\n\n# This information is passed to a container promise at drawtime.\nstruct ParentDrawContext\n    t::Transform\n    units::UnitBox\n    box::Absolute2DBox\nend\n\n# TODO:\n# Optionl in layouts will typically be expressed with ad hoc container promises,\n# since we can that way avoid realizing layout possibilities that are not used.\n# That means we need to be able to express size constraints on these.\n\nmutable struct AdhocContainerPromise <: ContainerPromise\n    # A function of the form:\n    #   f(parent::ParentDrawContext) → Container\n    f::Function\n\n    # Z-order of this context relative to its siblings.\n    order::Int\n\n    # Ignore this context and everything under it if we are\n    # not drawing to the SVGJS backend.\n    withjs::Bool\n\n    # Ignore this context if we are drawing on SVGJS\n    withoutjs::Bool\n\n    # Minimum sizes needed to draw the realized subtree correctly.\n    minwidth::Maybe(Float64)\n    minheight::Maybe(Float64)\nend\n\nfunction ctxpromise(f::Function; order=0, withjs::Bool=false,\n                    withoutjs::Bool=false, minwidth=nothing, minheight=nothing)\n    if isa(minwidth, Measure)\n        minwidth = minwidth.abs\n    end\n\n    if isa(minheight, Measure)\n        minheight = minwidth.abs\n    end\n\n    return AdhocContainerPromise(f, order, withjs, withoutjs, minwidth, minheight)\nend\n\nrealize(promise::AdhocContainerPromise, drawctx::ParentDrawContext) = promise.f(drawctx)\n\nfunction compose!(a::Context, b::Container)\n    a.container_children = cons(b, a.container_children)\n    return a\nend\n\nfunction compose!(a::Context, b::Form)\n    a.form_children = cons(b, a.form_children)\n    return a\nend\n\nfunction compose!(a::Context, b::Property)\n    a.property_children = cons(b, a.property_children)\n    return a\nend\n\nfunction compose(a::Context, b::ComposeNode)\n    a = copy(a)\n    compose!(a, b)\n    return a\nend\n\n# higher-order compositions\n\"\"\"\n    compose!(a::Context, b, c, ds...) -> a\n\nAdd `b`, `c`, and `ds` to the graphic as children of `a`.\n\"\"\"\ncompose!(a::Context, b, c, ds...) = compose!(compose!(a, b), c, ds...)\ncompose!(a::Context, bs::AbstractArray) = compose!(a, compose!(bs...))\ncompose!(a::Context, bs::Tuple) = compose!(a, compose!(bs...))\ncompose!(a::Context) = a\n\n\"\"\"\n    compose(a::Context, b, c, ds...) -> Context\n\nAdd `b`, `c`, and `ds` to the graphic as children of a copy of `a`.\n\"\"\"\ncompose(a::Context, b, c, ds...) = compose(compose(a, b), c, ds...)\ncompose(a::Context, bs::AbstractArray) = compose(a, compose(bs...))\ncompose(a::Context, bs::Tuple) = compose(a, compose(bs...))\ncompose(a::Context) = a\ncompose(a, b::Nothing) = a\n\nfor (f, S, T) in [(:compose!, Property, Nothing),\n                  (:compose!, Form, Nothing),\n                  (:compose!, Property, Any),\n                  (:compose!, Form, Any),\n                  (:compose, Property, Nothing),\n                  (:compose, Form, Nothing),\n                  (:compose, Property, Any),\n                  (:compose, Form, Any)]\n    eval(\n        quote\n            function $(f)(a::$(S), b::$(T))\n                error(@sprintf(\"Invalid composition: %s cannot be a root node.\", $(S).name))\n            end\n        end)\nend\n\n\"\"\"\n    draw(b::Backend, c::Context)\n\nOutput the tree-structured graphic in `c` to the given backend.\n\n# Examples\n```\ndraw(SVGJS(\"foo.svg\"), compose(context(), text(0,0,\"foo\")))\n```\n\"\"\"\nfunction draw(backend::Backend, root_container::Container)\n    isfinished(backend) && error(\"The backend has already been drawn upon.\")\n\n    drawpart(backend, root_container, IdentityTransform(), UnitBox(), root_box(backend))\n    finish(backend)\nend\n\nregister_coords(backend::Backend, box, units, transform, form) = nothing\n\nstruct DrawState\n    pop_poperty::Bool\n    container::Container\n    parent_transform::Transform\n    units::UnitBox\n    parent_box::Absolute2DBox\nend\nDrawState(container, parent_transform, units, parent_box) =\n        DrawState(false, container, parent_transform, units, parent_box)\nDrawState() = DrawState(true)  ### ???\n\n# Draw without finishing the backend\n#\n# Drawing is basically a depth-first traversal of the tree, pushing and popping\n# properties, expanding context promises, etc. as needed.\n#\nfunction drawpart(backend::Backend, container::Container,\n                  parent_transform::Transform, units::UnitBox,\n                  parent_box::Absolute2DBox)\n\n    ((iswithjs(container) && !iswithjs(backend)) ||\n       (iswithoutjs(container) && iswithjs(backend))) && return\n\n    if isa(container, ContainerPromise)\n        container = realize(container,\n                            ParentDrawContext(parent_transform, units, parent_box))\n        isa(container, Container) ||\n                error(\"Error: A container promise function did not evaluate to a container\")\n        drawpart(backend, container, parent_transform, units, parent_box)\n        return\n    end\n\n    ctx = optimize_batching(container)\n    box = resolve(parent_box, units, parent_transform, ctx.box)\n\n    transform = parent_transform\n    if ctx.units !== nothing\n        units = resolve(box, units, transform, ctx.units)\n    end\n\n    if ctx.rot !== nothing\n        rot = resolve(box, units, parent_transform, ctx.rot)\n        transform = combine(convert(Transform, rot), transform)\n    end\n\n    if ctx.mir !== nothing\n        mir = resolve(box, units, parent_transform, ctx.mir)\n        transform = combine(convert(Transform, mir), transform)\n    end\n\n    if ctx.shear !== nothing\n        shear = resolve(box, units, parent_transform, ctx.shear)\n        transform = combine(convert(Transform, shear), transform)\n    end\n\n    if ctx.raster && @isdefined(Cairo) && isa(backend, SVG)\n        bitmapbackend = PNG(box.a[1], box.a[2], false)\n        draw(bitmapbackend, ctx)\n        f = bitmap(\"image/png\", take!(bitmapbackend.out),\n                   0, 0, 1w, 1h)\n\n        c = context(ctx.box.x0[1], ctx.box.x0[2],\n                    ctx.box.a[1], ctx.box.a[2],\n                    units=UnitBox(),\n                    order=ctx.order,\n                    clip=ctx.clip)\n        drawpart(backend, compose(c, f), parent_transform, units, parent_box)\n        return\n    end\n\n\n    has_properties = false\n    if !isa(ctx.property_children, ListNull) || ctx.clip\n        has_properties = true\n        properties = Array{Property}(undef, 0)\n\n        child = ctx.property_children\n        while !isa(child, ListNull)\n            register_coords(backend, box, units, transform, child.head)\n            push!(properties, resolve(parent_box, units, parent_transform, child.head))\n            child = child.tail\n        end\n\n        if ctx.clip\n            x0 = ctx.box.x0[1]\n            y0 = ctx.box.x0[2]\n            x1 = x0 + ctx.box.a[1]\n            y1 = y0 + ctx.box.a[2]\n            push!(properties,\n            resolve(parent_box, units, parent_transform,\n            clip([(x0, y0), (x1, y0), (x1, y1), (x0, y1)])))\n        end\n\n        push_property_frame(backend, properties)\n    end\n\n    trybatch = canbatch(backend)\n    child = ctx.form_children\n    while !isa(child, ListNull)\n        register_coords(backend, box, units, transform, child.head)\n        form = resolve(box, units, transform, child.head)\n        if isempty(form.primitives)\n            child = child.tail\n            continue\n        end\n\n        if trybatch\n            b = batch(form)\n            if b !== nothing\n                draw(backend, b)\n                child = child.tail\n                continue\n            end\n        end\n\n        draw(backend, form)\n        child = child.tail\n    end\n\n    # Order children if needed\n    ordered_children = false\n    child = ctx.container_children\n    while !isa(child, ListNull)\n        if order(child.head) != 0\n            ordered_children = true\n            break\n        end\n        child = child.tail\n    end\n\n    if ordered_children\n        container_children = Array{Tuple{Int, Int, Container}}(undef, 0)\n        child = ctx.container_children\n        while !isa(child, ListNull)\n            push!(container_children,\n                  (order(child.head), 1 + length(container_children), child.head))\n            child = child.tail\n        end\n\n        sort!(container_children)\n        for i in 1:length(container_children)\n            drawpart(backend, container_children[i][3], transform, units, box)\n        end\n        empty!(container_children)\n    else\n        child = ctx.container_children\n        while !isa(child, ListNull)\n            drawpart(backend, child.head, transform, units, box)\n            child = child.tail\n        end\n    end\n\n    has_properties && pop_property_frame(backend)\nend\n\n# Produce a tree diagram representing the tree structure of a graphic.\n#\n# Args:\n#   root: graphic to represent\n#\n# Returns:\n#   A Context giving a tree diagram.\n#\nfunction introspect(root::Context)\n    positions = Dict{ComposeNode, Tuple{Float64, Float64}}()\n    level_count = Int[]\n    max_level = 0\n\n    # TODO: It would be nice if we can try to do a better job of positioning\n    # nodes within their levels\n\n    q = Queue{Tuple{ComposeNode, Int}}()\n    enqueue!(q, (root, 1))\n    figs = compose!(context(), stroke(\"#333\"), linewidth(0.5mm))\n    figsize = 6mm\n    while !isempty(q)\n        node, level = dequeue!(q)\n\n        if level > length(level_count)\n            push!(level_count, 1)\n        else\n            level_count[level] += 1\n        end\n        max_level = max(max_level, level)\n\n        # draw shit\n        fig = context(level_count[level] - 1, level - 1)\n        if isa(node, Context)\n            compose!(fig, circle(0.5, 0.5, figsize/2), fill(LCHab(92, 10, 77)))\n            for child in children(node)\n                enqueue!(q, (child, level + 1))\n            end\n        elseif isa(node, Container)\n            # TODO: should be slightly different than Context...\n            compose!(fig, circle(0.5, 0.5, figsize/2), fill(LCHab(92, 10, 77)))\n        elseif isa(node, Form)\n            compose!(fig,\n                (context(),\n                 text(0.5cx, 0.5cy, form_string(node), hcenter, vcenter),\n                 fill(colorant\"black\")),\n                (context(),\n                 rectangle(0.5cx - figsize/2, 0.5cy - figsize/2, figsize, figsize),\n                 fill(LCHab(68, 74, 192))))\n        elseif isa(node, Property)\n            # TODO: what should the third color be?\n            compose!(fig,\n                polygon([(0.5cx - figsize/2, 0.5cy - figsize/2),\n                         (0.5cx + figsize/2, 0.5cy - figsize/2),\n                         (0.5cx, 0.5cy + figsize/2)]),\n                fill(LCHab(68, 74, 29)))\n        else\n            error(\"Unknown node type $(typeof(node))\")\n        end\n        compose!(figs, fig)\n\n        positions[node] = (level_count[level] - 0.5, level - 0.5)\n    end\n\n    # make a second traversal of the tree to draw lines between parents and\n    # children\n    lines_ctx = compose!(context(order=-1), stroke(LCHab(92, 10, 77)))\n    enqueue!(q, (root, 1))\n    while !isempty(q)\n        node, level = dequeue!(q)\n        isa(node, Context) || continue\n        pos = positions[node]\n\n        for child in children(node)\n            childpos = positions[child]\n            compose!(lines_ctx,\n                     line([(pos[1], pos[2]), (childpos[1], childpos[2])]))\n            enqueue!(q, (child, level + 1))\n        end\n    end\n\n    return compose!(context(units=UnitBox(0, 0, maximum(level_count), max_level)),\n                    (context(order=-2), rectangle(), fill(\"#333\")),\n                    lines_ctx, figs)\nend\n\nfunction show(io::IO, ctx::Context)\n    if get(io, :compact, false)\n        print(io, \"Context(\")\n        first = true\n        for c in children(ctx)\n            first || print(io, \",\")\n            first = false\n            isa(c, AbstractArray) ? showcompact_array(io, c) : show(io, c)\n        end\n        print(io, \")\")\n    else\n        invoke(show, Tuple{IO, Any}, io, ctx)\n    end\nend\n\nfunction showcompact_array(io::IO, a::AbstractArray)\n    print(io, \"[\")\n    first = true\n    for c in a\n        first || print(io, \",\")\n        first = false\n        show(io, c)\n    end\n    print(io, \"]\")\nend\nshowcompact_array(io::IO, a::AbstractRange) = show(io, a)\nshow(io::IO, f::Compose.Form) = get(io, :compact, false) ? print(io, Compose.form_string(f)) : invoke(show, Tuple{IO, Any}, io, f)\nshow(io::IO, p::Compose.Property) = get(io, :compact, false) ? print(io, Compose.prop_string(p)) : invoke(show, Tuple{IO, Any}, io, p)\nshow(io::IO, cp::ContainerPromise) = get(io, :compact, false) ? print(io, typeof(cp).name.name) : invoke(show, Tuple{IO, Any}, io, cp)\n"
  },
  {
    "path": "src/fontfallback.jl",
    "content": "# Font handling when pango and fontconfig are not available.\n\n# Define this even if we're not calling pango, since cairo needs it.\nconst PANGO_SCALE = 1024.0\n\n# Serialized glyph sizes for commont fonts.\nconst glyphsizes = open(fd -> JSON.parse(read(fd, String)),\n                        joinpath(@__DIR__, \"..\", \"deps\", \"glyphsize.json\"))\n\n# It's better to overestimate text extents than to underestimes, since the later\n# leads to overlaping where the former just results in some extra space. So, we\n# scale estimated text extends by this number. Width on the other hand tends be\n# overestimated since it doesn't take kerning into account.\nconst text_extents_scale_x = 1.0\nconst text_extents_scale_y = 1.0\n\n# Normalized Levenshtein distance between two strings.\nfunction levenshtein(a::AbstractString, b::AbstractString)\n    a = replace(lowercase(a), r\"\\s+\"=>\"\")\n    b = replace(lowercase(b), r\"\\s+\"=>\"\")\n    n = length(a)\n    m = length(b)\n    D = zeros(UInt, n + 1, m + 1)\n\n    D[:,1] = 0:n\n    D[1,:] = 0:m\n\n    for i in 2:n, j in 2:m\n        if a[i - 1] == b[j - 1]\n            D[i, j] = D[i - 1, j - 1]\n        else\n            D[i, j] = 1 +  min(D[i - 1, j - 1], D[i, j - 1], D[i - 1, j])\n        end\n    end\n\n    return D[n,m] / max(n, m)\nend\n\n# Find the nearst typeface from the glyph size table.\nlet\n    matched_font_cache = Dict{AbstractString, AbstractString}()\n    global match_font\n\n    function match_font(families::AbstractString)\n        haskey(matched_font_cache, families) && return matched_font_cache[families]\n\n        smallest_dist = Inf\n        best_match = \"Helvetica\"\n        for family in [lowercase(strip(family, [' ', '\"', '\\''])) for family in split(families, ',')]\n            for available_family in keys(glyphsizes)\n                d = levenshtein(family, available_family)\n                if d < smallest_dist\n                    smallest_dist = d\n                    best_match = available_family\n                end\n            end\n        end\n\n        matched_font_cache[families] = best_match\n        return best_match\n    end\nend\n\n# Approximate width of a text in millimeters.\n#\n# Args:\n#   widths: A glyph width table from glyphsizes.\n#   text: Any string.\n#   size: Font size in points.\n#\n# Returns:\n#   Approximate text width in millimeters.\n#\nfunction text_width(widths::Dict, text::AbstractString, size::Float64)\n    stripped_text = replace(text, r\"<[^>]*>\"=>\"\")\n    width = 0\n    for c in stripped_text\n        width += get(widths, string(c), widths[\"w\"])\n    end\n    width\nend\n\nfunction max_text_extents(font_family::AbstractString, size::Measure,\n                          texts::AbstractString...)\n    isa(size, AbsoluteLength) ||\n            error(\"text_extents requries font size be in absolute units\")\n    scale = size / 12pt\n    font_family = match_font(font_family)\n    glyphheight = glyphsizes[font_family][\"height\"]\n    widths = glyphsizes[font_family][\"widths\"]\n\n    fontsize = size/pt\n    chunkwidths = Float64[]\n    textheights = Float64[]\n    for text in texts\n        textheight = 0.0\n        for chunk in split(text, '\\n')\n            chunkheight = glyphheight\n            if match(r\"<su(p|b)>\", String(chunk)) != nothing\n                chunkheight *= 1.5\n            end\n            textheight += chunkheight\n            push!(chunkwidths, text_width(widths, chunk, fontsize))\n        end\n        push!(textheights, textheight)\n    end\n\n    width = maximum(chunkwidths)\n    height = maximum(textheights)\n    (text_extents_scale_x * scale * width * mm,\n     text_extents_scale_y * scale * height * mm)\nend\n\nfunction text_extents(font_family::AbstractString, size::Measure, texts::AbstractString...)\n    scale = size / 12pt\n    font_family = match_font(font_family)\n    glyphheight = glyphsizes[font_family][\"height\"]\n    glyphwidths = glyphsizes[font_family][\"widths\"]\n    fontsize = size/pt\n\n    extents = Array{Tuple{Measure, Measure}}(undef, length(texts))\n    for (i, text) in enumerate(texts)\n        chunkwidths = Float64[]\n        textheight = 0.0\n        for chunk in split(text, \"\\n\")\n            chunkheight = glyphheight\n            if match(r\"<su(p|b)>\", String(chunk)) != nothing\n                chunkheight *= 1.5\n            end\n            textheight += chunkheight\n            push!(chunkwidths, text_width(glyphwidths, chunk, fontsize))\n\n        end\n        width = maximum(chunkwidths)\n        extents[i] = (text_extents_scale_x * scale * width * mm,\n                      text_extents_scale_y * scale * textheight * mm)\n    end\n\n    return extents\nend\n\nconst carriage_shift0 = 1.1\nconst carriage_shift_supsub = 0.1\nconst supsub_shift = 0.4\n\n# Amazingly crude fallback to parse pango markup into svg.\nfunction pango_to_svg(text::AbstractString)\n    pat = r\"<(/?)\\s*([^>]*)\\s*>\"\n    output = IOBuffer()\n    output_line = IOBuffer()\n    textlines = split(text, \"\\n\")\n    carriage_shift = carriage_shift0\n    for (itextline,textline) in enumerate(textlines)\n        input = codeunits(textline)\n        lastpos = 1\n        baseline_shift = 0.0\n        sup = sub = false\n        open_tag = false\n\n        for mat in eachmatch(pat, String(textline))\n            write(output_line, input[lastpos:mat.offset-1])\n            lastpos = mat.offset + length(mat.match)\n\n            closing_tag = mat.captures[1] == \"/\"\n\n            open_tag && !closing_tag && write(output_line, \"</tspan>\")\n\n            if closing_tag\n                write(output_line, \"</tspan>\")\n            else\n                if mat.captures[2] == \"sup\"\n                    write(output_line, \"<tspan dy=\\\"-$(supsub_shift)em\\\" font-size=\\\"83%\\\">\")\n                    baseline_shift = -supsub_shift * 0.83\n                    sup = true\n                elseif mat.captures[2] == \"sub\"\n                    write(output_line, \"<tspan dy=\\\"$(supsub_shift)em\\\" font-size=\\\"83%\\\">\")\n                    baseline_shift = supsub_shift * 0.83\n                    sub = true\n                elseif mat.captures[2] == \"i\"\n                    write(output_line, \"<tspan font-style=\\\"italic\\\">\")\n                elseif mat.captures[2] == \"b\"\n                    write(output_line, \"<tspan font-weight=\\\"bold\\\">\")\n                end\n            end\n\n            if closing_tag && baseline_shift != 0.0\n                if lastpos < length(input)\n                    @printf(output_line, \"<tspan dy=\\\"%0.4fem\\\">\", -baseline_shift)\n                    baseline_shift = 0.0\n                    open_tag = true\n                else\n                    open_tag = false\n                end\n            end\n        end\n        write(output_line, input[lastpos:end])\n        open_tag && write(output_line, \"</tspan>\")\n        itextline>1 && @printf(output,\n                               \"<tspan x=\\\"0\\\" dy=\\\"%0.4fem\\\">\",\n                               carriage_shift + sup*carriage_shift_supsub)\n        write(output, String(take!(output_line)))\n        itextline>1 && write(output, \"</tspan>\")\n        carriage_shift = carriage_shift0 - baseline_shift + sub*carriage_shift_supsub\n    end\n    String(take!(output))\nend\n"
  },
  {
    "path": "src/form.jl",
    "content": "# A form is something that ends up as geometry in the graphic.\n\nabstract type FormPrimitive end\n\nconst empty_tag = Symbol(\"\")\n\nstruct Form{P <: FormPrimitive} <: ComposeNode\n    primitives::Vector{P}\n    tag::Symbol\n\n    Form{P}(prim, tag::Symbol=empty_tag) where P = new{P}(prim, tag)\nend\n\nForm(primitives::Vector{P}, tag::Symbol=empty_tag) where P <: FormPrimitive =\n        Form{P}(primitives, tag)\n\nisempty(f::Form) = isempty(f.primitives)\n\nisscalar(f::Form) =\n        length(f.primitives) == 1\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, form::Form) =\n        Form([resolve(box, units, t, primitive) for primitive in form.primitives])\n\nBase.similar(f::Form{T}) where T = Form{T}(T[])\n\nform_string(::Form) = \"FORM\"  # fallback definition\n\n# Polygon\n# -------\n\nstruct SimplePolygonPrimitive{P <: Vec} <: FormPrimitive\n    points::Vector{P}\nend\n\nconst SimplePolygon{P<:SimplePolygonPrimitive} = Form{P}\n\nconst Polygon = SimplePolygon\nconst PolygonPrimitive = SimplePolygonPrimitive\n\npolygon() = Form([PolygonPrimitive(Vec[])])\n\n\"\"\"\n    polygon(points)\n\nDefine a polygon. `points` is an array of `(x,y)` tuples\nthat specify the corners of the polygon.\n\"\"\"\nfunction polygon(points::AbstractArray{T}, tag=empty_tag) where T <: XYTupleOrVec\n    XM, YM = narrow_polygon_point_types(Vector[points])\n    if XM == Any\n        XM = Measure\n    end\n    if YM == Any\n        YM = Measure\n    end\n    VecType = Tuple{XM, YM}\n\n    return Form([PolygonPrimitive(VecType[(x_measure(point[1]), y_measure(point[2]))\n                    for point in points])], tag)\nend\n\n\"\"\"\n    polygon(point_arrays::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfunction polygon(point_arrays::AbstractArray, tag=empty_tag)\n    XM, YM = narrow_polygon_point_types(point_arrays)\n    VecType = XM == YM == Any ? Vec : Tuple{XM, YM}\n    PrimType = XM == YM == Any ? PolygonPrimitive : PolygonPrimitive{VecType}\n\n    polyprims = Array{PrimType}(undef, length(point_arrays))\n    for (i, point_array) in enumerate(point_arrays)\n        polyprims[i] = PrimType(VecType[(x_measure(point[1]), y_measure(point[2]))\n                                        for point in point_array])\n    end\n    return Form{PrimType}(polyprims, tag)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::PolygonPrimitive) =\n        PolygonPrimitive{AbsoluteVec2}( AbsoluteVec2[\n            resolve(box, units, t, point) for point in p.points])\n\nfunction boundingbox(form::PolygonPrimitive, linewidth::Measure,\n                     font::AbstractString, fontsize::Measure)\n    x0 = minimum([p[1] for p in form.points])\n    x1 = maximum([p[1] for p in form.points])\n    y0 = minimum([p[2] for p in form.points])\n    y1 = maximum([p[2] for p in form.points])\n    return BoundingBox(x0 - linewidth,\n                       y0 - linewidth,\n                       x1 - x0 + linewidth,\n                       y1 - y0 + linewidth)\nend\n\nform_string(::SimplePolygon) = \"SP\"\n\nstruct ComplexPolygonPrimitive{P <: Vec} <: FormPrimitive\n    rings::Vector{Vector{P}}\nend\n\nconst ComplexPolygon{P<:ComplexPolygonPrimitive} = Form{P}\n\ncomplexpolygon() = ComplexPolygon([ComplexPolygonPrimitive(Vec[])])\n\nfunction complexpolygon(rings::Vector{Vector}, tag=empty_tag)\n    XM, YM = narrow_polygon_point_types(rings)\n    if XM == Any\n        XM = Length{:cx, Float64}\n    end\n    if YM == Any\n        YM = Length{:cy, Float64}\n    end\n    VecType = Tuple{XM, YM}\n\n    return ComplexPolygon([\n        ComplexPolygonPrimitive([VecType[(x_measure(point[1]), y_measure(point[2]))\n                                         for point in points]])], tag)\nend\n\nfunction complexpolygon(ring_arrays::Vector{Vector{Vector}}, tag=empty_tag)\n    XM, YM = narrow_polygon_point_types(coords)\n    VecType = XM == YM == Any ? Vec : Tuple{XM, YM}\n    PrimType = XM == YM == Any ? ComplexPolygonPrimitive : ComplexPolygonPrimitive{VecType}\n\n    ComplexPolygon([PrimType[[(x_measure(x), y_measure(y)) for (x, y) in ring]\n                    for ring in ring_array] for ring_array in ring_arrays], tag)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::ComplexPolygonPrimitive) =\n        ComplexPolygonPrimitive( [AbsoluteVec2[\n            resolve(box, units, t, point, t) for point in ring] for ring in p.rings])\n\nform_string(::ComplexPolygon) = \"CP\"\n\n\n# Rectangle\n# ---------\n\nstruct RectanglePrimitive{P <: Vec, M1 <: Measure, M2 <: Measure} <: FormPrimitive\n    corner::P\n    width::M1\n    height::M2\nend\n\nconst Rectangle{P<:RectanglePrimitive} = Form{P}\n\n\"\"\"\n    rectangle()\n\nDefine a rectangle that fills the current context completely.\n\"\"\"\nfunction rectangle()\n    prim = RectanglePrimitive((0.0w, 0.0h), 1.0w, 1.0h)\n    return Rectangle{typeof(prim)}([prim])\nend\n\n\"\"\"\n    rectangle(x0, y0, width, height)\n\nDefine a rectangle of size `width`x`height` with its top left corner at the point (`x`, `y`).\n\"\"\"\nfunction rectangle(x0, y0, width, height, tag=empty_tag)\n    corner = (x_measure(x0), y_measure(y0))\n    width = x_measure(width)\n    height = y_measure(height)\n    prim = RectanglePrimitive(corner, width, height)\n    return Rectangle{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    rectangle(x0s::AbstractArray, y0s::AbstractArray, widths::AbstractArray, heights::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nrectangle(x0s::AbstractArray, y0s::AbstractArray,\n                   widths::AbstractArray, heights::AbstractArray, tag=empty_tag) =\n        @makeform (x0 in x0s, y0 in y0s, width in widths, height in heights),\n            RectanglePrimitive{Vec2, Measure, Measure}((x_measure(x0), y_measure(y0)),\n                                                    x_measure(width), y_measure(height)) tag\n\n\nfunction resolve(box::AbsoluteBox, units::UnitBox, t::Transform,\n                 p::RectanglePrimitive)\n    corner = resolve(box, units, t, p.corner)\n    width = resolve(box, units, t, p.width)\n    height = resolve(box, units, t, p.height)\n\n    if isxflipped(units) && hasunits(Length{:cx}, p.corner[1])\n        # if coordinates are flipped we end up with the corner on the other end\n        # of the rectangle, which is fix here\n        x = corner[1] - width\n    else\n        x = corner[1]\n    end\n\n    if isyflipped(units) && hasunits(Length{:cy}, p.corner[2])\n        y = corner[2] - height\n    else\n        y = corner[2]\n    end\n\n    return RectanglePrimitive{AbsoluteVec2, AbsoluteLength, AbsoluteLength}(\n        (x, y), width, height)\nend\n\nboundingbox(form::RectanglePrimitive, linewidth::Measure,\n                     font::AbstractString, fontsize::Measure) =\n        BoundingBox(form.corner.x - linewidth,\n                    form.corner.y - linewidth,\n                    form.width + 2*linewidth,\n                    form.height + 2*linewidth)\n\nform_string(::Rectangle) = \"R\"\n\n# Circle\n# ------\n\nstruct CirclePrimitive{P <: Vec, M <: Measure} <: FormPrimitive\n    center::P\n    radius::M\nend\n\nCirclePrimitive(center::P, radius::M) where {P, M} = CirclePrimitive{P, M}(center, radius)\nCirclePrimitive(x, y, r) = CirclePrimitive((x_measure(x), y_measure(y)), x_measure(r))\n\nconst Circle{P<:CirclePrimitive} = Form{P}\n\n\"\"\"\n    circle()\n\nDefine a circle in the center of the current context with a diameter equal to the width of the context.\n\"\"\"\nfunction circle()\n    prim = CirclePrimitive((0.5w, 0.5h), 0.5w)\n    return Circle{typeof(prim)}([prim])\nend\n\n\"\"\"\n    circle(x, y, r)\n\nDefine a circle with its center at (`x`,`y`) and a radius of `r`.\n\"\"\"\nfunction circle(x, y, r, tag=empty_tag)\n    prim = CirclePrimitive(x, y, r)\n    return Circle{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    circle(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations.\n\"\"\"\nfunction circle(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray, tag=empty_tag)\n    if isempty(xs) || isempty(ys) || isempty(rs)\n        prima = CirclePrimitive[]\n        return Circle{eltype(prima)}(prima, tag)\n    end\n\n    return @makeform (x in xs, y in ys, r in rs),\n        CirclePrimitive{Vec2, Measure}((x_measure(x), y_measure(y)), x_measure(r)) tag\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::CirclePrimitive) =\n        CirclePrimitive{AbsoluteVec2, AbsoluteLength}(\n            resolve(box, units, t, p.center),\n            resolve(box, units, t, p.radius))\n\nboundingbox(form::CirclePrimitive, linewidth::Measure, font::AbstractString, fontsize::Measure) =\n        BoundingBox(form.center[1] - form.radius - linewidth,\n            form.center[2] - form.radius - linewidth,\n            2 * (form.radius + linewidth),\n            2 * (form.radius + linewidth))\n\nform_string(::Circle) = \"C\"\n\n# Ellipse\n# -------\n\nstruct EllipsePrimitive{P1<:Vec, P2<:Vec, P3<:Vec} <: FormPrimitive\n    center::P1\n    x_point::P2\n    y_point::P3\nend\n\nconst Ellipse{P<:EllipsePrimitive} = Form{P}\n\n\"\"\"\n    ellipse()\n\nDefine an ellipse in the center of the current context with `x_radius=0.5w` and `y_radius=0.5h`.\n\"\"\"\nfunction ellipse()\n    prim = EllipsePrimitive((0.5w, 0.5h),\n                            (1.0w, 0.5h),\n                            (0.5w, 1.0h))\n    return Ellipse{typeof(prim)}([prim])\nend\n\n\"\"\"\n    ellipse(x, y, x_radius, y_radius)\n\nDefine an ellipse with its center at (`x`,`y`) with radii `x_radius` and `y_radius`.\n\"\"\"\nfunction ellipse(x, y, x_radius, y_radius, tag=empty_tag)\n    xm = x_measure(x)\n    ym = y_measure(y)\n    prim = EllipsePrimitive((xm, ym),\n                            (xm + x_measure(x_radius), ym),\n                            (xm, ym + y_measure(y_radius)))\n    return Ellipse{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    ellipse(xs::AbstractArray, ys::AbstractArray, x_radiuses::AbstractArray, y_radiuses::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations.\n\"\"\"\nfunction ellipse(xs::AbstractArray, ys::AbstractArray,\n                 x_radiuses::AbstractArray, y_radiuses::AbstractArray, tag=empty_tag)\n        return @makeform (x in xs, y in ys, x_radius in x_radiuses, y_radius in y_radiuses),\n            EllipsePrimitive((x_measure(x), y_measure(y)),\n                     (x_measure(x) + x_measure(x_radius), y_measure(y)),\n                     (x_measure(x), y_measure(y) + y_measure(y_radius))) tag\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::EllipsePrimitive) =\n        EllipsePrimitive{AbsoluteVec2, AbsoluteVec2, AbsoluteVec2}(\n            resolve(box, units, t, p.center),\n            resolve(box, units, t, p.x_point),\n            resolve(box, units, t, p.y_point))\n\nfunction boundingbox(form::EllipsePrimitive, linewidth::Measure,\n                     font::AbstractString, fontsize::Measure)\n    x0 = min(form.x_point.x, form.y_point.x)\n    x1 = max(form.x_point.x, form.y_point.x)\n    y0 = min(form.x_point.y, form.y_point.y)\n    y1 = max(form.x_point.y, form.y_point.y)\n    xr = x1 - x0\n    yr = y1 - y0\n    return BoundingBox(x0 - linewidth - xr,\n                       y0 - linewidth - yr,\n                       2 * (xr + linewidth),\n                       2 * (yr + linewidth))\nend\n\nform_string(::Ellipse) = \"E\"\n\n# Text\n# ----\n\nabstract type HAlignment end\nstruct HLeft   <: HAlignment end\nstruct HCenter <: HAlignment end\nstruct HRight  <: HAlignment end\n\nconst hleft   = HLeft()\nconst hcenter = HCenter()\nconst hright  = HRight()\n\nabstract type VAlignment end\nstruct VTop    <: VAlignment end\nstruct VCenter <: VAlignment end\nstruct VBottom <: VAlignment end\n\nconst vtop    = VTop()\nconst vcenter = VCenter()\nconst vbottom = VBottom()\n\nstruct TextPrimitive{P<:Vec, R<:Rotation, O<:Vec} <: FormPrimitive\n    position::P\n    value::AbstractString\n    halign::HAlignment\n    valign::VAlignment\n\n    # Text forms need their own rotation field unfortunately, since there is no\n    # way to give orientation with just a position point.\n    rot::R\n\n    offset::O\nend\n\nconst Text{P<:TextPrimitive} = Form{P}\n\n\"\"\"\n    text(x, y, value [,halign::HAlignment [,valign::VAlignment [,rot::Rotation]]])\n\nDraw the text `value` at the position (`x`,`y`) relative to the current context.\n\nThe default alignment of the text is `hleft` `vbottom`. The vertical and horizontal\nalignment is specified by passing `hleft`, `hcenter` or `hright` and `vtop`,\n`vcenter` or `vbottom` as values for `halign` and `valign` respectively.\n\"\"\"\nfunction text(x, y, value,\n              halign::HAlignment=hleft, valign::VAlignment=vbottom,\n              rot=Rotation(), offset::Vec2=(0mm,0mm);\n              tag::Symbol=empty_tag)\n    moffset = (x_measure(offset[1]), y_measure(offset[2]))\n    prim = TextPrimitive((x_measure(x), y_measure(y)), string(value), halign, valign, rot, moffset)\n    Text{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    text(xs::AbstractArray, ys::AbstractArray, values::AbstractArray [,haligns::HAlignment [,valigns::VAlignment [,rots::Rotation]]])\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\ntext(xs::AbstractArray, ys::AbstractArray, values::AbstractArray{AbstractString},\n              haligns::AbstractArray=[hleft], valigns::AbstractArray=[vbottom],\n              rots::AbstractArray=[Rotation()], offsets::AbstractArray=[(0mm,0mm)];\n              tag::Symbol=empty_tag) =\n    @makeform (x in xs, y in ys, value in values, halign in haligns, valign in valigns, rot in rots, offset in offsets),\n        TextPrimitive((x_measure(x), y_measure(y)), value, halign, valign, rot, (x_measure(offset[1]), y_measure(offset[2]))) tag\n\ntext(xs::AbstractArray, ys::AbstractArray, values::AbstractArray,\n              haligns::AbstractArray=[hleft], valigns::AbstractArray=[vbottom],\n              rots::AbstractArray=[Rotation()], offsets::AbstractArray=[(0mm,0mm)];\n              tag::Symbol=empty_tag) =\n    @makeform (x in xs, y in ys, value in values, halign in haligns, valign in valigns, rot in rots, offset in offsets),\n        TextPrimitive((x_measure(x), y_measure(y)), value, halign, valign, rot, (x_measure(offset[1]), y_measure(offset[2]))) tag\n\nfunction resolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::TextPrimitive{P,R,O}) where {P,R,O}\n    rot = resolve(box, units, t, p.rot)\n    return TextPrimitive{AbsoluteVec2, typeof(rot), O}(\n                resolve(box, units, t, p.position),\n                p.value, p.halign, p.valign, rot, p.offset)\nend\n\nfunction boundingbox(form::TextPrimitive, linewidth::Measure,\n                     font::AbstractString, fontsize::Measure)\n\n    width, height = text_extents(font, fontsize, form.value)[1]\n\n    if form.halign == hleft\n        x0 = form.position.x\n    elseif form.halign == hcenter\n        x0 = form.position.x - width/2\n    elseif form.halign == hright\n        x0 = form.position.x - width\n    end\n\n    if form.valign == vbottom\n        y0 = form.position.y - height\n    elseif form.valign == vcenter\n        y0 = form.position.y - height/2\n    elseif form.valign == vtop\n        y0 = form.position.y\n    end\n\n    return BoundingBox(x0 - linewidth + form.offset[1],\n                       y0 - linewidth + form.offset[2],\n                       width + linewidth,\n                       height + linewidth)\nend\n\nform_string(::Text) = \"T\"\n\n# Line\n# ----\n\nstruct LinePrimitive{P<:Vec} <: FormPrimitive\n    points::Vector{P}\nend\n\nconst Line{P<:LinePrimitive} = Form{P}\n\nfunction line()\n    prim = LinePrimitive(Vec[])\n    return Line{typeof(prim)}([prim])\nend\n\n\"\"\"\n    line(points)\n    \nDefine a line. `points` is an array of `(x,y)` tuples.\n\"\"\"\nfunction line(points::AbstractArray{T}, tag=empty_tag) where T <: XYTupleOrVec\n    XM, YM = narrow_polygon_point_types(Vector[points])\n    VecType = XM == YM == Any ? Vec2 : Tuple{XM, YM}\n    prim = LinePrimitive(VecType[(x_measure(point[1]), y_measure(point[2])) for point in points])\n    return Line{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    line(point_arrays::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfunction line(point_arrays::AbstractArray, tag=empty_tag)\n    XM, YM = narrow_polygon_point_types(point_arrays)\n    VecType = XM == YM == Any ? Vec2 : Tuple{XM, YM}\n    PrimType = XM == YM == Any ? LinePrimitive : LinePrimitive{VecType}\n\n    lineprims = Array{PrimType}(undef, length(point_arrays))\n    for (i, point_array) in enumerate(point_arrays)\n        p = PrimType(VecType[(x_measure(point[1]), y_measure(point[2]))\n                             for point in point_array])\n        lineprims[i] = p\n    end\n    return Form{PrimType}(lineprims, tag)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::LinePrimitive) =\n        LinePrimitive{AbsoluteVec2}(\n            AbsoluteVec2[resolve(box, units, t, point) for point in p.points])\n\nfunction boundingbox(form::LinePrimitive, linewidth::Measure,\n                     font::AbstractString, fontsize::Measure)\n    x0 = minimum([p.x for p in form.points])\n    x1 = maximum([p.x for p in form.points])\n    y0 = minimum([p.y for p in form.points])\n    y1 = maximum([p.y for p in form.points])\n    return BoundingBox(x0 - linewidth,\n                       y0 - linewidth,\n                       x1 - x0 + linewidth,\n                       y1 - y0 + linewidth)\nend\n\nform_string(::Line) = \"L\"\n\n# Curve\n# -----\n\nstruct CurvePrimitive{P1<:Vec, P2<:Vec, P3<:Vec, P4<:Vec} <: FormPrimitive\n    anchor0::P1\n    ctrl0::P2\n    ctrl1::P3\n    anchor1::P4\nend\n\nconst Curve{P<:CurvePrimitive} = Form{P}\n\n\n\"\"\"\n    curve(anchor0, ctrl0, ctrl1, anchor1)\n\nDefine a bezier curve between `anchor0` and `anchor1` with control points `ctrl0` and `ctrl1`.\n\"\"\"\nfunction curve(anchor0::XYTupleOrVec, ctrl0::XYTupleOrVec,\n               ctrl1::XYTupleOrVec, anchor1::XYTupleOrVec, tag=empty_tag)\n    prim = CurvePrimitive((x_measure(anchor0[1]), y_measure(anchor0[2])),\n                                 (x_measure(ctrl0[1]), y_measure(ctrl0[2])),\n                                 (x_measure(ctrl1[1]), y_measure(ctrl1[2])),\n                                 (x_measure(anchor1[1]), y_measure(anchor1[2])))\n    return Curve{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    curve(anchor0s::AbstractArray, ctrl0s::AbstractArray, ctrl1s::AbstractArray, anchor1s::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations.\n\"\"\"\ncurve(anchor0s::AbstractArray, ctrl0s::AbstractArray,\n               ctrl1s::AbstractArray, anchor1s::AbstractArray, tag=empty_tag) =\n        @makeform (anchor0 in anchor0s, ctrl0 in ctrl0s, ctrl1 in ctrl1s, anchor1 in anchor1s),\n            CurvePrimitive((x_measure(anchor0[1]), y_measure(anchor0[2])),\n                (x_measure(ctrl0[1]), y_measure(ctrl0[2])),\n                (x_measure(ctrl1[1]), y_measure(ctrl1[2])),\n                (x_measure(anchor1[1]), y_measure(anchor1[2]))) tag\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::CurvePrimitive) =\n        CurvePrimitive{AbsoluteVec2, AbsoluteVec2, AbsoluteVec2, AbsoluteVec2}(\n            resolve(box, units, t, p.anchor0),\n            resolve(box, units, t, p.ctrl0),\n            resolve(box, units, t, p.ctrl1),\n            resolve(box, units, t, p.anchor1))\n\nform_string(::Curve) = \"CV\"\n\n# Bitmap\n# ------\n\nstruct BitmapPrimitive{P <: Vec, XM <: Measure, YM <: Measure} <: FormPrimitive\n    mime::AbstractString\n    data::Vector{UInt8}\n    corner::P\n    width::XM\n    height::YM\nend\n\nconst Bitmap{P<:BitmapPrimitive} = Form{P}\n\n\"\"\"\n    bitmap(mime, data, x0, y0, width, height)\n\nDefine a bitmap of size `width`x`height` with its top left corner at the point (`x`, `y`).\n\"\"\"\nfunction bitmap(mime::AbstractString, data::Vector{UInt8}, x0, y0, width, height, tag=empty_tag)\n    corner = (x_measure(x0), y_measure(y0))\n    width = x_measure(width)\n    height = y_measure(height)\n    prim = BitmapPrimitive(mime, data, corner, width, height)\n    return Bitmap{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    bitmap(mimes::AbstractArray, datas::AbstractArray, x0s::AbstractArray, y0s::AbstractArray, widths::AbstractArray, heights::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations.\n\"\"\"\nbitmap(mimes::AbstractArray, datas::AbstractArray,\n                x0s::AbstractArray, y0s::AbstractArray,\n                widths::AbstractArray, heights::AbstractArray, tag=empty_tag) =\n        @makeform (mime in mimes, data in datas, x0 in x0s, y0 in y0s, width in widths, height in heights),\n            BitmapPrimitive(mime, data, (x_measure(x0), y_measure(y0)), x_measure(width), y_measure(height)) tag\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::BitmapPrimitive) =\n        BitmapPrimitive{AbsoluteVec2, AbsoluteLength, AbsoluteLength}(\n            p.mime, p.data,\n            resolve(box, units, t, p.corner),\n            resolve(box, units, t, p.width),\n            resolve(box, units, t, p.height))\n\nboundingbox(form::BitmapPrimitive, linewidth::Measure, font::AbstractString, fontsize::Measure) =\n        BoundingBox(form.corner.x, form.corner.y, form.width, form.height)\n\nform_string(::Bitmap) = \"B\"\n\n\n\n\n# Arc\n# -------\nstruct ArcPrimitive{P<:Vec, M<:Measure} <: Compose.FormPrimitive\n    center::P\n    radius::M\n    angle1::Float64\n    angle2::Float64\n    sector::Bool\nend\n\nArcPrimitive(center::P, radius::M, angle1, angle2, sector) where {P, M} = ArcPrimitive{P, M}(center, radius, angle1, angle2, sector)\nArcPrimitive(x, y, r, θ1, θ2, sector) = ArcPrimitive((x_measure(x), y_measure(y)), x_measure(r), θ1, θ2, sector)\n\n Arc{P<:ArcPrimitive} = Compose.Form{P}\n\n\n\"\"\"\n    arc(x, y, r, θ1, θ2, sector)\n\nDefine an arc with its center at (`x`,`y`), radius of `r`, between `θ1` and `θ2`.  \n`sector` (optional) is true or false, true for a pie sector, false for an arc.\nArcs are drawn clockwise from θ1 to θ2.    \n\"\"\"\nfunction arc(x, y, r, θ1, θ2, sector=false, tag=empty_tag)\n    prim = ArcPrimitive(x, y, r, θ1, θ2, sector)\n    return Arc{typeof(prim)}([prim], tag)\nend\n\n\"\"\"\n    sector(x, y, r, θ1, θ2)\n\nDefine a pie sector with its center at (`x`,`y`), radius of `r`, between `θ1` and `θ2`.  \n\"\"\"\nsector(x, y, r, θ1, θ2) = arc(x,y,r,θ1,θ2,true)\n\n\"\"\"\n    arc(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, θ1s::AbstractVector, θ2s::AbstractVector, sectors::AbstractVector)\n\nArguments can be passed in arrays in order to perform multiple drawing operations.\n\"\"\"\nfunction arc(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, θ1s::AbstractVector, θ2s::AbstractVector, sectors::AbstractVector=[false], tag=empty_tag)\n        return @makeform (x in xs, y in ys, r in rs, θ1 in θ1s, θ2 in θ2s, sector in sectors), \n            ArcPrimitive((x_measure(x), y_measure(y)), x_measure(r), θ1, θ2, sector) tag\nend\n\n\"\"\"\n    sector(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, θ1s::AbstractVector, θ2s::AbstractVector)\n\nArguments can be passed in arrays in order to perform multiple drawing operations.\n\"\"\"\nsector(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, θ1s::AbstractVector, θ2s::AbstractVector) = \n    arc(xs, ys, rs, θ1s, θ2s, [true])\n\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Compose.Transform, p::ArcPrimitive) =\n        ArcPrimitive{AbsoluteVec2, AbsoluteLength}(\n            resolve(box, units, t, p.center),\n            resolve(box, units, t, p.radius), p.angle1, p.angle2, p.sector)\n\nboundingbox(form::ArcPrimitive, linewidth::Measure, font::AbstractString, fontsize::Measure) =\n        BoundingBox(form.center[1] - form.radius - linewidth,\n            form.center[2] - form.radius - linewidth,\n            2 * (form.radius + linewidth),\n            2 * (form.radius + linewidth))\n\nform_string(::Arc) = \"A\"\n\n@deprecate slice(x,y,r,θ1,θ2) sector(x,y,r,θ1,θ2)\n\n\n\n### Polygon primitive forms\n\n\"\"\"\n    ngon(x, y, r, n::Int)\n\nDefine a `n`-sided polygon with its center at (`x`,`y`), and radius of `r`.  For an upside-down ngon, use `-r`.  \n\"\"\"\nfunction ngon(x, y, r, n::Int, tag=empty_tag)\n    θ = range(-π/2, stop=1.5π, length=n+1)\n    x1 = x_measure(x) .+ size_x_measure(r).*cos.(θ)\n    y1 = y_measure(y) .+ size_x_measure(r).*sin.(θ)\n    points = collect(Tuple{Measure, Measure}, zip(x1, y1))\n    return Form([PolygonPrimitive(points)], tag)\nend\n\n\n\"\"\"\n    ngon(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, ns::AbstractVector{Int})\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfunction ngon(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, ns::AbstractVector{Int}, tag=empty_tag)\n    VecType = Tuple{Measure, Measure}\n    PrimType = PolygonPrimitive{VecType}\n    polyprims = PrimType[]\n    for (x, y, r, n) in Compose.cyclezip(xs, ys, rs, ns)\n        p = ngon( x, y, r, n)\n        push!(polyprims, PrimType(p.primitives[1].points))\n    end\n    return Form{PrimType}(polyprims, tag)\nend\n\n\n\"\"\"\n    star(x, y, r, n::Int, ratio)\n\nDefine a `n`-pointed star with its center at (`x`,`y`), outer radius of `r`, and inner radius equal to `r*ratio`. For an upside-down star, use `-r`.\n\"\"\"\nfunction star(x, y, r, n::Int, ratio::Float64=0.3, tag=empty_tag)\n    θ = range(-π/2, stop=1.5π, length=2*n+1)[1:end-1]\n    r1 = repeat([r, r*ratio], outer=n)\n    x1 = x_measure(x) .+ size_x_measure(r1).*cos.(θ)\n    y1 = y_measure(y) .+ size_x_measure(r1).*sin.(θ)\n    points = collect(Tuple{Measure, Measure}, zip(x1, y1))\n    return Form([PolygonPrimitive(points)], tag)\nend\n\n\n\"\"\"\n    star(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, ns::AbstractVector{Int}, ratios::AbstractVector{Float64})\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfunction star(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, ns::AbstractVector{Int}, ratios::AbstractVector{Float64}=[0.3], tag=empty_tag)\n    VecType = Tuple{Measure, Measure}\n    PrimType = PolygonPrimitive{VecType}\n    polyprims = PrimType[]\n    for (x, y, r, n, ratio) in Compose.cyclezip(xs, ys, rs, ns, ratios)\n        p = star( x, y, r, n, ratio)\n        push!(polyprims, PrimType(p.primitives[1].points))\n    end\n    return Form{PrimType}(polyprims, tag)\nend\n\n\n\"\"\"\n    xgon(x, y, r, n::Int, ratio)\n\nDefine a cross with `n` arms with its center at (`x`,`y`), outer radius of `r`, and inner radius equal to `r*ratio`. For an upside-down xgon, use `-r`.\n\"\"\"\nfunction xgon(x, y, r, n::Int, ratio::Float64=0.1, tag=empty_tag)\n    θ₁ = range(-0.75π, stop=1.25π, length=n+1)[1:end-1]\n    w = 2*r*ratio*sin(π/n)\n    dₒ = abs(asin(0.5*w/r))\n    dᵢ = abs(asin(0.5*w/(r*ratio)))   \n    r₂ = repeat([r*ratio,r,r], outer=n)\n    θ₂ = vec([θ+x  for x in [-dᵢ, -dₒ, dₒ], θ in θ₁])\n    x1 = x_measure(x) .+ size_x_measure(r₂).*cos.(θ₂)\n    y1 = y_measure(y) .+ size_x_measure(r₂).*sin.(θ₂)\n    points = collect(Tuple{Measure, Measure}, zip(x1, y1))\n    return Form([PolygonPrimitive(points)], tag)\nend\n\n\"\"\"\n    xgon(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, ns::AbstractVector{Int}, ratios::AbstractVector{Float64})\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfunction xgon(xs::AbstractVector, ys::AbstractVector, rs::AbstractVector, ns::AbstractVector{Int}, ratios::AbstractVector{Float64}=[0.3], tag=empty_tag)\n    VecType = Tuple{Measure, Measure}\n    PrimType = PolygonPrimitive{VecType}\n    polyprims = PrimType[]\n    for (x, y, r, n, ratio) in Compose.cyclezip(xs, ys, rs, ns, ratios)\n        p = xgon(x, y, r, n, ratio)\n        push!(polyprims, PrimType(p.primitives[1].points))\n    end\n    return Form{PrimType}(polyprims, tag)\nend\n\n\"\"\"\n    points(x::Compose.Form)\n\nExtract points from a Compose.Form\n\"\"\"\npoints(x::Compose.Form) = x.primitives[1].points\n\n\n# Bezigon\n# -------\n\nstruct BezierPolygonPrimitive{P<:Vec} <: FormPrimitive\n    anchor::P\n    sides::Vector{Vector{P}}\nend\n\nBezigon{P<:BezierPolygonPrimitive} = Form{P}\n\n\n\"\"\"\n     bezigon(anchor0::Tuple, sides::Vector{<:Vector{<:Tuple}})\n \n Define a bezier polygon. `anchor0` is the starting point as an `(x,y)` tuple.\n `sides` contains Vectors of side points (tuples): each vector has the control point(s) and end point for each side \n (the end point forms the next starting point).\n The sides can be linear (1 point), quadratic (2 points) or cubic (3 points).\n \"\"\"\nfunction bezigon(anchor0::XYTupleOrVec, sides::Vector{T}, tag=empty_tag) where T<:Vector{<:XYTupleOrVec}\n    anchor = (x_measure(anchor0[1]), y_measure(anchor0[2]))\n    sv = Vector{Vec2}[]\n    for side in sides\n        s = collect(Vec2, zip(x_measure.(first.(side)), y_measure.(last.(side))))\n        push!(sv, s)\n    end\n    Form([BezierPolygonPrimitive(anchor, sv)], tag)\nend\n\n \n\"\"\"\n    bezigon(anchors::Vector{Tuple}, polysides=Vector{<:Vector{<:Vector{<:Tuple}}})\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfunction bezigon(anchors::Vector, polysides::Vector{T}, tag=empty_tag) where T<:Vector{<:Vector}\n    polyprims = BezierPolygonPrimitive[]\n    for (anchor0, sides) in cyclezip(anchors, polysides)\n        anchor = (x_measure(anchor0[1]), y_measure(anchor0[2]))\n        sv = Vector{Vec2}[]\n        for side in sides\n            s = collect(Vec2, zip(x_measure.(first.(side)), y_measure.(last.(side))))\n            push!(sv, s)\n        end\n        push!(polyprims, BezierPolygonPrimitive(anchor, sv))\n    end\n    Form(polyprims, tag)\nend\n\n\nfunction resolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::BezierPolygonPrimitive)\n    anchor = resolve(box, units, t, p.anchor)\n    sv = Vector{Vec}[]\n    for side in p.sides\n        push!(sv, [resolve(box, units, t, point) for point in side])\n    end\n    return BezierPolygonPrimitive(anchor, sv)\nend\n\n\nfunction boundingbox(prim::BezierPolygonPrimitive, linewidth::Measure,\n                      font::AbstractString, fontsize::Measure)\n    points = [prim.anchor; reduce(vcat, prim.sides)]\n    x, y = first.(points), last.(points)\n    x0, x1 = extrema(x)\n    y0, y1 = extrema(y)\n    return BoundingBox(x0-linewidth, y0-linewidth, x1-x0+linewidth, y1-y0+linewidth)\nend\n \n \nform_string(::Bezigon) = \"BP\"\n\n\n\n"
  },
  {
    "path": "src/immerse_backend.jl",
    "content": "# The Immerse backend\n# To the Cairo backend, this adds just one feature: keeping track of\n# the rendered coordinates of tagged objects and svgclass(\"plotpanel\")\n# objects\n\nexport ImmerseBackend\n\nmutable struct ImmerseBackend <: Backend\n    cb::CAIROSURFACE\n    coords::Dict{Symbol,Any}\n    panelcoords::Vector\nend\n\nfunction ImmerseBackend(c::CairoSurface)\n    cb = CAIROSURFACE(c)\n    ImmerseBackend(cb, Dict{Symbol,Any}(), Any[])\nend\n\nfunction (img::ImmerseBackend)(x)\n    draw(img, x)\nend\n\nroot_box(backend::ImmerseBackend) = root_box(backend.cb)\n\npush_property_frame(backend::ImmerseBackend, properties) =\n        push_property_frame(backend.cb, properties)\npop_property_frame(backend::ImmerseBackend) = pop_property_frame(backend.cb)\n\nfunction draw(backend::ImmerseBackend, root_container::Container)\n    empty!(backend.coords)\n    empty!(backend.panelcoords)\n    drawpart(backend, root_container, IdentityTransform(), UnitBox(), root_box(backend))\n    finish(backend)\nend\n\nfunction register_coords(backend::ImmerseBackend, box, units, transform, form::Form)\n    if form.tag != empty_tag\n        backend.coords[form.tag] = (box, units, transform)\n    end\n    nothing\nend\n\nfunction register_coords(backend::ImmerseBackend, box, units, transform, property::SVGClass)\n    length(property.primitives) == 1 && property.primitives[1].value == \"plotpanel\" &&\n            push!(backend.panelcoords, (box, units, transform))\n    nothing\nend\n\nabsolute_native_units(backend::ImmerseBackend, u::Float64) = absolute_native_units(backend.cb, u)\nabsolute_native_units(backend::ImmerseBackend, l::Length{:mm}) =\n        absolute_native_units(backend.cb, l.value)\nabsolute_native_units(backend::ImmerseBackend, l::Tuple{Length{:mm},Length{:mm}}) =\n        (absolute_native_units(backend.cb, l[1].value),\n         absolute_native_units(backend.cb, l[2].value))\n\nsurface(backend::ImmerseBackend) = surface(backend.cb)\n\ndraw(backend::ImmerseBackend, form::Form) = draw(backend.cb, form)\n\nfinish(backend::ImmerseBackend) = finish(backend.cb)\n\niswithjs(::ImmerseBackend) = false\niswithousjs(::ImmerseBackend) = true\n"
  },
  {
    "path": "src/list.jl",
    "content": "# Basic list\n\nabstract type List{T} end\n\nstruct ListNull{T} <: List{T} end\n\ncopy(l::ListNull{T}) where {T} = l\n\nmutable struct ListNode{T} <: List{T}\n    head::T\n    tail::List{T}\nend\n\ncopy(l::ListNode{T}) where {T} = ListNode{T}(l.head, l.tail)\n\nhead(l::ListNode) = l.head\ntail(l::ListNode) = l.tail\n\n# iterator\nfunction Base.iterate(l::List, state=l)\n    return typeof(state) <: ListNull ? nothing : (state.head, state.tail)\nend\ncons(value, l::List{T}) where T = ListNode{T}(value, l)\n\nfunction length(l::List{T}) where T\n    n = 0\n    while typeof(l) != ListNull{T}\n        n += 1\n        l = l.tail\n    end\n    n\nend\n\nfunction cat(a::List{T}, b::List{T}) where T\n    if a === nothing\n        b\n    else\n        a = copy(a)\n        u = a\n        while u.tail !== nothing\n            u.tail = copy(u.tail)\n            u = u.tail\n        end\n        u.tail = b\n        a\n    end\nend\n\nfunction show(io::IO, a::List{T}) where T\n    print(io, \"List([\")\n    while typeof(a) != ListNull{T}\n        print(io, a.head)\n        if typeof(a.tail) != ListNull{T}\n            print(io, \", \")\n        end\n        a = a.tail\n    end\n    print(io, \"])\")\nend\n"
  },
  {
    "path": "src/measure.jl",
    "content": "using Measures: Add, Min, Max, Div, Mul, Neg\nusing LinearAlgebra\n\n# Measure Constants\n# -----------------\n\nconst cx = Length{:cx}\nconst cy = Length{:cy}\nconst sx = Length{:sx}\nconst sy = Length{:sy}\n\n*(a::T, b::Type{cx}) where T = x_measure(a)\n*(a::T, b::Type{cy}) where T = y_measure(a)\n*(a::T, b::Type{sx}) where T = size_x_measure(a)\n*(a::T, b::Type{sy}) where T = size_y_measure(a)\n\n# Pixels are not typically used in Compose in preference of absolute\n# measurements or measurements relative to parent canvases. So for the\n# 'px' constant, we just punt and give something do something vaguely\n# reasonable.\n\nconst assumed_ppmm = 3.78 # equivalent to 96 DPI\nconst px = mm/assumed_ppmm\n\nconst MeasureOrNumber = Union{Measure, Number}\nconst XYTupleOrVec = Union{Tuple{MeasureOrNumber,MeasureOrNumber}, Vec}\n\n\n# Scaling w and h components\n# --------------------------\n\n# Compute the length of the given type.\nsum_component(::Type{T}, l) where T <: Length = 0.0\nsum_component(::Type{T}, l::T) where T <: Length = l.value\nsum_component(::Type{T}, l::Add) where T <: Length = sum_component(T, l.a) + sum_component(T, l.b)\n\n# Scale a length component by some factor.\nscale_component(::Type{T}, scale, l) where T <: Length = l\nscale_component(::Type{T}, scale, l::T) where T <: Length = T(scale * l.value)\nscale_component(::Type{T}, scale, l::Add) where T <: Length =\n        scale_component(T, scale, l.a) + scale_component(T, scale, l.b)\n\n\n# Interpretation of bare numbers\n# ------------------------------\n\nx_measure(a::Measure) = a\nx_measure(a::T) where T = Length{:cx, T}(a)\n\ny_measure(a::Measure) = a\ny_measure(a::T) where T = Length{:cy, T}(a)\n\nsize_x_measure(a::Measure) = a\nsize_x_measure(a::T) where T = Length{:sx, T}(a)\nsize_y_measure(a::Measure) = a\nsize_y_measure(a::T) where T = Length{:sy, T}(a)\n\nx_measure(a::Vector{T}) where T <: Measure = a\nx_measure(a::Vector) = Measure[x_measure(x) for x in a]\n\ny_measure(a::Vector{T}) where T <: Measure = a\ny_measure(a::Vector) = Measure[y_measure(y) for y in a]\n\nsize_x_measure(a::Vector{T}) where T <: Measure = a\nsize_x_measure(a::Vector) = Measure[size_x_measure(x) for x in a]\nsize_y_measure(a::Vector{T}) where T <: Measure = a\nsize_y_measure(a::Vector) = Measure[size_y_measure(y) for y in a]\n\nsize_measure(a::Measure) = a\nsize_measure(a) = a * mm\n\nx_measure(a::Missing) = x_measure(NaN)\ny_measure(a::Missing) = y_measure(NaN)\n\nsize_x_measure(a::Missing) = size_x_measure(NaN)\nsize_y_measure(a::Missing) = size_y_measure(NaN)\n\n# Higher-order measures\n# ---------------------\n\n# Compute the union of two bounding boxes.\n#\n# In other words, given two bounding boxes, return a new bounding box that\n# contains both.\n#\n# Unfortunately this is in general uncomputable without knowing the absolute\n# size of the parent canvas which may be passed in via the last two parameters.\n# If not passed, this throws an error if they would have been required.\n#\nfunction union(a::BoundingBox, b::BoundingBox, units=nothing, parent_abs_width=nothing, parent_abs_height=nothing)\n    (a.width == Measure() || a.height == Measure()) && return b\n    (b.width == Measure() || b.height == Measure()) && return a\n    x0 = min(a.x0, b.x0)\n    y0 = min(a.y0, b.y0)\n    x1 = max(a.x0 + a.width, b.x0 + b.width)\n    y1 = max(a.y0 + a.height, b.y0 + b.height)\n    # Check whether we had any problematic computations\n    for m in (x0,y0,x1,y1)\n        # Pure absolute or pure relative points are fine. When they are mixed,\n        # there are problems\n        if !isabsolute(m) && m.abs != 0.0\n            units == nothing || parent_abs_width == nothing || parent_abs_height == nothing &&\n                    error(\"\"\"Bounding boxes are uncomputable without knowledge of the\n                        absolute dimensions of the top canvase due to mixing of relative\n                        and absolute coordinates. Either pass the dimension as a parameter\n                        or restrict the context to one kind of coordinates.\"\"\")\n            parent_box = AbsoluteBox(0.0,0.0,parent_abs_width,parent_abs_height)\n            abb = union(absolute_units(a,IdentityTransform(),units,parent_box),\n                        absolute_units(b,IdentityTransform(),units,parent_box))\n            return BoundingBox(Measure(;abs = abb.x0), Measure(;abs = abb.x0),\n                               Measure(;abs = abb.width), Measure(;abs = abb.height))\n        end\n    end\n    return BoundingBox(x0, y0, x1 - x0, y1 - y0)\nend\n\nfunction union(a::AbsoluteBox, b::AbsoluteBox)\n    (a.width == 0.0 || a.height == 0.0) && return b\n    (b.width == 0.0 || b.height == 0.0) && return a\n    x0 = min(a.x0, b.x0)\n    y0 = min(a.y0, b.y0)\n    x1 = max(a.x0 + a.width, b.x0 + b.width)\n    y1 = max(a.y0 + a.height, b.y0 + b.height)\n    return AbsoluteBox(x0, y0, x1 - x0, y1 - y0)\nend\n\n# The same type-signature is used for a box used to assign\n# a custom coordinate system to a canvas.\n\nstruct UnitBox{S,T,U,V}\n    x0::S\n    y0::T\n    width::U\n    height::V\n\n    leftpad::AbsoluteLength\n    rightpad::AbsoluteLength\n    toppad::AbsoluteLength\n    bottompad::AbsoluteLength\nend\n\n\"\"\"\n    UnitBox(x0, y0, width, height; leftpad=0mm, rightpad=0mm, toppad=0mm, bottompad=0mm)\n\nSpecifies the coordinate system for a [`context`](@ref), with origin `x0`, `y0`, plus `width`, `height`.\n\"\"\"\nUnitBox(x0, y0, width, height; leftpad=0mm, rightpad=0mm, toppad=0mm, bottompad=0mm) =\n        UnitBox{typeof(x0), typeof(y0), typeof(width), typeof(height)}(\n            x0, y0, width, height, leftpad, rightpad, toppad, bottompad)\n\n\"\"\"\n    UnitBox(width, height; leftpad=0mm, rightpad=0mm, toppad=0mm, bottompad=0mm)\n\nSpecifies the coordinate system for a [`context`](@ref), with origin `0, 0` plus `width`, `height`.\n\"\"\"\nfunction UnitBox(width, height; leftpad=0mm, rightpad=0mm, toppad=0mm, bottompad=0mm)\n    S, T = typeof(width), typeof(height)\n    UnitBox{S,T,S,T}(zero(S), zero(T), width, height, leftpad, rightpad, toppad, bottompad)\nend\n\n\"\"\"\n    UnitBox() = UnitBox(0.0, 0.0, 1.0, 1.0)\n\"\"\"\nUnitBox() = UnitBox(0.0, 0.0, 1.0, 1.0)\n\n# copy with substitution\nUnitBox(units::UnitBox;\n                 x0=nothing,\n                 y0=nothing,\n                 width=nothing,\n                 height=nothing,\n                 leftpad=nothing,\n                 rightpad=nothing,\n                 toppad=nothing,\n                 bottompad=nothing) =\n        UnitBox(ifelse(x0 === nothing,     units.x0,     x0),\n            ifelse(y0 === nothing,     units.y0,     y0),\n            ifelse(width === nothing,  units.width,  width),\n            ifelse(height === nothing, units.height, height),\n            leftpad   = ifelse(leftpad === nothing,   units.leftpad,   leftpad),\n            rightpad  = ifelse(rightpad === nothing,  units.rightpad,  rightpad),\n            toppad    = ifelse(toppad === nothing,    units.toppad,    toppad),\n            bottompad = ifelse(bottompad === nothing, units.bottompad, bottompad))\n\nMeasures.width(units::UnitBox) = units.width\nMeasures.height(units::UnitBox) = units.height\n\nispadded(units::UnitBox) = units.leftpad != 0mm || units.rightpad != 0mm ||\n        units.toppad != 0mm || units.bottompad != 0mm\n\nisxflipped(units::UnitBox{S, T, U, V}) where {S, T, U, V} = units.width < zero(U)\nisyflipped(units::UnitBox{S, T, U, V}) where {S, T, U, V} = units.height < zero(V)\nhasunits(::Type, x::Measure) = false\nhasunits(::Type{Length{u}}, x::Length{u, T}) where {u, T} = true\nhasunits(T::Type, x::Measures.BinaryOp) = hasunits(T, x.a) || hasunits(T, x.b)\n\n\n# Canvas Transforms\n# -----------------\n\n# Transform matrix in absolute coordinates\n\nabstract type Transform end\n\nstruct IdentityTransform <: Transform\nend\n\nstruct MatrixTransform <: Transform\n    M::Matrix{Float64}\nend\n\ncombine(a::IdentityTransform, b::IdentityTransform) = a\ncombine(a::IdentityTransform, b::MatrixTransform) = b\ncombine(a::MatrixTransform, b::IdentityTransform) = a\ncombine(a::MatrixTransform, b::MatrixTransform) = MatrixTransform(a.M * b.M)\n\n# Rotation about a point.\nstruct Rotation{P <: Vec}\n    theta::Float64\n    offset::P\nend\n\nRotation(theta::Number, offset::XYTupleOrVec) =\n        Rotation(convert(Float64, theta), (x_measure(offset[1]), y_measure(offset[2])))\n\n\"\"\"\n    Rotation(θ, x, y)\n\nRotate all forms in context around point `(x,y)` by angle `θ` in radians.  \n\"\"\"\nRotation(theta::Number, offset_x, offset_y) =\n        Rotation(convert(Float64, theta), (x_measure(offset_x), y_measure(offset_y)))\n\n\"\"\"\n    Rotation(θ)\n    \n`Rotation(θ)=Rotation(θ, 0.5w, 0.5h)`\n\"\"\"        \nRotation(theta::Number) = Rotation{Vec2}(convert(Float64, theta), (0.5w, 0.5h))\nRotation() = Rotation(0.0, (0.5w, 0.5h))\n\ncopy(rot::Rotation) = Rotation(rot)\n\nfunction convert(::Type{Transform}, rot::Rotation)\n    if rot.theta == 0.0\n        return IdentityTransform()\n    else\n        ct = cos(rot.theta)\n        st = sin(rot.theta)\n        x0 = rot.offset[1] - (ct * rot.offset[1] - st * rot.offset[2])\n        y0 = rot.offset[2] - (st * rot.offset[1] + ct * rot.offset[2])\n        return MatrixTransform([ct  -st  x0.value\n                                st   ct  y0.value\n                                0.0 0.0  1.0])\n    end\nend\n\n# Mirror about a point at a given angle\nmutable struct Mirror\n    theta::Float64\n    point::Vec\nend\n\n\"\"\"\n    Mirror(θ, x, y)\n\nMirror line passing through point `(x,y)` at angle `θ` (in radians).\n\"\"\"\nMirror(theta::Number, offset_x, offset_y) = Mirror(convert(Float64, theta), (offset_x, offset_y))\nMirror(theta::Number, offset::XYTupleOrVec) =\n        Mirror(convert(Float64, theta), (x_measure(offset[1]), y_measure(offset[2])))\n\n\"\"\"\n    Mirror(θ)\n        \n`Mirror(θ)=Mirror(θ, 0.5w, 0.5h)`\n\"\"\"        \nMirror(theta::Number) = Mirror(convert(Float64, theta), 0.5w, 0.5h)\nMirror() = Mirror(0.0, (0.5w, 0.5h))\n\n# copy constructor\nMirror(mir::Mirror) = Mirror(copy(mir.theta), copy(mir.offset))\n\nfunction convert(::Type{Transform}, mir::Mirror)\n    n = [cos(mir.theta), sin(mir.theta)]\n    x0 = mir.point[1]\n    y0 = mir.point[2]\n\n    offset = (2I - 2n*n') * [x0.value, y0.value]\n    scale  = (2n*n' - I)\n    M = vcat(hcat(scale, offset), [0 0 1])\n\n    return MatrixTransform(M)\nend\n\ncopy(mir::Mirror) = Mirror(mir)\n\n\n# Resolution\n# ----------\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length) = resolve(box, a)\nresolve_position(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:cx}) =\n        ((a.value - units.x0) / width(units)) * box.a[1]\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:cx}) =\n        abs(a.value / width(units)) * box.a[1]\nresolve_position(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:cy}) =\n        ((a.value - units.y0) / height(units)) * box.a[2]\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:cy}) =\n        abs(a.value / height(units)) * box.a[2]\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:sx}) =\n        a.value / width(units) * box.a[1]\n    \nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Length{:sy}) =\n        a.value / height(units) * box.a[2]\n    \n\nfunction resolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::Vec2)\n    xy = (resolve_position(box, units, t, p[1]) + box.x0[1],\n          resolve_position(box, units, t, p[2]) + box.x0[2])\n    return xy\nend\n\nfunction resolve(box::AbsoluteBox, units::UnitBox, t::MatrixTransform, p::Vec2)\n    x = resolve_position(box, units, t, p[1]) + box.x0[1]\n    y = resolve_position(box, units, t, p[2]) + box.x0[2]\n    xy = t.M * [x.value, y.value, 1]\n    return (xy[1]mm, xy[2]mm)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::BoundingBox) =\n        BoundingBox(resolve(box, units, IdentityTransform(), a.x0),\n            (resolve(box, units, t, a.a[1]), resolve(box, units, t, a.a[2])))\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Rotation) =\n        Rotation(a.theta, resolve(box, units, t, a.offset))\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Mirror) =\n        Mirror(a.theta, resolve(box, units, t, a.point))\n\nfunction resolve(box::AbsoluteBox, units::UnitBox, t::Transform, u::UnitBox)\n    if !ispadded(u)\n        return u\n    else\n        leftpad   = resolve(box, units, t, u.leftpad)\n        rightpad  = resolve(box, units, t, u.rightpad)\n        toppad    = resolve(box, units, t, u.toppad)\n        bottompad = resolve(box, units, t, u.bottompad)\n\n        # just give up trying to pad the units if it's impossible\n        if leftpad + rightpad >= box.a[1] ||\n           toppad + bottompad >= box.a[2]\n            return UnitBox(u.x0, u.y0, u.width, u.height)\n        end\n\n        width = u.width * (box.a[1] / (box.a[1] - leftpad - rightpad))\n        height = u.height * (box.a[2] / (box.a[2] - toppad - bottompad))\n        x0 = u.x0 - width * (leftpad / box.a[1])\n        y0 = u.y0 - height * (toppad / box.a[2])\n\n        return UnitBox(x0, y0, width, height)\n    end\nend\n\n# Equivalent to the resolve functions in Measures, but pass through the `units`\n# and `transform` parameters.\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, x::Neg) =\n        -resolve(box, units, t, x.a)\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, x::Add) =\n        resolve(box, units, t, x.a) + resolve(box, units, t, x.b)\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, x::Mul) =\n        resolve(box, units, t, x.a) * x.b\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, x::Div) =\n        resolve(box, units, t, x.a) / x.b\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, x::Min) =\n        min(resolve(box, units, t, x.a), resolve(box, units, t, x.b))\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, x::Max) =\n        max(resolve(box, units, t, x.a), resolve(box, units, t, x.b))\n\nresolve_position(box::AbsoluteBox, units::UnitBox, t::Transform, a) = resolve(box, units, t, a)\nresolve_position(box::AbsoluteBox, units::UnitBox, t::Transform, op::Add) =\n        resolve_position(box, units, t, op.a) + resolve_position(box, units, t, op.b)\n\n\n# Shear about a point at a given angle\nstruct Shear\n    shear::Float64\n    theta::Float64\n    point::Vec\nend\n\n\"\"\"\n    Shear(s, θ, x, y)\n\nShear line passing through point `(x,y)` at angle `θ` (in radians), with shear `s`.\n\"\"\"\nShear(shear::Number, theta::Number, offset_x, offset_y) = \n        Shear(float(shear), float(theta), (offset_x, offset_y))\n\nShear(shear::Number, theta::Number, offset::XYTupleOrVec) = \n        Shear(float(shear), float(theta), (x_measure(offset[1]), y_measure(offset[2])))\n\n\"\"\"\n    Shear(s, θ)\n\n`Shear(s, θ)=Shear(s, θ, 0.5w, 0.5h)`\n\"\"\"\nShear(shear::Number, theta::Number) = Shear(float(shear), float(theta), 0.5w, 0.5h)\n\n\nfunction convert(::Type{Transform}, shear::Shear)\n    x, y = shear.point[1].value, shear.point[2].value    \n    ct, st = cos(shear.theta), sin(shear.theta)\n    M1 = [1.0 0 x; 0 1 y; 0 0 1 ]\n    M2 = I + shear.shear .* [ct*st -ct*ct 0; st*st -ct*st 0; 0 0 0.0]\n    M3 = [1.0 0 -x; 0 1 -y; 0 0 1 ]\n    M = M1*M2*M3\n    return  MatrixTransform(M)\nend\n\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, a::Shear) =\n            Shear(a.shear, a.theta, resolve(box, units, t, a.point))\n\n\n# for transforming quadratic to cubic  \ncontrolpnt(anchor::XYTupleOrVec, control::XYTupleOrVec) = 0.333*anchor + 0.667*control\n"
  },
  {
    "path": "src/misc.jl",
    "content": "iszero(x::T) where T = x == zero(T)\n\nMaybe(T::Type) = Union{T, Nothing}\n\nfunction in_expr_args(ex::Expr)\n    ex.head === :in && return ex.args[1], ex.args[2]\n(ex.head === :call && length(ex.args) == 3 && ex.args[1] === :in) &&\n    return ex.args[2], ex.args[3]\n    error(\"Not an `in` expression\")\nend\n\n# Cycle-zip. Zip two or more arrays, cycling the short ones.\nfunction cyclezip(xs::AbstractArray...)\n    any(map(isempty, xs)) && return Any[]\n    n = maximum([length(x) for x in xs])\n    return takestrict(zip([Iterators.cycle(x) for x in xs]...), n)\nend\n\n\n# This generates optimized code for a reoccuring pattern in forms and patterns\n# that looks like:\n#\n#   return Circle([CirclePrimitive((x, y), x_measure(r))\n#                  for (x, y, r) in cyclezip(xs, ys, rs)])\n#\n# This macro does the equivalent with\n#\n#   return @makeform (x in xs, y in ys, r in rs),\n#                    CirclePrimitive((x, y), x_measure(r)))\n#\n# but much more efficiently.\nmacro makeform(args...)\n    @assert 1 <= length(args) <= 2\n    tag = length(args) == 2 ? args[2] : empty_tag\n    args = args[1]\n    @assert args.head == :tuple\n    @assert length(args.args) == 2\n    iterators, constructor = args.args\n\n    maxlen_ex = quote begin n = 0 end end\n    type_ex = quote begin end end\n    iter_ex = quote begin end end\n\n    for iterator in iterators.args\n        var, arr = in_expr_args(iterator::Expr)\n        ivar = Symbol(string(\"i_\", var))\n\n        push!(maxlen_ex.args,\n            quote\n                n = max(n, length($(arr)))\n                if isempty($(arr))\n                    error(\"Form cannot be constructed from an empty array\")\n                end\n            end)\n        push!(type_ex.args, quote\n              $(ivar) = 1\n              $(var) = $(arr)[1]\n              end)\n        push!(iter_ex.args, quote\n              $(ivar) += 1\n              $(ivar) = $(ivar) > length($(arr)) ? 1 : $(ivar)\n              $(var) = $(arr)[$(ivar)]\n        end)\n    end\n\n    esc(quote\n        $(maxlen_ex)\n\n        $(type_ex)\n        prim1 = $(constructor)\n        T = typeof(prim1)\n\n        primitives = Array{T}(undef, n)\n        primitives[1] = prim1\n        for i in 2:n\n            $(iter_ex)\n            primitives[i] = $(constructor)::T\n        end\n        Form{T}(primitives, $(tag))\n    end)\nend\n\n\n# TODO: Remove after replacing usage in property.jl\nmacro makeprimitives(args)\n    @assert args.head == :tuple\n    @assert length(args.args) == 3\n    T, iterators, constructor = args.args\n\n    maxlen_ex = quote begin n = 0 end end\n    iter_ex = quote begin end end\n\n    for iterator in iterators.args\n        var, arr = in_expr_args(iterator::Expr)\n\n        push!(maxlen_ex.args, quote\n            if isempty($(esc(arr)))\n                primitives = Array{$(T)}(undef, 0)\n                @goto done\n            end end)\n        push!(maxlen_ex.args, quote n = max(n, length($(esc(arr)))) end)\n        push!(iter_ex.args, quote\n            $(esc(var)) = $(esc(arr))[((i - 1) % length($(esc(arr)))) + 1]\n        end)\n    end\n\n    quote\n        $(maxlen_ex)\n        primitives = Array{$(esc(T))}(undef, n)\n        for i in 1:n\n            $(iter_ex)\n            primitives[i] = $(esc(constructor))\n        end\n        @label done\n        primitives\n    end\nend\n\n\nnarrow_polygon_point_types(point_arrays::AbstractArray{Vector{Tuple{XM, YM}}}) where {XM <: Measure, YM <: Measure} = (XM, YM)\n\ntype_params(p::Type{Tuple{XM, YM}}) where {XM, YM} = (Any, Any)\ntype_params(p::Type{Tuple{XM, YM}}) where {XM <: Measure, YM <: Measure} = (XM, YM)\ntype_params(p::Type{Union{}}) = (Any, Any)\n\nfunction narrow_polygon_point_types(point_arrays::AbstractArray)\n    if !isempty(point_arrays) && all([eltype(arr) <: Vec for arr in point_arrays])\n        xm, ym = type_params(eltype(point_arrays[1]))\n        for i in 2:length(point_arrays)\n            type_params(eltype(point_arrays[i])) == (xm, ym) || return Any, Any\n        end\n        return xm, ym\n    else\n        return Any, Any\n    end\nend\n\nfunction narrow_polygon_point_types(ring_arrays::Vector{Vector{Vector{P}}}) where P <: Tuple\n    type_params(p::Type{Tuple{XM, YM}}) where {XM, YM} = (XM, YM)\n\n    xm = nothing\n    ym = nothing\n    for point_arrays in ring_arrays\n        if !isempty(point_arrays) && all([eltype(arr) <: Vec for arr in point_arrays])\n            if xm == nothing\n                xm, ym = type_params(eltype(point_arrays[1]))\n            end\n            for i in 2:length(point_arrays)\n                type_params(eltype(point_arrays[i])) == (xm, ym) || Any, Any\n            end\n        end\n    end\n    if xm == nothing\n        return Any, Any\n    else\n        return xy, ym\n    end\nend\n\n\n# Hacks to make Dates time work as coordinates\n\nif !hasmethod(/, (Dates.Day, Dates.Day))\n    /(a::Dates.Day, b::Dates.Day) = a.value / b.value\nend\n\nif !hasmethod(/, (Dates.Day, Real))\n    /(a::Dates.Day, b::Real) = Dates.Day(round(Int64, (a.value / b)))\nend\n/(a::Dates.Day, b::AbstractFloat) = convert(Dates.Millisecond, a) / b\n\nif !hasmethod(/, (Dates.Millisecond, Dates.Millisecond))\n    /(a::Dates.Millisecond, b::Dates.Millisecond) = a.value / b.value\nend\n\nif !hasmethod(/, (Dates.Millisecond, Real))\n    /(a::Dates.Millisecond, b::Real) = Dates.Millisecond(round(Int64, (a.value / b)))\nend\n/(a::Dates.Millisecond, b::AbstractFloat) = Dates.Millisecond(round(Int64, (a.value / b)))\n\n\nif !hasmethod(-, (Dates.Date, Dates.DateTime))\n    -(a::Dates.Date, b::Dates.DateTime) = convert(Dates.DateTime, a) - b\nend\n\n+(a::Dates.Date, b::Dates.Millisecond) = convert(Dates.DateTime, a) + b\n\nif !hasmethod(-, (Dates.DateTime, Dates.Date))\n    -(a::Dates.DateTime, b::Dates.Date) = a - convert(Dates.DateTime, b)\nend\n\n\nif !hasmethod(/, (Dates.Day, Dates.Millisecond))\n    /(a::Dates.Day, b::Dates.Millisecond) = convert(Dates.Millisecond, a) / b\nend\n\nif !hasmethod(/, (Dates.Millisecond, Dates.Day))\n    /(a::Dates.Millisecond, b::Dates.Day) = a / convert(Dates.Millisecond, b)\nend\n\n\nfor T in [Dates.Hour, Dates.Minute, Dates.Second, Dates.Millisecond]\n    if !hasmethod(-, (Dates.Date, T))\n        @eval begin\n            -(a::Dates.Date, b::$(T)) = convert(Dates.DateTime, a) - b\n        end\n    end\nend\n\n\n*(a::AbstractFloat, b::Dates.Day) = Dates.Day(round(Int64, (a * b.value)))\n*(a::Dates.Day, b::AbstractFloat) = b * a\n*(a::AbstractFloat, b::Dates.Millisecond) = Dates.Millisecond(round(Int64, (a * b.value)))\n*(a::Dates.Millisecond, b::AbstractFloat) = b * a\n\n"
  },
  {
    "path": "src/pango.jl",
    "content": "# Estimation of text extents using pango.\n\nconst libpangocairo = Cairo.libpangocairo\nconst libpango = Cairo.libpango\nconst libgobject = Cairo.libgobject\n\n# Cairo text backend\nconst CAIRO_FONT_TYPE_TOY = 0\nconst CAIRO_FONT_TYPE_FT = 1\nconst CAIRO_FONT_TYPE_WIN32 = 2\nconst CAIRO_FONT_TYPE_QUARTZ = 3\nconst CAIRO_FONT_TYPE_USER = 4\n\n# Mirroring a #define in the pango header.\nconst PANGO_SCALE = 1024.0\n\n# Use the freetype/fontconfig backend to find the best match to a font\n# description.\n#\n# Args:\n#   desc: A string giving the font description. This can\n#         also provide a comma-separated list of families. E.g.,\n#         \"Helvetica, Arial 10\"\n#\n# Returns:\n#   A pointer to a PangoFontDescription with the closest match.\n#\nlet available_font_families = Set{AbstractString}()\n    for font_pattern in Fontconfig.list()\n        push!(available_font_families, lowercase(Fontconfig.format(font_pattern, \"%{family}\")))\n    end\n\n    meta_families = Set([\"serif\", \"sans\", \"sans-serif\", \"monospace\", \"cursive\", \"fantasy\"])\n\n    global match_font\n    function match_font(families::AbstractString, size::Float64)\n        matched_family = \"sans-serif\"\n        for family in [lowercase(strip(family, [' ', '\"', '\\''])) for family in split(families, ',')]\n            if family in available_font_families || family in meta_families\n                matched_family = family\n                break\n            end\n        end\n        family = Fontconfig.format(match(Fontconfig.Pattern(family=matched_family)), \"%{family}\")\n        desc = @sprintf(\"%s %fpx\", family, size)\n        fd = ccall((:pango_font_description_from_string, libpango), Ptr{Cvoid}, (Ptr{UInt8},), desc)\n        return fd\n    end\nend\n\n# Thin wrapper for a pango_layout object.\nmutable struct PangoLayout\n    layout::Ptr{Cvoid}\nend\n\nfunction PangoLayout()\n    layout = ccall((:pango_layout_new, libpango),\n                   Ptr{Cvoid}, (Ptr{Cvoid},), pango_cairo_ctx[])\n    # TODO: finalizer?\n\n    PangoLayout(layout)\nend\n\n# Set the layout's font.\nfunction pango_set_font(pangolayout::PangoLayout, family::AbstractString, pts::Number)\n    fd = match_font(family, pts)\n    ccall((:pango_layout_set_font_description, libpango),\n          Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), pangolayout.layout, fd)\nend\n\n# Find the width and height of a string.\n#\n# Args:\n#   pangolayout: a pango layout object, with font, etc, set.\n#   text: a string we might like to draw.\n#\n# Returns:\n#   A (width, height) tuple in absolute units.\n#\nfunction pango_text_extents(pangolayout::PangoLayout, text::AbstractString)\n    textarray = convert(String, text)\n    ccall((:pango_layout_set_markup, libpango),\n          Cvoid, (Ptr{Cvoid}, Ptr{UInt8}, Int32),\n          pangolayout.layout, textarray, sizeof(textarray))\n\n    extents = Array{Int32}(undef, 4)\n    ccall((:pango_layout_get_extents, libpango),\n          Cvoid, (Ptr{Cvoid}, Ptr{Int32}, Ptr{Int32}),\n          pangolayout.layout, extents, C_NULL)\n\n    width, height = (extents[3] / PANGO_SCALE)pt, (extents[4] / PANGO_SCALE)pt\nend\n\n# Find the minimum width and height needed to fit any of the given strings.\n#\n# (A \"user-friendly\" wrapper for pango_text_extents.)\n#\n# Args:\n#   font_family: Something like a font name.\n#   pts: Font size in points.\n#   texts: One or more strings.\n#\n# Returns:\n#   A (width, height) tuple in absolute units.\n#\nfunction max_text_extents(font_family::AbstractString, pts::Float64, texts::AbstractString...)\n    pango_set_font(pangolayout[]::PangoLayout, font_family, pts)\n    max_width  = 0mm\n    max_height = 0mm\n    for text in texts\n        (width, height) = pango_text_extents(pangolayout[]::PangoLayout, text)\n        max_width  = max_width.value  < width.value  ? width  : max_width\n        max_height = max_height.value < height.value ? height : max_height\n    end\n    return (max_width, max_height)\nend\n\n# Same as max_text_extents but with font_size in arbitrary absolute units.\nfunction max_text_extents(font_family::AbstractString, size::Measure,\n                      texts::AbstractString...)\n    isa(size, AbsoluteLength) || error(\"text_extents requries font size be in absolute units\")\n    return max_text_extents(font_family, size/pt, texts...)\nend\n\n# Return an array with the extents of each element\nfunction text_extents(font_family::AbstractString, pts::Float64, texts::AbstractString...)\n    pango_set_font(pangolayout[]::PangoLayout, font_family, pts)\n    return [pango_text_extents(pangolayout[]::PangoLayout, text) for text in texts]\nend\n\ntext_extents(font_family::AbstractString, size::Measure, texts::AbstractString...) =\n        text_extents(font_family, size/pt, texts...)\n\nconst pango_attrs = [\n    (:PANGO_ATTR_LANGUAGE,        :PangoAttrLanguage),\n    (:PANGO_ATTR_FAMILY,          :PangoAttrString),\n    (:PANGO_ATTR_STYLE,           :PangoAttrInt),\n    (:PANGO_ATTR_WEIGHT,          :PangoAttrInt),\n    (:PANGO_ATTR_VARIANT,         :PangoAttrInt),\n    (:PANGO_ATTR_STRETCH,         :PangoAttrInt),\n    (:PANGO_ATTR_SIZE,            :PangoAttrSize),\n    (:PANGO_ATTR_FONT_DESC,       :PangoAttrFontDesc),\n    (:PANGO_ATTR_FOREGROUND,      :PangoAttrColor),\n    (:PANGO_ATTR_BACKGROUND,      :PangoAttrColor),\n    (:PANGO_ATTR_UNDERLINE,       :PangoAttrInt),\n    (:PANGO_ATTR_STRIKETHROUGH,   :PangoAttrInt),\n    (:PANGO_ATTR_RISE,            :PangoAttrInt),\n    (:PANGO_ATTR_SHAPE,           :PangoAttrShape),\n    (:PANGO_ATTR_SCALE,           :PangoAttrFloat),\n    (:PANGO_ATTR_FALLBACK,        :PangoAttrFallback),\n    (:PANGO_ATTR_LETTER_SPACING,  :PangoAttrInt),\n    (:PANGO_ATTR_UNDERLINE_COLOR, :PangoAttrColor),\n    (:PANGO_ATTR_ABSOLUTE_SIZE,   :PangoAttrSize),\n    (:PANGO_ATTR_GRAVITY,         :PangoAttrInt),\n    (:PANGO_ATTR_GRAVITY_HINT,    :PangoAttrInt)]\n\nfor (i, (attr, t)) in enumerate(pango_attrs)\n    @eval begin\n        const $attr = $i\n    end\nend\n\nconst PANGO_STYLE_NORMAL  = 0\nconst PANGO_STYLE_OBLIQUE = 1\nconst PANGO_STYLE_ITALIC  = 2\n\nconst PANGO_WEIGHT_THIN = 100\nconst PANGO_WEIGHT_ULTRALIGHT = 200\nconst PANGO_WEIGHT_LIGHT = 300\nconst PANGO_WEIGHT_BOOK = 380\nconst PANGO_WEIGHT_NORMAL = 400\nconst PANGO_WEIGHT_MEDIUM = 500\nconst PANGO_WEIGHT_SEMIBOLD = 600\nconst PANGO_WEIGHT_BOLD = 700\nconst PANGO_WEIGHT_ULTRABOLD = 800\nconst PANGO_WEIGHT_HEAVY = 900\nconst PANGO_WEIGHT_ULTRAHEAVY = 1000\n\n# A Julia manifestation of a set of pango attributes\nmutable struct PangoAttr\n    rise::Maybe(Int)\n    scale::Maybe(Float64)\n    style::Maybe(Int)\n    weight::Maybe(Int)\nend\n\nPangoAttr() = PangoAttr(nothing, nothing, nothing, nothing)\n\nisempty(attr::PangoAttr) = all([getfield(attr, name) === nothing for name in fieldnames(PangoAttr)])\n\n# Set an attribute in a PangoAttr\n#\n# Args:\n#   attr: A PangoAttr to update.\n#   attr_name: A pango attribute name (e.g., :PANGO_ATTR_RISE)\n#   value: The value with which to update the attribute.\n#\n# Returns:\n#   The attr.\nfunction update_pango_attr(attr::PangoAttr, attr_name::Symbol, value)\n    if attr_name == :PANGO_ATTR_RISE\n        attr.rise = Int64(value)\n    elseif attr_name == :PANGO_ATTR_SCALE\n        attr.scale = value\n    elseif attr_name == :PANGO_ATTR_STYLE\n        attr.style = Int64(value)\n    elseif attr_name == :PANGO_ATTR_WEIGHT\n        attr.weight = Int64(value)\n    end\n    attr\nend\n\n# Unpack the first part of a pango attribute\n#\n# Args:\n#   ptr: A pointer to a PangoAttribute\n#   t: The type of the attribute (e.g. PangoAttrInt)\n#\n# Returns:\n#   A tuple of the form (start_idx, end_idx, value)\n#\nfunction unpack_pango_attr(ptr::Ptr{Cvoid}, t::Symbol)\n    ptr += sizeof(Ptr{Cvoid}) # skip `klass` pointer\n    ptr = convert(Ptr{UInt32}, ptr)\n    idx = unsafe_wrap(Array, ptr, (2,), own=false)\n    ptr += 2 * sizeof(UInt32)\n    ptr = convert(Ptr{Cvoid}, ptr)\n\n    if t == :PangoAttrInt\n        value = unpack_pango_int(ptr)\n    elseif t == :PangoAttrFloat\n        value = unpack_pango_float(ptr)\n    else\n        value = nothing\n    end\n\n    (idx[1], idx[2], value)\nend\n\n# Unpack a pango int attribute.\n#\n# Args:\n#   ptr: A point to a PangoAttrInt plus sizeof(PangoAttribute)\n#\n# Returns:\n#   And int value.\nunpack_pango_int(ptr::Ptr{Cvoid}) = unsafe_wrap(Array, convert(Ptr{Int32}, ptr), (1,), own=false)[1]\nunpack_pango_float(ptr::Ptr{Cvoid}) = unsafe_wrap(Array, convert(Ptr{Float64}, ptr), (1,), own=false)[1]\n\n#function unpack_pango_size(ptr::Ptr{Cvoid})\n    #ptr = convert(Ptr{Int32}, ptr)\n    #size = point_to_array(ptr, (1,))[1]\n    #ptr = convert(Ptr{UInt32}, ptr)\n    #absolute = point_to_array(ptr, (1,))[1] & 0x1\n\n    #println(size, absolute)\n    #nothing\n#end\n\n# TODO: unpacking other attributes\n\n# Unpack a list of pango attributes\n#\n# Args:\n#   ptr: A pointer to a PangoAttrList\n#\n# Returns:\n#   A list of the form [(start_idx, attribute), ...] in which the start_idx\n#   values are increasing and the attribute is a set of attributes that\n#   should be applied starting at that position.\n#\nfunction unpack_pango_attr_list(ptr::Ptr{Cvoid})\n    attr_it = ccall((:pango_attr_list_get_iterator, libpango),\n                    Ptr{Cvoid}, (Ptr{Cvoid},), ptr)\n\n    # Alias some ugly C calls.\n    attr_it_next = () -> ccall((:pango_attr_iterator_next, libpango),\n                               Int32, (Ptr{Cvoid},), attr_it)\n\n    attr_it_get = attr_name -> ccall((:pango_attr_iterator_get, libpango),\n                                     Ptr{Cvoid}, (Ptr{Cvoid}, Int32),\n                                     attr_it, eval(attr_name))\n\n    attr_it_range = () -> begin\n        start_idx = Array{Int32}(undef, 1)\n        end_idx = Array{Int32}(undef, 1)\n        ccall((:pango_attr_iterator_range, libpango),\n              Cvoid, (Ptr{Cvoid}, Ptr{Int32}, Ptr{Int32}),\n              attr_it, start_idx, end_idx)\n        (start_idx[1], end_idx[1])\n    end\n\n\n    attrs = Array{Tuple{Int, PangoAttr}}(undef, 0)\n\n    while attr_it_next() != 0\n        attr = PangoAttr()\n        local start_idx\n\n        for (attr_name, attr_type) in pango_attrs\n            c_attr = attr_it_get(attr_name)\n            (start_idx, end_idx) = attr_it_range()\n\n            if c_attr != C_NULL\n                (_, _, value) = unpack_pango_attr(c_attr, attr_type)\n                update_pango_attr(attr, attr_name, value)\n            end\n        end\n\n        push!(attrs, (start_idx, attr))\n    end\n\n    ccall((:pango_attr_iterator_destroy, libpango),\n          Cvoid, (Ptr{Cvoid},), attr_it)\n\n  attrs\nend\n\nfunction pango_to_svg(text::AbstractString)\n    # TODO: do c_stripped_text and c_attr_list need to be freed?\n    c_stripped_text = Ref{Ptr{UInt8}}()\n    c_attr_list = Ref{Ptr{Cvoid}}()\n\n    output = IOBuffer()\n    output_line = IOBuffer()\n\n    textlines = split(text, \"\\n\")\n    carriage_shift = carriage_shift0 \n    for (itextline,textline) in enumerate(textlines)\n        ret = ccall((:pango_parse_markup, libpango),\n                    Int32, (Cstring, Int32, UInt32, Ptr{Ptr{Cvoid}},\n                            Ptr{Ptr{UInt8}}, Ptr{UInt32}, Ptr{Cvoid}),\n                    textline, -1, 0, c_attr_list, c_stripped_text,\n                    C_NULL, C_NULL)\n        ret == 0 && error(\"Could not parse pango markup.\")\n\n        input = codeunits(unsafe_string(c_stripped_text[]))\n\n        lastpos = 1\n        baseline_shift = 0.0\n        sup = sub = false\n        open_tag = false\n\n        for (idx, attr) in unpack_pango_attr_list(c_attr_list[])\n            write(output_line, input[lastpos:idx])\n            lastpos = idx + 1\n\n            closing_tag = isempty(attr)\n\n            open_tag && !closing_tag && write(output_line, \"</tspan>\")\n \n            if closing_tag\n                write(output_line, \"</tspan>\")\n            else\n                write(output_line, \"<tspan\")\n\n                # pango doesn't know the real font size here it seems,\n                # so ignore it's absolute rise and use a relative shift\n                # that matches fontfallback's behavior\n                if attr.rise !== nothing\n                    if attr.rise<0\n                        baseline_shift = +supsub_shift\n                        sub = true\n                    else\n                        baseline_shift = -supsub_shift\n                        sup = true\n                    end\n                    @printf(output_line, \" dy=\\\"%0.1fem\\\"\", baseline_shift)\n                end\n\n                if attr.scale !== nothing\n                    @printf(output_line, \" font-size=\\\"%0.4f%%\\\"\", 100.0 * attr.scale)\n                    baseline_shift *= attr.scale\n                end\n\n                if attr.style !== nothing\n                    if attr.style == PANGO_STYLE_NORMAL\n                        @printf(output_line, \" font-style=\\\"%s\\\"\", \"normal\")\n                    elseif attr.style == PANGO_STYLE_OBLIQUE\n                        @printf(output_line, \" font-style=\\\"%s\\\"\", \"oblique\")\n                    elseif attr.style == PANGO_STYLE_ITALIC\n                        @printf(output_line, \" font-style=\\\"%s\\\"\", \"italic\")\n                    end\n                end\n\n                attr.weight === nothing || @printf(output_line,\n                                                   \" font-weight=\\\"%d\\\"\",\n                                                   attr.weight)\n\n                write(output_line, \">\")\n            end\n\n            if closing_tag && baseline_shift != 0.0\n                if lastpos < length(input)\n                    @printf(output_line, \"<tspan dy=\\\"%0.4fem\\\">\", -baseline_shift)\n                    baseline_shift = 0.0\n                    open_tag = true\n                else\n                    open_tag = false\n                end \n            end     \n        end\n        write(output_line, input[lastpos:end])\n        open_tag && write(output_line, \"</tspan>\")\n        itextline>1 && @printf(output,\n                               \"<tspan x=\\\"0\\\" dy=\\\"%0.4fem\\\">\",\n                               carriage_shift + sup*carriage_shift_supsub)\n        write(output, String(take!(output_line)))\n        itextline>1 && write(output, \"</tspan>\")\n        carriage_shift = carriage_shift0 - baseline_shift + sub*carriage_shift_supsub\n    end\n    String(take!(output))\nend\n"
  },
  {
    "path": "src/pgf_backend.jl",
    "content": "mutable struct PGFPropertyFrame\n    # Vector properties in this frame.\n    vector_properties::Dict{Type, Property}\n\n    # True if this property frame has scalar properties. Scalar properties are\n    # emitted as a group {scope} that must be closed when the frame is popped.\n    has_scalar_properties::Bool\nend\n\nPGFPropertyFrame() = PGFPropertyFrame(Dict{Type, Property}(), false)\n\nmutable struct PGF <: Backend\n    # Image size in millimeters.\n    width::AbsoluteLength\n    height::AbsoluteLength\n\n    # Output stream.\n    out::IO\n\n    # Fill properties cannot be \"cleanly\" applied to\n    # multiple form primitives.  It must be applied\n    # each time an object is drawn\n    fill::Union{Color, Nothing}\n    fill_opacity::Float64\n\n    stroke::Union{Color, Nothing}\n    stroke_opacity::Float64\n\n    fontfamily::Union{AbstractString, Nothing}\n    fontsize::Float64\n\n    # Current level of indentation.\n    indentation::Int\n\n    # Output buffer.  We want the ability to add to the beginning of\n    buf::IOBuffer\n\n    # Skip drawing if visible is false\n    visible::Bool\n\n    # Have not found an easy way to define color as\n    # a draw parameter.  Whenever we encounter a color, we add it to the\n    # color_set set.  That way, we can write out all the color\n    # definitions at the same time.\n    color_set::Set{Color}\n\n    # Stack of property frames (groups of properties) currently in effect.\n    property_stack::Vector{PGFPropertyFrame}\n\n    # SVG forbids defining the same property twice, so we have to keep track\n    # of which vector property of which type is in effect. If two properties of\n    # the same type are in effect, the one higher on the stack takes precedence.\n    vector_properties::Dict{Type, Union{Property, Nothing}}\n\n    # Clip-paths that need to be defined at the end of the document.\n    # Not quite sure how to deal with clip paths yet\n    clippath::Union{ClipPrimitive, Nothing}\n    # clippaths::Dict{ClipPrimitive, String}\n\n    # True when finish has been called and no more drawing should occur\n    finished::Bool\n\n    # Backend is responsible for opening/closing the file\n    ownedfile::Bool\n\n    # Filename when ownedfile is true\n    filename::Union{AbstractString, Nothing}\n\n    # Emit the graphic on finish when writing to a buffer.\n    emit_on_finish::Bool\n\n    # Emit only the tikzpicture environment\n    only_tikz::Bool\n\n    # Use default TeX fonts instead of fonts specified by the theme.\n    texfonts::Bool\nend\n\nfunction PGF(out::IO,\n             width::AbsoluteLength,\n             height::AbsoluteLength,\n             emit_on_finish::Bool=true,\n             only_tikz = false;\n\n             texfonts = false,\n             fill = default_fill_color,\n             fill_opacity = 1.0,\n             stroke = default_stroke_color,\n             stroke_opacity = 1.0,\n             fontfamily = nothing,\n             fontsize = 12.0,\n             indentation = 0,\n             buf = IOBuffer(),\n             visible = true,\n             color_set = Set{Color}([colorant\"black\"]),\n             property_stack = Array{PGFPropertyFrame}(undef, 0),\n             vector_properties = Dict{Type, Union{Property, Nothing}}(),\n             clippath = nothing,\n             finished = false,\n             ownedfile = false,\n             filename = nothing)\n    PGF(width, height, out,\n        fill, fill_opacity,\n        stroke, stroke_opacity,\n        fontfamily, fontsize,\n        indentation,\n        buf,\n        visible,\n        color_set,\n        property_stack,\n        vector_properties,\n        clippath,\n        finished,\n        ownedfile,\n        filename,\n        emit_on_finish,\n        only_tikz,\n        texfonts)\nend\n\n# Write to a file.\n\"\"\"\n    PGF([output::Union{IO,AbstractString}], width=√200cm, height=10cm, only_tikz=false; texfonts=false) -> Backend\n\nCreate a Portable Graphics Format backend.  The output is normally passed to\n[`draw`](@ref).   Specify a filename using a string as the first argument.  If\n`only_tikz` is true then the output is a \"tikzpicture\", otherwise the output is a\ncomplete latex document with headers and footers.  If `texfonts` is false,\ninclude \"\\\\usepackage{fontspec}\" in the headers.\n\n# Examples\n```\nc = compose(context(), rectangle())\ndraw(PGF(\"myplot.tex\", 10cm, 5cm, true, texfonts=true), c)\n```\n\"\"\"\nPGF(filename::AbstractString, width=default_graphic_width, height=default_graphic_height,\n            only_tikz=false; texfonts=false) =\n        PGF(open(filename, \"w\"), width, height, true, only_tikz;\n            texfonts = texfonts, ownedfile=true, filename=filename)\n\n# Write to buffer.\nPGF(width::MeasureOrNumber=default_graphic_width, height::MeasureOrNumber=default_graphic_height,\n            emit_on_finish::Bool=true, only_tikz=false; texfonts=false) =\n        PGF(IOBuffer(), width, height, emit_on_finish, only_tikz, texfonts=texfonts)\n\nfunction (img::PGF)(x)\n    draw(img, x)\nend\n\nfunction finish(img::PGF)\n    img.finished && return\n\n    while !isempty(img.property_stack)\n        pop_property_frame(img)\n    end\n\n    # if length(img.clippaths) > 0\n    #     for (clippath, id) in img.clippaths\n    #         write(img.out, \"\\\\clip\")\n    #         print_svg_path(img.out, clippath.points)\n    #         write(img.out, \";\\n\")\n    #     end\n    # end\n\n    writeheader(img)\n    writecolors(img)\n    write(img.out, take!(img.buf))\n    write(img.out,\n        \"\"\"\n        \\\\end{tikzpicture}\n        \"\"\"\n        )\n    !img.only_tikz &&  write(img.out,\n        \"\"\"\n        \\\\end{document}\n        \"\"\"\n        )\n\n    hasmethod(flush, (typeof(img.out),)) && flush(img.out)\n    close(img.buf)\n\n    img.ownedfile && close(img.out)\n\n    img.finished = true\n\n    # If we are writing to a buffer. Collect the string and emit it.\n    img.emit_on_finish && typeof(img.out) == IOBuffer && display(img)\nend\n\nisfinished(img::PGF) = img.finished\nroot_box(img::PGF) = BoundingBox(0mm, 0mm, img.width, img.height)\n\nfunction writeheader(img::PGF)\n    !img.only_tikz && write(img.out,\n        \"\"\"\n        \\\\documentclass{minimal}\n        \\\\usepackage{pgfplots}\n        $(img.texfonts ? \"\" : \"\\\\usepackage{fontspec}\")\n        \\\\usepackage{amsmath}\n        \\\\usepackage[active,tightpage]{preview}\n        \\\\PreviewEnvironment{tikzpicture}\n        \\\\begin{document}\n        \"\"\")\n    write(img.out, \"\"\"\n        \\\\begin{tikzpicture}[x=1mm,y=-1mm]\n        \"\"\")\n    return img\nend\n\nfunction writecolors(img::PGF)\n    for color in img.color_set\n        @printf(img.out, \"\\\\definecolor{mycolor%s}{rgb}{%s,%s,%s}\\n\",\n            hex(color),\n            svg_fmt_float(color.r),\n            svg_fmt_float(color.g),\n            svg_fmt_float(color.b)\n            )\n    end\nend\n\nfunction print_pgf_path(out::IO, points::Vector{AbsoluteVec2},\n                        bridge_gaps::Bool=false)\n    isfirst = true\n    for point in points\n        x, y = point[1].value, point[2].value\n        if !(isfinite(x) && isfinite(y))\n            isfirst = true\n            continue\n        end\n\n        if isfirst\n            isfirst = false\n            @printf(out, \" (%s,%s)\",\n                    svg_fmt_float(x),\n                    svg_fmt_float(y))\n        else\n            @printf(out, \" -- (%s,%s)\",\n                    svg_fmt_float(x),\n                    svg_fmt_float(y))\n        end\n    end\nend\n\nfunction get_vector_properties(img::PGF, idx::Int)\n    props_str = String[]\n    modifiers = String[]\n    for (propertytype, property) in img.vector_properties\n        (property === nothing) && continue\n        primitives = property.primitives\n        idx > length(primitives) &&\n                error(\"Vector form and vector property differ in length. Can't distribute.\")\n        push_property!(props_str, img, primitives[idx])\n    end\n\n    if img.fill !== nothing\n        push!(props_str, string(\"fill=mycolor\",hex(img.fill)))\n        img.fill_opacity < 1.0 &&\n                push!(props_str, string(\"fill opacity=\",svg_fmt_float(img.fill_opacity)))\n    end\n\n    if img.stroke !== nothing\n        push!(props_str, string(\"draw=mycolor\",hex(img.stroke)))\n        img.stroke_opacity < 1.0 &&\n                push!(props_str, string(\"draw opacity=\",svg_fmt_float(img.stroke_opacity)))\n    end\n\n    return modifiers, props_str\nend\n\npush_property!(props_str, img::PGF, property::StrokeDashPrimitive) =\n        isempty(property.value) ||\n            push!(props_str, string(\"dash pattern=\", join(map( v -> join(v, \" \"),\n                zip(Iterators.cycle([\"on\", \"off\"]),\n                map(v -> string(svg_fmt_float(v.value), \"mm\"), property.value)) ),\" \")))\n\nfunction push_property!(props_str, img::PGF, property::StrokePrimitive)\n    if isa(property.color, TransparentColor)\n        img.stroke = color(property.color)\n        img.stroke_opacity = property.color.alpha\n    else\n        img.stroke = property.color\n    end\n\n    (img.stroke === nothing) || push!(img.color_set, convert(RGB, property.color))\nend\n\nfunction push_property!(props_str, img::PGF, property::FillPrimitive)\n    if isa(property.color, TransparentColor)\n        img.fill = color(property.color)\n        property.color.alpha<1.0 && (img.fill_opacity = property.color.alpha)\n    else\n        img.fill = property.color\n    end\n\n    (img.fill === nothing) || push!(img.color_set, convert(RGB, property.color))\nend\n\npush_property!(props_str, img::PGF, property::VisiblePrimitive) =\n        img.visible = property.value\npush_property!(props_str, img::PGF, property::LineWidthPrimitive) =\n        push!(props_str, string(\"line width=\", svg_fmt_float(property.value.value), \"mm\"))\n\npgf_fmt_linecap(::LineCapButt) = \"butt\"\npgf_fmt_linecap(::LineCapSquare) = \"rect\"\npgf_fmt_linecap(::LineCapRound) = \"round\"\n\npush_property!(props_str, img::PGF, property::StrokeLineCapPrimitive) =\n        push!(props_str, string(\"line cap=\", pgf_fmt_linecap(property.value)))\npush_property!(props_str, img::PGF, property::StrokeLineJoinPrimitive) =\n        push!(props_str, string(\"line join=\", svg_fmt_linejoin(property.value)))\npush_property!(props_str, img::PGF, property::FillOpacityPrimitive) =\n        img.fill_opacity = property.value\npush_property!(props_str, img::PGF, property::StrokeOpacityPrimitive) =\n        img.stroke_opacity = property.value\n# Can only only work with one font family for now\npush_property!(props_str, img::PGF, property::FontPrimitive) =\n        img.fontfamily = strip(split(escape_string(property.family),',')[1],'\\'')\npush_property!(props_str, img::PGF, property::FontSizePrimitive) =\n        img.fontsize = property.value.value\n# Not quite sure how to handle clipping yet, stub for now\npush_property!(props_str, img::PGF, property::ClipPrimitive) =\n        img.clippath = property\n\n# Stubs for SVG and JS specific properties\npush_property!(props_str, img::PGF, property::JSIncludePrimitive) = nothing\npush_property!(props_str, img::PGF, property::JSCallPrimitive) = nothing\npush_property!(props_str, img::PGF, property::SVGClassPrimitive) = nothing\npush_property!(props_str, img::PGF, property::SVGAttributePrimitive) = nothing\niswithjs(img::PGF) = false\niswithousjs(img::PGF) = true\n\npush_property!(props_str, img::PGF, property::ArrowPrimitive) =\n        push!(props_str, \"arrows=->\")\n\n\n# Form Drawing\n# ------------\n\nfunction draw(img::PGF, form::Form)\n    for (idx, primitive) in enumerate(form.primitives)\n        draw(img, primitive, idx)\n    end\nend\n\nfunction draw(img::PGF, prim::LinePrimitive, idx::Int)\n    n = length(prim.points)\n    n <= 1 && return\n\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] \", join(props, \",\"));\n    print_pgf_path(img.buf, prim.points, true)\n    write(img.buf, \";\\n\")\nend\n\nfunction draw(img::PGF, prim::RectanglePrimitive, idx::Int)\n    width = max(prim.width.value, 0.01)\n    height = max(prim.height.value, 0.01)\n\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] \", join(props, \",\"));\n    @printf(img.buf, \"(%s,%s) rectangle +(%s,%s);\\n\",\n            svg_fmt_float(prim.corner[1].value),\n            svg_fmt_float(prim.corner[2].value),\n            svg_fmt_float(width),\n            svg_fmt_float(height))\nend\n\nfunction draw(img::PGF, prim::PolygonPrimitive, idx::Int)\n    n = length(prim.points)\n    n <= 1 && return\n\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] \", join(props, \",\"))\n    print_pgf_path(img.buf, prim.points, true)\n    write(img.buf, \" -- cycle;\\n\")\nend\n\nfunction draw(img::PGF, prim::CirclePrimitive, idx::Int)\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] \", join(props, \",\"))\n    @printf(img.buf, \"(%s,%s) circle [radius=%s];\\n\",\n        svg_fmt_float(prim.center[1].value),\n        svg_fmt_float(prim.center[2].value),\n        svg_fmt_float(prim.radius.value))\nend\n\nfunction draw(img::PGF, prim::EllipsePrimitive, idx::Int)\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n\n    cx = prim.center[1].value\n    cy = prim.center[2].value\n    rx = sqrt((prim.x_point[1].value - cx)^2 +\n              (prim.x_point[2].value - cy)^2)\n    ry = sqrt((prim.y_point[1].value - cx)^2 +\n              (prim.y_point[2].value - cy)^2)\n    theta = rad2deg(atan(prim.x_point[2].value - cy,\n                         prim.x_point[1].value - cx))\n\n    all(isfinite,[cx, cy, rx, ry, theta]) || return\n\n    modifiers, props = get_vector_properties(img, idx)\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] \", join(props, \",\"))\n    @printf(img.buf, \"(%s,%s) circle [x radius=%s, y radius=%s\",\n        svg_fmt_float(cx),\n        svg_fmt_float(cy),\n        svg_fmt_float(rx),\n        svg_fmt_float(ry))\n    abs(theta) > 1e-4 && @printf(img.buf, \" rotate=%s\", svg_fmt_float(theta))\n    write(img.buf, \"];\\n\")\nend\n\nfunction draw(img::PGF, prim::CurvePrimitive, idx::Int)\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] \", join(props, \",\"))\n    @printf(img.buf, \"(%s,%s) .. controls (%s,%s) and (%s,%s) .. (%s,%s);\\n\",\n        svg_fmt_float(prim.anchor0[1].value),\n        svg_fmt_float(prim.anchor0[2].value),\n        svg_fmt_float(prim.ctrl0[1].value),\n        svg_fmt_float(prim.ctrl0[2].value),\n        svg_fmt_float(prim.ctrl1[1].value),\n        svg_fmt_float(prim.ctrl1[2].value),\n        svg_fmt_float(prim.anchor1[1].value),\n        svg_fmt_float(prim.anchor1[2].value))\nend\n\nfunction draw(img::PGF, prim::TextPrimitive, idx::Int)\n    # Rotation direction is reversed!\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n    push!(props, string(\n        \"rotate around={\",\n            svg_fmt_float(-rad2deg(prim.rot.theta)),\n            \": (\",\n            svg_fmt_float(prim.rot.offset[1].value - prim.position[1].value),\n            \",\",\n            svg_fmt_float(prim.rot.offset[2].value - prim.position[2].value),\n            \")}\"))\n    if prim.halign === hcenter\n        push!(props, \"inner sep=0.0\")\n    elseif prim.halign === hright\n        push!(props, \"left,inner sep=0.0\")\n    else\n        push!(props, \"right,inner sep=0.0\")\n    end\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\draw (%s,%s) node [%s]{\\\\fontsize{%smm}{%smm}\\\\selectfont \\$%s\\$};\\n\",\n        svg_fmt_float(prim.position[1].value),\n        svg_fmt_float(prim.position[2].value),\n        replace(join(props, \",\"), \"fill\"=>\"text\"),\n        svg_fmt_float(img.fontsize),\n        svg_fmt_float(1.2*img.fontsize),\n        pango_to_pgf(prim.value)\n    )\nend\n\nfunction indent(img::PGF)\n    for i in 1:img.indentation\n        write(img.buf, \"  \")\n    end\nend\n\nfunction push_property_frame(img::PGF, properties::Vector{Property})\n    isempty(properties) && return\n\n    frame = PGFPropertyFrame()\n    applied_properties = Set{Type}()\n    scalar_properties = Array{Property}(undef, 0)\n    for property in properties\n        if !isrepeatable(property) && (typeof(property) in applied_properties)\n            continue\n        elseif isscalar(property)\n            push!(scalar_properties, property)\n            push!(applied_properties, typeof(property))\n            frame.has_scalar_properties = true\n            img.vector_properties[typeof(property)] = nothing\n        else\n            frame.vector_properties[typeof(property)] = property\n            img.vector_properties[typeof(property)] = property\n        end\n    end\n    push!(img.property_stack, frame)\n    isempty(scalar_properties) && return\n\n    write(img.buf, \"\\\\begin{scope}\\n\")\n    prop_str = AbstractString[]\n    for property in scalar_properties\n        push_property!(prop_str, img, property.primitives[1])\n    end\n    if length(prop_str) > 0\n        @printf(img.buf, \"[%s]\\n\", join(prop_str, \",\"))\n    end\n    if img.clippath !== nothing\n        write(img.buf, \"\\\\clip \")\n        print_pgf_path(img.buf, img.clippath.points)\n        write(img.buf, \";\\n\")\n    end\n    if (!img.texfonts && (img.fontfamily !== nothing))\n        @printf(img.buf, \"\\\\fontspec{%s}\\n\", img.fontfamily)\n    end\nend\n\nfunction pop_property_frame(img::PGF)\n    @assert !isempty(img.property_stack)\n    frame = pop!(img.property_stack)\n\n    if frame.has_scalar_properties\n        write(img.buf, \"\\\\end{scope}\\n\")\n        # There should be a better way to to this:\n        # Maybe put the applied properties on a stack then\n        # pop them out here?\n        # FIX ME!\n        img.fill = default_fill_color\n        img.stroke = default_stroke_color\n        img.fill_opacity = 1.0\n        img.stroke_opacity = 1.0\n        img.fontfamily = nothing\n        img.clippath = nothing\n        img.visible = true\n    end\n\n    for (propertytype, property) in frame.vector_properties\n        img.vector_properties[propertytype] = nothing\n        for i in length(img.property_stack):-1:1\n            if haskey(img.property_stack[i].vector_properties, propertytype)\n                img.vector_properties[propertytype] =\n                        img.property_stack[i].vector_properties[propertytype]\n            end\n        end\n    end\nend\n\n# Horrible abuse of Latex inline math mode just to\n# get something working first.\n# FIX ME!\nfunction pango_to_pgf(text::AbstractString)\n    pat = r\"<(/?)\\s*([^>]*)\\s*>\"\n    input = codeunits(escape_tex_chars(text))\n    output = IOBuffer()\n    lastpos = 1\n    for mat in eachmatch(pat, text)\n        write(output, \"\\\\text{\")\n        write(output, input[lastpos:mat.offset-1])\n        write(output, \"}\")\n\n        if mat.captures[2] == \"sup\"\n            if mat.captures[1] == \"/\"\n                write(output, \"}\")\n            else\n                write(output, \"^{\")\n            end\n        elseif mat.captures[2] == \"sub\"\n            if mat.captures[1] == \"/\"\n                write(output, \"}\")\n            else\n                write(output, \"_{\")\n            end\n        elseif mat.captures[2] == \"i\"\n            if mat.captures[1] == \"/\"\n                write(output, \"}\")\n            else\n                write(output, \"\\\\textit{\")\n            end\n        elseif mat.captures[2] == \"b\"\n            if mat.captures[1] == \"/\"\n                write(output, \"}\")\n            else\n                write(output, \"\\\\textbf{\")\n            end\n        end\n        lastpos = mat.offset + length(mat.match)\n    end\n    write(output, \"\\\\text{\")\n    write(output, input[lastpos:end])\n    write(output, \"}\")\n\n    return String(take!(output))\nend\n\nfunction escape_tex_chars(text::AbstractString)\n\t# see http://www.cespedes.org/blog/85/how-to-escape-latex-special-characters\n\tescaped_str = text\n\tescaped_str = replace(escaped_str, \"\\\\\"=>\"\\\\textbackslash{}\")\n\tescaped_str = replace(escaped_str, \"#\"=>\"\\\\#\")\n\tescaped_str = replace(escaped_str, \"\\$\"=>\"\\\\\\$\")\n\tescaped_str = replace(escaped_str, \"%\"=>\"\\\\%\")\n\tescaped_str = replace(escaped_str, \"&\"=>\"\\\\&\")\n\tescaped_str = replace(escaped_str, \"_\"=>\"\\\\_\")\n\tescaped_str = replace(escaped_str, \"{\"=>\"\\\\{\")\n\tescaped_str = replace(escaped_str, \"}\"=>\"\\\\}\")\n\tescaped_str = replace(escaped_str, \"^\"=>\"\\\\textasciicircum{}\")\n\tescaped_str = replace(escaped_str, \"~\"=>\"\\\\textasciitilde{}\")\n\tescaped_str = replace(escaped_str, \"\\\\textbackslash\\\\{\\\\}\"=>\"\\\\textbackslash{}\")\nend\n\n\n\nfunction draw(img::PGF, prim::ArcPrimitive, idx::Int)\n    \n    angle2 = prim.angle2 + (prim.angle2<prim.angle1)*2π\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] (%s,%s) \",\n        join(props, \",\"), \n        svg_fmt_float(prim.center[1].value),\n        svg_fmt_float(prim.center[2].value) )\n    prim.sector && write(img.buf, \"-- \")\n    @printf(img.buf, \"+(%s:%s) \",\n        svg_fmt_float(rad2deg(prim.angle1)),\n        svg_fmt_float(prim.radius.value) )\n    @printf(img.buf,  \"arc [radius=%s, start angle=%s, end angle=%s]\",\n        svg_fmt_float(prim.radius.value),\n        svg_fmt_float(rad2deg(prim.angle1)),\n        svg_fmt_float(rad2deg(angle2)) )\n    prim.sector && write(img.buf, \" -- cycle\")\n    write(img.buf, \";\\n\")\nend\n\n\nfunction draw(img::PGF, prim::BezierPolygonPrimitive, idx::Int)\n    modifiers, props = get_vector_properties(img, idx)\n    img.visible || return\n    write(img.buf, join(modifiers))\n    @printf(img.buf, \"\\\\path [%s] \", join(props, \",\"))\n    @printf(img.buf, \"(%s,%s)\", svg_fmt_float(prim.anchor[1].value),\n        svg_fmt_float(prim.anchor[2].value))\n    currentpoint = prim.anchor\n    for side in prim.sides\n        if length(side)==1\n            point = side[1]\n            @printf(img.buf, \" -- (%s,%s)\", svg_fmt_float(point[1].value),\n                svg_fmt_float(point[2].value))\n        else\n        cp1, cp2, ep = if length(side)==2\n                controlpnt(currentpoint, side[1]),\n                controlpnt(side[2], side[1]), side[2]\n            elseif length(side)==3\n                side[1], side[2], side[3]\n            end\n            @printf(img.buf, \" .. controls (%s,%s) and (%s,%s) .. (%s,%s)\",\n            svg_fmt_float(cp1[1].value), svg_fmt_float(cp1[2].value),\n            svg_fmt_float(cp2[1].value), svg_fmt_float(cp2[2].value),\n            svg_fmt_float(ep[1].value), svg_fmt_float(ep[2].value))\n        end\n        currentpoint = side[end]\n    end\n    write(img.buf, \" -- cycle;\\n\")\nend\n\n"
  },
  {
    "path": "src/property.jl",
    "content": "abstract type PropertyPrimitive end\n\n# Meaningless isless function used to sort in optimize_batching\nfunction Base.isless(a::T, b::T) where T <: PropertyPrimitive\n    for field in fieldnames(T)\n        x = getfield(a, field)\n        y = getfield(b, field)\n        if isa(x, Colorant)\n            if color_isless(x, y)\n                return true\n            elseif color_isless(y, x)\n                return false\n            end\n        else\n            if x < y\n                return true\n            elseif x > y\n                return false\n            end\n        end\n    end\n    return false\nend\n\n\nstruct Property{P <: PropertyPrimitive} <: ComposeNode\n    primitives::Vector{P}\nend\n\nisempty(p::Property) = isempty(p.primitives)\n\nisscalar(p::Property) = length(p.primitives) == 1\n\n\n# Some properties can be applied multiple times, most cannot.\nisrepeatable(p::Property) = false\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, p::Property{T}) where T =\n        Property{T}([resolve(box, units, t, primitive) for primitive in p.primitives])\n\n# Property primitive catchall: most properties don't need measure transforms\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, primitive::PropertyPrimitive) = primitive\n\n\n# Stroke\n# ------\n\nstruct StrokePrimitive <: PropertyPrimitive\n\tcolor::RGBA{Float64}\nend\n\nconst Stroke = Property{StrokePrimitive}\n\nstroke(c::Nothing) = Stroke([StrokePrimitive(RGBA{Float64}(0, 0, 0, 0))])\nstroke(c::Union{Colorant, AbstractString}) = Stroke([StrokePrimitive(parse_colorant(c))])\nstroke(cs::AbstractArray) = Stroke([StrokePrimitive(c == nothing ?\n        RGBA{Float64}(0, 0, 0, 0) : parse_colorant(c)) for c in cs])\n\nprop_string(::Stroke) = \"s\"\n\n\n# Fill\n# ----\n\nstruct FillPrimitive <: PropertyPrimitive\n\tcolor::RGBA{Float64}\nend\n\nconst Fill = Property{FillPrimitive}\n\nfill(c::Nothing) = Fill([FillPrimitive(RGBA{Float64}(0.0, 0.0, 0.0, 0.0))])\n\n\"\"\"\n    fill(c)\n\nDefine a fill color, where `c` can be a `Colorant` or `String`.\n\"\"\"\nfill(c::Union{Colorant, AbstractString}) = Fill([FillPrimitive(parse_colorant(c))])\n\n\"\"\"\n    fill(cs::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfill(cs::AbstractArray) = Fill([FillPrimitive(c == nothing ?\n        RGBA{Float64}(0.0, 0.0, 0.0, 0.0) : parse_colorant(c)) for c in cs])\n\nprop_string(::Fill) = \"f\"\n\n\n# StrokeDash\n# ----------\n\nstruct StrokeDashPrimitive <: PropertyPrimitive\n    value::Vector{Measure}\nend\n\nconst StrokeDash = Property{StrokeDashPrimitive}\n\nstrokedash(values::AbstractArray) = StrokeDash([StrokeDashPrimitive(collect(Measure, values))])\nstrokedash(values::AbstractArray{<:AbstractArray}) =\n        StrokeDash([StrokeDashPrimitive(collect(Measure, value)) for value in values])\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, primitive::StrokeDashPrimitive) =\n        StrokeDashPrimitive([resolve(box, units, t, v) for v in primitive.value])\n\nprop_string(::StrokeDash) = \"sd\"\n\n\n# StrokeLineCap\n# -------------\n\nabstract type LineCap end\nstruct LineCapButt <: LineCap end\nstruct LineCapSquare <: LineCap end\nstruct LineCapRound <: LineCap end\n\nstruct StrokeLineCapPrimitive <: PropertyPrimitive\n    value::LineCap\nend\n\nStrokeLineCapPrimitive(value::Type{LineCap}) = StrokeLineCapPrimitive(value())\n\nconst StrokeLineCap = Property{StrokeLineCapPrimitive}\n\nstrokelinecap(value::Union{LineCap, Type{LineCap}}) =\n        StrokeLineCap([StrokeLineCapPrimitive(value)])\nstrokelinecap(values::AbstractArray) =\n        StrokeLineCap([StrokeLineCapPrimitive(value) for value in values])\n\nprop_string(::StrokeLineCap) = \"slc\"\n\n\n# StrokeLineJoin\n# --------------\n\nabstract type LineJoin end\nstruct LineJoinMiter <: LineJoin end\nstruct LineJoinRound <: LineJoin end\nstruct LineJoinBevel <: LineJoin end\n\nstruct StrokeLineJoinPrimitive <: PropertyPrimitive\n    value::LineJoin\nend\n\nStrokeLineCapPrimitive(value::Type{LineJoin}) = new(value())\n\nconst StrokeLineJoin = Property{StrokeLineJoinPrimitive}\n\nstrokelinejoin(value::Union{LineJoin, Type{LineJoin}}) =\n        StrokeLineJoin([StrokeLineJoinPrimitive(value)])\nstrokelinejoin(values::AbstractArray) =\n        StrokeLineJoin([StrokeLineJoinPrimitive(value) for value in values])\n\nprop_string(::StrokeLineJoin) = \"slj\"\n\n\n# LineWidth\n# ---------\n\nstruct LineWidthPrimitive <: PropertyPrimitive\n    value::Measure\n\n    function LineWidthPrimitive(value)\n        return new(size_measure(value))\n    end\nend\n\nconst LineWidth = Property{LineWidthPrimitive}\n\nlinewidth(value::Union{Measure, Number}) = LineWidth([LineWidthPrimitive(value)])\nlinewidth(values::AbstractArray) =\n        LineWidth([LineWidthPrimitive(value) for value in values])\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, primitive::LineWidthPrimitive) =\n        LineWidthPrimitive(resolve(box, units, t, primitive.value))\n\nprop_string(::LineWidth) = \"lw\"\n\n\n# Visible\n# -------\n\nstruct VisiblePrimitive <: PropertyPrimitive\n    value::Bool\nend\n\nconst Visible = Property{VisiblePrimitive}\n\nvisible(value::Bool) = Visible([VisiblePrimitive(value)])\nvisible(values::AbstractArray) = Visible([VisiblePrimitive(value) for value in values])\n\nprop_string(::Visible) = \"v\"\n\n\n# FillOpacity\n# -----------\n\nstruct FillOpacityPrimitive <: PropertyPrimitive\n    value::Float64\n\n    function FillOpacityPrimitive(value_::Number)\n        value = Float64(value_)\n        (value < 0.0 || value > 1.0) && error(\"Opacity must be between 0 and 1.\")\n        return new(value)\n    end\nend\n\nconst FillOpacity = Property{FillOpacityPrimitive}\n\n\"\"\"\n    fillopacity(value)\n\nDefine a fill opacity, where 0≤value≤1.  For svg, nested contexts  will inherit from parent contexts e.g. `(context(), fillopacity(a), (context(), fill(c::String), circle()))`.\n\"\"\"\nfillopacity(value::Float64) = FillOpacity([FillOpacityPrimitive(value)])\n\n\"\"\"\n    fillopacity(values::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\nfillopacity(values::AbstractArray) =\n        FillOpacity([FillOpacityPrimitive(value) for value in values])\n\nprop_string(::FillOpacity) = \"fo\"\n\n\n# StrokeOpacity\n# -------------\n\nstruct StrokeOpacityPrimitive <: PropertyPrimitive\n    value::Float64\n\n    function StrokeOpacityPrimitive(value_::Number)\n        value = Float64(value_)\n        (value < 0.0 || value > 1.0) && error(\"Opacity must be between 0 and 1.\")\n        return new(value)\n    end\nend\n\nconst StrokeOpacity = Property{StrokeOpacityPrimitive}\n\nstrokeopacity(value::Float64) = StrokeOpacity([StrokeOpacityPrimitive(value)])\nstrokeopacity(values::AbstractArray) =\n        StrokeOpacity([StrokeOpacityPrimitive(value) for value in values])\n\nprop_string(::StrokeOpacity) = \"so\"\n\n\n# Clip\n# ----\n\nstruct ClipPrimitive{P <: Vec} <: PropertyPrimitive\n    points::Vector{P}\nend\n\nconst Clip = Property{ClipPrimitive}\n\nclip() = Clip([ClipPrimitive(Array{Vec}(undef, 0))])\n\n\n\"\"\"\n    clip(points::AbstractArray)\n\n`clip()` is a property.  Only forms inside the clip shape will be visible.\n\"\"\"\nfunction clip(points::AbstractArray{T}) where T <: XYTupleOrVec\n    XM, YM = narrow_polygon_point_types(Vector[points])\n    if XM == Any\n        XM = Length{:cx, Float64}\n    end\n    if YM == Any\n        YM = Length{:cy, Float64}\n    end\n    VecType = Tuple{XM, YM}\n\n    prim = ClipPrimitive(VecType[(x_measure(point[1]), y_measure(point[2])) for point in points])\n    return Clip(typeof(prim)[prim])\nend\n\n\n\"\"\"\n    clip(point_arrays::AbstractArray...)\n\nArguments can be passed in arrays in order to perform multiple clipping operations at once.\n\"\"\"\nfunction clip(point_arrays::AbstractArray...)\n    XM, YM = narrow_polygon_point_types(point_arrays)\n    VecType = XM == YM == Any ? Vec : Vec{XM, YM}\n    PrimType = XM == YM == Any ? ClipPrimitive : ClipPrimitive{VecType}\n\n    clipprims = Array{PrimType}(undef, length(point_arrays))\n    for (i, point_array) in enumerate(point_arrays)\n        clipprims[i] = ClipPrimitive(VecType[(x_measure(point[1]), y_measure(point[2]))\n                                             for point in point_array])\n    end\n    return Property{PrimType}(clipprims)\nend\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, primitive::ClipPrimitive) =\n        ClipPrimitive{AbsoluteVec2}( AbsoluteVec2[\n            resolve(box, units, t, point) for point in primitive.points])\n\nprop_string(::Clip) = \"clp\"\n\n\n# Font\n# ----\n\nstruct FontPrimitive <: PropertyPrimitive\n    family::AbstractString\nend\n\nconst Font = Property{FontPrimitive}\n\nfont(family::AbstractString) = Font([FontPrimitive(family)])\nfont(families::AbstractArray) =\n        Font([FontPrimitive(family) for family in families])\n\nprop_string(::Font) = \"fnt\"\n\nBase.hash(primitive::FontPrimitive, h::UInt) = hash(primitive.family, h)\n\n==(a::FontPrimitive, b::FontPrimitive) = a.family == b.family\n\n\n# FontSize\n# --------\n\nstruct FontSizePrimitive <: PropertyPrimitive\n    value::Measure\n\n    function FontSizePrimitive(value)\n        return new(size_measure(value))\n    end\nend\n\nconst FontSize = Property{FontSizePrimitive}\n\nfontsize(value::Union{Number, Measure}) = FontSize([FontSizePrimitive(value)])\n\nfontsize(values::AbstractArray) = FontSize([FontSizePrimitive(value) for value in values])\n\nresolve(box::AbsoluteBox, units::UnitBox, t::Transform, primitive::FontSizePrimitive) =\n        FontSizePrimitive(resolve(box, units, t, primitive.value))\n\nprop_string(::FontSize) = \"fsz\"\n\n\n# SVGID\n# -----\n\nstruct SVGIDPrimitive <: PropertyPrimitive\n    value::AbstractString\nend\n\nconst SVGID = Property{SVGIDPrimitive}\n\nsvgid(value::AbstractString) = SVGID([SVGIDPrimitive(value)])\nsvgid(values::AbstractArray) = SVGID([SVGIDPrimitive(value) for value in values])\n\nprop_string(::SVGID) = \"svgid\"\n\nBase.hash(primitive::SVGIDPrimitive, h::UInt) = hash(primitive.value, h)\n\n==(a::SVGIDPrimitive, b::SVGIDPrimitive) = a.value == b.value\n\n\n# SVGClass\n# --------\n\nstruct SVGClassPrimitive <: PropertyPrimitive\n    value::String\nend\n\nconst SVGClass = Property{SVGClassPrimitive}\n\nsvgclass(value::AbstractString) = SVGClass([SVGClassPrimitive(value)])\nsvgclass(values::AbstractArray) =\n        SVGClass([SVGClassPrimitive(value) for value in values])\n\nfunction prop_string(svgc::SVGClass)\n    if isscalar(svgc)\n        return string(\"svgc(\", svgc.primitives[1].value, \")\")\n    else\n        return string(\"svgc(\", svgc.primitives[1].value, \"...)\")\n    end\nend\n\nBase.hash(primitive::SVGClassPrimitive, h::UInt) = hash(primitive.value, h)\n\n==(a::SVGClassPrimitive, b::SVGClassPrimitive) = a.value == b.value\n\n\n# SVGAttribute\n# ------------\n\nstruct SVGAttributePrimitive <: PropertyPrimitive\n    attribute::String\n    value::String\nend\n\nconst SVGAttribute = Property{SVGAttributePrimitive}\n\nsvgattribute(attribute::AbstractString, value) =\n        SVGAttribute([SVGAttributePrimitive(attribute, string(value))])\nsvgattribute(attribute::AbstractString, values::AbstractArray) =\n        SVGAttribute([SVGAttributePrimitive(attribute, string(value)) for value in values])\n\nsvgattribute(attributes::AbstractArray, values::AbstractArray) =\n    SVGAttribute( @makeprimitives SVGAttributePrimitive,\n            (attribute in attributes, value in values),\n            SVGAttributePrimitive(attribute, string(value)))\n\nprop_string(::SVGAttribute) = \"svga\"\n\nfunction Base.hash(primitive::SVGAttributePrimitive, h::UInt)\n    h = hash(primitive.attribute, h)\n    h = hash(primitive.value, h)\n    return h\nend\n\n==(a::SVGAttributePrimitive, b::SVGAttributePrimitive) =\n        a.attribute == b.attribute && a.value == b.value\n\n\n# JSInclude\n# ---------\n\nstruct JSIncludePrimitive <: PropertyPrimitive\n    value::AbstractString\n    jsmodule::Union{Nothing, Tuple{AbstractString, AbstractString}}\nend\n\nconst JSInclude = Property{JSIncludePrimitive}\n\njsinclude(value::AbstractString, module_name=nothing) =\n        JSInclude([JSIncludePrimitive(value, module_name)])\n\n# Don't bother with a vectorized version of this. It wouldn't really make #\n# sense.\n\nprop_string(::JSInclude) = \"jsip\"\n\n# JSCall\n# ------\n\nstruct JSCallPrimitive <: PropertyPrimitive\n    code::AbstractString\n    args::Vector{Measure}\nend\n\nconst JSCall = Property{JSCallPrimitive}\n\njscall(code::AbstractString, arg::Vector{Measure}=Measure[]) = JSCall([JSCallPrimitive(code, arg)])\n\njscall(codes::AbstractArray, args::AbstractArray{Vector{Measure}}=Vector{Measure}[Measure[]]) =\n        JSCall( @makeprimitives JSCallPrimitive,\n            (code in codes, arg in args),\n            JSCallPrimitive(code, arg))\n\nfunction resolve(box::AbsoluteBox, units::UnitBox, t::Transform, primitive::JSCallPrimitive)\n    # we are going to build a new string by scanning across \"code\" and\n    # replacing %x with translated x values, %y with translated y values\n    # and %s with translated size values.\n    newcode = IOBuffer()\n\n    i = 1\n    validx = 1\n    while true\n        j = findnext(primitive.code, \"%\", i)\n\n        if j === nothing\n            write(newcode, primitive.code[i:end])\n            break\n        end\n\n        write(newcode, primitive.code[i:j-1])\n        if j == length(primitive.code)\n            write(newcode, '%')\n            break\n        elseif primitive.code[j+1] == '%'\n            write(newcode, '%')\n        elseif primitive.code[j+1] == 'x'\n            val = resolve(box, units, t, (primitive.args[validx], 0mm))\n            write(newcode, svg_fmt_float(val[1].value))\n            validx += 1\n        elseif primitive.code[j+1] == 'y'\n            val = resolve(box, units, t, (0mm, primitive.args[validx]))\n            write(newcode, svg_fmt_float(val[2].value))\n            validx += 1\n        elseif primitive.code[j+1] == 's'\n            val = resolve(box, units, t, primitive.args[validx])\n            write(newcode, svg_fmt_float(val.value))\n            validx += 1\n        else\n            write(newcode, '%', primitive.code[j+1])\n        end\n\n        i = j + 2\n    end\n\n    return JSCallPrimitive(String(take!(newcode)), Measure[])\nend\n\nisrepeatable(p::JSCall) = true\n\nBase.isless(a::FillPrimitive, b::FillPrimitive) = color_isless(a.color, b.color)\nBase.isless(a::StrokePrimitive, b::StrokePrimitive) = color_isless(a.color, b.color)\n\nprop_string(::JSCall) = \"jsc\"\n\n\n\n# Arrow Property\n# -------\n\nstruct ArrowPrimitive <: PropertyPrimitive\n    value::Bool\nend\n\nconst Arrow = Property{ArrowPrimitive}\n\n\n\"\"\"\n    arrow()\n\n    `arrow() = arrow(true)`\n\"\"\"\narrow() = Arrow([ArrowPrimitive(true)])\n\n\n\"\"\"\n    arrow(value::Bool)\n\n    `arrow()` is a property of arcs, lines and curves. The color of the arrowhead is the same as `stroke()`, but for svg the results will be browser-dependent. \n\"\"\"\narrow(value::Bool) = Arrow([ArrowPrimitive(value)])\n\n\"\"\"\n    arrow(values::AbstractArray)\n\nArguments can be passed in arrays in order to perform multiple drawing operations at once.\n\"\"\"\narrow(values::AbstractArray) = Arrow([ArrowPrimitive(value) for value in values])\n\nprop_string(::Arrow) = \"arrow\"\n\n\n\n\n"
  },
  {
    "path": "src/stack.jl",
    "content": "# Convenience function for rearranging contexts\n\n# Create a new context containing the given contexts stacked horizontally.\n#\n# Args:\n#  x0: X-position of the new root context\n#  y0: Y-position of the new root context\n#  height: Height of the root context.\n#  aligned_contexts: One or more canvases accompanied with a vertical alignment\n#                    specifier, giving the vertical positioning of the context.\n#\nfunction hstack(x0, y0, height, aligned_contexts::(Tuple{Context, VAlignment})...)\n    isempty(aligned_contexts) && return context(x0, y0, 0, height)\n\n    widths = [aligned_context[1].box.a[1] for aligned_context in aligned_contexts]\n    width = sum(widths)\n    total_width_units = sum_component(Length{:w, Float64}, width)\n\n    if total_width_units > 0.0\n        width -= total_width_units*w\n        width += 1w\n    end\n\n    height = y_measure(height)\n\n    root = context(x0, y0, width, height)\n    x = 0w\n    for (ctx, aln) in aligned_contexts\n        ctx = copy(ctx)\n\n        w_component = sum_component(Length{:w, Float64}, ctx.box.a[1])\n        box_w = ctx.box.a[1]\n        if w_component != 0.0\n            box_w = scale_component(Length{:w, Float64},\n                                    w_component / total_width_units,\n                                    ctx.box.a[1])\n        end\n\n        y = ctx.box.x0[2]\n        if aln == vtop\n            y = 0h\n        elseif aln == vcenter\n            y = (height / 2) - (ctx.box.a[2] / 2)\n        elseif aln == vbottom\n            y = height - ctx.box.a[2]\n        end\n\n        ctx.box = BoundingBox((x, y), (box_w, ctx.box.a[2]))\n        root = compose!(root, ctx)\n        x += ctx.box.a[1]\n    end\n\n    return root\nend\n\nhstack() = context()\n\n# Create a new context containing the given contexts stacked horizontally.\n#\n# This is the simple version of hstack. The root context will be placed on 0cx,\n# 0cy, and its height will be the maximum of the contexts it contains. All\n# contexts will be centered vertically.\n#\nfunction hstack(contexts::Context...; x0::MeasureOrNumber=0,\n                y0::MeasureOrNumber=0, height=0)\n    if height == 0\n        height = maximum([context.box.a[2] for context in contexts])\n    end\n    return hstack(x0, y0, height, [(context, vcenter) for context in contexts]...)\nend\n\n# Create a new context containing the given contexts stacked vertically.\n#\n# Args:\n#  x0: X-position of the new root context\n#  y0: Y-position of the new root context\n#  width: Height of the root context.\n#  aligned_contexts: One or more canvases accompanied with a horizontal alignment\n#                    specifier, giving the horizontal positioning of the context.\n#\nfunction vstack(x0, y0, width, aligned_contexts::(Tuple{Context, HAlignment})...)\n    isempty(aligned_contexts) && return context(x0, y0, width, 0)\n\n    heights = [aligned_context[1].box.a[2] for aligned_context in aligned_contexts]\n    height = sum(heights)\n    total_height_units = sum_component(Length{:h, Float64}, height)\n\n    if total_height_units > 0.0\n        height -= total_height_units*h\n        height += 1h\n    end\n\n    width = x_measure(width)\n\n    root = context(x0, y0, width, height)\n    y = 0h\n    for (ctx, aln) in aligned_contexts\n        ctx = copy(ctx)\n\n        h_component = sum_component(Length{:h, Float64}, ctx.box.a[2])\n        box_h = ctx.box.a[2]\n        if h_component != 0.0\n            box_h = scale_component(Length{:h, Float64},\n                                    h_component / total_height_units,\n                                    ctx.box.a[2])\n        end\n\n        x = ctx.box.x0[1]\n        if aln == hleft\n            x = 0w\n        elseif aln == hcenter\n            x = (width / 2) - (ctx.box.a[1] / 2)\n        elseif aln == hright\n            x = width - ctx.box.a[1]\n        end\n\n        ctx.box = BoundingBox((x, y), (ctx.box.a[1], box_h))\n        root = compose!(root, ctx)\n        y += ctx.box.a[2]\n    end\n\n    return root\nend\n\nvstack() = context()\n\n# Create a new context containing the given contexts stacked horizontally.\n#\n# The simple version of vstack. The root context will be placed on 0cx, 0cy, and\n# its width will be the maximum of the contexts it contains. All contexts will\n# be centered horizontally..\n#\nfunction vstack(contexts::Context...; x0::MeasureOrNumber=0,\n                y0::MeasureOrNumber=0, width::MeasureOrNumber=0)\n    if width == 0\n        width = maximum([context.box.a[1] for context in contexts])\n    end\n    return vstack(x0, y0, width, [(context, hcenter) for context in contexts]...)\nend\n"
  },
  {
    "path": "src/svg.jl",
    "content": "using Base64\nusing UUIDs\nusing Random\n\nconst snapsvgjs = joinpath(@__DIR__, \"..\", \"deps\", \"snap.svg-min.js\")\n\n# Packages can insert extra XML namespaces here to be defined in the output\n# SVG.\nconst xmlns = Dict()\n\n# All svg (in our use) coordinates are in millimeters. This number gives the\n# largest deviation from the true position allowed in millimeters.\nconst eps = 0.01\n\n# Format a floating point number into a decimal string of reasonable precision.\nfunction svg_fmt_float(x::Fractional)\n    a = @sprintf(\"%0.8f\", round(x / eps) * eps)\n    n = length(a)\n\n    while a[n] == '0'\n        n -= 1\n    end\n\n    if a[n] == '.'\n        n -= 1\n    end\n\n    a[1:n]\nend\n\n# A much faster version of svg_fmt_float. This does not allocate any\n# temporary buffers, because it writes directly to the output.\nfunction svg_print_float(io::IO, x::AbstractFloat)\n    ndig = 2\n\n    if isfinite(x)\n        if x < 0\n            write(io, '-')\n            x = abs(x)\n        end\n        x = round(x/eps)*eps\n        xt = trunc(UInt, x)\n        dx = x - convert(Float64, xt)\n        0 <= dx < 1 || error(\"Formatting overflow\")\n        svg_print_uint(io, xt, 1)  # width=1 prints 0.2 instead of .2\n        dxi = round(UInt, dx/eps)\n        if dxi != 0\n            write(io, '.')\n            svg_print_uint(io, dxi, ndig, true)\n        end\n    elseif isnan(x)\n        write(io, \"NaN\")\n    elseif x == Inf\n        write(io, \"Inf\")\n    elseif x == -Inf\n        write(io, \"-Inf\")\n    end\nend\n\nlet a = Array{UInt8}(undef, 20)\n    global svg_print_uint\n    function svg_print_uint(io::IO, x::Unsigned, width = 0, drop = false)\n        n = length(a)\n        while x > 0 && n > 0\n            x, r = divrem(x, 10)\n            a[n] = r\n            n -= 1\n        end\n        n == 0 && error(\"Formatting overflow\")\n        for i = 1:width-(length(a)-n)\n            write(io, '0')\n        end\n        last = length(a)\n        if drop\n            while last > n && a[last] == 0\n                last -= 1\n            end\n        end\n        for i = n+1:last\n            write(io, '0'+a[i])\n        end\n    end\nend\n\n# Format a color for SVG.\nsvg_fmt_color(c::Color) = string(\"#\", hex(c))\nsvg_fmt_color(c::Nothing) = \"none\"\n\n# Javascript in a <script> tag in SVG needs to escape '\"' and '<'.\n#=function escape_script(js::AbstractString)=#\n    #=return replace(replace(js, \"&\", \"&amp;\"), \"<\", \"&lt;\")=#\n#=end=#\n\n# Javascript contained to CDATA block needs to avoid ']]'\nescape_script(js::AbstractString) = replace(js, \"]]\"=>\"] ]\")\n\n# When subtree rooted at a context is drawn, it pushes its property children\n# in the form of a property frame.\nmutable struct SVGPropertyFrame\n    # Vector properties in this frame.\n    vector_properties::Dict{Type, Property}\n\n    # True if this property frame has scalar properties. Scalar properties are\n    # emitted as a group (<g> tag) that must be closed when the frame is popped.\n    has_scalar_properties::Bool\n\n    # True if this property frame includes a link (<a> tag) that needs\n    # to be closed.\n    has_link::Bool\n\n    # True if the property frame include a mask (<mask> tag) than needs to be\n    # closed.\n    has_mask::Bool\n\n    # True if the property frame has a scalar Clip property, which needs some\n    # special handling.\n    has_scalar_clip::Bool\nend\n\nSVGPropertyFrame() = SVGPropertyFrame(Dict{Type, Property}(), false, false, false, false)\n\nconst LOCAL_RNG = MersenneTwister()\n\nmutable struct SVG <: Backend\n    # Image size in millimeters.\n    width::AbsoluteLength\n    height::AbsoluteLength\n\n    # Output stream.\n    out::IO\n\n    # Save output from IOBuffers to allow multiple calls to writemime\n    cached_out::Union{AbstractString, Nothing}\n\n    # Unique ID for the figure.\n    id::AbstractString\n\n    # Current level of indentation.\n    indentation::Int\n\n    # Stack of property frames (groups of properties) currently in effect.\n    property_stack::Vector{SVGPropertyFrame}\n\n    # SVG forbids defining the same property twice, so we have to keep track\n    # of which vector property of which type is in effect. If two properties of\n    # the same type are in effect, the one higher on the stack takes precedence.\n    vector_properties::Dict{Type, Union{Property, Nothing}}\n\n    # Clip-paths that need to be defined at the end of the document.\n    clippaths::OrderedDict{ClipPrimitive, String}\n\n    # Batched forms to be included within <def> tags.\n    batches::Vector{Tuple{FormPrimitive, String}}\n\n    # Embedded objects included immediately before the </svg> tag, such as extra\n    # javascript or css.\n    embobj::Set{AbstractString}\n\n    # True when finish has been called and no more drawing should occur\n    finished::Bool\n\n    # Backend is responsible for opening/closing the file\n    ownedfile::Bool\n\n    # Filename when ownedfile is true\n    filename::Union{AbstractString, Nothing}\n\n    # Emit the graphic on finish when writing to a buffer.\n    emit_on_finish::Bool\n\n    # IDs of the SVG element currently being generated. `has_current_id` is\n    # false if the element being drawn does not have an id.\n    current_id::AbstractString\n    has_current_id::Bool\n\n    # A counter used to generate unique IDs\n    id_count::Int\n\n    # Filenames of javsacript to include before any JSCall code.\n    jsheader::Set{AbstractString}\n\n    # (Name, binding) pairs of javascript modules the embedded code depends on\n    jsmodules::Set{Tuple{AbstractString, AbstractString}}\n\n    # User javascript from JSCall attributes\n    scripts::Vector{AbstractString}\n\n    # Use javascript extensions to add interactivity, etc.\n    withjs::Bool\n\n    # What to do with javascript. One of:\n    #    none: generate a static SVG without any javascript\n    #    exclude: exclude external javascript libraries           ### vestigial?\n    #    embed: embed external libraries\n    #    linkabs: link to external libraries (absolute path)\n    #    linkrel: link to external libraries (relative path)\n    #\n    jsmode::Symbol\n\n    panelcoords::Tuple\nend\n\nfunction SVG(out::IO,\n             width::AbsoluteLength,\n             height::AbsoluteLength,\n             emit_on_finish::Bool=true,\n             jsmode::Symbol=:none;\n\n             cached_out = nothing,\n             id = string(\"img-\", string(uuid4(LOCAL_RNG))[1:8]),\n             indentation = 0,\n             property_stack = Array{SVGPropertyFrame}(undef, 0),\n             vector_properties = Dict{Type, Union{Property, Nothing}}(),\n             clippaths = OrderedDict{ClipPrimitive, String}(),\n             batches = Array{Tuple{FormPrimitive, String}}(undef, 0),\n             embobj = Set{AbstractString}(),\n             finished = false,\n             ownedfile = false,\n             filename = nothing,\n             current_id = \"\",\n             has_current_id = false,\n             id_count = 0,\n             jsheader = Set{AbstractString}(),\n             jsmodules = Set{Tuple{AbstractString, AbstractString}}(((\"Snap.svg\", \"Snap\"),)),\n             scripts = AbstractString[],\n             withjs = jsmode != :none,\n             panelcoords = ())\n\n    img = SVG(width,\n              height,\n              out,\n              cached_out,\n              id,\n              indentation,\n              property_stack,\n              vector_properties,\n              clippaths,\n              batches,\n              embobj,\n              finished,\n              ownedfile,\n              filename,\n              emit_on_finish,\n              current_id,\n              has_current_id,\n              id_count,\n              jsheader,\n              jsmodules,\n              scripts,\n              withjs,\n              jsmode,\n              panelcoords)\n\n    writeheader(img)\n    return img\nend\n\n# Write to a file or IOBuffer.\n\"\"\"\n    SVG([output::Union{IO,AbstractString}], width=√200cm, height=10cm, jsmode=:none) -> Backend\n\nCreate a Scalable Vector Graphic backend.  The output is normally passed to\n[`draw`](@ref).  Specify a filename using a string as the first argument.\n`jsmode` can be one of `:none`, `:embed`, `:linkabs`, or `:linkrel`.  See also\n[`SVGJS`](@ref).\n\n# Examples\n```\nc = compose(context(), line())\ndraw(SVG(\"myplot.svg\"), c)\n```\n\"\"\"\nSVG(filename::AbstractString, width=default_graphic_width, height=default_graphic_height,\n        jsmode::Symbol=:none) =\n        SVG(open(filename, \"w\"), width, height, true, jsmode; ownedfile=true, filename=filename)\n\n# Write to buffer.\nSVG(width::MeasureOrNumber=default_graphic_width, height::MeasureOrNumber=default_graphic_height,\n        emit_on_finish::Bool=true, jsmode::Symbol=:none) =\n        SVG(IOBuffer(), width, height, emit_on_finish, jsmode)\n\ncanbatch(img::SVG) = true\niswithjs(img::SVG) = img.withjs\n\nfunction (img::SVG)(x)\n    draw(img, x)\nend\n\n# Return the next unique element ID. Sort of like gensym for SVG elements.\nfunction genid(img::SVG)\n    img.id_count += 1\n    return @sprintf(\"%s-%d\", img.id, img.id_count)\nend\n\n# Constructors that turn javascript extensions on\n\"\"\"\n    SVGJS([output::Union{IO,AbstractString}], width=√200cm, height=10cm, jsmode=:embed) -> Backend\n\nCreate a Scalable Vector Graphic backend that enables Gadfly's interactivity\n(pan, zoom, etc.).  The default `jsmode` splices the requisite javascript\ndirectly into the output.  One can alternatively link to identical external\njavascript with `:linkabs` and `:linkrel`.  The output is normally passed to\n[`draw`](@ref).  Specify a filename using a string as the first argument.  See\nalso [`SVG`](@ref).\n\"\"\"\nSVGJS(out::IO, width=default_graphic_width, height=default_graphic_height,\n        emit_on_finish::Bool=true; jsmode::Symbol=:embed) =\n        SVG(out, width, height, emit_on_finish, jsmode)\nSVGJS(filename::AbstractString, width=default_graphic_width, height=default_graphic_height;\n        jsmode::Symbol=:embed) = SVG(filename, width, height, jsmode)\nSVGJS(width::MeasureOrNumber=default_graphic_width, height::MeasureOrNumber=default_graphic_height,\n        emit_on_finish::Bool=true, jsmode::Symbol=:embed) =\n        SVG(width, height, emit_on_finish, jsmode)\n\nfunction writeheader(img::SVG)\n    widthstr = svg_fmt_float(img.width.value)\n    heightstr = svg_fmt_float(img.height.value)\n    write(img.out,\n          \"\"\"\n          <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n          <svg xmlns=\"http://www.w3.org/2000/svg\"\n               xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n          \"\"\")\n\n    for (ns, uri) in xmlns\n        write(img.out, \"\"\"     xmlns:$(ns)=\"$(uri)\"\\n\"\"\")\n    end\n\n    write(img.out,\n          \"\"\"\n               version=\"1.2\"\n               width=\"$(widthstr)mm\" height=\"$(heightstr)mm\" viewBox=\"0 0 $(widthstr) $(heightstr)\"\n               stroke=\"$(svg_fmt_color(default_stroke_color))\"\n               fill=\"$(svg_fmt_color(default_fill_color))\"\n               stroke-width=\"$(svg_fmt_float(default_line_width.value))\"\n               font-size=\"$(svg_fmt_float(default_font_size.value))\"\n          \"\"\")\n    img.withjs && write(img.out, \"\\n     id=\\\"$(img.id)\\\"\")\n    write(img.out, \">\\n\")\n    write(img.out,\n          \"\"\"\n          <defs>\n            <marker id=\"arrow\" markerWidth=\"15\" markerHeight=\"7\" refX=\"5\" refY=\"3.5\" orient=\"auto\" markerUnits=\"strokeWidth\">\n              <path d=\"M0,0 L15,3.5 L0,7 z\" stroke=\"context-stroke\" fill=\"context-stroke\"/>\n            </marker>\n          </defs>\n          \"\"\")\n#    write(img.out, \"\\n\")\n          return img\nend\n\nfunction register_coords(backend::SVG, box, units, transform, property::SVGClass)\n    if length(property.primitives) == 1 && property.primitives[1].value == \"plotpanel\"\n        backend.panelcoords = (box, units, transform)\n    end\n    nothing\nend\n\nfunction reset(img::SVG)\n    if img.ownedfile\n        img.out = open(img.filename, \"w\")\n    else\n        try\n            seekstart(img.out)\n        catch\n            error(\"Backend can't be reused, since the output stream is not seekable.\")\n        end\n    end\n    writeheader(img)\n    img.finished = false\nend\n\nfunction finish(img::SVG)\n    img.finished && return\n\n    while !isempty(img.property_stack)\n        pop_property_frame(img)\n    end\n\n    for obj in img.embobj\n        write(img.out, obj)\n        write(img.out, \"\\n\")\n    end\n\n    # defs\n    if !isempty(img.clippaths) | !isempty(img.batches)\n        write(img.out, \"<defs>\\n\")\n        img.indentation += 1\n    end\n\n    for (clippath, id) in img.clippaths\n        indent(img)\n        img.indentation += 1\n        write(img.out, \"<clipPath id=\\\"$(id)\\\">\\n\")\n        indent(img)\n        write(img.out, \"<path d=\\\"\")\n        print_svg_path(img.out, clippath.points)\n        write(img.out, \"\\\" />\\n\")\n        img.indentation -= 1\n        indent(img)\n        write(img.out, \"</clipPath>\\n\")\n    end\n\n    for (primitive, id) in img.batches\n        indent(img)\n        print(img.out, \"<g id=\\\"\", id, \"\\\">\\n\")\n        img.indentation += 1\n        draw(img, primitive, 1)\n        img.indentation -= 1\n        indent(img)\n        print(img.out, \"</g>\\n\")\n    end\n\n    if !isempty(img.clippaths) | !isempty(img.batches)\n        img.indentation -= 1\n        write(img.out, \"</defs>\\n\")\n    end\n\n    if img.withjs\n        if img.jsmode == :embed\n            write(img.out,\n                \"\"\"\n                <script> <![CDATA[\n                $(escape_script(read(snapsvgjs, String)))\n                ]]> </script>\n                \"\"\")\n        elseif img.jsmode == :linkabs\n            write(img.out,\n                \"\"\"\n                <script xlink:href=\"$(snapsvgjs)\"></script>\n                \"\"\")\n        elseif img.jsmode == :linkrel\n            write(img.out,\n                \"\"\"\n                <script xlink:href=\"$(basename(snapsvgjs))\"></script>\n                \"\"\")\n        end\n\n        if !isempty(img.scripts) || !isempty(img.jsheader)\n            if img.jsmode == :embed\n                write(img.out, \"<script> <![CDATA[\\n\")\n                for script in img.jsheader\n                    write(img.out, escape_script(read(script, String)), \"\\n\")\n                end\n            elseif img.jsmode == :linkabs\n                for script in img.jsheader\n                    write(img.out,\n                        \"\"\"\n                        <script xlink:href=\"$(script)\"></script>\n                        \"\"\")\n                end\n                write(img.out, \"<script> <![CDATA[\\n\")\n            elseif img.jsmode == :linkrel\n                for script in img.jsheader\n                    write(img.out,\n                        \"\"\"\n                        <script xlink:href=\"$(basename(script))\"></script>\n                        \"\"\")\n                end\n                write(img.out, \"<script> <![CDATA[\\n\")\n            end\n\n            mod_names = join([string(\"\\\"\", name, \"\\\"\") for (name, binding) in img.jsmodules], \", \")\n            mod_bindings = join([binding for (name, binding) in img.jsmodules], \", \")\n            glob_mod_bindings = join([string(\"glob.\", binding)\n                                      for (name, binding) in img.jsmodules], \", \")\n\n            write(img.out,\n                \"\"\"\n                (function (glob, factory) {\n                    // AMD support\n                      if (typeof require === \"function\" && typeof define === \"function\" && define.amd) {\n                        require([$(mod_names)], function ($(mod_bindings)) {\n                            factory($(mod_bindings));\n                        });\n                      } else {\n                          factory($(glob_mod_bindings));\n                      }\n                })(window, function ($(mod_bindings)) {\n                    var fig = Snap(\\\"#$(img.id)\\\");\n                \"\"\")\n\n            for script in img.scripts\n                write(img.out, escape_script(script), \"\\n\")\n            end\n\n            write(img.out,\n                \"\"\"\n                    });\n                \"\"\")\n\n            write(img.out, \"]]> </script>\\n\")\n        end\n\n    end\n\n    write(img.out, \"</svg>\\n\")\n    hasmethod(flush, (typeof(img.out),)) && flush(img.out)\n\n    img.ownedfile && close(img.out)\n\n    img.finished = true\n\n    # If we are writing to a buffer. Collect the string and emit it.\n    img.emit_on_finish && typeof(img.out) == IOBuffer && display(img)\nend\n\nisfinished(img::SVG) = img.finished\n\nfunction show(io::IO, ::MIME\"text/html\", img::SVG)\n    if img.cached_out === nothing\n        img.cached_out = String(take!(img.out))\n    end\n    write(io, \n        \"\"\"\n        <html>\n        $(img.cached_out)\n        </html>\n        \"\"\")\nend\n\nfunction show(io::IO, ::MIME\"image/svg+xml\", img::SVG)\n    if img.cached_out === nothing\n        img.cached_out = String(take!(img.out))\n    end\n    write(io, img.cached_out)\nend\n\nroot_box(img::SVG) = BoundingBox(0mm, 0mm, img.width, img.height)\n\nfunction indent(img::SVG)\n    for i in 1:img.indentation\n        write(img.out, \"  \")\n    end\nend\n\n# Draw\n\n# Generate SVG path data from an array of points.\n#\n# Args:\n#   out: Output stream.\n#   points: points on the path\n#   bridge_gaps: when true, remove non-finite values, rather than forming\n#                separate lines. (this arg is never used)\n#\n# Returns:\n#   A string containing SVG path data.\n#\nfunction print_svg_path(out, points::Vector{AbsoluteVec2})\n    isfirst = true\n    endp = points[end]\n    for (currp, nxtp) in zip(points[1:end-1], points[2:end])\n        x1, y1 = currp[1].value, currp[2].value\n        x2, y2 = nxtp[1].value, nxtp[2].value\n        currentp = isfinite(x1) && isfinite(y1)\n        nextp = isfinite(x2) && isfinite(y2)\n    \n        if  (isfirst && currentp && nextp)\n            write(out, 'M')\n            svg_print_float(out, x1)\n            write(out, ',')\n            svg_print_float(out, y1)\n            write(out, \" L\")\n            isfirst = false\n        elseif (!isfirst && currentp)\n            svg_print_float(out, x1)\n            write(out, ',')\n            svg_print_float(out, y1)\n            write(out, ' ')\n        end\n        !nextp && (isfirst = true)\n    end\n    svg_print_float(out, endp[1].value)\n    write(out, ',')\n    svg_print_float(out, endp[2].value)\n    write(out, ' ')\nend\n\n\n\n# Property Printing\n# -----------------\n\nfunction print_property(img::SVG, property::StrokePrimitive)\n    if property.color.alpha != 1.0\n        @printf(img.out, \" stroke=\\\"%s\\\" stroke-opacity=\\\"%0.3f\\\"\",\n                svg_fmt_color(color(property.color)), property.color.alpha)\n    else\n        @printf(img.out, \" stroke=\\\"%s\\\"\", svg_fmt_color(color(property.color)))\n    end\nend\n\nfunction print_property(img::SVG, property::FillPrimitive)\n    if property.color.alpha != 1.0\n        @printf(img.out, \" fill=\\\"%s\\\" fill-opacity=\\\"%0.3f\\\"\",\n                svg_fmt_color(color(property.color)), property.color.alpha)\n    else\n        @printf(img.out, \" fill=\\\"%s\\\"\", svg_fmt_color(color(property.color)))\n    end\nend\n\nfunction print_property(img::SVG, property::StrokeDashPrimitive)\n    if isempty(property.value)\n        print(img.out, \" stroke-dasharray=\\\"none\\\"\")\n    else\n        print(img.out, \" stroke-dasharray=\\\"\")\n        svg_print_float(img.out, property.value[1].value)\n        for i in 2:length(property.value)\n            print(img.out, ',')\n            svg_print_float(img.out, property.value[i].value)\n        end\n        print(img.out, '\"')\n    end\nend\n\n# Format a line-cap specifier into the attribute string that SVG expects.\nsvg_fmt_linecap(::LineCapButt) = \"butt\"\nsvg_fmt_linecap(::LineCapSquare) = \"square\"\nsvg_fmt_linecap(::LineCapRound) = \"round\"\n\nprint_property(img::SVG, property::StrokeLineCapPrimitive) =\n        @printf(img.out, \" stroke-linecap=\\\"%s\\\"\", svg_fmt_linecap(property.value))\n\n# Format a line-join specifier into the attribute string that SVG expects.\nsvg_fmt_linejoin(::LineJoinMiter) = \"miter\"\nsvg_fmt_linejoin(::LineJoinRound) = \"round\"\nsvg_fmt_linejoin(::LineJoinBevel) = \"bevel\"\n\nprint_property(img::SVG, property::StrokeLineJoinPrimitive) =\n        @printf(img.out, \" stroke-linejoin=\\\"%s\\\"\", svg_fmt_linejoin(property.value))\n\nfunction print_property(img::SVG, property::LineWidthPrimitive)\n    print(img.out, \" stroke-width=\\\"\")\n    svg_print_float(img.out, property.value.value)\n    print(img.out, '\"')\nend\n\nfunction print_property(img::SVG, property::FillOpacityPrimitive)\n    print(img.out, \" fill-opacity=\\\"\")\n    svg_print_float(img.out, property.value)\n    print(img.out, '\"')\nend\n\nfunction print_property(img::SVG, property::StrokeOpacityPrimitive)\n    print(img.out, \" stroke-opacity=\\\"\")\n    svg_print_float(img.out, property.value)\n    print(img.out, '\"')\nend\n\nprint_property(img::SVG, property::VisiblePrimitive) =\n        @printf(img.out, \" visibility=\\\"%s\\\"\", property.value ? \"visible\" : \"hidden\")\n\n# I may end up applying the same clip path to many forms separately, so I\n# shouldn't make a new one for each applicaiton. Where should that happen?\nfunction print_property(img::SVG, property::ClipPrimitive)\n    url = clippathurl(img, property)\n    @printf(img.out, \" clip-path=\\\"url(#%s)\\\"\", url)\nend\n\nprint_property(img::SVG, property::FontPrimitive) =\n        @printf(img.out, \" font-family=\\\"%s\\\"\", escape_string(property.family))\n\nfunction print_property(img::SVG, property::FontSizePrimitive)\n    print(img.out, \" font-size=\\\"\")\n    svg_print_float(img.out, property.value.value)\n    print(img.out, '\"')\nend\n\nprint_property(img::SVG, property::SVGIDPrimitive) =\n        @printf(img.out, \" id=\\\"%s\\\"\", escape_string(property.value))\nprint_property(img::SVG, property::SVGClassPrimitive) =\n        @printf(img.out, \" class=\\\"%s\\\"\", escape_string(property.value))\nprint_property(img::SVG, property::SVGAttributePrimitive) =\n        @printf(img.out, \" %s=\\\"%s\\\"\", property.attribute, escape_string(property.value))\n\nfunction print_property(img::SVG, property::JSIncludePrimitive)\n    push!(img.jsheader, property.value)\n    if property.jsmodule != nothing\n        push!(img.jsmodules, property.jsmodule)\n    end\nend\n\nfunction print_property(img::SVG, property::JSCallPrimitive)\n    @assert img.has_current_id\n    push!(img.scripts, @sprintf(\"fig.select(\\\"#%s\\\")\\n   .%s;\", img.current_id, property.code))\nend\n\n# Print the property at the given index in each vector property\nfunction print_vector_properties(img::SVG, idx::Int, suppress_fill::Bool=false)\n    if haskey(img.vector_properties, JSCall)\n        if haskey(img.vector_properties, SVGID)\n            img.current_id = img.vector_properties[SVGID].primitives[idx].value\n        else\n            img.current_id = genid(img)\n            print_property(img, SVGIDPrimitive(img.current_id))\n        end\n        img.has_current_id = true\n    end\n\n    has_stroke_opacity = haskey(img.vector_properties, StrokeOpacity)\n    has_fill_opacity = haskey(img.vector_properties, FillOpacity)\n\n    for (propertytype, property) in img.vector_properties\n        if property === nothing ||\n           (propertytype == Fill && suppress_fill)\n            continue\n        end\n\n        idx > length(property.primitives) &&\n                error(\"Vector form and vector property differ in length. Can't distribute.\")\n\n        # let the opacity primitives clobber the alpha value in fill and stroke\n        if propertytype == Fill && has_fill_opacity\n           print_property(img, FillPrimitive(RGBA{Float64}(color(property.primitives[idx].color), 1.0)))\n        elseif propertytype == Stroke && has_stroke_opacity\n           print_property(img, StrokePrimitive(RGBA{Float64}(color(property.primitives[idx].color), 1.0)))\n        else\n            print_property(img, property.primitives[idx])\n        end\n    end\n\n    img.has_current_id = false\nend\n\n\nfunction print_property(img::SVG, property::ArrowPrimitive)\n    print(img.out, \" marker-end=\\\"url(#arrow)\\\"\")\nend\n\n\n# Form Drawing\n# ------------\n\nfunction draw(img::SVG, form::Form{T}) where T\n    for i in 1:length(form.primitives)\n        draw(img, form.primitives[i], i)\n    end\nend\n\nfunction draw(img::SVG, prim::RectanglePrimitive, idx::Int)\n    # SVG will hide rectangles with zero height or width. We'd prefer to have\n    # zero width/height rectangles stroked, so this is a work-around.\n    width = max(prim.width, 0.01mm)\n    height = max(prim.height, 0.01mm)\n\n    x0 = prim.corner[1] + width/2\n    y0 = prim.corner[2] + height/2\n    translated_path = [(-width/2,-height/2), ( width/2,-height/2),\n                       ( width/2, height/2), (-width/2, height/2)]\n\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, x0.value)\n    print(img.out, \",\")\n    svg_print_float(img.out, y0.value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<path d=\\\"\")\n    print_svg_path(img.out, translated_path)\n    write(img.out, \" z\\\"\")\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\nfunction draw(img::SVG, prim::PolygonPrimitive, idx::Int)\n    n = length(prim.points)\n    n <= 1 && return\n\n    x0, y0 = prim.points[1][1], prim.points[1][2]\n    for p in prim.points[2:end]\n        x0 += p[1]\n        y0 += p[2]\n    end\n    x0, y0 = x0/n, y0/n\n    translated_path = [(p[1]-x0,p[2]-y0) for p in prim.points]\n\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, x0.value)\n    print(img.out, \",\")\n    svg_print_float(img.out, y0.value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    write(img.out, \"<path d=\\\"\")\n    print_svg_path(img.out, translated_path)\n    write(img.out, \" z\\\"\")\n    print(img.out, \" class=\\\"primitive\\\"\")\n    write(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\nfunction draw(img::SVG, prim::ComplexPolygonPrimitive, idx::Int)\n    Compose.write(img.out, \"<path d=\\\"\")\n    for ring in prim.rings\n        indent(img)\n        print_svg_path(img.out, ring)\n        write(img.out, \" \")\n    end\n    write(img.out, \" z\\\"\")\n    print_vector_properties(img, idx)\n    write(img.out, \"/>\\n\")\nend\n\nfunction draw(img::SVG, prim::CirclePrimitive, idx::Int)\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, prim.center[1].value)\n    print(img.out, \",\")\n    svg_print_float(img.out, prim.center[2].value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<circle cx=\\\"0\\\" cy=\\\"0\\\" r=\\\"\")\n    svg_print_float(img.out, prim.radius.value)\n    print(img.out, \"\\\"\")\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\nfunction draw(img::SVG, prim::EllipsePrimitive, idx::Int)\n    cx = prim.center[1].value\n    cy = prim.center[2].value\n    rx = sqrt((prim.x_point[1].value - cx)^2 +\n              (prim.x_point[2].value - cy)^2)\n    ry = sqrt((prim.y_point[1].value - cx)^2 +\n              (prim.y_point[2].value - cy)^2)\n    theta = rad2deg(atan(prim.x_point[2].value - cy,\n                         prim.x_point[1].value - cx))\n\n    all(isfinite,[cx, cy, rx, ry, theta]) || return\n\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, cx)\n    print(img.out, \",\")\n    svg_print_float(img.out, cy)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<ellipse cx=\\\"0\\\" cy=\\\"0\\\" rx=\\\"\")\n    svg_print_float(img.out, rx)\n    print(img.out, \"\\\" ry=\\\"\")\n    svg_print_float(img.out, ry)\n    print(img.out, '\"')\n    if abs(theta) > 1e-4\n        print(img.out, \" transform=\\\"rotate(\")\n        svg_print_float(img.out, theta)\n        print(img.out, \")\\\"\")\n\n    end\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\nfunction draw(img::SVG, prim::LinePrimitive, idx::Int)\n    length(prim.points)<=1 && return\n\n    i = [isfinite(p[1].value) && isfinite(p[2].value) for p in prim.points]\n    any(i) || return\n    x0, y0 = mean(prim.points[i])\n    translated_path = prim.points .- [(x0, y0)]\n\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, x0.value)\n    print(img.out, \",\")\n    svg_print_float(img.out, y0.value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx, true)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<path fill=\\\"none\\\" d=\\\"\")\n    print_svg_path(img.out, translated_path)\n    print(img.out, \"\\\"\")\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\nfunction draw(img::SVG, prim::TextPrimitive, idx::Int)\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, prim.position[1].value)\n    print(img.out, \",\")\n    svg_print_float(img.out, prim.position[2].value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx, true)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<g class=\\\"primitive\\\">\\n\")\n    img.indentation += 1\n    indent(img)\n    print(img.out, \"<text\")\n\n    if prim.halign === hcenter\n        print(img.out, \" text-anchor=\\\"middle\\\"\")\n    elseif prim.halign === hright\n        print(img.out, \" text-anchor=\\\"end\\\"\")\n    end\n\n    # NOTE: \"dominant-baseline\" is the correct way to vertically center text\n    # in SVG, but implementations are pretty inconsistent (chrome in particular\n    # does a really bad job). We fake it by shifting by some reasonable amount.\n    nlines = length(split(prim.value,\"\\n\"))-1\n    if prim.valign === vcenter\n#        print(img.out, \" dy=\\\"0.35em\\\"\")\n        print(img.out, \" dy=\\\"$(svg_fmt_float(0.35-0.6*nlines))em\\\"\")\n        #print(img.out, \" style=\\\"dominant-baseline:central\\\"\")\n    elseif prim.valign === vtop\n        print(img.out, \" dy=\\\"0.6em\\\"\")\n        #print(img.out, \" style=\\\"dominant-baseline:text-before-edge\\\"\")\n    elseif prim.valign === vbottom\n        print(img.out, \" dy=\\\"$(svg_fmt_float(-1.2*nlines))em\\\"\")\n    end\n\n    if abs(prim.rot.theta) > 1e-4 || sum(abs.(prim.offset)) > 1e-4mm\n        print(img.out, \" transform=\\\"\")\n        if abs(prim.rot.theta) > 1e-4\n            print(img.out, \"rotate(\")\n            svg_print_float(img.out, rad2deg(prim.rot.theta))\n            print(img.out, \",\")\n            svg_print_float(img.out, prim.rot.offset[1].value-prim.position[1].value)\n            print(img.out, \", \")\n            svg_print_float(img.out, prim.rot.offset[2].value-prim.position[2].value)\n            print(img.out, \")\")\n        end\n        if sum(abs.(prim.offset)) > 1e-4mm\n            print(img.out, \"translate(\")\n            svg_print_float(img.out, prim.offset[1].value)\n            print(img.out, \",\")\n            svg_print_float(img.out, prim.offset[2].value)\n            print(img.out, \")\")\n        end\n        print(img.out, \"\\\"\")\n    end\n\n    @printf(img.out, \">%s</text>\\n\", pango_to_svg(prim.value))\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\nfunction draw(img::SVG, prim::CurvePrimitive, idx::Int)\n    x0, y0 = prim.anchor0[1], prim.anchor0[2]\n    x0, y0 += prim.ctrl0[1], prim.ctrl0[2]\n    x0, y0 += prim.ctrl1[1], prim.ctrl1[2]\n    x0, y0 += prim.anchor1[1], prim.anchor1[2]\n    x0, y0 = x0/4, y0/4\n    translated_anchor0 = [prim.anchor0...] - [x0,y0]\n    translated_ctrl0 = [prim.ctrl0...] - [x0,y0]\n    translated_ctrl1 = [prim.ctrl1...] - [x0,y0]\n    translated_anchor1 = [prim.anchor1...] - [x0,y0]\n\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, x0.value)\n    print(img.out, \",\")\n    svg_print_float(img.out, y0.value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx, true)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<path fill=\\\"none\\\" d=\\\"M\")\n    svg_print_float(img.out, translated_anchor0[1].value)\n    print(img.out, ',')\n    svg_print_float(img.out, translated_anchor0[2].value)\n    print(img.out, \" C\")\n    svg_print_float(img.out, translated_ctrl0[1].value)\n    print(img.out, ',')\n    svg_print_float(img.out, translated_ctrl0[2].value)\n    print(img.out, ' ')\n    svg_print_float(img.out, translated_ctrl1[1].value)\n    print(img.out, ',')\n    svg_print_float(img.out, translated_ctrl1[2].value)\n    print(img.out, ' ')\n    svg_print_float(img.out, translated_anchor1[1].value)\n    print(img.out, ',')\n    svg_print_float(img.out, translated_anchor1[2].value)\n    print(img.out, '\"')\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\nfunction draw(img::SVG, prim::BitmapPrimitive, idx::Int)\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, prim.corner[1].value)\n    print(img.out, \",\")\n    svg_print_float(img.out, prim.corner[2].value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx, true)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<image x=\\\"0\\\" y=\\\"0\\\" width=\\\"\")\n    svg_print_float(img.out, prim.width.value)\n    print(img.out, \"\\\" height=\\\"\")\n    svg_print_float(img.out, prim.height.value)\n    print(img.out, '\"')\n    print_vector_properties(img, idx)\n\n    print(img.out, \" xlink:href=\\\"data:\", prim.mime, \";base64,\")\n    b64pipe = Base64EncodePipe(img.out)\n    write(b64pipe, prim.data)\n    close(b64pipe)\n    print(img.out, \"\\\"\")\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\n\n\n# FormBatch Drawing\n# -----------------\n\nfunction draw(img::SVG, batch::FormBatch)\n    id = genid(img)\n    push!(img.batches, (batch.primitive, id))\n    for i in 1:length(batch.offsets)\n        indent(img)\n        print(img.out, \"<use xlink:href=\\\"#\", id, \"\\\" x=\\\"\")\n        svg_print_float(img.out, batch.offsets[i][1].value)\n        print(img.out, \"\\\" y=\\\"\")\n        svg_print_float(img.out, batch.offsets[i][2].value)\n        print(img.out, \"\\\"\")\n        print_vector_properties(img, i)\n        print(img.out, \"/>\\n\")\n    end\nend\n\n\n# Applying properties\n# -------------------\n\n# Return a URL corresponding to a ClipPrimitive\nclippathurl(img::SVG, property::ClipPrimitive) = get!(() -> genid(img), img.clippaths, property)\n\nfunction push_property_frame(img::SVG, properties::Vector{Property})\n    isempty(properties) && return\n    svgalphatest(properties)\n\n    frame = SVGPropertyFrame()\n    applied_properties = Set{Type}()\n    scalar_properties = Array{Property}(undef, 0)\n    isplotpanel = false\n    for property in properties\n        if !isrepeatable(property) && (typeof(property) in applied_properties)\n            continue\n        elseif isscalar(property) && isa(property, Clip)\n            # clip-path needs to be in it's own group. Otherwise it can cause\n            # problems if we apply a transform to the group.\n            indent(img)\n            img.indentation += 1\n            write(img.out, \"<g\")\n            print_property(img, property.primitives[1])\n            write(img.out, \">\\n\")\n            frame.has_scalar_clip = true\n        elseif isscalar(property)\n            push!(scalar_properties, property)\n            push!(applied_properties, typeof(property))\n            frame.has_scalar_properties = true\n            img.vector_properties[typeof(property)] = nothing\n        else\n            frame.vector_properties[typeof(property)] = property\n            img.vector_properties[typeof(property)] = property\n        end\n        isplotpanel |= typeof(property)==SVGClass &&\n                length(property.primitives)==1 && property.primitives[1].value==\"plotpanel\"\n    end\n    push!(img.property_stack, frame)\n    isempty(scalar_properties) && return\n\n    id_needed = any([isa(property, JSCall) for property in scalar_properties])\n    for property in scalar_properties\n        if isa(property, SVGID)\n            img.current_id = property.primitives[1].value\n            img.has_current_id = true\n        end\n    end\n\n    if !img.has_current_id\n        img.current_id = genid(img)\n        push!(scalar_properties, svgid(img.current_id))\n        img.has_current_id = true\n    end\n\n    indent(img)\n    write(img.out, \"<g\")\n    for property in scalar_properties\n        print_property(img, property.primitives[1])\n    end\n    write(img.out, \">\\n\");\n    img.has_current_id = false\n    img.indentation += 1\n\n    if isplotpanel\n        indent(img)\n        write(img.out, \"<metadata>\\n\")\n        boundingBox = string(img.panelcoords[1].x0[1], ' ', img.panelcoords[1].x0[2], ' ',\n                          img.panelcoords[1].a[1], ' ', img.panelcoords[1].a[2])\n        unitBox = string(img.panelcoords[2].x0, ' ', img.panelcoords[2].y0, ' ',\n                          img.panelcoords[2].width, ' ', img.panelcoords[2].height)\n        indent(img)\n        write(img.out, \"  <boundingbox value=\\\"$boundingBox\\\"/>\\n\")\n        indent(img)\n        write(img.out, \"  <unitbox value=\\\"$unitBox\\\"/>\\n\")\n        indent(img)\n        write(img.out, \"</metadata>\\n\")\n    end\nend\n\nfunction pop_property_frame(img::SVG)\n    @assert !isempty(img.property_stack)\n    frame = pop!(img.property_stack)\n\n    if frame.has_scalar_properties\n        img.indentation -= 1\n        indent(img)\n        write(img.out, \"</g>\")\n        frame.has_link && write(img.out, \"</a>\")\n        frame.has_mask && write(img.out, \"</mask>\")\n        write(img.out, \"\\n\")\n    end\n\n    if frame.has_scalar_clip\n        img.indentation -= 1\n        indent(img)\n        write(img.out, \"</g>\\n\")\n    end\n\n    for (propertytype, property) in frame.vector_properties\n        img.vector_properties[propertytype] = nothing\n        for i in length(img.property_stack):-1:1\n            if haskey(img.property_stack[i].vector_properties, propertytype)\n                img.vector_properties[propertytype] =\n                    img.property_stack[i].vector_properties[propertytype]\n            end\n        end\n    end\nend\n\n\nfunction Compose.draw(img::SVG, prim::ArcPrimitive, idx::Int)\n    xc = prim.center[1].value\n    yc = prim.center[2].value\n    rx = ry = prim.radius.value\n    x1 =  rx*cos(prim.angle1)\n    y1 =  ry*sin(prim.angle1)\n    x2 =  rx*cos(prim.angle2)\n    y2 =  ry*sin(prim.angle2)\n    dθ = prim.angle2 - prim.angle1\n    dθ += 2π*(dθ<0)\n\n    indent(img)\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, xc)\n    print(img.out, \",\")\n    svg_print_float(img.out, yc)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<path d=\\\"M\")\n    prim.sector && print(img.out,\"0,0 L\")\n    svg_print_float(img.out, x1)\n    print(img.out, \",\")\n    svg_print_float(img.out, y1)\n    print(img.out, \" A\")\n    svg_print_float(img.out, rx)\n    print(img.out, \",\")\n    svg_print_float(img.out, ry)\n    print(img.out, ' ', 0, ' ', dθ>π ? 1 : 0,' ',1,' ')\n    svg_print_float(img.out, x2)\n    print(img.out, \",\")\n    svg_print_float(img.out, y2)\n    prim.sector && print(img.out, \"L0,0\")\n    print(img.out, '\"')\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n\n\n\n# Currently for svg, you can't use a transparent fill(color) and fillopacity() together,\n# so throw a warning if a user tries to do that. \n\nfunction svgalphatest(properties::Vector{Property})\n    has_fill_opacity = any(isa.(properties, Property{FillOpacityPrimitive}))\n    !has_fill_opacity && return\n    is_fill = isa.(properties, Property{FillPrimitive})\n    !any(is_fill) && return\n    fillproperties = properties[is_fill][1]\n    has_alpha = any([alpha(x.color) for x in fillproperties.primitives].<1.0) \n    has_alpha && @warn \"For svg transparent colors, use either e.g. fill(RGBA(r,g,b,a)) or fillopacity(a), but not both.\"\nend\n\n\nfunction draw(img::SVG, prim::BezierPolygonPrimitive, idx::Int)\n\n    points = [prim.anchor; reduce(vcat, prim.sides)]\n    x0 = sum(first.(points))/length(points)\n    y0 = sum(last.(points))/length(points)\n\n    sv = Vector{Vec}[]\n    for side in prim.sides\n        s = collect(Tuple{Measure, Measure}, zip(first.(side).-x0, last.(side).-y0))\n        push!(sv, s)\n    end\n    anchor0 = (prim.anchor[1]-x0, prim.anchor[2]-y0)\n\n    P = Dict(1=>\" L\", 2=>\" Q\", 3=>\" C\")\n    indent(img)\n\n    img.indentation += 1\n    print(img.out, \"<g transform=\\\"translate(\")\n    svg_print_float(img.out, x0.value)\n    print(img.out, \",\")\n    svg_print_float(img.out, y0.value)\n    print(img.out, \")\\\"\")\n    print_vector_properties(img, idx, true)\n    print(img.out, \">\\n\")\n    indent(img)\n\n    print(img.out, \"<path d=\\\"M\")\n    svg_print_float(img.out, anchor0[1].value)\n    print(img.out, \",\")\n    svg_print_float(img.out, anchor0[2].value)\n\n    for side in sv\n        print(img.out, P[length(side)])\n            for point in side\n                svg_print_float(img.out, point[1].value)\n                print(img.out, ',')\n                svg_print_float(img.out, point[2].value)\n                print(img.out, ' ')\n            end\n    end\n\n    print(img.out, \"z\\\"\")\n    print(img.out, \" class=\\\"primitive\\\"\")\n    print(img.out, \"/>\\n\")\n\n    img.indentation -= 1\n    indent(img)\n    print(img.out, \"</g>\\n\")\nend\n"
  },
  {
    "path": "src/table-jump.jl",
    "content": "using JuMP\n\nis_approx_integer(x::Float64) = abs(x - round(x)) < 1e-8\n\nfunction realize(tbl::Table, drawctx::ParentDrawContext)\n    model = Model()\n\n    m, n = size(tbl.children)\n\n    abswidth = drawctx.box.a[1].value\n    absheight = drawctx.box.a[2].value\n\n    c_indexes = Tuple{Int, Int, Int}[]\n    idx_cs = Dict{(Tuple{Int, Int}), Vector{Int}}()\n    for i in 1:m, j in 1:n\n        if length(tbl.children[i, j]) > 1\n            for k in 1:length(tbl.children[i, j])\n                push!(c_indexes, (i, j, k))\n\n                if haskey(idx_cs, (i, j))\n                    push!(idx_cs[(i,j)], length(c_indexes))\n                else\n                    idx_cs[(i,j)] = [length(c_indexes)]\n                end\n            end\n        end\n    end\n\n    penalties = Array{Float64}(undef, length(c_indexes))\n    for (l, (i, j, k)) in enumerate(c_indexes)\n        penalties[l] = tbl.children[i, j][k].penalty\n    end\n\n    # 0-1 configuration variables for every cell with multiple configurations\n    @defVar(model, c[1:length(c_indexes)], Bin)\n\n    # width for every column\n    @defVar(model, 0 <= w[1:n] <= abswidth)\n\n    # height for every row\n    @defVar(model, 0 <= h[1:m] <= absheight)\n\n    # maximize the \"size\" of the focused cells\n    @setObjective(model, Max, sum{w[j], j=tbl.x_focus} +\n                              sum{h[i], i=tbl.y_focus} -\n                              sum{penalties[i] * c[i], i=1:length(c_indexes)})\n\n    # optional proportionality constraints\n    if tbl.x_prop != nothing\n        k1 = 1\n        while k1 < length(tbl.x_focus) && isnan(tbl.x_prop[k1])\n            k1 += 1\n        end\n\n        for k in 2:length(tbl.x_focus)\n            isnan(tbl.x_prop[k]) && continue\n\n            j1 = tbl.x_focus[k1]\n            jk = tbl.x_focus[k]\n            @addConstraint(model, w[j1] / tbl.x_prop[k1] ==  w[jk] / tbl.x_prop[k])\n        end\n    end\n\n    if tbl.y_prop != nothing\n        k1 = 1\n        while k1 < length(tbl.y_focus) && isnan(tbl.y_prop[k1])\n            k1 += 1\n        end\n\n        for k in 2:length(tbl.y_focus)\n            isnan(tbl.y_prop[k]) && continue\n\n            i1 = tbl.y_focus[k1]\n            ik = tbl.y_focus[k]\n            @addConstraint(model, h[i1] / tbl.y_prop[k1] ==  h[ik] / tbl.y_prop[k])\n        end\n    end\n\n    # fixed configuration constraints: constrain a set of cells\n    # to have tho same configuration.\n    for fixed_config in tbl.fixed_configs\n        isempty(fixed_config) && continue\n\n        (i0, j0) = fixed_config[1]\n        config_count = length(tbl.children[i0, j0])\n        for (i, j) in fixed_config[2:end], k in 1:config_count\n            !haskey(idx_cs, (i0, j0)) && !haskey(idx_cs, (i, j)) && continue\n            idx_a = idx_cs[i0, j0][k]\n            idx_b = idx_cs[(i,j)][k]\n            @addConstraint(model, c[idx_a] == c[idx_b])\n        end\n    end\n\n    # configurations are mutually exclusive\n    for cgroup in groupby(l -> (c_indexes[l][1], c_indexes[l][2]),\n                          1:length(c_indexes))\n        @addConstraint(model, sum{c[l], l=cgroup} == 1)\n    end\n\n    # minimum cell size constraints for cells with multiple configurations\n    for (l, (i, j, k)) in enumerate(c_indexes)\n        minw = minwidth(tbl.children[i, j][k])\n        minh = minheight(tbl.children[i, j][k])\n        minw == nothing || @addConstraint(model, w[j] >= minw * c[l])\n        minh == nothing || @addConstraint(model, h[i] >= minh * c[l])\n    end\n\n    # minimum cell size constraint for fixed cells\n    for i in 1:m, j in 1:n\n        if length(tbl.children[i, j]) == 1\n            minw = minwidth(tbl.children[i, j][1])\n            minh = minheight(tbl.children[i, j][1])\n            minw == nothing || @addConstraint(model, w[j] >= minw)\n            minh == nothing || @addConstraint(model, h[i] >= minh)\n        end\n    end\n\n    # widths and heights must add up\n    @addConstraint(model, sum{w[i], i=1:n} == abswidth)\n    @addConstraint(model, sum{h[i], i=1:m} == absheight)\n\n    status = solve(model,suppress_warnings=true)\n\n    w_solution = getValue(w)[:]\n    h_solution = getValue(h)[:]\n    c_solution = getValue(c)\n\n    if status == :Infeasible ||\n            !all([is_approx_integer(c_solution[l]) for l in 1:length(c_indexes)])\n        #println(STDERR, \"JuMP: Infeasible\")\n        # The brute force solver is better able to select between various\n        # non-feasible solutions. So we let it have a go.\n        return realize_brute_force(tbl, drawctx)\n    end\n\n    # Set positions and sizes of children\n    root = context(units=tbl.units, order=tbl.order)\n\n    x_solution = cumsum([w_solution[j] for j in 1:n]) .- w_solution\n    y_solution = cumsum([h_solution[i] for i in 1:m]) .- h_solution\n\n    tbl.aspect_ratio == nothing ||\n            force_aspect_ratio!(tbl, x_solution, y_solution, w_solution, h_solution)\n\n    # set child positions according to layout solution\n    feasible_eps = 1e-4\n    feasible = true\n    for i in 1:m, j in 1:n\n        if length(tbl.children[i, j]) == 1\n            ctx = copy(tbl.children[i, j][1])\n            feasible == feasible && issatisfied(ctx, w_solution[j], h_solution[i])\n            ctx.box = BoundingBox(\n                x_solution[j]*mm, y_solution[i]*mm,\n                w_solution[j]*mm, h_solution[i]*mm)\n            compose!(root, ctx)\n        end\n    end\n\n    for (l, (i, j, k)) in enumerate(c_indexes)\n        if round(c_solution[l]) == 1\n            ctx = copy(tbl.children[i, j][k])\n            feasible == feasible && issatisfied(ctx, w_solution[j], h_solution[i])\n            ctx.box = BoundingBox(\n                x_solution[j]*mm, y_solution[i]*mm,\n                w_solution[j]*mm, h_solution[i]*mm)\n            compose!(root, ctx)\n        end\n    end\n\n    feasible || warn(\"Graphic may not be drawn correctly at the given size.\")\n\n    return root\nend\n"
  },
  {
    "path": "src/table.jl",
    "content": "# A special kind of container promise that performs table layout optimization.\n\nmutable struct Table <: ContainerPromise\n    # Direct children must be Contexts, and not just Containers. If\n    # children[i,j] has a vector with multiple children it indicates multiple\n    # possible layouts for that cell in the table.\n    children::Matrix{Vector{Context}}\n\n    # In the formulation of the table layout problem used here, we are trying\n    # find a feasible solution in which the width + height of a particular\n    # group of cells in the table is maximized.\n    x_focus::UnitRange{Int}\n    y_focus::UnitRange{Int}\n\n    # If non-nothing, constrain the focused cells to have a proportional\n    # relationship.\n    x_prop::Union{Vector{Float64}, Nothing}\n    y_prop::Union{Vector{Float64}, Nothing}\n\n    # If non-nothing, constrain the focused cells to have a fixed aspect ratio.\n    aspect_ratio::Union{Float64, Nothing}\n\n    # fixed configuration\n    fixed_configs::Vector\n\n    # Coordinate system used for children\n    units::Union{UnitBox, Nothing}\n\n    # Z-order of this context relative to its siblings.\n    order::Int\n\n    # Ignore this context and everything under it if we are\n    # not drawing to the javascript backend.\n    withjs::Bool\n\n    # Ignore this context if we are drawing to the SVGJS backend.\n    withoutjs::Bool\nend\n\nfunction Table(m::Integer, n::Integer, y_focus::UnitRange{Int}, x_focus::UnitRange{Int};\n               y_prop=nothing, x_prop=nothing,\n               aspect_ratio=nothing,\n               units=nothing, order=0, withjs=false, withoutjs=false,\n               fixed_configs=Any[])\n\n    if x_prop != nothing\n        @assert length(x_prop) == length(x_focus)\n        x_prop ./= sum(filter(x -> !isnan(x), x_prop))\n    end\n\n    if y_prop != nothing\n        @assert length(y_prop) == length(y_focus)\n        y_prop ./= sum(filter(x -> !isnan(x), y_prop))\n    end\n\n    tbl = Table(Array{Vector{Context}}(undef, (m, n)),\n              x_focus, y_focus,\n              x_prop, y_prop,\n              aspect_ratio,\n              fixed_configs,\n              units, order, withjs, withoutjs)\n    for i in 1:m, j in 1:n\n        tbl.children[i, j] = Array{Context}(undef, 0)\n    end\n    return tbl\nend\n\nconst table = Table\n\ngetindex(t::Table, i::Integer, j::Integer) = t.children[i, j]\nsetindex!(t::Table, child, i::Integer, j::Integer) = t.children[i, j] = child\nsize(t::Table, i::Integer) = size(t.children, i)\nsize(t::Table) = size(t.children)\n\n# Adjust a table solution so that the aspect ratio matches tbl.aspect_ratio\n#\n# Returns:\n#   true if the solution is feasibly, false if not\n#\n# Modifies:\n#   x_solution, y_solution, w_solution, h_solution\n#\nfunction force_aspect_ratio!(tbl::Table,\n                             x_solution::Vector, y_solution::Vector,\n                             w_solution::Vector, h_solution::Vector)\n\n    w0 = sum(w_solution[tbl.x_focus])\n    h0 = sum(h_solution[tbl.y_focus])\n    adj = (w0 / h0) / tbl.aspect_ratio\n\n    # we can't expand either dimension (since it's presumably of maximum size)\n    # so shrink one or the other.\n    if adj > 1.0\n        w = w0 / adj\n        delta = w0 - w\n        x_solution[1:tbl.x_focus.stop] .+= delta/2\n        x_solution[tbl.x_focus.stop+1:end] .-= delta/2\n        w_solution[tbl.x_focus] ./= adj\n    else\n        w = w0\n        h = h0 * adj\n        delta = h0 - h\n        y_solution[1:tbl.y_focus.stop] .+= delta/2\n        y_solution[tbl.y_focus.stop+1:end] .-= delta/2\n        h_solution[tbl.y_focus] *= adj\n    end\nend\n\n# Return true if the minwidth and minheight constraints on ctx are satisfied\n# by the given width/height\nfunction issatisfied(ctx::Context, width, height)\n    eps = 1e-4\n    return (minwidth(ctx) == nothing || width + eps >= minwidth(ctx)) &&\n           (minheight(ctx) == nothing || height + eps >= minheight(ctx))\nend\n\n# Solve the table layout using a brute force approach, when a MILP isn't\n# available.\nfunction realize_brute_force(tbl::Table, drawctx::ParentDrawContext)\n    m, n = size(tbl.children)\n\n    maxobjective = 0.0\n    optimal_choice = nothing\n    feasible = false # is the current optimal_choice feasible\n\n    # if the current solution is infeasible, we try to minimize badness,\n    # which is basically \"size needed\" - \"size available\".\n    minbadness = Inf\n\n    focused_col_widths = Array{Float64}(undef, length(tbl.x_focus))\n    focused_row_heights = Array{Float64}(undef, length(tbl.y_focus))\n\n    # minimum sizes for each column and row\n    minrowheights = Array{Float64}(undef, m)\n    mincolwidths = Array{Float64}(undef, n)\n\n    # convert tbl.fixed_configs to linear indexing\n    fixed_configs = Any[\n        Set([(j-1)*m + i for (i, j) in fixed_config])\n        for fixed_config in tbl.fixed_configs\n    ]\n\n    # build equilavence classes of configurations basen on fixed_configs\n    constrained_cells = Set()\n    num_choices = [length(child) for child in tbl.children]\n    num_group_choices = Any[]\n    for fixed_config in fixed_configs\n        push!(num_group_choices, num_choices[first(fixed_config)])\n        for idx in fixed_config\n            push!(constrained_cells, idx)\n        end\n    end\n\n    for (idx, child) in enumerate(tbl.children)\n        if length(child) > 1\n            if !in(idx, constrained_cells)\n                push!(num_group_choices, length(child))\n                push!(fixed_configs, Set([idx]))\n            end\n        end\n    end\n\n    # compute the optimal column widths/row heights for fixed choice and\n    # pre-computed minrowheight/mincolwidths.\n    function update_focused_col_widths!(focused_col_widths)\n        minwidth = sum(mincolwidths)\n        total_focus_width =\n            drawctx.box.a[1].value - minwidth + sum(mincolwidths[tbl.x_focus])\n\n        if tbl.x_prop != nothing\n            for k in 1:length(tbl.x_focus)\n                if isnan(tbl.x_prop[k])\n                    total_focus_width -= mincolwidths[tbl.x_focus[k]]\n                end\n            end\n\n            for k in 1:length(tbl.x_focus)\n                if !isnan(tbl.x_prop[k])\n                    focused_col_widths[k] = tbl.x_prop[k] * total_focus_width\n                else\n                    focused_col_widths[k] = mincolwidths[tbl.x_focus[k]]\n                end\n            end\n        else\n            extra_width = total_focus_width - sum(mincolwidths[tbl.x_focus])\n            for k in 1:length(tbl.x_focus)\n                focused_col_widths[k] =\n                        mincolwidths[tbl.x_focus[k]] + extra_width / length(tbl.x_focus)\n            end\n        end\n    end\n\n    function update_focused_row_heights!(focused_row_heights)\n        total_focus_height =\n                drawctx.box.a[2].value - sum(minrowheights) + sum(minrowheights[tbl.y_focus])\n\n        if tbl.y_prop != nothing\n            for k in 1:length(tbl.y_focus)\n                if isnan(tbl.y_prop[k])\n                    total_focus_height -= minrowheights(tbl.y_focus[k])\n                end\n            end\n\n            for k in 1:length(tbl.y_focus)\n                if !isnan(tbl.y_prop[k])\n                    focused_row_heights[k] = tbl.y_prop[k] * total_focus_height\n                else\n                    focused_row_heights[k] = minrowheights[tbl.y_focus[k]]\n                end\n            end\n        else\n            extra_height = total_focus_height - sum(minrowheights[tbl.y_focus])\n            for k in 1:length(tbl.y_focus)\n                focused_row_heights[k] =\n                        minrowheights[tbl.y_focus[k]] + extra_height / length(tbl.y_focus)\n            end\n        end\n    end\n\n    # for a given configuration, compute the minimum width for every column and\n    # minimum height for every row. Return the penalty for choice.\n    function update_mincolrow_sizes!(choice, minrowheights, mincolwidths)\n        fill!(minrowheights, -Inf)\n        fill!(mincolwidths, -Inf)\n        penalty = 0.0\n        for i in 1:m, j in 1:n\n            isempty(tbl.children[i, j]) && continue\n\n            choice_ij = choice[(j-1)*m + i]\n            child = tbl.children[i, j][(choice_ij == 0 ? 1 : choice_ij)]\n            penalty += child.penalty\n            mw, mh = minwidth(child), minheight(child)\n            if mw != nothing && mw > mincolwidths[j]\n                mincolwidths[j] = mw\n            end\n            if mh != nothing && mh > minrowheights[i]\n                minrowheights[i] = mh\n            end\n        end\n\n        minrowheights[isfinite.(minrowheights) .== false] .= 0.0\n        mincolwidths[isfinite.(mincolwidths) .== false] .= 0.0\n\n        return penalty\n    end\n\n    it_count = 0\n    group_choices = [l == 0 ? (0:0) : (1:l) for l in num_group_choices]\n    choice = zeros(Int, m * n)\n    optimal_choice = nothing\n    if !isempty(group_choices)\n        for group_choice in Iterators.product(group_choices...)\n            it_count += 1\n            for (l, k) in enumerate(group_choice)\n                for p in fixed_configs[l]\n                    choice[p] = k\n                end\n            end\n\n            penalty = update_mincolrow_sizes!(choice, minrowheights, mincolwidths)\n\n            minheightval = sum(minrowheights)\n            minwidthval = sum(mincolwidths)\n\n            update_focused_col_widths!(focused_col_widths)\n            update_focused_row_heights!(focused_row_heights)\n\n            objective = sum(focused_col_widths) + sum(focused_row_heights) - penalty\n\n            # feasible?\n            if minwidthval < drawctx.box.a[1].value && minheightval < drawctx.box.a[2].value &&\n               all(focused_col_widths .>= mincolwidths[tbl.x_focus]) &&\n               all(focused_row_heights .>= minrowheights[tbl.y_focus])\n                if objective > maxobjective || !feasible\n                    maxobjective = objective\n                    minbadness = 0.0\n                    optimal_choice = copy(choice)\n                end\n                feasible = true\n            else\n                badness = max(minwidthval - drawctx.box.a[1].value, 0.0) +\n                          max(minheightval - drawctx.box.a[2].value, 0.0)\n                if badness < minbadness && !feasible\n                    minbadness = badness\n                    optimal_choice = copy(choice)\n                end\n            end\n\n            if optimal_choice === nothing\n                optimal_choice = copy(choice)\n            end\n        end\n    end\n\n    if optimal_choice === nothing\n        optimal_choice = zeros(Int, m * n)\n    end\n\n    update_mincolrow_sizes!(optimal_choice, minrowheights, mincolwidths)\n    update_focused_col_widths!(focused_col_widths)\n    update_focused_row_heights!(focused_row_heights)\n\n    w_solution = mincolwidths\n    h_solution = minrowheights\n\n    for k in 1:length(tbl.x_focus)\n        w_solution[tbl.x_focus[k]] = focused_col_widths[k]\n    end\n\n    for k in 1:length(tbl.y_focus)\n        h_solution[tbl.y_focus[k]] = focused_row_heights[k]\n    end\n\n    x_solution = cumsum(mincolwidths) .- w_solution\n    y_solution = cumsum(minrowheights) .- h_solution\n\n    if tbl.aspect_ratio != nothing\n        force_aspect_ratio!(tbl, x_solution, y_solution, w_solution, h_solution)\n    end\n\n    root = context(units=tbl.units, order=tbl.order)\n\n    feasible = true\n    for i in 1:m, j in 1:n\n        if isempty(tbl.children[i, j])\n            continue\n        elseif length(tbl.children[i, j]) == 1\n            ctx = copy(tbl.children[i, j][1])\n        elseif length(tbl.children[i, j]) > 1\n            idx = optimal_choice[(j-1)*m + i]\n            ctx = copy(tbl.children[i, j][idx])\n        end\n        feasible == feasible && issatisfied(ctx, w_solution[j], h_solution[i])\n        ctx.box = BoundingBox(\n            x_solution[j]*mm, y_solution[i]*mm,\n            w_solution[j]*mm, h_solution[i]*mm)\n        compose!(root, ctx)\n    end\n\n    feasible || warn(\"Graphic may not be drawn correctly at the given size.\")\n\n    return root\nend\n\n# TODO: Enable this when we have a mechanism for making it optional\n#if isinstalled(\"JuMP\") &&\n    #(isinstalled(\"GLPKMathProgInterface\") ||\n     #isinstalled(\"Cbc\"))\n    #include(\"table-jump.jl\")\n#else\nrealize(tbl::Table, drawctx::ParentDrawContext) = realize_brute_force(tbl, drawctx)\n#end\n\nfunction show(io::IO, t::Table)\n    if get(io, :compact, false)\n        println(io,\"$(size(t.children,1))x$(size(t.children,2)) Table:\")\n        for i = 1:size(t.children,1)\n            print(io, \"  \")\n            first = true\n            for j = 1:size(t.children,2)\n                first || print(io, \",\")\n                first = false\n                show(io, t.children[i,j])\n            end\n            println(io)\n        end\n    else\n        invoke(show, Tuple{IO, Any}, io, t)\n    end\nend\n"
  },
  {
    "path": "test/.gitignore",
    "content": "/*.pdf\n"
  },
  {
    "path": "test/Project.toml",
    "content": "[deps]\nCairo = \"159f3aea-2a34-519c-b102-8c37f9878175\"\nColors = \"5ae59095-9a9b-59fe-a467-6f913c188581\"\nEzXML = \"8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615\"\nFontconfig = \"186bb1d3-e1f7-5a2c-a377-96d770f13627\"\nMeasures = \"442fdcdd-2543-5da2-b0f3-8c86c306513e\"\nRandom = \"9a3f8284-a2c9-5f02-9a11-845980a1fd5c\"\nStatistics = \"10745b16-79ce-11e8-11f9-7d13ad32a3b2\"\nTest = \"8dfed614-e22c-5e08-85e1-65c5234f0b40\"\n\n[compat]\nCairo = \"0.7, 0.8, 1.0\"\nEzXML = \"0.8, 0.9, 1.0\"\nFontconfig = \"0.3, 0.4\"\n"
  },
  {
    "path": "test/examples/arc_sector.jl",
    "content": "using Compose\nimport Cairo, Fontconfig\n\nimgs = [SVG(\"arc_sector.svg\", 7cm, 7cm),\n        PDF(\"arc_sector.pdf\", 7cm, 7cm)]\n\na = range(-0.5π, stop=1.5π, length=13)\n    colv = repeat([\"white\",\"black\"], outer=6)\n    \nimg = compose(context(),\n    (context(), sector([0.5], [0.5], [0.4], a[1:12], a[2:13]), fill(colv)),\n    (context(order=2), arc([0.5], [0.5], [0.2], [0, π], [π,0]), \n            fill([\"black\",\"white\"]), stroke([\"white\",\"black\"]), linewidth(6pt))\n    ) \n\ndraw.(imgs, [img])\n\n\n\n\n\n\n\n"
  },
  {
    "path": "test/examples/arrow.jl",
    "content": "using Compose\nimport Cairo, Fontconfig\n\nimgs = [SVG(\"arrow.svg\", 5cm, 5cm),\n        PDF(\"arrow.pdf\", 5cm, 5cm)]\n\nX =  [0.047 0.87 0.95 0.93;\n0.22 0.01 0.21 0.7;\n0.86 0.85 0.95 0.21]\n\npoint_array = [[(x1,y1), (x2,y2)] for (x1,y1,x2,y2) in zip(X[:,1],X[:,2],X[:,3],X[:,4])]\nimg = compose(context(),\n    (context(), line(point_array), stroke([\"red\",\"green\",\"deepskyblue\"]), arrow())\n)\n\ndraw.(imgs, [img])\n"
  },
  {
    "path": "test/examples/bezigon.jl",
    "content": "using Compose\nimport Cairo, Fontconfig\n\nset_default_graphic_size(6.6inch, 3.3inch)\n\n# See Picasso's \"dog\" sketch\ndog = [[(183, 268), (186, 256), (189, 244)], [(290, 244), (300, 230), (339, 245)], [(350,290), (360, 300), (355, 210)], \n[(370, 207), (380,196), (375, 193)], [(310, 220), (190, 220), (164, 205)], [(135, 194), (135, 265), (153, 275)],\n[(168, 275), (170, 180), (150, 190)], [(122, 214), (142, 204), (85, 240)], [(100, 247), (125, 233), (140, 238)]]\n\n\nub = UnitBox(0,0, 500,500)\np = compose(context(), stroke(\"black\"), fillopacity(0.1),\n    (context(0,0, 0.5,1, units=ub,  rotation=Rotation(-π/8)), bezigon((180, 280), dog)),\n    (context(0.5,0, 0.5,1, units=ub, rotation=Rotation(π/8)),  bezigon([(180, 280)], [dog]))\n)\n\nimgs = [SVG(\"bezigon.svg\"), PNG(\"bezigon.png\")]\n\ndraw.(imgs, [p])\n\n"
  },
  {
    "path": "test/examples/dashedlines.jl",
    "content": "# Draw lines with various dash styles.\n\nusing Compose, Colors\nimport Cairo, Fontconfig\n\nfunction draw_lines(dash_patterns)\n    if length(dash_patterns) == 0\n        context()\n    else\n        compose(compose(context(), line([(0, 0), (1, 0)]), strokedash(dash_patterns[1])),\n                compose(context(0, 0.1), draw_lines(dash_patterns[2:end])))\n    end\nend\n\npatterns = Array[\n    [5mm, 5mm],\n    [5mm, 10mm],\n    [10mm, 5mm],\n    [5mm, 1mm],\n    [1mm, 5mm],\n    [0.9mm],\n    [15mm, 10mm, 5mm],\n    [15mm, 10mm, 5mm, 10mm],\n    [15mm, 10mm, 5mm, 10mm, 15mm],\n    [5mm, 5mm, 1mm, 5mm]]\n\n\nc = draw_lines(patterns)\n\nimgs = [PDF(\"dash.pdf\", 4inch, 4(sqrt(3)/2)inch),\n        PGF(\"dash.pgf\", 4inch, 4(sqrt(3)/2)inch),\n        PGF(\"dash_texfonts.pgf\", 4inch, 4(sqrt(3)/2)inch; texfonts=true)]\nfor img = imgs\n  compose(c, stroke(\"black\"), linewidth(1mm)) |> img\nend\n"
  },
  {
    "path": "test/examples/forms_and_nans.jl",
    "content": "using Compose\nimport Cairo, Fontconfig\n\nset_default_graphic_size(14cm, 10cm)\n\n\ny = [0.26, 0.5, missing, 0.4, NaN, 0.48, 0.58, 0.83]\np1 = collect(Tuple, zip(1:8, y))\np2 = collect(Tuple, zip(1:9, vcat(NaN, y)))\n\nimg = compose(context(units=UnitBox(0, 0, 10, 1)),\n    (context(), line([p1]), stroke(\"black\")),\n    (context(), line([p2]), stroke(\"red\"))\n)\n\nimgs = [SVG(\"forms_and_nans.svg\"), PDF(\"forms_and_nans.pdf\")]\n\ndraw.(imgs, [img])\n\n\n\n\n"
  },
  {
    "path": "test/examples/golden_rect.jl",
    "content": "using Compose, Colors\nusing Base.MathConstants\n\nfunction golden_rect(n::Int)\n    poly_points = [(0, 0), (1, 0), (1, 1), (0, 1)]\n    if n == 0 return context() end\n    c = compose(context(), polygon(poly_points), fill(LCHab(90, 80, 70-20n)), stroke(\"black\"))\n    compose(c, (context(0,0,1/φ,1/φ, rotation=Rotation(-π/2,1,0)),  golden_rect(n-1)))\nend\n\ndraw(SVG(\"golden_rect.svg\", φ*3inch, 3inch),\n     compose(golden_rect(8), linewidth(0.2mm)))\n"
  },
  {
    "path": "test/examples/linecaps.jl",
    "content": "# Draw lines with various dash styles.\n\nusing Compose, Colors\nimport Cairo, Fontconfig\n\nfunction draw_lines(caps)\n    if length(caps) == 0\n        context()\n    else\n        compose(compose(context(), line([(5mm, 5mm), (15mm, 5mm)]), strokelinecap(caps[1])),\n                compose(context(0, 5mm), draw_lines(caps[2:end])))\n    end\nend\n\ncaps = [LineCapButt(), LineCapSquare(), LineCapRound()]\nc = draw_lines(caps)\n\nimgs = [SVG(\"linecaps.svg\", 2cm, 3cm),\n        PDF(\"linecaps.pdf\", 2cm, 3cm)]\nfor img = imgs\n    draw(img, compose(c, stroke(\"black\"), linewidth(2mm)))\nend\n"
  },
  {
    "path": "test/examples/linejoins.jl",
    "content": "# Draw lines with various dash styles.\n\nusing Compose, Colors\nimport Cairo, Fontconfig\n\nfunction draw_lines(joins)\n    if length(joins) == 0\n        context()\n    else\n        compose(compose(context(), line([(5mm, 5mm), (10mm, 10mm), (15mm, 5mm)]), strokelinejoin(joins[1])),\n                compose(context(0, 5mm), draw_lines(joins[2:end])))\n    end\nend\n\njoins = [Compose.LineJoinRound(), Compose.LineJoinMiter(), Compose.LineJoinBevel()]\nc = draw_lines(joins)\n\nimgs = [SVG(\"linejoins.svg\", 2cm, 3cm),\n        PDF(\"linejoins.pdf\", 2cm, 3cm),\n        SVGJS(\"linejoins.js\", 2cm, 3cm)]\nfor img = imgs\n    draw(img, compose(c, stroke(\"black\"), linewidth(2mm), fill(nothing)))\nend\n"
  },
  {
    "path": "test/examples/polygon_forms.jl",
    "content": "using Compose\n\ncompose(context(),\n    ngon(0.15, 0.15, 0.08, 5),\n    star(0.35, 0.15, 0.08, 5, 0.3),\n    xgon(0.55, 0.15, 0.08, 5, 0.3)\n) |> SVG(\"polygon_forms.svg\", 3inch, 3inch)\n"
  },
  {
    "path": "test/examples/primitives.jl",
    "content": "using Compose\n\nrawimg = read(joinpath(@__DIR__,\"smiley.png\"));\n\ncompose(context(),\n    rectangle(0.1,0.1,0.1,0.1),\n    circle(0.3,0.15,0.05),\n    polygon([(0.45,0.1),(0.4,0.2),(0.5,0.2)]),\n    ellipse(0.65,0.15,0.1,0.05),\n    (context(), stroke(\"black\"),\n        line([(0.1,0.3),(0.2,0.3)]),\n        curve((0.25,0.35),(0.25,0.25),(0.35,0.25),(0.35,0.35))),\n    bitmap(\"image/png\",rawimg,0.4,0.25,0.1,0.1),\n    text(0.6,0.3,\"hello\")) |> SVG(\"primitives.svg\")\n"
  },
  {
    "path": "test/examples/text.jl",
    "content": "# Draw some text to a PNG image.\n\nusing Compose\n\nlines = \"hello and goodbye\\nFoo<sub>Sub</sub>Bar<sup>Sup</sup>\\nA Third Line\\nFoo<sub>Sub</sub>Bar<sup>Sup</sup>Pooh\\nA Fifth Line\\nA Sixth Line\"\nc = compose(context(),\n            (context(), text(0px, 250px, lines), fontsize(8pt), fill(\"tomato\")),\n            (context(), text(100px, 250px, lines), fontsize(24pt), fill(\"bisque\")))\ndraw(SVG(\"text-fontfallback.svg\", 400px, 400px), c)\n\nimport Cairo, Fontconfig\n\ndraw(SVG(\"text-pango.svg\", 400px, 400px), c)\ndraw(PNG(\"text.png\", 400px, 400px, dpi=192), c)\ndraw(PDF(\"text.pdf\", 400px, 400px), c)\n"
  },
  {
    "path": "test/examples/transformations.jl",
    "content": "using Compose\nimport Cairo, Fontconfig\n\nimgs = [SVG(\"transformations.svg\", 7cm, 7cm),\n        PDF(\"transformations.pdf\", 7cm, 7cm)]\n\n\nimg = compose(context(),\n    (context(0,0,0.5,0.5), xgon(0.2,0.2,0.1,3,0.3),\n    (context(mirror=Mirror(-π/4, 0.4,0.4)), xgon(0.2,0.2,0.1,3,0.3)) ),\n    (context(0.5,0,0.5,0.5), xgon(0.5,0.5,0.2,3,0.3), fill(\"silver\"),\n    (context(shear=Shear(0.8, 0, 0.5,0.5)), xgon(0.5,0.5,0.2,3,0.3), fill(nothing), stroke(\"black\")) )\n)\n\ndraw.(imgs, [img])\n\n"
  },
  {
    "path": "test/examples/unicode.jl",
    "content": "# Draw some unicodes to an image.\n# https://github.com/GiovineItalia/Compose.jl/pull/360#issuecomment-539283765\n\nusing Compose\nimport Cairo, Fontconfig\n\nc = compose(context(), text(0.5, 0.5, \"𝑎 𝑏 𝑐 𝑑 𝑒 𝑓\", hcenter, vcenter), font(\"Segoe UI\"), fontsize(20pt) ) # Note: the string is composed with \\ita \\itb etc.\n\nimgs = [\n    PDF(\"unicode.pdf\")\n    SVG(\"unicode.svg\")\n    PNG(\"unicode.png\")\n]\n\ndraw.(imgs, [c])\n"
  },
  {
    "path": "test/immerse.jl",
    "content": "# These tests are designed to ensure that Immerse.jl works. If you\n# need to edit these tests to make them pass, that's fine, but please\n# submit the corresponding fix to Immerse.\n\nusing Test\nusing Compose\nimport Cairo\nimport Measures\n\n### The Immerse backend\nsrf = Cairo.CairoImageSurface(10, 10, Cairo.FORMAT_RGB24)\nctx = compose(compose(context(), rectangle(0.0w, 0.0h, 0.8w, 0.7h, :rect)), fill(\"tomato\"))\nbe = Compose.ImmerseBackend(srf)\ndraw(be, ctx)\n@test be.coords[:rect][2] == Compose.UnitBox()\n\n### Finding tagged objects\nconst ContainersWithChildren = Union{Context,Compose.Table}\nconst Iterables = Union{ContainersWithChildren, AbstractArray}\n\niterable(ctx::ContainersWithChildren) = ctx.children\niterable(a::AbstractArray) = a\n\nfunction find_tagged(root)\n    handles = Dict{Symbol,Context}()\n    find_tagged!(handles, root)\nend\n\nfunction find_tagged!(handles, obj::Iterables)\n    for item in iterable(obj)\n        if has_tag(item)\n            handles[item.tag] = obj\n        else\n            find_tagged!(handles, item)\n        end\n    end\n    handles\nend\n\nfunction find_tagged!(handles, obj::Context)\n    for item in obj.form_children\n        if has_tag(item)\n            handles[item.tag] = obj\n        end\n    end\n    for item in obj.container_children\n        find_tagged!(handles, item)\n    end\n    handles\nend\n\nfind_tagged!(handles, obj) = obj\n\nhas_tag(form::Compose.Form, tag) = form.tag == tag\nhas_tag(form::Compose.Form)      = form.tag != Compose.empty_tag\n\nhas_tag(obj, tag) = false\nhas_tag(obj)      = false\n\n@test find_tagged(ctx)[:rect] == ctx\n\n### Coordinate computations\nfunction absolute_to_data(x, y, transform, unit_box, parent_box)\n    xt, yt = invert_transform(transform, x, y)\n    (unit_box.x0 + unit_box.width *(xt-parent_box.x0[1])/Measures.width(parent_box),\n     unit_box.y0 + unit_box.height*(yt-parent_box.x0[2])/Measures.height(parent_box))\nend\n\ninvert_transform(::Compose.IdentityTransform, x, y) = x, y\n\nfunction invert_transform(t::Compose.MatrixTransform, x, y)\n    @assert t.M[3,1] == t.M[3,2] == 0\n    xyt = t.M\\[x, y, 1.0]\n    xyt[1], xyt[2]\nend\n\nbox, units, transform = be.coords[:rect]\nxd, yd = absolute_to_data(0.5mm, 0.5mm, transform, units, box)\n@test isapprox(xd,0.1417; atol=0.0001)\n"
  },
  {
    "path": "test/misc.jl",
    "content": "using Compose, Test, Random, Colors\n\n# must be before importing cairo\n@testset \"missing cairo errors\" begin\n    ctx = compose(context(), circle(), fill(\"gold\"))\n    @test_throws ErrorException ctx |> PNG(\"test.png\")\n    @test_throws ErrorException ctx |> PDF(\"test.pdf\")\n    @test_throws ErrorException ctx |> PS(\"test.ps\")\n\n    io = IOBuffer()\n    @test_throws ErrorException show(io, MIME(\"image/png\"), ctx)\n    @test_throws ErrorException show(io, MIME(\"application/pdf\"), ctx)\n    @test_throws ErrorException show(io, MIME(\"application/ps\"), ctx)\nend\n\nimport Cairo\n\n# showcompact\n@testset \"printing\" begin\n    @testset \"context\" begin\n        io = IOBuffer()\n        tomato_bisque = compose(context(),\n                    (context(), circle(), fill(colorant\"bisque\")),\n                    (context(), rectangle(), fill(colorant\"tomato\")))\n\n        # compact printing\n        show(IOContext(io, :compact=>true), tomato_bisque)\n        str = String(take!(io))\n        @test str == \"Context(Context(R,f),Context(C,f))\"\n\n        # full printing\n        show(io, context())\n        str = String(take!(io))\n        @test replace(str, \" \"=>\"\") == \"Context(Measures.BoundingBox{Tuple{Measures.Length{:w,Float64},Measures.Length{:h,Float64}},Tuple{Measures.Length{:w,Float64},Measures.Length{:h,Float64}}}((0.0w,0.0h),(1.0w,1.0h)),nothing,nothing,nothing,nothing,List([]),List([]),List([]),0,false,false,false,false,nothing,nothing,0.0,Symbol(\\\"\\\"))\"\n    end\n    @testset \"table\" begin\n        t = Compose.Table(1, 1, UnitRange(1,1), UnitRange(3:3), aspect_ratio=1.6)\n        io = IOBuffer()\n\n        # compact printing\n        show(IOContext(io, :compact=>true), t)\n        str = String(take!(io))\n        @test str == \"1x1 Table:\\n  Context[]\\n\"\n\n        # full printing\n        show(io, t)\n        str = String(take!(io))\n        @test replace(str, \" \"=>\"\") == \"Compose.Table($(Vector{Context})[[]\" * (VERSION>=v\"1.7\" ? \";;\" : \"\") * \"],3:3,1:1,nothing,nothing,1.6,Any[],nothing,0,false,false)\"\n    end\nend\n\n# Tagging\npoints(xa, ya) = [(x, y) for (x, y) in zip(xa, ya)]\n\npnts = points(rand(5), rand(5))\np = polygon(pnts, :mypoints)\n@test p.tag == :mypoints\n\nr = rectangle(0, 1, 0.5, 0.8, :box)\n@test r.tag == :box\nr = rectangle(rand(5),rand(5),rand(5),rand(5),:manybox)\n@test r.tag == :manybox\n\nc = circle(0, 0.8, 1.2, :circle)\n@test c.tag == :circle\nc = circle(rand(5), rand(5), rand(5), :data)\n@test c.tag == :data\n\nelps = ellipse(0, 0.8, 1.2, 1.5, :ellipse)\n@test elps.tag == :ellipse\nelps = ellipse(rand(5),rand(5),rand(5),rand(5),:manyellipse)\n@test elps.tag == :manyellipse\n\ntxt = text(1.5, 15, \"hello\", tag=:hello)\n@test txt.tag == :hello\ntxt = text(rand(5),rand(5),map(x->randstring(5), 1:5), tag=:random)\n@test txt.tag == :random\n\nln = line(pnts, :line)\n@test ln.tag == :line\n\ncrv = curve((0,0), (1,0.5), (0.2,0.3), (0.7,-2.4), :curve)\n@test crv.tag == :curve\ncrv = curve(pnts, pnts, pnts, pnts, :manycurve)\n@test crv.tag == :manycurve\n\nbm = bitmap(\"fake\", rand(UInt8,10), 0, 1, 0.8, 0.7, :image)\n@test bm.tag == :image\n\n# type definitions & constructors (issue #149)\n@test isa(Compose.polygon(), Compose.Polygon)\n@test isa(Compose.polygon([(1,2),(3,5),(4,2)]), Compose.Polygon)\n\n@test isa(Compose.rectangle(), Compose.Rectangle)\n@test isa(Compose.rectangle(0,1,0.3,0.8), Compose.Rectangle)\n@test isa(Compose.rectangle(rand(5),rand(5),rand(5),rand(5)), Compose.Rectangle)\n\n@test isa(Compose.circle(), Compose.Circle)\n@test isa(Compose.circle(3.2,1.4,0.8), Compose.Circle)\n@test isa(Compose.circle(rand(5), rand(5), rand(5)), Compose.Circle)\n\n@test isa(Compose.ellipse(), Compose.Ellipse)\n@test isa(Compose.ellipse(0.5,0.5,0.3,0.2), Compose.Ellipse)\n@test isa(Compose.ellipse(rand(5), rand(5), rand(5), rand(5)), Compose.Ellipse)\n\n@test isa(Compose.text(0.5,0.4,\"hello\"), Compose.Text)\n@test isa(Compose.text(rand(5),rand(5),[\"hello\",\"there\"]), Compose.Text)\n\n@test isa(Compose.line(), Compose.Line)\n@test isa(Compose.line([(1,2),(3,5),(4,2)]), Compose.Line)\n\n# issue #172: default circle(xs, ys, rs) radius measure is context units\n@test isequal(circle([0.5], [0.5], [0.1]).primitives[1].radius, 0.1cx)\n\n# Gadfly issue 857 and 436\n# make sure that newlines are respected by `text_extents`\nfont_family = \"'PT Sans Caption','Helvetica Neue','Helvetica',sans-serif\"\noneline = text_extents(font_family, 8pt, \"PegPeg\")[1]\ntwolines = text_extents(font_family, 8pt, \"Peg\\nPeg\")[1]\n@test round(Int, oneline[1] / twolines[1]) == 2\n@test round(Int, twolines[2] / oneline[2]) == 2\n\n# PR 252\n@test Compose.parse_colorant(\"red\") == RGB(1.0,0.0,0.0)\n@test Compose.parse_colorant(colorant\"red\") == RGB(1.0,0.0,0.0)\n@test Compose.parse_colorant([\"red\",\"blue\"]) == [RGB(1.0,0.0,0.0), RGB(0.0,0.0,1.0)]\n@test Compose.parse_colorant((\"red\",\"blue\")) == [RGB(1.0,0.0,0.0), RGB(0.0,0.0,1.0)]\n@test Compose.parse_colorant(\"red\",\"blue\") == [RGB(1.0,0.0,0.0), RGB(0.0,0.0,1.0)]\n@test Compose.parse_colorant(\"red\",colorant\"blue\") == [RGB(1.0,0.0,0.0), RGB(0.0,0.0,1.0)]\n\n# PR 263\n@test ~ @isdefined Fontconfig\n@test Compose.pango_to_svg(\"hello world\") == \"hello world\"\nimport Fontconfig\n@test Compose.pango_to_svg(\"hello world\") == \"hello world\"\n\n@testset \"pango\" begin\n    @test Compose.escape_tex_chars(\"\\\\\") == \"\\\\textbackslash{}\"\n    @test Compose.pango_to_pgf(\"hello\\\\\") == \"\\\\text{hello\\\\textbackslash{}}\"\nend\n\n@testset \"pango utf-8 text_extents\" begin\n    @test @isdefined Fontconfig\n    w1 = Compose.text_extents(\"\", 8pt, \"α\")[1]\n    w2 = Compose.text_extents(\"\", 8pt, \"αα\")[1]\n    @test w1 < w2\nend\n\n@testset \"table\" begin\n    @testset \"force aspect ratio\" begin\n        tbl = Compose.Table(1, 1, UnitRange(1,1), UnitRange(3:3), aspect_ratio=1.6);\n        x_solution = [0.0, 5.8, 11.8]\n        w_solution = [5.8, 6.0, 119.6]\n        y_solution = [0.0, 85.3]\n        h_solution = [85.3, 4.7]\n        x0, w0 = copy(x_solution), copy(w_solution)\n        Compose.force_aspect_ratio!(tbl, x_solution, y_solution, w_solution, h_solution)\n        @test x_solution == x0\n        @test w_solution == w0\n        @test y_solution ≈ [5.275, 80.025]\n        @test h_solution == [74.75, 4.7]\n    end\nend\n\n@testset \"Image keyword args\" begin\n    @test isa(PNG(4inch, 3inch, dpi=172), Compose.Image)\nend\n\n@testset \"No Global RNG contamination\" begin\n    Random.seed!(23)\n    withoutcompose = rand()\n    Random.seed!(23)\n    draw(SVG(10cm, 8cm, false), compose(context()))\n    withcompose = rand()\n    @test withoutcompose == withcompose\nend\n\n@testset \"Image fillopacity\" begin\n    properties = [fill([\"red\",\"blue\"]), fillopacity(0.3), stroke(\"black\")]\n    img1 = PNG(); Compose.push_property_frame(img1, properties)\n    img2 = SVG(); Compose.push_property_frame(img2, properties)\n    img3 = PGF(); Compose.push_property_frame(img3, properties)\n    @test getfield.(img1.vector_properties[Compose.Property{Compose.FillOpacityPrimitive}].primitives, :value) == [0.3, 0.3]\n    @test occursin(\"fill-opacity=\\\"0.3\\\"\", String(img2.out.data))\n    @test img3.fill_opacity == 0.3\nend\n\n@testset \"Missing\" begin\n    @test Compose.x_measure(missing)===NaN*cx\n    @test Compose.y_measure(missing)===NaN*cy\n    @test Compose.size_x_measure(missing)===NaN*sx\n    @test Compose.size_y_measure(missing)===NaN*sy\nend\n\n@testset \"cx, sx and cy, sy units\" begin\n    ub, bb = UnitBox(-0.3, 0., 1.2, 1.), Compose.BoundingBox((50mm, 50mm), (40mm, 40mm))\n    tr = Compose.IdentityTransform()\n    xpos = (Compose.resolve_position(bb, ub, tr, 0sx), \n                Compose.resolve_position(bb, ub, tr, 0cx))\n    ub, bb = UnitBox(0., -0.3, 1., 1.2), Compose.BoundingBox((10mm, 10mm), (40mm, 40mm))\n    ypos = (Compose.resolve_position(bb, ub, tr, 0sy),\n                Compose.resolve_position(bb, ub, tr, 0cy))\n    @test ypos == xpos == (0.0mm, 10.0mm)\nend\n"
  },
  {
    "path": "test/runtests.jl",
    "content": "using Test\n\ninclude(\"misc.jl\")\ninclude(\"svg.jl\")\ninclude(\"immerse.jl\")\n\n# Run the examples\ncd(joinpath(@__DIR__, \"output\"))\nexampledir = joinpath(@__DIR__, \"examples\")\nfor ex in readdir(exampledir)\n    endswith(ex, \".jl\") || continue\n    file = joinpath(exampledir, ex)\n    run(`$(Base.julia_cmd()) --startup-file=no $file`)\nend\n"
  },
  {
    "path": "test/svg.jl",
    "content": "using Compose\nusing Test\nusing EzXML\nusing Colors\nusing Measures\n\n@testset \"Issue 267\" begin\n    global c = compose(context(), fill([\"red\", \"blue\"]),\n                [context(), fill(\"green\"), circle([0.25, 0.75], [0.5], [0.25])])\n    img = SVG(8cm, 6cm, false)\n    draw(img, c)\n    svgxml = root(parsexml(String(take!(img.out))))\n    # get all grouped values that have the fill attribute\n    fillcolors = nodecontent.(findall(\"//ns:g[@fill]/@fill\", svgxml, [\"ns\"=>namespace(svgxml)]))\n    # there should only be a single color (because green should clobber the red\n    # and blue colors)\n    @test length(fillcolors) == 1\n    # make sure it's green\n    @test fillcolors[1] == \"#008000\"\nend\n"
  }
]