├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── codeql.yml │ └── dotnetcore.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Directory.Packages.props ├── LICENSE ├── README.md ├── SvgDocTest ├── .vs │ └── SvgDocTest.csproj.dtbcache.json ├── App.ico ├── AssemblyInfo.cs ├── Assert.cs ├── DocForm.Designer.cs ├── DocForm.cs ├── DocForm.resx ├── Extensions.cs ├── StringExtensions.cs ├── SvgDocTest.csproj ├── SvgDocTest.csproj.user └── app.config ├── SvgDotNetCoreTest ├── Program.cs └── SvgDotNetCoreTest.csproj ├── SvgGdiTest ├── .vs │ └── SvgGdiTest.csproj.dtbcache.json ├── App.ico ├── AssemblyInfo.cs ├── RectAlignedTextTest.cs ├── SvgGdiTest.csproj ├── SvgGdiTest.csproj.user ├── SvgGdiTestForm.Designer.cs ├── SvgGdiTestForm.cs ├── SvgGdiTestForm.resx ├── TestApp.ndoc ├── app.config ├── test.bmp └── testdoc.xml ├── SvgNet.sln ├── SvgNet ├── Elements │ ├── SvgBaseTextElement.cs │ ├── SvgElement.cs │ ├── SvgEllipseElement.cs │ ├── SvgFilterElement.cs │ ├── SvgGroupElement.cs │ ├── SvgLineElement.cs │ ├── SvgLinearGradientElement.cs │ ├── SvgPathElement.cs │ ├── SvgPatternElement.cs │ ├── SvgPolygonElement.cs │ ├── SvgPolylineElement.cs │ ├── SvgRadialGradientElement.cs │ ├── SvgRectElement.cs │ ├── SvgStopElement.cs │ ├── SvgStyledElement.cs │ ├── SvgSvgElement.cs │ ├── SvgSymbolElement.cs │ ├── SvgTextElement.cs │ ├── SvgTitleElement.cs │ ├── SvgTrefElement.cs │ ├── SvgTspanElement.cs │ └── TextNode.cs ├── Exceptions │ ├── EmfException.cs │ ├── SvgException.cs │ └── SvgGdiNotImplementedException.cs ├── Extensions │ ├── FloatExtensions.cs │ ├── MatrixExtensions.cs │ ├── ObjectExtensions.cs │ ├── StringExtensions.cs │ └── StringExtensionsForOlderDotNet.cs ├── GlobalSuppressions.cs ├── ImplementedGraphics │ ├── GDIGraphics.cs │ ├── SVGGraphics.BitmapDrawer.cs │ └── SVGGraphics.cs ├── Interfaces │ ├── IElementWithText.cs │ ├── IElementWithXRef.cs │ └── IGraphics.cs ├── MetafileTools │ ├── EmfTools.cs │ └── MetafileParser.cs ├── SvgFactory.cs ├── SvgNet.csproj ├── Types │ ├── PathSeg.cs │ ├── SvgAngle.cs │ ├── SvgAngleType.cs │ ├── SvgColor.cs │ ├── SvgHelpers.cs │ ├── SvgLength.cs │ ├── SvgLengthType.cs │ ├── SvgNumList.cs │ ├── SvgNumber.cs │ ├── SvgPath.cs │ ├── SvgPathSegType.cs │ ├── SvgPoints.cs │ ├── SvgStyle.cs │ ├── SvgTransform.cs │ ├── SvgTransformList.cs │ ├── SvgUriReference.cs │ └── SvgXRef.cs └── svgnet.png ├── SvgNetUnitTests ├── Main.cs ├── SvgColorTests.cs └── SvgNetUnitTests.csproj ├── TestShared └── TestShared.cs └── global.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "nuget" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL Advanced" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '22 5 * * 3' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | permissions: 32 | # required for all workflows 33 | security-events: write 34 | 35 | # required to fetch internal or private CodeQL packs 36 | packages: read 37 | 38 | # only required for workflows in private repositories 39 | actions: read 40 | contents: read 41 | 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | include: 46 | - language: csharp 47 | build-mode: manual 48 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 49 | # Use `c-cpp` to analyze code written in C, C++ or both 50 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 51 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 52 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 53 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 54 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 55 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 56 | steps: 57 | - name: Checkout repository 58 | uses: actions/checkout@v4 59 | 60 | # Initializes the CodeQL tools for scanning. 61 | - name: Initialize CodeQL 62 | uses: github/codeql-action/init@v3 63 | with: 64 | languages: ${{ matrix.language }} 65 | build-mode: ${{ matrix.build-mode }} 66 | # If you wish to specify custom queries, you can do so here or in a config file. 67 | # By default, queries listed here will override any specified in a config file. 68 | # Prefix the list here with "+" to use these queries and those in the config file. 69 | 70 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 71 | # queries: security-extended,security-and-quality 72 | 73 | - name: Setup .NET 9.0 74 | uses: actions/setup-dotnet@v4 75 | with: 76 | dotnet-version: 9.0.201 77 | 78 | - shell: bash 79 | run: dotnet build --configuration Release -v:m 80 | 81 | - name: Perform CodeQL Analysis 82 | uses: github/codeql-action/analyze@v3 83 | with: 84 | category: "/language:${{matrix.language}}" 85 | -------------------------------------------------------------------------------- /.github/workflows/dotnetcore.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: Setup .NET 9.0 14 | uses: actions/setup-dotnet@v4 15 | with: 16 | dotnet-version: 9.0.201 17 | 18 | - shell: bash 19 | run: dotnet build --configuration Release -v:m 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | *.user 4 | *.suo 5 | *.xml 6 | *.userprefs 7 | *.pidb 8 | *.nupkg 9 | test-results/ 10 | /packages/ 11 | /.vs 12 | InternalTrace.*.log 13 | SvgNet/svgnetdoc.xml 14 | /SvgNet/svgnetdoc.xml 15 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at monoman@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to SvgNet 2 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 3 | 4 | - Reporting a bug 5 | - Discussing the current state of the code 6 | - Submitting a fix 7 | - Proposing new features 8 | - Becoming a maintainer 9 | 10 | ## We Develop with Github 11 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 12 | 13 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests 14 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: 15 | 16 | 1. Fork the repo and create your branch from `main`. 17 | 2. If you've added code that should be tested, add tests. 18 | 3. If you've changed APIs, update the documentation. 19 | 4. Ensure the test suite passes. 20 | 5. Make sure your code follows our standards. 21 | 6. Issue that pull request! 22 | 23 | ## Any contributions you make will be under the *BSD 3-Clause "New" or "Revised" License* 24 | In short, when you submit code changes, your submissions are understood to be under the same [BSD 3-Clause "New" or "Revised" License](https://opensource.org/licenses/BSD-3-Clause) that covers the project. Feel free to contact the maintainers if that's a concern. 25 | 26 | ## Report bugs using Github's [issues](https://github.com/managed-commons/SvgNet/issues) 27 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/managed-commons/SvgNet/issues); it's that easy! 28 | 29 | ## Write bug reports with detail, background, and sample code 30 | 31 | **Great Bug Reports** tend to have: 32 | 33 | - A quick summary and/or background 34 | - Steps to reproduce 35 | - Be specific! 36 | - Give sample code if you can. 37 | - What you expected would happen 38 | - What actually happens 39 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 40 | People *love* thorough bug reports. I'm not even kidding. 41 | 42 | *We have a template for bug reports, please use it.* 43 | 44 | ## Use a Consistent Coding Style 45 | * 4 spaces for indentation rather than tabs 46 | * use the 'dotnet format' following our .editorconfig settings 47 | 48 | ## License 49 | By contributing, you agree that your contributions will be licensed under its *BSD 3-Clause "New" or "Revised" License*. 50 | 51 | ## References 52 | This document was adapted from an adaptation of the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md) done by @briandk. 53 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | $(NoWarn);NU1507 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003 RiskCare Ltd. 2 | Copyright (c) 2010 SvgNet & SvgGdi Bridge Project 3 | Copyright (c) 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of SvgNet nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SvgNet 2 | 3 | A fork of the SvgNet & SvgGdi bridge (http://www.codeproject.com/KB/cs/svgnet.aspx) to ressurrect it. 4 | 5 | __SvgNet is now available as a Nuget:__ [SvgNet](https://www.nuget.org/packages/SvgNet/). 6 | 7 | __Latest version 3.5.0 is .NET Standard 2.0 and 2.1 and also .NET 8.0/9.0 (base and Windows) compatible and works with .NET Core 2.x and 3.x and .NET 5.0/6.0/7.0/8.0/9.0, but now requires .NET Framework 4.6.2 or higher__ 8 | 9 | To build this version properly you need .NET 8.0.404+ & 9.0.100 SDKs installed 10 | 11 | ## License: BSD 12 | 13 | Copyright © 2003 RiskCare Ltd. All rights reserved. 14 | 15 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 16 | 17 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 18 | 19 | Redistribution and use in source and binary forms, with or without 20 | modification, are permitted provided that the following conditions 21 | are met: 22 | 23 | 1. Redistributions of source code must retain the above copyright 24 | notice, this list of conditions and the following disclaimer. 25 | 2. Redistributions in binary form must reproduce the above copyright 26 | notice, this list of conditions and the following disclaimer in the 27 | documentation and/or other materials provided with the distribution. 28 | 3. The name of the author may not be used to endorse or promote products 29 | derived from this software without specific prior written permission. 30 | 31 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 32 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 33 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 34 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 35 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 37 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 38 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 40 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | -------------------------------------------------------------------------------- /SvgDocTest/.vs/SvgDocTest.csproj.dtbcache.json: -------------------------------------------------------------------------------- 1 | {"RootPath":"D:\\myrepos\\SvgNet\\SvgDocTest","ProjectFileName":"SvgDocTest.csproj","Configuration":"Debug|AnyCPU","FrameworkPath":"","Sources":[{"SourceFile":"AssemblyInfo.cs"},{"SourceFile":"Assert.cs"},{"SourceFile":"DocForm.cs"},{"SourceFile":"DocForm.Designer.cs"},{"SourceFile":"StringExtensions.cs"},{"SourceFile":"obj\\Debug\\.NETFramework,Version=v4.7.1.AssemblyAttributes.cs"}],"References":[{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.7.1\\mscorlib.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"D:\\myrepos\\SvgNet\\SvgNet\\bin\\Debug\\net462\\SVG.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":true,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.7.1\\System.Core.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.7.1\\System.Data.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.7.1\\System.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.7.1\\System.Drawing.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.7.1\\System.Windows.Forms.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.7.1\\System.Xml.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""}],"Analyzers":[],"Outputs":[{"OutputItemFullPath":"D:\\myrepos\\SvgNet\\SvgDocTest\\bin\\Debug\\SvgDocTest.exe","OutputItemRelativePath":"SvgDocTest.exe"},{"OutputItemFullPath":"D:\\myrepos\\SvgNet\\SvgDocTest\\bin\\Debug\\SvgDocTest.pdb","OutputItemRelativePath":"SvgDocTest.pdb"}],"CopyToOutputEntries":[]} -------------------------------------------------------------------------------- /SvgDocTest/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/managed-commons/SvgNet/9aa368c6dafc05f61f97ad7b1c8ec5eda316ec1a/SvgDocTest/App.ico -------------------------------------------------------------------------------- /SvgDocTest/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Reflection; 10 | 11 | [assembly: AssemblyTitle("SvgNet Document Loading Test")] 12 | [assembly: AssemblyDescription("Testing the C# framework for creating SVG images")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("RiskCare Ltd, SvgNet and SvgGdi Bridge Project, Rafael Teixeira, Mojmír Němeček")] 15 | [assembly: AssemblyProduct("SvgNet")] 16 | [assembly: AssemblyCopyright("Copyright 2003, 2010, 2015-2024 RiskCare Ltd, SvgNet and SvgGdi Bridge Project, Rafael Teixeira, Mojmír Němeček, Benjamin Peterson")] 17 | [assembly: AssemblyTrademark("")] 18 | [assembly: AssemblyCulture("")] 19 | [assembly: AssemblyVersion("1.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0")] 21 | [assembly: AssemblyInformationalVersion("1.0.0")] 22 | [assembly: AssemblyDelaySign(false)] 23 | [assembly: AssemblyKeyFile("")] 24 | [assembly: AssemblyKeyName("")] 25 | -------------------------------------------------------------------------------- /SvgDocTest/Assert.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System; 10 | 11 | namespace SvgDocTest { 12 | public static class Assert { 13 | public static void Equals(float a, float b) { 14 | if (a != b) { 15 | throw new Exception("Assert.Equals"); 16 | } 17 | } 18 | 19 | public static void Equals(bool a, bool b) { 20 | if (a != b) { 21 | throw new Exception("Assert.Equals"); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SvgDocTest/DocForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows.Forms; 4 | 5 | using SvgNet; 6 | using SvgNet.Elements; 7 | using SvgNet.Types; 8 | 9 | namespace SvgDocTest { 10 | public partial class DocForm : Form { 11 | public DocForm() => InitializeComponent(); 12 | 13 | private static readonly string _tempFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.svg"); 14 | 15 | /// 16 | /// The main entry point for the application. 17 | /// 18 | [STAThread] 19 | private static void Main() => Application.Run(new DocForm()); 20 | 21 | private void Button1_Click(object sender, EventArgs e) { 22 | using (var dlg = new OpenFileDialog { 23 | AutoUpgradeEnabled = true, 24 | CheckFileExists = true, 25 | DefaultExt = ".svg", 26 | Filter = "Scalable Vector Graphics|*.svg", 27 | Multiselect = false, 28 | Title = "Choose one Scalable Vector Graphics file" 29 | }) { 30 | if (dlg.ShowDialog() == DialogResult.OK) { 31 | ProcessSvgFile(dlg.FileName); 32 | } 33 | } 34 | } 35 | 36 | private void Button2_Click(object sender, EventArgs e) { 37 | var root = new SvgSvgElement("4in", "4in", "-10,-10 250,250"); 38 | 39 | //adding multiple children 40 | 41 | _ = root.AddChildren( 42 | new SvgRectElement(5, 5, 5, 5), 43 | new SvgEllipseElement(20, 20, 8, 12) { 44 | Style = "fill:yellow;stroke:red" 45 | }, 46 | 47 | new SvgAElement("https://github.com/managed-commons/SvgNet").AddChildren( 48 | new SvgTextElement("Textastic!", "30px", "20px") { 49 | Style = "fill:midnightblue;stroke:navy;stroke-width:1px;font-size:30px;font-family:Calibri" 50 | }) 51 | ); 52 | 53 | //group and path 54 | 55 | var grp = new SvgGroupElement("green_group") { 56 | Style = "fill:green;stroke:black;" 57 | }; 58 | 59 | grp.AddChild(new SvgRectElement(30, 30, 5, 20)); 60 | 61 | var ell = new SvgEllipseElement { 62 | CX = 50, 63 | CY = 50, 64 | RX = 10, 65 | RY = 20 66 | }; 67 | 68 | var pathy = new SvgPathElement { 69 | D = "M 20,80 C 20,90 30,80 70,100 C 70,100 40,60 50,60 z", 70 | Style = ell.Style 71 | }; 72 | 73 | root.AddChild(grp); 74 | 75 | //cloning and style arithmetic 76 | 77 | _ = grp.AddChildren(ell, pathy); 78 | 79 | grp.Style.Set("fill", "blue"); 80 | 81 | var grp2 = (SvgGroupElement)SvgFactory.CloneElement(grp); 82 | 83 | grp2.Id = "cloned_red_group"; 84 | 85 | grp2.Style.Set("fill", "red"); 86 | 87 | grp2.Style += "opacity:0.5"; 88 | 89 | grp2.Transform = "scale (1.2, 1.2) translate(10)"; 90 | 91 | root.AddChild(grp2); 92 | 93 | //output 94 | SvgFactory.ResetNamespaces(); 95 | string s = root.WriteSVGString(true); 96 | tbIn.Text = s; 97 | tbOut.Text = s; 98 | 99 | string tempFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.svg"); 100 | 101 | using (var tw = new StreamWriter(tempFile, false)) 102 | tw.Write(s); 103 | panelTop.Text = $"Input: {tempFile}"; 104 | svgOut.RefreshFrom(tempFile); 105 | svgIn.RefreshFrom(tempFile); 106 | } 107 | 108 | private void Button3_Click(object sender, EventArgs e) { 109 | SvgNumList a = "3, 5.6 901 -7 "; 110 | Assert.Equals(a[3], -7f); 111 | 112 | SvgTransformList b = "rotate ( 45 ), translate (11, 10)skewX(3)"; 113 | Assert.Equals(b[1].Matrix.OffsetX, 11f); 114 | 115 | SvgColor c = "rgb( 100%, 100%, 50%)"; 116 | Assert.Equals(c.Color.B, 0x7f); 117 | 118 | SvgColor d = "#abc"; 119 | Assert.Equals(d.Color.G, 0xbb); 120 | 121 | SvgPath f = "M 5,5 L 1.1 -6 , Q 1,3 9,10 z"; 122 | Assert.Equals(f.Count, 4f); 123 | Assert.Equals(f[1].Abs, true); 124 | Assert.Equals(f[2].Data[3], 10f); 125 | 126 | _ = MessageBox.Show("Tests completed Ok"); 127 | } 128 | private void ProcessSvgFile(string svgFileName) { 129 | panelTop.Text = $"Input: {svgFileName}"; 130 | tbIn.Text = svgFileName.LoadText(); 131 | tbOut.Text = SvgFactory.LoadFromXML(svgFileName.LoadXml(), null).WriteSVGString(true); 132 | File.WriteAllText(_tempFileName, tbOut.Text); 133 | svgIn.RefreshFrom(svgFileName); 134 | svgOut.RefreshFrom(_tempFileName); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /SvgDocTest/DocForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /SvgDocTest/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace SvgDocTest { 5 | public static class Extensions { 6 | public static void RefreshFrom(this WebBrowser browser, string filename) { 7 | browser.Navigate(new Uri(filename)); 8 | browser.Refresh(WebBrowserRefreshOption.Completely); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SvgDocTest/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.IO; 10 | using System.Xml; 11 | 12 | namespace SvgDocTest { 13 | public static class StringExtensions { 14 | public static string LoadText(this string svgFileName) => File.ReadAllText(svgFileName); 15 | 16 | public static XmlDocument LoadXml(this string svgFileName) { 17 | var doc = new XmlDocument(); 18 | doc.Load(svgFileName); 19 | return doc; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SvgDocTest/SvgDocTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | net471 7 | Local 8 | App.ico 9 | 10 | 11 | JScript 12 | Grid 13 | IE50 14 | false 15 | WinExe 16 | 17 | false 18 | true 19 | true 20 | 21 | 22 | 285212672 23 | 24 | 4096 25 | false 26 | false 27 | CS1591 28 | 29 | 30 | 285212672 31 | 32 | 4096 33 | false 34 | false 35 | none 36 | CS1591 37 | 38 | 39 | 40 | 41 | System 42 | 43 | 44 | System.Data 45 | 46 | 47 | System.Drawing 48 | 49 | 50 | System.XML 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /SvgDocTest/SvgDocTest.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 7 | 8 | 9 | 0 10 | ProjectFiles 11 | 0 12 | 13 | 14 | false 15 | false 16 | false 17 | false 18 | false 19 | 20 | Project 21 | 22 | 23 | 24 | 25 | 26 | true 27 | 28 | 29 | false 30 | false 31 | false 32 | false 33 | false 34 | 35 | Project 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | 45 | Form 46 | 47 | 48 | -------------------------------------------------------------------------------- /SvgDocTest/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SvgDotNetCoreTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | using SvgNet; 5 | using SvgNet.Interfaces; 6 | 7 | namespace SvgDotNetCoreTest; 8 | public static class Program { 9 | public static void Main(string[] args) { 10 | foreach (System.Collections.Generic.KeyValuePair> pair in TestShared.Renderers) { 11 | using var ig = new SvgGraphics(Color.WhiteSmoke); 12 | Console.WriteLine($"=== Renderer {pair.Key}"); 13 | pair.Value(ig); 14 | Console.WriteLine(ig.WriteSVGString(640, 480)); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SvgDotNetCoreTest/SvgDotNetCoreTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | 12 5 | net8.0-windows;net9.0-windows 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /SvgGdiTest/.vs/SvgGdiTest.csproj.dtbcache.json: -------------------------------------------------------------------------------- 1 | {"RootPath":"D:\\myrepos\\SvgNet\\SvgGdiTest","ProjectFileName":"SvgGdiTest.csproj","Configuration":"Debug|AnyCPU","FrameworkPath":"","Sources":[{"SourceFile":"RectAlignedTextTest.cs"},{"SourceFile":"..\\TestShared\\TestShared.cs"},{"SourceFile":"AssemblyInfo.cs"},{"SourceFile":"SvgGdiTestForm.Designer.cs"},{"SourceFile":"SvgGdiTestForm.cs"},{"SourceFile":"obj\\Debug\\.NETFramework,Version=v4.6.2.AssemblyAttributes.cs"}],"References":[{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.2\\mscorlib.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"D:\\myrepos\\SvgNet\\SvgNet\\bin\\Debug\\net462\\SVG.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":true,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.2\\System.Core.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.2\\System.Data.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.2\\System.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.2\\System.Drawing.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.2\\System.Windows.Forms.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""},{"Reference":"C:\\Program Files (x86)\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.6.2\\System.Xml.dll","ResolvedFrom":"","OriginalItemSpec":"","Name":"","EmbedInteropTypes":false,"CopyLocal":false,"IsProjectReference":false,"ProjectPath":""}],"Analyzers":[],"Outputs":[{"OutputItemFullPath":"D:\\myrepos\\SvgNet\\SvgGdiTest\\bin\\Debug\\SvgGdiTest.exe","OutputItemRelativePath":"SvgGdiTest.exe"},{"OutputItemFullPath":"D:\\myrepos\\SvgNet\\SvgGdiTest\\bin\\Debug\\SvgGdiTest.pdb","OutputItemRelativePath":"SvgGdiTest.pdb"}],"CopyToOutputEntries":[]} -------------------------------------------------------------------------------- /SvgGdiTest/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/managed-commons/SvgNet/9aa368c6dafc05f61f97ad7b1c8ec5eda316ec1a/SvgGdiTest/App.ico -------------------------------------------------------------------------------- /SvgGdiTest/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Reflection; 10 | 11 | [assembly: AssemblyTitle("SvgNet Visual Test")] 12 | [assembly: AssemblyDescription("Testing the C# framework for creating SVG images")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("RiskCare Ltd, SvgNet and SvgGdi Bridge Project, Rafael Teixeira, Mojmír Němeček, Benjamin Peterson")] 15 | [assembly: AssemblyProduct("SvgNet")] 16 | [assembly: AssemblyCopyright("Copyright 2003, 2010, 2015-2024 RiskCare Ltd, SvgNet and SvgGdi Bridge Project, Rafael Teixeira, Mojmír Němeček, Benjamin Peterson")] 17 | [assembly: AssemblyTrademark("")] 18 | [assembly: AssemblyCulture("")] 19 | [assembly: AssemblyVersion("1.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0")] 21 | [assembly: AssemblyInformationalVersion("1.0.0")] 22 | [assembly: AssemblyDelaySign(false)] 23 | [assembly: AssemblyKeyFile("")] 24 | [assembly: AssemblyKeyName("")] 25 | -------------------------------------------------------------------------------- /SvgGdiTest/RectAlignedTextTest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Drawing; 10 | 11 | using SvgNet.Interfaces; 12 | 13 | namespace SvgGdiTest; 14 | public static class RectAlignedTextTest { 15 | public static void RenderRectAlignedText(IGraphics ig, float width, float height, Font baseFont) { 16 | ig.Clear(Color.White); 17 | ig.ScaleTransform(width / _canvasSize, height / _canvasSize); 18 | DrawTest(ig, baseFont); 19 | } 20 | 21 | private const int _canvasSize = (3 * _rectSize) + (4 * _rectGap); 22 | private const int _rectFontSize = 20; 23 | private const int _rectGap = 20; 24 | private const int _rectSize = 150; 25 | 26 | private static void DrawRect(IGraphics canvas, string id, Rectangle rect, StringAlignment horizontalAlignment, StringAlignment verticalAlignment, Font baseFont) { 27 | var format = new StringFormat { 28 | Alignment = horizontalAlignment, 29 | LineAlignment = verticalAlignment, 30 | FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip 31 | }; 32 | 33 | var pen = new Pen(new SolidBrush(Color.Black), 1); 34 | canvas.DrawRectangle(pen, rect); 35 | 36 | var font = new Font(baseFont.Name, _rectFontSize, baseFont.Style, baseFont.Unit); 37 | 38 | { 39 | // Draw label 40 | var labelFormat = new StringFormat { 41 | Alignment = StringAlignment.Near, 42 | LineAlignment = StringAlignment.Center, 43 | FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip 44 | }; 45 | var labelRect = new Rectangle(rect.X, rect.Y - _rectGap, _rectGap, _rectGap); 46 | var labelFont = new Font(baseFont.Name, _rectFontSize * 0.8f, baseFont.Style, baseFont.Unit); 47 | canvas.DrawString(id, labelFont, new SolidBrush(Color.Black), labelRect, labelFormat); 48 | } 49 | 50 | canvas.DrawString("Helloy", font, new SolidBrush(Color.Blue), rect, format); 51 | } 52 | 53 | private static void DrawTest(IGraphics canvas, Font baseFont) { 54 | canvas.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, _canvasSize, _canvasSize)); 55 | 56 | var alignments = new StringAlignment[] { StringAlignment.Near, StringAlignment.Center, StringAlignment.Far }; 57 | 58 | int id = 1; 59 | foreach (StringAlignment verticalAlignment in alignments) 60 | foreach (StringAlignment horizontalAlignment in alignments) { 61 | int x = _rectGap + ((int)horizontalAlignment * (_rectSize + _rectGap)); 62 | int y = _rectGap + ((int)verticalAlignment * (_rectSize + _rectGap)); 63 | var rect = new Rectangle(x, y, _rectSize, _rectSize); 64 | DrawRect(canvas, id.ToString(), rect, horizontalAlignment, verticalAlignment, baseFont); 65 | id++; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /SvgGdiTest/SvgGdiTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | net471 7 | Local 8 | 12 9 | App.ico 10 | 11 | 12 | JScript 13 | Grid 14 | IE50 15 | false 16 | WinExe 17 | 18 | false 19 | true 20 | true 21 | 22 | 23 | 285212672 24 | 25 | testdoc.xml 26 | 4096 27 | false 28 | false 29 | CS1591 30 | 31 | 32 | 285212672 33 | 34 | 4096 35 | false 36 | false 37 | none 38 | CS1591 39 | 40 | 41 | 42 | System 43 | 44 | 45 | System.Data 46 | 47 | 48 | System.Drawing 49 | 50 | 51 | System.XML 52 | 53 | 54 | 55 | 56 | TestShared.cs 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /SvgGdiTest/SvgGdiTest.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 7 | 8 | 9 | 0 10 | ProjectFiles 11 | 0 12 | 13 | 14 | false 15 | false 16 | false 17 | false 18 | false 19 | 20 | Project 21 | 22 | 23 | 24 | 25 | 26 | true 27 | 28 | 29 | false 30 | false 31 | false 32 | false 33 | false 34 | 35 | Project 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | 45 | Form 46 | 47 | 48 | -------------------------------------------------------------------------------- /SvgGdiTest/SvgGdiTestForm.Designer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.ComponentModel; 10 | using System.Windows.Forms; 11 | 12 | namespace SvgGdiTest { 13 | public partial class SvgGdiTestForm 14 | { 15 | public SvgGdiTestForm() 16 | { 17 | // 18 | // Required for Windows Form Designer support 19 | // 20 | InitializeComponent(); 21 | } 22 | 23 | /// 24 | /// Clean up any resources being used. 25 | /// 26 | /// if should be disposing the components 27 | protected override void Dispose(bool disposing) 28 | { 29 | if (disposing) 30 | { 31 | if (components != null) 32 | { 33 | components.Dispose(); 34 | } 35 | } 36 | base.Dispose(disposing); 37 | } 38 | 39 | private ComboBox cbWhat; 40 | 41 | /// 42 | /// Required designer variable. 43 | /// 44 | private Container components = null; 45 | 46 | private Label label1; 47 | private Label label2; 48 | private Panel panel1; 49 | private WebBrowser svgCtl; 50 | private TextBox tbSVG; 51 | 52 | /// 53 | /// Required method for Designer support - do not modify 54 | /// the contents of this method with the code editor. 55 | /// 56 | private void InitializeComponent() 57 | { 58 | this.panel1 = new System.Windows.Forms.Panel(); 59 | this.tbSVG = new System.Windows.Forms.TextBox(); 60 | this.cbWhat = new System.Windows.Forms.ComboBox(); 61 | this.label1 = new System.Windows.Forms.Label(); 62 | this.label2 = new System.Windows.Forms.Label(); 63 | this.svgCtl = new System.Windows.Forms.WebBrowser(); 64 | this.SuspendLayout(); 65 | // 66 | // panel1 67 | // 68 | this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 69 | this.panel1.Location = new System.Drawing.Point(8, 46); 70 | this.panel1.Name = "panel1"; 71 | this.panel1.Size = new System.Drawing.Size(426, 300); 72 | this.panel1.TabIndex = 2; 73 | this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.PanelPaint); 74 | // 75 | // tbSVG 76 | // 77 | this.tbSVG.BackColor = System.Drawing.SystemColors.Info; 78 | this.tbSVG.Font = new System.Drawing.Font("Cascadia Code", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 79 | this.tbSVG.Location = new System.Drawing.Point(440, 46); 80 | this.tbSVG.Multiline = true; 81 | this.tbSVG.Name = "tbSVG"; 82 | this.tbSVG.ScrollBars = System.Windows.Forms.ScrollBars.Both; 83 | this.tbSVG.Size = new System.Drawing.Size(664, 643); 84 | this.tbSVG.TabIndex = 3; 85 | // 86 | // cbWhat 87 | // 88 | this.cbWhat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 89 | this.cbWhat.Items.AddRange(new object[] { 90 | "Clipping", 91 | "Transforms", 92 | "Arcs/Pies", 93 | "Lines", 94 | "Curves", 95 | "Transparency", 96 | "Images", 97 | "Text", 98 | "Rect-aligned Text", 99 | "Fills", 100 | "Path", 101 | "Path Polygon", 102 | "Path 2 (Slow)"}); 103 | this.cbWhat.Location = new System.Drawing.Point(732, 12); 104 | this.cbWhat.MaxDropDownItems = 30; 105 | this.cbWhat.Name = "cbWhat"; 106 | this.cbWhat.Size = new System.Drawing.Size(372, 22); 107 | this.cbWhat.TabIndex = 5; 108 | this.cbWhat.SelectedIndexChanged += new System.EventHandler(this.ComboWhat_SelectedIndexChanged); 109 | // 110 | // label1 111 | // 112 | this.label1.Font = new System.Drawing.Font("Calibri", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 113 | this.label1.Location = new System.Drawing.Point(8, 26); 114 | this.label1.Name = "label1"; 115 | this.label1.Size = new System.Drawing.Size(72, 17); 116 | this.label1.TabIndex = 6; 117 | this.label1.Text = "GDI:"; 118 | // 119 | // label2 120 | // 121 | this.label2.Font = new System.Drawing.Font("Calibri", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 122 | this.label2.Location = new System.Drawing.Point(8, 365); 123 | this.label2.Name = "label2"; 124 | this.label2.Size = new System.Drawing.Size(72, 21); 125 | this.label2.TabIndex = 7; 126 | this.label2.Text = "SVG:"; 127 | // 128 | // svgCtl 129 | // 130 | this.svgCtl.Location = new System.Drawing.Point(8, 389); 131 | this.svgCtl.MinimumSize = new System.Drawing.Size(20, 20); 132 | this.svgCtl.Name = "svgCtl"; 133 | this.svgCtl.Size = new System.Drawing.Size(426, 300); 134 | this.svgCtl.TabIndex = 8; 135 | // 136 | // SvgGdiTestForm 137 | // 138 | this.AutoScaleBaseSize = new System.Drawing.Size(5, 15); 139 | this.ClientSize = new System.Drawing.Size(1116, 697); 140 | this.Controls.Add(this.svgCtl); 141 | this.Controls.Add(this.label2); 142 | this.Controls.Add(this.label1); 143 | this.Controls.Add(this.cbWhat); 144 | this.Controls.Add(this.tbSVG); 145 | this.Controls.Add(this.panel1); 146 | this.Font = new System.Drawing.Font("Calibri", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 147 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 148 | this.MaximizeBox = false; 149 | this.Name = "SvgGdiTestForm"; 150 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 151 | this.Text = "SvgNet.SvgGdi demonstration/test app"; 152 | this.Load += new System.EventHandler(this.SvgGdiTestForm_Load); 153 | this.ResumeLayout(false); 154 | this.PerformLayout(); 155 | 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /SvgGdiTest/SvgGdiTestForm.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System; 10 | using System.Drawing; 11 | using System.Drawing.Drawing2D; 12 | using System.Globalization; 13 | using System.IO; 14 | using System.Linq; 15 | using System.Threading; 16 | using System.Windows.Forms; 17 | 18 | using SvgNet; 19 | using SvgNet.Interfaces; 20 | 21 | namespace SvgGdiTest; 22 | /// 23 | /// Summary description for Form1. 24 | /// 25 | public partial class SvgGdiTestForm : Form { 26 | /// 27 | /// The main entry point for the application. 28 | /// 29 | [STAThread] 30 | private static void Main() { 31 | Thread.CurrentThread.CurrentCulture = new CultureInfo("nb-NO"); // To catch culture formatting errors 32 | Application.Run(new SvgGdiTestForm()); 33 | } 34 | 35 | private void ComboWhat_SelectedIndexChanged(object sender, EventArgs e) { 36 | using var ig = new SvgGraphics(Color.WhiteSmoke); 37 | Render(ig); 38 | string s = ig.WriteSVGString(); 39 | tbSVG.Text = s; 40 | string tempFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "foo.svg"); 41 | var tw = new StreamWriter(tempFile, false); 42 | tw.Write(s); 43 | tw.Close(); 44 | 45 | svgCtl.Navigate(new Uri(tempFile)); 46 | svgCtl.Refresh(WebBrowserRefreshOption.Completely); 47 | 48 | panel1.Invalidate(); 49 | } 50 | 51 | private void PanelPaint(object sender, PaintEventArgs e) { 52 | Graphics g = e.Graphics; 53 | Render(new GdiGraphics(g)); 54 | g.Flush(); 55 | } 56 | 57 | private void Render(IGraphics ig) { 58 | string s = cbWhat.Text; 59 | if (string.IsNullOrEmpty(s)) 60 | return; 61 | if (TestShared.Renderers.ContainsKey(s)) 62 | TestShared.Renderers[s](ig); 63 | else 64 | throw new NotImplementedException(); 65 | } 66 | 67 | private void RenderImages(IGraphics ig) { 68 | var ike = new Icon(GetType(), "App.ico"); 69 | ig.DrawIcon(ike, 10, 10); 70 | ig.DrawIcon(ike, new Rectangle(50, 10, ike.Width * 2, ike.Height * 3)); 71 | 72 | var bmp = new Bitmap(GetType(), "test.bmp"); 73 | ig.DrawImage(bmp, 100f, 150f); 74 | GraphicsContainer cnt = ig.BeginContainer(); 75 | ig.RotateTransform(7.5f); 76 | ig.DrawImage(bmp, 160f, 50f, 120f, 70f); 77 | ig.EndContainer(cnt); 78 | //ig.DrawImageUnscaled(bmp, 270, 450, 20, 20); 79 | } 80 | 81 | private void SvgGdiTestForm_Load(object sender, EventArgs e) { 82 | TestShared.Renderers.Add("Images", RenderImages); 83 | TestShared.Renderers.Add("Text Rect Aligned", ig => RectAlignedTextTest.RenderRectAlignedText(ig, panel1.ClientSize.Width, panel1.ClientSize.Height, Font)); 84 | cbWhat.Items.Clear(); 85 | cbWhat.Items.AddRange([.. TestShared.Renderers.Keys.OrderBy(s => s)]); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /SvgGdiTest/SvgGdiTestForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /SvgGdiTest/TestApp.ndoc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /SvgGdiTest/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SvgGdiTest/test.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/managed-commons/SvgNet/9aa368c6dafc05f61f97ad7b1c8ec5eda316ec1a/SvgGdiTest/test.bmp -------------------------------------------------------------------------------- /SvgGdiTest/testdoc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SvgGdiTest 5 | 6 | 7 | 8 | 9 | Summary description for Form1. 10 | 11 | 12 | 13 | 14 | The main entry point for the application. 15 | 16 | 17 | 18 | 19 | Clean up any resources being used. 20 | 21 | if should be disposing the components 22 | 23 | 24 | 25 | Required designer variable. 26 | 27 | 28 | 29 | 30 | Required method for Designer support - do not modify 31 | the contents of this method with the code editor. 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SvgNet.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.0.31825.309 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgNet", "SvgNet\SvgNet.csproj", "{BB4C8021-B5E1-4DE2-82CB-14BDFB9837E4}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgDocTest", "SvgDocTest\SvgDocTest.csproj", "{6A203A21-673D-45E1-A7F5-D7112E114D6E}" 8 | EndProject 9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgGdiTest", "SvgGdiTest\SvgGdiTest.csproj", "{0C963EB6-C1B4-453D-B694-E81EC26D3AB9}" 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{45898DE7-2090-4F7C-B198-A5D7A15447FF}" 12 | ProjectSection(SolutionItems) = preProject 13 | .editorconfig = .editorconfig 14 | .gitignore = .gitignore 15 | .github\workflows\codeql.yml = .github\workflows\codeql.yml 16 | .github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml 17 | global.json = global.json 18 | LICENSE = LICENSE 19 | README.md = README.md 20 | EndProjectSection 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgDotNetCoreTest", "SvgDotNetCoreTest\SvgDotNetCoreTest.csproj", "{0AE45FD9-68B8-4232-A8C0-7D9E446F73C9}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgNetUnitTests", "SvgNetUnitTests\SvgNetUnitTests.csproj", "{5510CC3E-821A-45F8-AE32-77B9E49BA0DE}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Release|Any CPU = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {BB4C8021-B5E1-4DE2-82CB-14BDFB9837E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {BB4C8021-B5E1-4DE2-82CB-14BDFB9837E4}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {BB4C8021-B5E1-4DE2-82CB-14BDFB9837E4}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {BB4C8021-B5E1-4DE2-82CB-14BDFB9837E4}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {6A203A21-673D-45E1-A7F5-D7112E114D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {6A203A21-673D-45E1-A7F5-D7112E114D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {6A203A21-673D-45E1-A7F5-D7112E114D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {6A203A21-673D-45E1-A7F5-D7112E114D6E}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {0C963EB6-C1B4-453D-B694-E81EC26D3AB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {0C963EB6-C1B4-453D-B694-E81EC26D3AB9}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {0C963EB6-C1B4-453D-B694-E81EC26D3AB9}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {0C963EB6-C1B4-453D-B694-E81EC26D3AB9}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {0AE45FD9-68B8-4232-A8C0-7D9E446F73C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {0AE45FD9-68B8-4232-A8C0-7D9E446F73C9}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {0AE45FD9-68B8-4232-A8C0-7D9E446F73C9}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {0AE45FD9-68B8-4232-A8C0-7D9E446F73C9}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {5510CC3E-821A-45F8-AE32-77B9E49BA0DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {5510CC3E-821A-45F8-AE32-77B9E49BA0DE}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {5510CC3E-821A-45F8-AE32-77B9E49BA0DE}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {5510CC3E-821A-45F8-AE32-77B9E49BA0DE}.Release|Any CPU.Build.0 = Release|Any CPU 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | GlobalSection(ExtensibilityGlobals) = postSolution 57 | SolutionGuid = {CFB59A8F-8484-428B-B69E-0FAADCD5CC77} 58 | EndGlobalSection 59 | EndGlobal 60 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgBaseTextElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | public abstract class SvgBaseTextElement : SvgStyledTransformedElement { 11 | public SvgLength DX { 12 | get => (SvgLength)_atts["dx"]; 13 | set => _atts["dx"] = value; 14 | } 15 | 16 | public SvgLength DY { 17 | get => (SvgLength)_atts["dy"]; 18 | set => _atts["dy"] = value; 19 | } 20 | 21 | public string LengthAdjust { 22 | get => (string)_atts["lengthAdjust"]; 23 | set => _atts["lengthAdjust"] = value; 24 | } 25 | 26 | public SvgNumList Rotate { 27 | get => (SvgNumList)_atts["rotate"]; 28 | set => _atts["rotate"] = value; 29 | } 30 | 31 | public SvgLength X { 32 | get => (SvgLength)_atts["x"]; 33 | set => _atts["x"] = value; 34 | } 35 | 36 | public SvgLength Y { 37 | get => (SvgLength)_atts["y"]; 38 | set => _atts["y"] = value; 39 | } 40 | } -------------------------------------------------------------------------------- /SvgNet/Elements/SvgElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Xml; 10 | 11 | using SvgNet.Elements; 12 | 13 | namespace SvgNet.Elements; 14 | /// 15 | /// The base class for SVG elements. It represents some part of an SVG document, either an element (rect, circle etc) or a text item. Duties include: 16 | /// 17 | /// 18 | /// 19 | /// Maintains a list of child elements and a list of attributes. 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// Writes itself and its children to an Xml document. 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// Reads itself and its children from an Xml document. 30 | /// 31 | /// 32 | /// 33 | /// 34 | public class SvgElement { 35 | public SvgElement() => Id = GenerateNewId(); 36 | 37 | public SvgElement(string id) => Id = id; 38 | 39 | /// 40 | /// A hashtable containing this element's attributes. Keys are strings but values can be any type; they will only be 41 | /// reduced to strings when this element needs to convert itself to XML. 42 | /// 43 | public Hashtable Attributes => _atts; 44 | 45 | /// 46 | /// An ArrayList containing this element's children 47 | /// 48 | public ArrayList Children { get; protected set; } = []; 49 | 50 | public string Id { 51 | get => (string)_atts["id"]; 52 | set => _atts["id"] = value; 53 | } 54 | 55 | /// 56 | /// The name of the XML element that this SVG element represents. 57 | /// 58 | public virtual string Name => "?"; 59 | 60 | public SvgElement Parent { get; protected set; } 61 | 62 | /// 63 | /// The element whose child this element is; can be null, because SvgElements may only be inserted into a full SVG tree 64 | /// long after they are created. 65 | /// 66 | /// 67 | /// A quick way to get and set attributes. 68 | /// 69 | public object this[string attname] { 70 | get => _atts[attname]; 71 | set => _atts[attname] = value; 72 | } 73 | 74 | /// 75 | /// Adds a child, and sets the child's parent to this element. 76 | /// 77 | /// 78 | public virtual void AddChild(SvgElement ch) { 79 | if (ch.Parent != null) throw new SvgException("Child already has a parent", ch.ToString()); 80 | 81 | _ = Children.Add(ch); 82 | ch.Parent = this; 83 | } 84 | 85 | /// 86 | /// Adds a variable number of children 87 | /// 88 | /// 89 | public virtual SvgElement AddChildren(params SvgElement[] ch) { 90 | foreach (SvgElement el in ch) AddChild(el); 91 | return this; 92 | } 93 | 94 | /// 95 | /// Given a document and a current node, read this element from the node. 96 | /// 97 | /// 98 | /// 99 | public virtual void ReadXmlElement(XmlDocument doc, XmlElement el) { 100 | foreach (XmlAttribute att in el.Attributes) 101 | if (att.Name.StartsWith("xmlns", StringComparison.Ordinal)) 102 | #if NET5_0_OR_GREATER 103 | SvgFactory._namespaces.TryAdd(att.Name, att.Value); 104 | #else 105 | { 106 | if (!SvgFactory._namespaces.ContainsKey(att.Name)) 107 | SvgFactory._namespaces.Add(att.Name, att.Value); 108 | } 109 | #endif 110 | else 111 | this[att.Name] = att.Value; 112 | } 113 | 114 | /// 115 | /// A simple ToString() for use in debugging. 116 | /// 117 | /// 118 | public override string ToString() => "<" + Name + " id='" + Id + "'/>"; 119 | 120 | /// 121 | /// Get a string that contains a complete SVG document. XML version, DOCTYPE etc are included. 122 | /// 123 | /// 124 | /// Should usually be set true. Causes the XML output to be optimized so that 125 | /// long attributes like styles and transformations are represented with entities. 126 | public string WriteSVGString(bool compressAttributes) => 127 | WriteSVGString(compressAttributes, null); 128 | 129 | /// 130 | /// Get a string that contains a complete SVG document. XML version, DOCTYPE etc are included. 131 | /// 132 | /// 133 | /// Should usually be set true. Causes the XML output to be optimized so that 134 | /// long attributes like styles and transformations are represented with entities. 135 | /// Width/Height values to add as attributes to the svg element 136 | public string WriteSVGString(bool compressAttributes, SizeF? bounds) { 137 | var doc = new XmlDocument(); 138 | 139 | XmlDeclaration declaration = doc.CreateXmlDeclaration("1.0", null, "yes"); 140 | _ = doc.AppendChild(declaration); 141 | 142 | //write out our SVG tree to the new XmlDocument 143 | WriteXmlElements(doc, null); 144 | 145 | foreach (KeyValuePair pair in SvgFactory._namespaces) 146 | doc.DocumentElement.SetAttribute(pair.Key, pair.Value); 147 | string ents = string.Empty; 148 | if (compressAttributes) 149 | ents = SvgFactory.CompressXML(doc, doc.DocumentElement); 150 | if (bounds != null) { 151 | doc.DocumentElement.SetAttribute("width", bounds.Value.Width.ToInvariantString()); 152 | doc.DocumentElement.SetAttribute("height", bounds.Value.Height.ToInvariantString()); 153 | } 154 | doc.XmlResolver = new DummyXmlResolver(); 155 | _ = doc.InsertAfter( 156 | doc.CreateDocumentType("svg", "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", ents), 157 | declaration 158 | ); 159 | return ToXmlString(doc); 160 | } 161 | 162 | /// 163 | /// Given an XML document and a parent node, write out this node and its descendants as XmlElements. 164 | /// 165 | /// A document 166 | /// A node, or null if this element is to be the root element 167 | public virtual void WriteXmlElements(XmlDocument doc, XmlElement parent) { 168 | XmlElement me = doc.CreateElement("", Name, doc.NamespaceURI); 169 | foreach (string s in _atts.Keys) _ = _atts[s] switch { 170 | float singleValue => me.SetAttribute(s, doc.NamespaceURI, singleValue.ToString(CultureInfo.InvariantCulture)), 171 | double doubleValue => me.SetAttribute(s, doc.NamespaceURI, doubleValue.ToString(CultureInfo.InvariantCulture)), 172 | _ => SetAttribute(doc, me, s, _atts[s].ToString()), 173 | }; 174 | 175 | foreach (SvgElement el in Children) el.WriteXmlElements(doc, me); 176 | 177 | _ = parent == null ? doc.AppendChild(me) : parent.AppendChild(me); 178 | } 179 | 180 | protected Hashtable _atts = []; 181 | protected object FirstChild => Children[0]; 182 | 183 | protected T GetTypedAttribute(string attributeName, Func fromString) where T : new() { 184 | T SetNewAttributeValue(T st) { 185 | _atts[attributeName] = st; 186 | return st; 187 | } 188 | object o = _atts[attributeName]; 189 | //in case the property was set as a string, make a real object and save it. 190 | return o == null ? SetNewAttributeValue(new T()) : o is T t ? t : SetNewAttributeValue(fromString(o)); 191 | } 192 | 193 | private static int _idcounter; 194 | 195 | private static string ToXmlString(XmlDocument doc) => doc.OuterXml; 196 | 197 | private static string GenerateNewId() => _idcounter++.ToString(); 198 | 199 | protected static string SetAttribute(XmlDocument doc, XmlElement me, string name, string value) { 200 | string[] parts = name.Split(':'); 201 | return parts.Length switch { 202 | 1 => me.SetAttribute(name, doc.NamespaceURI, value), 203 | 2 => me.SetAttribute(parts[1], NamespaceForPrefix("xmlns:" + parts[0]), value), 204 | _ => throw new InvalidOperationException($"Attribute name has more tha one ':' => '{name}'") 205 | }; 206 | } 207 | 208 | private static string NamespaceForPrefix(string xmlns) => SvgFactory._namespaces.TryGetValue(xmlns, out string namespaceURI) ? namespaceURI : string.Empty; 209 | private class DummyXmlResolver : XmlResolver { 210 | public override System.Net.ICredentials Credentials { set { } } 211 | 212 | public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) => new MemoryStream(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgEllipseElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an ellipse element. 12 | /// 13 | public class SvgEllipseElement : SvgStyledTransformedElement { 14 | public SvgEllipseElement() { 15 | } 16 | 17 | public SvgEllipseElement(SvgLength cx, SvgLength cy, SvgLength rx, SvgLength ry) { 18 | CX = cx; 19 | CY = cy; 20 | RX = rx; 21 | RY = ry; 22 | } 23 | 24 | public SvgLength CX { 25 | get => (SvgLength)_atts["cx"]; 26 | set => _atts["cx"] = value; 27 | } 28 | 29 | public SvgLength CY { 30 | get => (SvgLength)_atts["cy"]; 31 | set => _atts["cy"] = value; 32 | } 33 | 34 | public override string Name => "ellipse"; 35 | 36 | public SvgLength RX { 37 | get => (SvgLength)_atts["rx"]; 38 | set => _atts["rx"] = value; 39 | } 40 | 41 | public SvgLength RY { 42 | get => (SvgLength)_atts["ry"]; 43 | set => _atts["ry"] = value; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgFilterElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | public class SvgFilterElement : SvgElement { 11 | public SvgFilterElement() { 12 | } 13 | 14 | public SvgFilterElement(SvgLength x, SvgLength y, SvgLength w, SvgLength h) { 15 | X = x; 16 | Y = y; 17 | Width = w; 18 | Height = h; 19 | } 20 | 21 | public string FilterRes { 22 | get => (string)_atts["filterRes"]; 23 | set => _atts["filterRes"] = value; 24 | } 25 | 26 | public string FilterUnits { 27 | get => (string)_atts["filterUnits"]; 28 | set => _atts["filterUnits"] = value; 29 | } 30 | 31 | public SvgLength Height { 32 | get => (SvgLength)_atts["height"]; 33 | set => _atts["height"] = value; 34 | } 35 | 36 | public override string Name => "filter"; 37 | 38 | public string PrimitiveUnits { 39 | get => (string)_atts["primitiveUnits"]; 40 | set => _atts["primitiveUnits"] = value; 41 | } 42 | 43 | public SvgLength Width { 44 | get => (SvgLength)_atts["width"]; 45 | set => _atts["width"] = value; 46 | } 47 | 48 | public SvgLength X { 49 | get => (SvgLength)_atts["x"]; 50 | set => _atts["x"] = value; 51 | } 52 | 53 | public SvgLength Y { 54 | get => (SvgLength)_atts["y"]; 55 | set => _atts["y"] = value; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgGroupElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an a element. It has an xref and a target. 12 | /// 13 | public class SvgAElement : SvgStyledTransformedElement, IElementWithXRef { 14 | public SvgAElement() { 15 | } 16 | 17 | public SvgAElement(string href) => Href = href; 18 | 19 | public string Href { 20 | get => (string)_atts["xlink:href"]; 21 | set => _atts["xlink:href"] = value; 22 | } 23 | 24 | public override string Name => "a"; 25 | 26 | public string Target { 27 | get => (string)_atts["target"]; 28 | set => _atts["target"] = value; 29 | } 30 | 31 | public SvgXRef XRef { 32 | get => new(this); 33 | set => value.WriteToElement(this); 34 | } 35 | } 36 | 37 | /// 38 | /// Represents a clippath element. It has no particular properties of its own. 39 | /// 40 | public class SvgClipPathElement : SvgElement { 41 | public SvgClipPathElement() { 42 | } 43 | 44 | public SvgClipPathElement(string id) : base(id) { 45 | } 46 | 47 | public override string Name => "clipPath"; 48 | } 49 | 50 | /// 51 | /// Represents a defs element. It has no particular properties of its own. 52 | /// 53 | public class SvgDefsElement : SvgElement { 54 | public SvgDefsElement() { 55 | } 56 | 57 | public SvgDefsElement(string id) : base(id) { 58 | } 59 | 60 | public override string Name => "defs"; 61 | } 62 | 63 | /// 64 | /// Represents an element that is not yet represented by a class of its own. 65 | /// 66 | public class SvgGenericElement : SvgElement { 67 | public SvgGenericElement() => _name = "generic svg node"; 68 | 69 | public SvgGenericElement(string name) => _name = name; 70 | 71 | public override string Name => _name; 72 | private readonly string _name; 73 | } 74 | 75 | /// 76 | /// Represents a g element. It has no particular properties of its own. 77 | /// 78 | public class SvgGroupElement : SvgStyledTransformedElement { 79 | public SvgGroupElement() { 80 | } 81 | 82 | public SvgGroupElement(string id) : base(id) { 83 | } 84 | 85 | public override string Name => "g"; 86 | } 87 | 88 | /// 89 | /// Represents a switch element. It has no particular properties of its own. 90 | /// 91 | public class SvgSwitchElement : SvgStyledTransformedElement { 92 | public SvgSwitchElement() { 93 | } 94 | 95 | public SvgSwitchElement(string id) : base(id) { 96 | } 97 | 98 | public override string Name => "g"; 99 | } 100 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgLineElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a line element 12 | /// 13 | public class SvgLineElement : SvgStyledTransformedElement { 14 | public SvgLineElement() { 15 | } 16 | 17 | public SvgLineElement(SvgLength x1, SvgLength y1, SvgLength x2, SvgLength y2) { 18 | X1 = x1; 19 | Y1 = y1; 20 | X2 = x2; 21 | Y2 = y2; 22 | } 23 | 24 | public override string Name => "line"; 25 | 26 | public SvgLength X1 { 27 | get => (SvgLength)_atts["x1"]; 28 | set => _atts["x1"] = value; 29 | } 30 | 31 | public SvgLength X2 { 32 | get => (SvgLength)_atts["x2"]; 33 | set => _atts["x2"] = value; 34 | } 35 | 36 | public SvgLength Y1 { 37 | get => (SvgLength)_atts["y1"]; 38 | set => _atts["y1"] = value; 39 | } 40 | 41 | public SvgLength Y2 { 42 | get => (SvgLength)_atts["y2"]; 43 | set => _atts["y2"] = value; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgLinearGradientElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an SVG linearGradient element 12 | /// 13 | public class SvgLinearGradientElement : SvgStyledTransformedElement { 14 | public SvgLinearGradientElement() { 15 | } 16 | 17 | public SvgLinearGradientElement(SvgLength x1, SvgLength y1, SvgLength x2, SvgLength y2) { 18 | X1 = x1; 19 | Y1 = y1; 20 | X2 = x2; 21 | Y2 = y2; 22 | } 23 | 24 | public SvgTransformList GradientTransform { 25 | get => (SvgTransformList)_atts["gradientTransform"]; 26 | set => _atts["gradientTransform"] = value; 27 | } 28 | 29 | public string GradientUnits { 30 | get => (string)_atts["gradientUnits"]; 31 | set => _atts["gradientUnits"] = value; 32 | } 33 | 34 | public override string Name => "linearGradient"; 35 | 36 | public string SpreadMethod { 37 | get => (string)_atts["spreadMethod"]; 38 | set => _atts["spreadMethod"] = value; 39 | } 40 | 41 | public SvgLength X1 { 42 | get => (SvgLength)_atts["x1"]; 43 | set => _atts["x1"] = value; 44 | } 45 | 46 | public SvgLength X2 { 47 | get => (SvgLength)_atts["x2"]; 48 | set => _atts["x2"] = value; 49 | } 50 | 51 | public SvgLength Y1 { 52 | get => (SvgLength)_atts["y1"]; 53 | set => _atts["y1"] = value; 54 | } 55 | 56 | public SvgLength Y2 { 57 | get => (SvgLength)_atts["y2"]; 58 | set => _atts["y2"] = value; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgPathElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a path element 12 | /// 13 | public class SvgPathElement : SvgStyledTransformedElement { 14 | public SvgPathElement() { 15 | } 16 | 17 | public SvgPath D { 18 | get => (SvgPath)_atts["d"]; 19 | set => _atts["d"] = value.ToString(); 20 | } 21 | 22 | public override string Name => "path"; 23 | 24 | public SvgNumber PathLength { 25 | get => (SvgNumber)_atts["pathlength"]; 26 | set => _atts["pathlength"] = value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgPatternElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an SVG pattern element, which defines a fill pattern by defining a viewport onto a subscene. 12 | /// 13 | public class SvgPatternElement : SvgStyledTransformedElement { 14 | public SvgPatternElement() { 15 | } 16 | 17 | public SvgPatternElement(SvgLength width, SvgLength height, SvgNumList vport) { 18 | Width = width; 19 | Height = height; 20 | ViewBox = vport; 21 | } 22 | 23 | public SvgPatternElement(SvgLength x, SvgLength y, SvgLength width, SvgLength height, SvgNumList vport) { 24 | X = x; 25 | Y = y; 26 | Width = width; 27 | Height = height; 28 | ViewBox = vport; 29 | } 30 | 31 | public SvgLength Height { 32 | get => (SvgLength)_atts["height"]; 33 | set => _atts["height"] = value; 34 | } 35 | 36 | public override string Name => "pattern"; 37 | 38 | public string PatternContentUnits { 39 | get => (string)_atts["patternContentUnits"]; 40 | set => _atts["patternContentUnits"] = value; 41 | } 42 | 43 | public SvgTransformList PatternTransform { 44 | get => (SvgTransformList)_atts["patternTransform"]; 45 | set => _atts["patternTransform"] = value; 46 | } 47 | 48 | public string PatternUnits { 49 | get => (string)_atts["patternUnits"]; 50 | set => _atts["patternUnits"] = value; 51 | } 52 | 53 | public string PreserveAspectRatio { 54 | get => (string)_atts["preserveAspectRatio"]; 55 | set => _atts["preserveAspectRatio"] = value; 56 | } 57 | 58 | public SvgNumList ViewBox { 59 | get => (SvgNumList)_atts["viewBox"]; 60 | set => _atts["viewBox"] = value; 61 | } 62 | 63 | public SvgLength Width { 64 | get => (SvgLength)_atts["width"]; 65 | set => _atts["width"] = value; 66 | } 67 | 68 | public SvgLength X { 69 | get => (SvgLength)_atts["x"]; 70 | set => _atts["x"] = value; 71 | } 72 | 73 | public SvgLength Y { 74 | get => (SvgLength)_atts["y"]; 75 | set => _atts["y"] = value; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgPolygonElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a polygon element 12 | /// 13 | public class SvgPolygonElement : SvgStyledTransformedElement { 14 | public SvgPolygonElement(params PointF[] points) => Points = points; 15 | 16 | public SvgPolygonElement(SvgPoints points) => Points = points; 17 | 18 | public SvgPolygonElement() { 19 | // used by some reflection code 20 | } 21 | 22 | public override string Name => "polygon"; 23 | 24 | public SvgPoints Points { 25 | get => (SvgPoints)_atts["points"]; 26 | set => _atts["points"] = value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgPolylineElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a polyline element 12 | /// 13 | public class SvgPolylineElement : SvgStyledTransformedElement { 14 | public SvgPolylineElement() { 15 | } 16 | 17 | public SvgPolylineElement(SvgPoints points) => Points = points; 18 | 19 | public override string Name => "polyline"; 20 | 21 | public SvgPoints Points { 22 | get => (SvgPoints)_atts["points"]; 23 | set => _atts["points"] = value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgRadialGradientElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an svg radialGradient element 12 | /// 13 | public class SvgRadialGradientElement : SvgStyledTransformedElement { 14 | public SvgRadialGradientElement() { 15 | } 16 | 17 | public SvgLength CX { 18 | get => (SvgLength)_atts["cx"]; 19 | set => _atts["cx"] = value; 20 | } 21 | 22 | public SvgLength CY { 23 | get => (SvgLength)_atts["cy"]; 24 | set => _atts["cy"] = value; 25 | } 26 | 27 | public SvgLength FX { 28 | get => (SvgLength)_atts["fx"]; 29 | set => _atts["fx"] = value; 30 | } 31 | 32 | public SvgLength FY { 33 | get => (SvgLength)_atts["fy"]; 34 | set => _atts["fy"] = value; 35 | } 36 | 37 | public SvgTransformList GradientTransform { 38 | get => (SvgTransformList)_atts["gradientTransform"]; 39 | set => _atts["gradientTransform"] = value; 40 | } 41 | 42 | public string GradientUnits { 43 | get => (string)_atts["gradientUnits"]; 44 | set => _atts["gradientUnits"] = value; 45 | } 46 | 47 | public override string Name => "radialGradient"; 48 | 49 | public SvgLength R { 50 | get => (SvgLength)_atts["r"]; 51 | set => _atts["r"] = value; 52 | } 53 | 54 | public string SpreadMethod { 55 | get => (string)_atts["spreadMethod"]; 56 | set => _atts["spreadMethod"] = value; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgRectElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a rect element 12 | /// 13 | public class SvgRectElement : SvgStyledTransformedElement { 14 | public SvgRectElement() { 15 | } 16 | 17 | public SvgRectElement(SvgLength x, SvgLength y, SvgLength w, SvgLength h) { 18 | X = x; 19 | Y = y; 20 | Width = w; 21 | Height = h; 22 | } 23 | 24 | public SvgLength Height { 25 | get => (SvgLength)_atts["height"]; 26 | set => _atts["height"] = value; 27 | } 28 | 29 | public override string Name => "rect"; 30 | 31 | public SvgLength Width { 32 | get => (SvgLength)_atts["width"]; 33 | set => _atts["width"] = value; 34 | } 35 | 36 | public SvgLength X { 37 | get => (SvgLength)_atts["x"]; 38 | set => _atts["x"] = value; 39 | } 40 | 41 | public SvgLength Y { 42 | get => (SvgLength)_atts["y"]; 43 | set => _atts["y"] = value; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgStopElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an SVG stop element, which specifies one color in a gradient. 12 | /// 13 | public class SvgStopElement : SvgStyledTransformedElement { 14 | public SvgStopElement() { 15 | } 16 | 17 | public SvgStopElement(SvgLength num, SvgColor col) { 18 | Offset = num; 19 | 20 | Style.Set("stop-color", col); 21 | } 22 | 23 | public override string Name => "stop"; 24 | 25 | public SvgLength Offset { 26 | get => (SvgLength)_atts["offset"]; 27 | set => _atts["offset"] = value; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgStyledElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Xml; 10 | 11 | namespace SvgNet.Elements; 12 | /// 13 | /// This is an SvgElement that can have a CSS style and an SVG transformation list. It contains special properties to make reading and setting the style 14 | /// and the transformation easier. All SVG elements that actually represent visual entities or groups of entities are SvgStyledTransformedElements. 15 | /// 16 | public class SvgStyledTransformedElement : SvgElement { 17 | public SvgStyledTransformedElement() { 18 | } 19 | 20 | public SvgStyledTransformedElement(string id) : base(id) { 21 | } 22 | 23 | /// 24 | /// Provides an easy way to get the attribute called "style" as an SvgStyle object. If no such attribute has been set, one is created when 25 | /// this property is read. 26 | /// 27 | public SvgStyle Style { 28 | get => GetTypedAttribute("style", (obj) => new SvgStyle(obj.ToString())); 29 | set => _atts["style"] = value; 30 | } 31 | 32 | /// 33 | /// Provides an easy way to get the attribute called "transform" as an SvgTransformList object. If no such attribute has been set, one is created when 34 | /// this property is read. 35 | /// 36 | public SvgTransformList Transform { 37 | get => GetTypedAttribute("transform", (obj) => new SvgTransformList(obj.ToString())); 38 | set => _atts["transform"] = value; 39 | } 40 | 41 | /// 42 | /// Given a document and a current node, read this element from the node. 43 | /// 44 | /// 45 | /// 46 | public override void ReadXmlElement(XmlDocument doc, XmlElement el) { 47 | foreach (XmlAttribute att in el.Attributes) { 48 | string name = att.Name; 49 | string value = att.Value; 50 | switch (name) { 51 | case "style": 52 | Style = new SvgStyle(value); 53 | break; 54 | case "transform": 55 | Transform = new SvgTransformList(value); 56 | break; 57 | default: 58 | this[name] = value; 59 | break; 60 | } 61 | } 62 | } 63 | 64 | /// 65 | /// Overridden in this class to provide special handling for the style and transform attributes, 66 | /// which are often long and complicated. For instance, it may be desirable for styles to be written as entities or as separate 67 | /// attributes. 68 | /// 69 | /// 70 | /// 71 | public override void WriteXmlElements(XmlDocument doc, XmlElement parent) { 72 | XmlElement me = doc.CreateElement("", Name, doc.NamespaceURI); 73 | foreach (string name in _atts.Keys) { 74 | object attribute = _atts[name]; 75 | if (attribute is null) 76 | continue; 77 | switch (name) { 78 | case "style": 79 | WriteStyle(doc, me, attribute); 80 | break; 81 | case "transform": 82 | WriteTransform(doc, me, attribute); 83 | break; 84 | default: 85 | SetAttribute(doc, me, name, attribute.ToString()); 86 | break; 87 | } 88 | } 89 | 90 | foreach (SvgElement el in Children) 91 | el.WriteXmlElements(doc, me); 92 | 93 | _ = parent == null ? doc.AppendChild(me) : parent.AppendChild(me); 94 | } 95 | 96 | private static void WriteStyle(XmlDocument doc, XmlElement me, object o) { 97 | if (o.GetType() != typeof(SvgStyle)) { 98 | _ = me.SetAttribute("style", doc.NamespaceURI, o.ToString()); 99 | return; 100 | } 101 | 102 | var style = (SvgStyle)o; 103 | 104 | /* 105 | foreach(string s in style.Keys) 106 | { 107 | me.SetAttribute(s, doc.NamespaceURI, style.Get(s).ToString()); 108 | } 109 | */ 110 | 111 | _ = me.SetAttribute("style", doc.NamespaceURI, style.ToString()); 112 | 113 | _ = doc.CreateEntityReference("pingu"); 114 | } 115 | 116 | private static void WriteTransform(XmlDocument doc, XmlElement me, object o) => 117 | //if (o.GetType() != typeof(SvgTransformList)) 118 | //{ 119 | me.SetAttribute("transform", doc.NamespaceURI, o.ToString());//} 120 | } 121 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgSvgElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a svg element 12 | /// 13 | public class SvgSvgElement : SvgElement { 14 | public SvgSvgElement() { 15 | } 16 | 17 | public SvgSvgElement(SvgLength width, SvgLength height) { 18 | Width = width; 19 | Height = height; 20 | } 21 | 22 | public SvgSvgElement(SvgLength width, SvgLength height, SvgNumList vport) { 23 | Width = width; 24 | Height = height; 25 | ViewBox = vport; 26 | } 27 | 28 | public SvgSvgElement(SvgLength x, SvgLength y, SvgLength width, SvgLength height, SvgNumList vport) { 29 | X = x; 30 | Y = y; 31 | Width = width; 32 | Height = height; 33 | ViewBox = vport; 34 | } 35 | 36 | public SvgLength Height { 37 | get => (SvgLength)_atts["height"]; 38 | set => _atts["height"] = value; 39 | } 40 | 41 | public override string Name => "svg"; 42 | 43 | public string PreserveAspectRatio { 44 | get => (string)_atts["preserveAspectRatio"]; 45 | set => _atts["preserveAspectRatio"] = value; 46 | } 47 | 48 | public string Version { 49 | get => (string)_atts["version"]; 50 | set => _atts["version"] = value; 51 | } 52 | 53 | public SvgNumList ViewBox { 54 | get => (SvgNumList)_atts["viewBox"]; 55 | set => _atts["viewBox"] = value; 56 | } 57 | 58 | public SvgLength Width { 59 | get => (SvgLength)_atts["width"]; 60 | set => _atts["width"] = value; 61 | } 62 | 63 | public SvgLength X { 64 | get => (SvgLength)_atts["x"]; 65 | set => _atts["x"] = value; 66 | } 67 | 68 | public SvgLength Y { 69 | get => (SvgLength)_atts["y"]; 70 | set => _atts["y"] = value; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgSymbolElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an SVG image element. 12 | /// 13 | public class SvgImageElement : SvgStyledTransformedElement, IElementWithXRef { 14 | public SvgImageElement() { 15 | } 16 | 17 | public SvgImageElement(SvgXRef xref) => XRef = xref; 18 | 19 | public SvgImageElement(SvgLength x, SvgLength y, SvgXRef xref) { 20 | XRef = xref; 21 | X = x; 22 | Y = y; 23 | } 24 | 25 | public SvgImageElement(string href) => Href = href; 26 | 27 | public SvgImageElement(SvgLength x, SvgLength y, string href) { 28 | Href = href; 29 | X = x; 30 | Y = y; 31 | } 32 | 33 | public SvgLength Height { 34 | get => (SvgLength)_atts["height"]; 35 | set => _atts["height"] = value; 36 | } 37 | 38 | public string Href { 39 | get => (string)_atts["xlink:href"]; 40 | set => _atts["xlink:href"] = value; 41 | } 42 | 43 | public override string Name => "image"; 44 | 45 | public string PreserveAspectRatio { 46 | get => (string)_atts["preserveAspectRatio"]; 47 | set => _atts["preserveAspectRatio"] = value; 48 | } 49 | 50 | public SvgLength Width { 51 | get => (SvgLength)_atts["width"]; 52 | set => _atts["width"] = value; 53 | } 54 | 55 | public SvgLength X { 56 | get => (SvgLength)_atts["x"]; 57 | set => _atts["x"] = value; 58 | } 59 | 60 | public SvgXRef XRef { 61 | get => new(this); 62 | set => value.WriteToElement(this); 63 | } 64 | 65 | public SvgLength Y { 66 | get => (SvgLength)_atts["y"]; 67 | set => _atts["y"] = value; 68 | } 69 | } 70 | 71 | /// 72 | /// Represents an SVG symbol element. 73 | /// 74 | public class SvgSymbolElement : SvgElement { 75 | public SvgSymbolElement() { 76 | } 77 | 78 | public override string Name => "symbol"; 79 | 80 | public string PreserveAspectRatio { 81 | get => (string)_atts["preserveAspectRatio"]; 82 | set => _atts["preserveAspectRatio"] = value; 83 | } 84 | 85 | public SvgNumList ViewBox { 86 | get => (SvgNumList)_atts["viewBox"]; 87 | set => _atts["viewBox"] = value; 88 | } 89 | } 90 | 91 | /// 92 | /// Represents an SVG use element. 93 | /// 94 | public class SvgUseElement : SvgStyledTransformedElement, IElementWithXRef { 95 | public SvgUseElement() { 96 | } 97 | 98 | public SvgUseElement(SvgXRef xref) => XRef = xref; 99 | 100 | public SvgUseElement(string href) => Href = href; 101 | 102 | public SvgUseElement(SvgLength x, SvgLength y, SvgXRef xref) { 103 | XRef = xref; 104 | X = x; 105 | Y = y; 106 | } 107 | 108 | public SvgUseElement(SvgLength x, SvgLength y, string href) { 109 | Href = href; 110 | X = x; 111 | Y = y; 112 | } 113 | 114 | public SvgLength Height { 115 | get => (SvgLength)_atts["height"]; 116 | set => _atts["height"] = value; 117 | } 118 | 119 | public string Href { 120 | get => (string)_atts["xlink:href"]; 121 | set => _atts["xlink:href"] = value; 122 | } 123 | 124 | public override string Name => "use"; 125 | 126 | public SvgLength Width { 127 | get => (SvgLength)_atts["width"]; 128 | set => _atts["width"] = value; 129 | } 130 | 131 | public SvgLength X { 132 | get => (SvgLength)_atts["x"]; 133 | set => _atts["x"] = value; 134 | } 135 | 136 | public SvgXRef XRef { 137 | get => new(this); 138 | set => value.WriteToElement(this); 139 | } 140 | 141 | public SvgLength Y { 142 | get => (SvgLength)_atts["y"]; 143 | set => _atts["y"] = value; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgTextElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a text element. The SVG text element is unusual in that it expects actual XML text nodes below 12 | /// it, rather than consisting only of attributes and child elements (other elements like this are title, desc, and tspan). 13 | /// SvgTextElement therefore has to be serialized 14 | /// to XML slightly differently. 15 | /// 16 | public class SvgTextElement : SvgBaseTextElement, IElementWithText { 17 | 18 | public SvgTextElement() { 19 | } 20 | 21 | public SvgTextElement(string s) { 22 | var tn = new TextNode(s); 23 | AddChild(tn); 24 | } 25 | 26 | [Obsolete("Must pass SvgLength, for x and y, which specifies a unit. Use the new constructor override")] 27 | public SvgTextElement(string s, float x, float y) { 28 | var tn = new TextNode(s); 29 | AddChild(tn); 30 | X = x; 31 | Y = y; 32 | } 33 | 34 | public SvgTextElement(string s, SvgLength x, SvgLength y) { 35 | var tn = new TextNode(s); 36 | AddChild(tn); 37 | X = x; 38 | Y = y; 39 | } 40 | 41 | public override string Name => "text"; 42 | 43 | public string Text { 44 | get => ((TextNode)FirstChild).Text; 45 | set => ((TextNode)FirstChild).Text = value; 46 | } 47 | 48 | public SvgLength TextLength { 49 | get => (SvgLength)_atts["textLength"]; 50 | set => _atts["textLength"] = value; 51 | } 52 | } -------------------------------------------------------------------------------- /SvgNet/Elements/SvgTitleElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents an SVG desc element. As with the SvgTextElement, the payload is in the enclosed text rather than in attributes and 12 | /// subelements, so we need to specially add text when serializing. 13 | /// 14 | public class SvgDescElement : SvgElement, IElementWithText { 15 | public SvgDescElement() { 16 | var tn = new TextNode(""); 17 | AddChild(tn); 18 | } 19 | 20 | public SvgDescElement(string s) { 21 | var tn = new TextNode(s); 22 | AddChild(tn); 23 | } 24 | 25 | public override string Name => "desc"; 26 | 27 | public string Text { 28 | get => ((TextNode)FirstChild).Text; 29 | set => ((TextNode)FirstChild).Text = value; 30 | } 31 | } 32 | 33 | /// 34 | /// Represents an SVG desc element. As with the SvgTextElement, the payload is in the enclosed text rather than in attributes and 35 | /// subelements, so we need to specially add text when serializing. 36 | /// 37 | public class SvgTitleElement : SvgElement, IElementWithText { 38 | public SvgTitleElement() { 39 | var tn = new TextNode(""); 40 | AddChild(tn); 41 | } 42 | 43 | public SvgTitleElement(string s) { 44 | var tn = new TextNode(s); 45 | AddChild(tn); 46 | } 47 | 48 | public override string Name => "title"; 49 | 50 | public string Text { 51 | get => ((TextNode)FirstChild).Text; 52 | set => ((TextNode)FirstChild).Text = value; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /SvgNet/Elements/SvgTrefElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a tref element. 12 | /// 13 | [Obsolete("Don't use it anymore")] 14 | public class SvgTrefElement : SvgBaseTextElement, IElementWithXRef { 15 | 16 | public SvgTrefElement() { 17 | } 18 | 19 | public SvgTrefElement(string href) => Href = href; 20 | 21 | public SvgTrefElement(string href, float x, float y) { 22 | Href = href; 23 | X = x; 24 | Y = y; 25 | } 26 | 27 | public string Href { 28 | get => (string)_atts["xlink:href"]; 29 | set => _atts["xlink:href"] = value; 30 | } 31 | 32 | public override string Name => "tref"; 33 | 34 | public SvgXRef XRef { 35 | get => new(this); 36 | set => value.WriteToElement(this); 37 | } 38 | } -------------------------------------------------------------------------------- /SvgNet/Elements/SvgTspanElement.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Elements; 10 | /// 11 | /// Represents a tspan element. The tspan element is unique in that it expects actual XML text nodes below 12 | /// it, rather than consisting only of attributes and child elements. SvgTextElement therefore has to be serialized 13 | /// to XML slightly differently. 14 | /// 15 | public class SvgTspanElement : SvgTextElement { 16 | 17 | public SvgTspanElement() { 18 | } 19 | 20 | public SvgTspanElement(string s) : base(s) { 21 | } 22 | 23 | [Obsolete("Use constructor override that receives SvgLength for x and y")] 24 | public SvgTspanElement(string s, float x, float y) : base(s, x, y) { 25 | } 26 | 27 | public SvgTspanElement(string s, SvgLength x, SvgLength y) : base(s, x, y) { 28 | } 29 | 30 | public override string Name => "tspan"; 31 | } -------------------------------------------------------------------------------- /SvgNet/Elements/TextNode.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Xml; 10 | 11 | namespace SvgNet.Elements; 12 | /// 13 | /// Represents the text contained in a title, desc, text, or tspan element. Maps to an XmlText object in an XML document. It is inherited from 14 | /// 15 | public class TextNode : SvgElement { 16 | public TextNode() { 17 | } 18 | 19 | public TextNode(string s) => Text = s; 20 | 21 | public override string Name => "a text node, not an svg element"; 22 | 23 | public string Text { get; set; } 24 | 25 | /// 26 | /// Adds a child, and sets the child's parent to this element. 27 | /// 28 | /// 29 | public override void AddChild(SvgElement ch) => throw new SvgException("A TextNode cannot have children"); 30 | 31 | /// 32 | /// Adds a variable number of children 33 | /// 34 | /// 35 | public override SvgElement AddChildren(params SvgElement[] ch) => throw new SvgException("A TextNode cannot have children"); 36 | 37 | /// 38 | /// Given a document and a current node, read this element from the node. 39 | /// 40 | /// 41 | /// 42 | public override void ReadXmlElement(XmlDocument doc, XmlElement el) => throw new SvgException("TextNode::ReadXmlElement should not be called; " + 43 | "the value should be filled in with a string when the XML doc is being read.", ""); 44 | 45 | /// 46 | /// Overridden to simply create an XML text node below the parent. 47 | /// 48 | /// 49 | /// 50 | public override void WriteXmlElements(XmlDocument doc, XmlElement parent) { 51 | XmlText xt = doc.CreateTextNode(Text); 52 | _ = parent switch { 53 | null => doc.AppendChild(xt), 54 | _ => parent.AppendChild(xt), 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /SvgNet/Exceptions/EmfException.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Exceptions; 10 | 11 | [Serializable] 12 | public sealed class EmfException : Exception { 13 | public EmfException(string message) 14 | : base(message) { 15 | } 16 | 17 | public EmfException() : base() { 18 | } 19 | 20 | public EmfException(string message, Exception innerException) : base(message, innerException) { 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /SvgNet/Exceptions/SvgException.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Exceptions; 10 | /// 11 | /// A general-purpose exception for problems that occur in SvgNet. 12 | /// 13 | [Serializable] 14 | public class SvgException : Exception { 15 | public SvgException(string msg, string ctx) : base(msg) { 16 | Msg = msg; 17 | Ctx = ctx; 18 | } 19 | 20 | public SvgException(string msg) : this(msg, "") { 21 | } 22 | 23 | public SvgException() : this("", "") { 24 | } 25 | 26 | public SvgException(string msg, Exception innerException) : this(msg, "", innerException) { 27 | } 28 | 29 | public SvgException(string msg, string ctx, Exception innerException) : base(msg, innerException) { 30 | Msg = msg; 31 | Ctx = ctx; 32 | } 33 | 34 | /// 35 | /// A string intended to supply context information. 36 | /// 37 | public string Ctx { get; } 38 | 39 | /// 40 | /// A message describing the problem. 41 | /// 42 | public string Msg { get; } 43 | } 44 | -------------------------------------------------------------------------------- /SvgNet/Exceptions/SvgGdiNotImplementedException.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Exceptions; 10 | /// 11 | /// Exception thrown when a GDI+ operation is attempted on an IGraphics implementor that does not support the operation. 12 | /// For instance, SvgGraphics does not support any of the MeasureString methods. 13 | /// 14 | [Serializable] 15 | public sealed class SvgGdiNotImplementedException : NotImplementedException { 16 | public SvgGdiNotImplementedException(string method) => Method = method; 17 | 18 | public SvgGdiNotImplementedException() => Method = "?"; 19 | 20 | public SvgGdiNotImplementedException(string message, Exception innerException) : base(message, innerException) => Method = "?"; 21 | 22 | public SvgGdiNotImplementedException(string method, string message, Exception innerException) : base(message, innerException) => Method = method; 23 | 24 | public string Method { get; } 25 | } 26 | -------------------------------------------------------------------------------- /SvgNet/Extensions/FloatExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace System; 10 | 11 | public static class FloatExtensions { 12 | public static string ToInvariantString(this float value) => 13 | value.ToString(CultureInfo.InvariantCulture); 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /SvgNet/Extensions/MatrixExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace System.Drawing.Drawing2D; 10 | 11 | public static class MatrixExtensions { 12 | 13 | public static Matrix MultiplyAll(this List stack, Matrix result = null) { 14 | result ??= new Matrix(); 15 | foreach (Matrix mat in stack) 16 | if (!mat.IsIdentity) 17 | result.Multiply(mat); 18 | return result; 19 | } 20 | } -------------------------------------------------------------------------------- /SvgNet/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace System; 10 | public static class ObjectExtensions { 11 | public static object CloneIfPossible(this object o) => (o as ICloneable)?.Clone() ?? o; 12 | } 13 | -------------------------------------------------------------------------------- /SvgNet/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace System; 10 | 11 | public static class StringExtensions { 12 | public static int ParseHex(this string s, int startIndex, int length = 1) 13 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 14 | => int.Parse(s.AsSpan(startIndex, length), NumberStyles.HexNumber, CultureInfo.InvariantCulture); 15 | #else 16 | => int.Parse(s.Substring(startIndex, length), NumberStyles.HexNumber, CultureInfo.InvariantCulture); 17 | #endif 18 | 19 | public static string SkipFirst(this string s) 20 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 21 | => s[1..]; 22 | #else 23 | => s.Substring(1); 24 | #endif 25 | 26 | #if NET8_0_OR_GREATER 27 | private static readonly Buffers.SearchValues digits = Buffers.SearchValues.Create(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']); 28 | #else 29 | private static readonly char[] digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; 30 | #endif 31 | public static bool TrySplitNumberAndSuffix(this string s, out string number, out string suffix) { 32 | #if NET8_0_OR_GREATER 33 | int i = s.AsSpan().LastIndexOfAny(digits) + 1; 34 | #else 35 | int i = s.LastIndexOfAny(digits) + 1; 36 | #endif 37 | suffix = number = null; 38 | if (i == 0) 39 | return false; 40 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 41 | number = s[..i]; 42 | suffix = s[i..]; 43 | #else 44 | number = s.Substring(0, i); 45 | suffix = s.Substring(i); 46 | #endif 47 | return true; 48 | } 49 | 50 | public static bool TryParseTransformation(this string s, out string name, out float[] points) { 51 | int idx = s.IndexOf('('); 52 | int idx2 = s.IndexOf(')'); 53 | if (idx == -1 || idx2 <= idx) { 54 | name = null; 55 | points = null; 56 | return false; 57 | } 58 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 59 | name = s[..idx]; 60 | #else 61 | name = s.Substring(0, idx); 62 | #endif 63 | name = name.Trim(' ', ','); 64 | points = SvgNumList.String2Floats(s.Substring(idx + 1, idx2 - idx - 1)); 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /SvgNet/Extensions/StringExtensionsForOlderDotNet.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet; 10 | 11 | #if !(NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER) 12 | public static class StringExtensionsForOlderDotNet { 13 | public static bool Contains(this string s, char c) 14 | => s.IndexOf(c) >= 0; 15 | } 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /SvgNet/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "We use Graphics library not for drawing")] 9 | -------------------------------------------------------------------------------- /SvgNet/ImplementedGraphics/SVGGraphics.BitmapDrawer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using SvgNet.Elements; 10 | 11 | namespace SvgNet; 12 | 13 | public sealed partial class SvgGraphics { 14 | private class BitmapDrawer(SvgGroupElement g, float x, float y, float scaleX, float scaleY) { 15 | public SvgGroupElement DrawBitmapData(Bitmap b) { 16 | for (int line = 0; line < b.Height; ++line) { 17 | float scaledLine = y + (line * scaleY); 18 | // Only draws the last 'set' of pixels when a new color is encountered or it's the last pixel in the line. 19 | Color currentColor = GetPixelColor(b, line, 0); 20 | int consecutive = 1; 21 | for (int col = 0; col < b.Width; ++col) { 22 | try { 23 | if (col == b.Width - 1) 24 | DrawPixel(scaledLine, col, consecutive, currentColor); 25 | else { 26 | // This is SO slow, but better than making the whole library 'unsafe' 27 | Color nextColor = GetPixelColor(b, line, col + 1); 28 | if (nextColor != currentColor) { 29 | DrawPixel(scaledLine, col, consecutive, currentColor); 30 | currentColor = nextColor; 31 | consecutive = 1; 32 | } else consecutive++; 33 | } 34 | } catch { } 35 | } 36 | } 37 | return g; 38 | } 39 | 40 | // This could be optimized in an unsafe version of the lib 41 | private static Color GetPixelColor(Bitmap b, int y, int x) => b.GetPixel(x, y); 42 | 43 | private void DrawPixel(float scaledLine, int col, int consecutive, Color color) => 44 | DrawImagePixel(g, 45 | color, 46 | x + ((col - consecutive - 1) * scaleX), 47 | scaledLine, 48 | consecutive * scaleX, 49 | scaleY); 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /SvgNet/Interfaces/IElementWithText.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Interfaces; 10 | /// 11 | /// Interface for SvgElements that have a text node. 12 | /// 13 | public interface IElementWithText { 14 | string Text { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /SvgNet/Interfaces/IElementWithXRef.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Interfaces; 10 | /// 11 | /// Interface for SvgElements that xlink to another element, e.g. use 12 | /// 13 | public interface IElementWithXRef { 14 | string Href { get; set; } 15 | 16 | SvgXRef XRef { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /SvgNet/MetafileTools/EmfTools.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.MetafileTools.EmfTools; 10 | // Classes in this namespace were inspired by the code in http://wmf.codeplex.com/ 11 | public interface IBinaryRecord { 12 | void Read(BinaryReader reader); 13 | } 14 | 15 | public static class BinaryReaderExtensions { 16 | /// 17 | /// Skips excess bytes. Work-around for some WMF files that contain undocumented fields. 18 | /// 19 | /// 20 | /// 21 | public static void Skip(this BinaryReader reader, int excess) { 22 | if (excess > 0) { 23 | //Skip unknown bytes 24 | _ = reader.BaseStream.Seek(excess, SeekOrigin.Current); 25 | //var dummy = reader.ReadBytes(excess); 26 | } 27 | } 28 | } 29 | 30 | /// 31 | /// Implements a EMF META record 32 | /// 33 | public abstract class EmfBinaryRecord : IBinaryRecord { 34 | /// 35 | /// Gets or sets record length 36 | /// 37 | public uint RecordSize { 38 | get; 39 | set; 40 | } 41 | 42 | /// 43 | /// Gets or sets record type (aka RecordFunction) 44 | /// 45 | public EmfPlusRecordType RecordType { 46 | get; 47 | set; 48 | } 49 | 50 | /// 51 | /// Reads a record from binary stream. If this method is not overridden it will skip this record and go to next record. 52 | /// NOTE: When overriding this method remove the base.Read(reader) line from code. 53 | /// 54 | /// 55 | public virtual void Read(BinaryReader reader) { 56 | } 57 | 58 | protected EmfBinaryRecord() { 59 | } 60 | } 61 | 62 | /// 63 | /// Low-level EMF parser 64 | /// 65 | public class EmfReader(Stream stream) : IDisposable { 66 | public bool IsEndOfFile => stream.Length == stream.Position; 67 | 68 | public void Dispose() { 69 | if (_reader is not null) { 70 | _reader.Close(); 71 | _reader = null; 72 | } 73 | GC.SuppressFinalize(this); 74 | } 75 | 76 | public IBinaryRecord Read() { 77 | long begin = _reader.BaseStream.Position; 78 | 79 | var rt = (EmfPlusRecordType)_reader.ReadUInt32(); 80 | uint recordSize = _reader.ReadUInt32(); 81 | 82 | var record = new EmfUnknownRecord { 83 | RecordType = rt, 84 | RecordSize = recordSize 85 | }; 86 | record.Read(_reader); 87 | 88 | long end = _reader.BaseStream.Position; 89 | long rlen = end - begin; //Read length 90 | long excess = recordSize - rlen; 91 | if (excess > 0) { 92 | //Oops, reader did not read whole record?! 93 | _reader.Skip((int)excess); 94 | } 95 | 96 | return record; 97 | } 98 | 99 | private BinaryReader _reader = new(stream); 100 | } 101 | 102 | public class EmfUnknownRecord : EmfBinaryRecord { 103 | public byte[] Data { 104 | get; 105 | set; 106 | } 107 | 108 | public override void Read(BinaryReader reader) { 109 | int length = (int)base.RecordSize - sizeof(uint) - sizeof(uint); 110 | Data = length > 0 ? reader.ReadBytes(length) : _emptyData; 111 | } 112 | 113 | private static readonly byte[] _emptyData = []; 114 | } 115 | 116 | -------------------------------------------------------------------------------- /SvgNet/SvgFactory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Reflection; 10 | using System.Xml; 11 | 12 | using SvgNet.Elements; 13 | 14 | namespace SvgNet; 15 | /// 16 | /// Static methods to produce/write/copy Svg documents reside in this class. 17 | /// 18 | public static class SvgFactory { 19 | public const string svgNamespaceURI = "http://www.w3.org/2000/svg"; 20 | public const string xlinkNamespaceURI = "http://www.w3.org/1999/xlink"; 21 | 22 | /// 23 | /// Used by LoadFromXML 24 | /// 25 | public static Hashtable BuildElementNameDictionary() { 26 | var dict = new Hashtable(); 27 | var asm = Assembly.GetExecutingAssembly(); 28 | Type[] ta = asm.GetExportedTypes(); 29 | foreach (Type t in ta) { 30 | if (t.IsSubclassOf(typeof(SvgElement)) && !t.IsAbstract) { 31 | ConstructorInfo ci = t.GetConstructor([]) ?? throw new InvalidOperationException($"Type {t.Name} doesn't have the mandatory public parameterless constructor"); 32 | var e = (SvgElement)ci.Invoke([]); 33 | if (e.Name != "?" /* default name of abstract SvgElements */) { 34 | dict[e.Name] = e.GetType(); 35 | } 36 | } 37 | } 38 | return dict; 39 | } 40 | 41 | /// 42 | /// Create a complete deep copy of the given tree of SvgElement objects. 43 | /// A new set of elements is created, and if the attributes are cloneable they are deep-copied too. 44 | /// Since strings and all SvgType classes are cloneable, the new tree is independant of the old. 45 | /// 46 | /// 47 | /// 48 | public static SvgElement CloneElement(SvgElement el) { 49 | var clone = (SvgElement)el.GetType().GetConstructor([]).Invoke([]); 50 | 51 | foreach (string key in el.Attributes.Keys) { 52 | clone[key] = el[key].CloneIfPossible(); 53 | } 54 | 55 | foreach (SvgElement ch in el.Children) { 56 | clone.AddChild(CloneElement(ch)); 57 | } 58 | 59 | return clone; 60 | } 61 | 62 | internal static Dictionary _namespaces = new() { ["xmlns"] = svgNamespaceURI, ["xmlns:xlink"] = xlinkNamespaceURI }; 63 | 64 | /// 65 | /// Given an xml document and (optionally) a particular element to start from, read the xml nodes and construct 66 | /// a tree of objects. Xml tags that do not correspond to a particular class will be 67 | /// represented by an . This means that literally any XML input can be read in 68 | /// and written out, even if it has nothing to do with Svg. More usefully, it means that new and unsupported tags 69 | /// and attributes will be represented in the SvgElement tree and written out correctly even if SvgNet does 70 | /// not understand them. 71 | /// 72 | /// 73 | /// 74 | /// 75 | public static SvgElement LoadFromXML(XmlDocument doc, XmlElement el) { 76 | ResetNamespaces(); 77 | if (el == null) { 78 | foreach (XmlNode noddo in doc.ChildNodes) { 79 | if (noddo.GetType() == typeof(XmlElement)) { 80 | el = (XmlElement)noddo; 81 | break; 82 | } 83 | } 84 | } 85 | 86 | if (el == null) 87 | return null; 88 | 89 | _elementNameDictionary ??= BuildElementNameDictionary(); 90 | 91 | var t = (Type)_elementNameDictionary[el.Name]; 92 | 93 | var e = (SvgElement)t.GetConstructor([]).Invoke([]); 94 | 95 | RecLoadFromXML(e, doc, el); 96 | 97 | return e; 98 | } 99 | 100 | /// 101 | /// Helper function to compress long xml attributes into entities. 102 | /// 103 | /// This would work on any XML, it is not SVG specific, so it should eventually be in some 'xml tools' class. 104 | /// 105 | /// 106 | /// 107 | /// 108 | /// A string of entities which can be inserted into the DOCTYPE when the document is written. 109 | internal static string CompressXML(XmlDocument doc, XmlElement el) { 110 | var entities = new Hashtable(); 111 | var singletons = new Hashtable(); 112 | 113 | int idx = 0; 114 | RecCompXML(entities, singletons, doc, el, ref idx); 115 | 116 | // "Uncompress" attribute values with only one reference as it would actually 117 | // make the resulting XML bigger 118 | foreach (DictionaryEntry pair in singletons) { 119 | string attributeValue = (string)pair.Key; 120 | var singleton = (EntitySingleton)pair.Value; 121 | 122 | // This is an inverse behavior of what RecCompXML did 123 | singleton.Element.RemoveAttribute(singleton.AttributeName); 124 | XmlAttribute attr = doc.CreateAttribute(singleton.AttributeName); 125 | attr.Value = attributeValue; 126 | _ = singleton.Element.SetAttributeNode(attr); 127 | 128 | entities.Remove(attributeValue); 129 | } 130 | 131 | var ents = new StringBuilder(); 132 | 133 | if (entities.Count > 0) { 134 | _ = ents.Append('\n'); 135 | 136 | foreach (string key in entities.Keys) { 137 | _ = ents.Append(""); 142 | } 143 | 144 | _ = ents.Append('\n'); 145 | } 146 | 147 | return ents.ToString(); 148 | } 149 | 150 | private static Hashtable _elementNameDictionary; 151 | 152 | /// 153 | /// Used by CompressXML 154 | /// 155 | /// Map of attribute to entity name 156 | /// Output: List of single use attributes to 'uncompress' 157 | /// 158 | /// 159 | /// Number that is incremented to provide new entity names 160 | private static void RecCompXML(Hashtable entities, Hashtable singletons, XmlDocument doc, XmlElement el, ref int idx) { 161 | var keys = new ArrayList(); 162 | 163 | foreach (XmlAttribute att in el.Attributes) { 164 | _ = keys.Add(att.Name); 165 | } 166 | 167 | foreach (string s in keys) { 168 | string val = el.Attributes[s].Value; 169 | 170 | if (val.Length > 30) { 171 | string entname; 172 | 173 | if (entities[val] == null) { 174 | idx++; 175 | entname = "E" + idx.ToString(); 176 | entities[val] = entname; 177 | 178 | singletons[val] = new EntitySingleton() { Element = el, AttributeName = s }; 179 | } else { 180 | entname = (string)entities[val]; 181 | 182 | singletons.Remove(val); 183 | } 184 | 185 | XmlAttribute attr = doc.CreateAttribute(s); 186 | _ = attr.AppendChild(doc.CreateEntityReference(entname)); 187 | _ = el.SetAttributeNode(attr); 188 | } 189 | } 190 | 191 | foreach (XmlNode ch in el.ChildNodes) { 192 | if (ch.GetType() == typeof(XmlElement)) 193 | RecCompXML(entities, singletons, doc, (XmlElement)ch, ref idx); 194 | } 195 | } 196 | 197 | /// 198 | /// Used by LoadFromXML 199 | /// 200 | /// 201 | /// 202 | /// 203 | private static void RecLoadFromXML(SvgElement e, XmlDocument doc, XmlElement el) { 204 | e.ReadXmlElement(doc, el); 205 | 206 | foreach (XmlNode noddo in el.ChildNodes) { 207 | if (noddo.GetType() == typeof(XmlElement)) { 208 | var childXml = (XmlElement)noddo; 209 | 210 | var t = (Type)_elementNameDictionary[childXml.Name]; 211 | 212 | SvgElement childSvg = t switch { 213 | null => new SvgGenericElement(childXml.Name), 214 | _ => (SvgElement)t.GetConstructor([]).Invoke([]), 215 | }; 216 | e.AddChild(childSvg); 217 | 218 | RecLoadFromXML(childSvg, doc, childXml); 219 | } else if (noddo.GetType() == typeof(XmlText)) { 220 | var xt = (XmlText)noddo; 221 | 222 | var tn = new TextNode(xt.InnerText); 223 | 224 | e.AddChild(tn); 225 | } 226 | } 227 | } 228 | 229 | public static void ResetNamespaces() => _namespaces = new() { ["xmlns"] = svgNamespaceURI, ["xmlns:xlink"] = xlinkNamespaceURI }; 230 | 231 | private struct EntitySingleton { 232 | public string AttributeName; 233 | public XmlElement Element; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /SvgNet/SvgNet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netstandard2.1;net8.0;net9.0 5 | $(TargetFrameworks);net462;net8.0-windows;net9.0-windows 6 | true 7 | preview 8 | SVG 9 | SvgNet 10 | 3.5.0 11 | SvgNet 12 | svgnetdoc.xml 13 | CS1591 14 | true 15 | C# framework for creating SVG images 16 | SvgNet and SvgGdi Bridge Project 17 | Rafael Teixeira, Mojmír Němeček, Benjamin Peterson 18 | 2003, 2010, 2015-2024 RiskCare Ltd, SvgNet and SvgGdi Bridge Project, Rafael Teixeira, Mojmír Němeček 19 | https://github.com/managed-commons/SvgNet 20 | svgnet.png 21 | https://github.com/managed-commons/SvgNet 22 | SVG 23 | SvgNet 24 | git 25 | 26 | - Drops building for .NET 6.0 which is beyond End of Support 27 | 28 | LICENSE 29 | README.md 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | DEBUG;TRACE 52 | NU1605 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | $(PackageId).$(Version).nupkg 64 | dotnet nuget push $(PackageOutputAbsolutePath)$(LatestPackage) --skip-duplicate --force-english-output --source nuget.org 65 | 66 | 67 | 68 | 69 | 70 | $([System.Text.RegularExpressions.Regex]::IsMatch('$(PushOutput)', 'already exists')) 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Latest version \d+\.\d+\.\d+ 86 | Latest version $(Version) 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | True 127 | 128 | 129 | 130 | True 131 | 132 | 133 | 134 | True 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /SvgNet/Types/PathSeg.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// A segment in an Svg path. This is not a real SVG type; it is not in the SVG spec. It is provided for making paths 12 | /// easier to specify and parse. 13 | /// 14 | public class PathSeg(SvgPathSegType type, bool abs, float[] data) : ICloneable { 15 | public bool Abs { get; } = abs; 16 | 17 | public string Char => type switch { 18 | SvgPathSegType.SVG_SEGTYPE_MOVETO => Abs ? "M" : "m", 19 | SvgPathSegType.SVG_SEGTYPE_CLOSEPATH => "z", 20 | SvgPathSegType.SVG_SEGTYPE_LINETO => Abs ? "L" : "l", 21 | SvgPathSegType.SVG_SEGTYPE_HLINETO => Abs ? "H" : "h", 22 | SvgPathSegType.SVG_SEGTYPE_VLINETO => Abs ? "V" : "v", 23 | SvgPathSegType.SVG_SEGTYPE_CURVETO => Abs ? "C" : "c", 24 | SvgPathSegType.SVG_SEGTYPE_SMOOTHCURVETO => Abs ? "S" : "s", 25 | SvgPathSegType.SVG_SEGTYPE_BEZIERTO => Abs ? "Q" : "q", 26 | SvgPathSegType.SVG_SEGTYPE_SMOOTHBEZIERTO => Abs ? "T" : "t", 27 | SvgPathSegType.SVG_SEGTYPE_ARCTO => Abs ? "A" : "a", 28 | 29 | _ => throw new SvgException("Invalid PathSeg type", type.ToString()), 30 | }; 31 | 32 | public float[] Data => data; 33 | 34 | public SvgPathSegType Type => type; 35 | 36 | public object Clone() => new PathSeg(type, Abs, (float[])data.Clone()); 37 | }; 38 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgAngle.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// An angle, as found here and there throughout the SVG spec 12 | /// 13 | public class SvgAngle : ICloneable { 14 | public SvgAngle(string s) => FromString(s); 15 | 16 | public SvgAngle(float num, SvgAngleType type) { 17 | Value = num; 18 | Type = type; 19 | } 20 | 21 | public SvgAngleType Type { get; set; } 22 | 23 | public float Value { get; set; } 24 | 25 | public static implicit operator SvgAngle(string s) => new(s); 26 | 27 | public object Clone() => new SvgAngle(Value, Type); 28 | 29 | public void FromString(string s) { 30 | if (s.TrySplitNumberAndSuffix(out string number, out string suffix)) { 31 | Value = int.Parse(number, CultureInfo.InvariantCulture); 32 | 33 | Type = suffix switch { 34 | "grad" => SvgAngleType.SVG_ANGLETYPE_GRAD, 35 | "rad" => SvgAngleType.SVG_ANGLETYPE_RAD, 36 | "deg" => SvgAngleType.SVG_ANGLETYPE_DEG, 37 | "" => SvgAngleType.SVG_ANGLETYPE_UNSPECIFIED, 38 | 39 | _ => throw new SvgException("Invalid SvgAngle", s), 40 | }; 41 | } 42 | } 43 | 44 | public override string ToString() { 45 | string s = Value.ToString("F", CultureInfo.InvariantCulture); 46 | switch (Type) { 47 | case SvgAngleType.SVG_ANGLETYPE_DEG: 48 | case SvgAngleType.SVG_ANGLETYPE_UNSPECIFIED: 49 | s += "deg"; 50 | break; 51 | 52 | case SvgAngleType.SVG_ANGLETYPE_GRAD: 53 | s += "grad"; 54 | break; 55 | 56 | case SvgAngleType.SVG_ANGLETYPE_RAD: 57 | s += "rad"; 58 | break; 59 | } 60 | return s; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgAngleType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// The units in which an SvgAngle can be specified 12 | /// 13 | public enum SvgAngleType { 14 | SVG_ANGLETYPE_UNKNOWN = 0, 15 | SVG_ANGLETYPE_UNSPECIFIED = 1, 16 | SVG_ANGLETYPE_DEG = 2, 17 | SVG_ANGLETYPE_RAD = 3, 18 | SVG_ANGLETYPE_GRAD = 4, 19 | } 20 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgColor.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Text.RegularExpressions; 10 | 11 | namespace SvgNet.Types; 12 | 13 | /// 14 | /// A color, as found in CSS2 and used in SVG. As well as a GDI Color object, SvgColor stores 15 | /// the string it was initialized from, so that when a color specified as 'black' is written out, 16 | /// it will be written 'black' rather than '#000000' 17 | /// 18 | public partial class SvgColor : ICloneable { 19 | public SvgColor(string s) => FromString(s); 20 | 21 | public SvgColor(Color c) => Color = c; 22 | 23 | public SvgColor(Color c, string s) { 24 | Color = c; 25 | _original_string = s; 26 | } 27 | 28 | public Color Color { get; set; } 29 | 30 | public static implicit operator SvgColor(Color c) => new(c); 31 | 32 | public static implicit operator SvgColor(string s) => new(s); 33 | 34 | public object Clone() => new SvgColor(Color, _original_string); 35 | 36 | /// 37 | /// If the SvgColor was constructed from a string, use that string; otherwise use rgb() form 38 | /// 39 | /// 40 | public override string ToString() => _original_string ?? $"rgb({Color.R},{Color.G},{Color.B})"; 41 | 42 | private string _original_string; 43 | 44 | /// 45 | /// As well as parsing the four types of CSS color descriptor (rgb, #xxxxxx, color name, and system color name), 46 | /// the FromString of this type stores the original string 47 | /// 48 | /// 49 | public void FromString(string s) { 50 | _original_string = s; 51 | Color = TryParseHexString(s, out Color color) || TryParseRGB(s, out color) || TryParseFromName(s, out color) 52 | ? color 53 | : throw new SvgException("Invalid SvgColor", s); 54 | 55 | static bool TryParseHexString(string s, out Color color) { 56 | if (HEX_COLOR_REGEX.Match(s).Success) { 57 | int r, g, b; 58 | if (s.Length == 4) { 59 | r = s.ParseHex(1, 1); 60 | g = s.ParseHex(2, 1); 61 | b = s.ParseHex(3, 1); 62 | r += r * 16; 63 | g += g * 16; 64 | b += b * 16; 65 | color = Color.FromArgb(r, g, b); 66 | return true; 67 | } 68 | if (s.Length == 7) { 69 | r = s.ParseHex(1, 2); 70 | g = s.ParseHex(3, 2); 71 | b = s.ParseHex(5, 2); 72 | color = Color.FromArgb(r, g, b); 73 | return true; 74 | } 75 | } 76 | color = Color.Transparent; 77 | return false; 78 | } 79 | 80 | static bool TryParseRGB(string s, out Color color) { 81 | if (RGB_PREFIX_REGEX.Match(s).Success) 82 | return TryParseScaling(s, RGB_INTEGERS_REGEX, 100, out color) || TryParseScaling(s, RGB_PERCENTS_REGEX, 255, out color); 83 | else { 84 | color = Color.Transparent; 85 | return false; 86 | } 87 | 88 | static bool TryParseScaling(string s, Regex regex, int scale, out Color color) { 89 | try { 90 | Match m = regex.Match(s); 91 | if (m.Success) { 92 | int r, g, b; 93 | r = Parse(m, "r") * scale / 100; 94 | g = Parse(m, "g") * scale / 100; 95 | b = Parse(m, "b") * scale / 100; 96 | color = Color.FromArgb(r, g, b); 97 | return true; 98 | 99 | static int Parse(Match m, string component) => 100 | int.Parse(m.Groups[component].Captures[0].Value, CultureInfo.InvariantCulture); 101 | } 102 | } catch { 103 | } 104 | color = Color.Transparent; 105 | return false; 106 | } 107 | } 108 | 109 | static bool TryParseFromName(string s, out Color color) { 110 | try { 111 | color = Color.FromName(s); 112 | return color.A != 0; 113 | } catch { 114 | color = Color.Transparent; 115 | return false; 116 | } 117 | } 118 | } 119 | 120 | #if NET7_0_OR_GREATER 121 | private static readonly Regex RGB_PREFIX_REGEX = RGB_PREFIX(); 122 | private static readonly Regex RGB_INTEGERS_REGEX = RGB_INTEGERS(); 123 | private static readonly Regex RGB_PERCENTS_REGEX = RGB_PERCENTS(); 124 | private static readonly Regex HEX_COLOR_REGEX = HEX_COLOR(); 125 | 126 | [GeneratedRegex("[rR][gG][bB]")] 127 | private static partial Regex RGB_PREFIX(); 128 | [GeneratedRegex("[rgbRGB ]+\\( *(?\\d+)[, ]+(?\\d+)[, ]+(?\\d+) *\\)")] 129 | private static partial Regex RGB_INTEGERS(); 130 | [GeneratedRegex("[rgbRGB ]+\\( *(?\\d+)%[, ]+(?\\d+)%[, ]+(?\\d+)% *\\)")] 131 | private static partial Regex RGB_PERCENTS(); 132 | [GeneratedRegex("^#[0-9A-Fa-f]+$")] 133 | private static partial Regex HEX_COLOR(); 134 | #else 135 | private static readonly Regex RGB_PREFIX_REGEX = new("[rR][gG][bB]"); 136 | private static readonly Regex RGB_INTEGERS_REGEX = new("[rgbRGB ]+\\( *(?\\d+)[, ]+(?\\d+)[, ]+(?\\d+) *\\)"); 137 | private static readonly Regex RGB_PERCENTS_REGEX = new("[rgbRGB ]+\\( *(?\\d+)%[, ]+(?\\d+)%[, ]+(?\\d+)% *\\)"); 138 | private static readonly Regex HEX_COLOR_REGEX = new("^#[0-9A-Fa-f]+$"); 139 | 140 | #endif 141 | } 142 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgHelpers.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet; 10 | 11 | internal static class SvgHelpers { 12 | 13 | public static readonly char[] CommonSeparators = [',', ' ', '\t', '\r', '\n']; 14 | } -------------------------------------------------------------------------------- /SvgNet/Types/SvgLength.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// A length or coordinate component (in SVG 1.1 the specification says they are the same) 12 | /// 13 | public class SvgLength : ICloneable { 14 | public SvgLength(string s) => FromString(s); 15 | 16 | public SvgLength(float f) { 17 | Value = f; 18 | Type = SvgLengthType.SVG_LENGTHTYPE_UNKNOWN; 19 | } 20 | 21 | public SvgLength(float f, SvgLengthType type) { 22 | Value = f; 23 | Type = type; 24 | } 25 | 26 | public SvgLengthType Type { get; set; } 27 | 28 | public float Value { get; set; } 29 | 30 | public static implicit operator SvgLength(string s) => new(s); 31 | 32 | public static implicit operator SvgLength(float s) => new(s); 33 | 34 | public object Clone() => new SvgLength(Value, Type); 35 | 36 | public void FromString(string s) { 37 | if (s.TrySplitNumberAndSuffix(out string number, out string suffix)) { 38 | Value = float.Parse(number, CultureInfo.InvariantCulture); 39 | 40 | Type = suffix switch { 41 | "%" => SvgLengthType.SVG_LENGTHTYPE_PERCENTAGE, 42 | "em" => SvgLengthType.SVG_LENGTHTYPE_EMS, 43 | "ex" => SvgLengthType.SVG_LENGTHTYPE_EXS, 44 | "px" => SvgLengthType.SVG_LENGTHTYPE_PX, 45 | "cm" => SvgLengthType.SVG_LENGTHTYPE_CM, 46 | "mm" => SvgLengthType.SVG_LENGTHTYPE_MM, 47 | "in" => SvgLengthType.SVG_LENGTHTYPE_IN, 48 | "pt" => SvgLengthType.SVG_LENGTHTYPE_PT, 49 | "pc" => SvgLengthType.SVG_LENGTHTYPE_PC, 50 | "" => SvgLengthType.SVG_LENGTHTYPE_UNKNOWN, 51 | _ => throw new SvgException("Invalid SvgLength", s), 52 | }; 53 | } 54 | } 55 | 56 | public override string ToString() { 57 | string s = Value.ToString("F", CultureInfo.InvariantCulture); 58 | switch (Type) { 59 | case SvgLengthType.SVG_LENGTHTYPE_PERCENTAGE: 60 | s += "%"; 61 | break; 62 | 63 | case SvgLengthType.SVG_LENGTHTYPE_EMS: 64 | s += "em"; 65 | break; 66 | 67 | case SvgLengthType.SVG_LENGTHTYPE_EXS: 68 | s += "ex"; 69 | break; 70 | 71 | case SvgLengthType.SVG_LENGTHTYPE_PX: 72 | s += "px"; 73 | break; 74 | 75 | case SvgLengthType.SVG_LENGTHTYPE_CM: 76 | s += "cm"; 77 | break; 78 | 79 | case SvgLengthType.SVG_LENGTHTYPE_MM: 80 | s += "mm"; 81 | break; 82 | 83 | case SvgLengthType.SVG_LENGTHTYPE_IN: 84 | s += "in"; 85 | break; 86 | 87 | case SvgLengthType.SVG_LENGTHTYPE_PT: 88 | s += "pt"; 89 | break; 90 | 91 | case SvgLengthType.SVG_LENGTHTYPE_PC: 92 | s += "pc"; 93 | break; 94 | } 95 | return s; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgLengthType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// The various units in which an SvgLength can be specified. 12 | /// 13 | public enum SvgLengthType { 14 | SVG_LENGTHTYPE_UNKNOWN = 0, 15 | SVG_LENGTHTYPE_NUMBER = 1, 16 | SVG_LENGTHTYPE_PERCENTAGE = 2, 17 | SVG_LENGTHTYPE_EMS = 3, 18 | SVG_LENGTHTYPE_EXS = 4, 19 | SVG_LENGTHTYPE_PX = 5, 20 | SVG_LENGTHTYPE_CM = 6, 21 | SVG_LENGTHTYPE_MM = 7, 22 | SVG_LENGTHTYPE_IN = 8, 23 | SVG_LENGTHTYPE_PT = 9, 24 | SVG_LENGTHTYPE_PC = 10, 25 | } 26 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgNumList.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// A number list, as used in the SVG spec for e.g. the value of a viewBox attribute. Basically a list of numbers in 12 | /// any format separated by whitespace and commas. 13 | /// 14 | public class SvgNumList : ICloneable { 15 | public SvgNumList(string s) => FromString(s); 16 | 17 | public SvgNumList(float[] pts) { 18 | foreach (float p in pts) _ = _pts.Add(p); 19 | } 20 | 21 | public int Count => _pts.Count; 22 | 23 | public float this[int idx] { 24 | get => (float)_pts[idx]; 25 | set => _pts[idx] = value; 26 | } 27 | 28 | public static implicit operator SvgNumList(string s) => new(s); 29 | 30 | public static implicit operator SvgNumList(float[] f) => new(f); 31 | 32 | public static float[] String2Floats(string s) { 33 | try { 34 | string[] sa = s.Split(SvgHelpers.CommonSeparators, StringSplitOptions.RemoveEmptyEntries); 35 | var arr = new ArrayList(); 36 | foreach (string str in sa) if (!string.IsNullOrWhiteSpace(str)) _ = arr.Add(float.Parse(str.Trim(), CultureInfo.InvariantCulture)); 37 | return (float[])arr.ToArray(typeof(float)); 38 | } catch (Exception) { 39 | throw new SvgException("Invalid number list", s); 40 | } 41 | } 42 | 43 | public object Clone() => new SvgNumList((float[])_pts.ToArray(typeof(float))); 44 | 45 | public void FromString(string s) { 46 | try { 47 | float[] fa = String2Floats(s); 48 | 49 | foreach (float f in fa) _ = _pts.Add(f); 50 | } catch (Exception) { 51 | throw new SvgException("Invalid SvgNumList", s); 52 | } 53 | } 54 | 55 | public override string ToString() { 56 | var builder = new StringBuilder(); 57 | foreach (float f in _pts) _ = builder.Append(f.ToString("F", CultureInfo.InvariantCulture)).Append(' '); 58 | 59 | return builder.ToString(); 60 | } 61 | 62 | private readonly ArrayList _pts = []; 63 | } 64 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgNumber.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// A number, as specified in the SVG standard. It is stored as a float. 12 | /// 13 | public class SvgNumber : ICloneable { 14 | public SvgNumber(string s) => FromString(s); 15 | 16 | public SvgNumber(int n) => _num = n; 17 | 18 | public SvgNumber(float n) => _num = n; 19 | 20 | public static implicit operator SvgNumber(string s) => new(s); 21 | 22 | public static implicit operator SvgNumber(int n) => new(n); 23 | 24 | public static implicit operator SvgNumber(float n) => new(n); 25 | 26 | public object Clone() => new SvgNumber(_num); 27 | 28 | /// 29 | /// float.Parse is used to parse the string. float.Parse does not follow the exact rules of the SVG spec. 30 | /// 31 | /// 32 | public void FromString(string s) { 33 | try { 34 | _num = float.Parse(s, CultureInfo.InvariantCulture); 35 | } catch { 36 | throw new SvgException("Invalid SvgNumber", s); 37 | } 38 | } 39 | 40 | /// 41 | /// float.ToString is used to output a string. This is true for all numbers in SvgNet. 42 | /// 43 | /// 44 | public override string ToString() => _num.ToString("F", CultureInfo.InvariantCulture); 45 | 46 | private float _num; 47 | } 48 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgPath.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// A path, composed of segments, as described in the SVG 1.1 spec section 8.3 12 | /// 13 | public class SvgPath : ICloneable { 14 | public SvgPath(string s) => FromString(s); 15 | 16 | public int Count => _path.Count; 17 | 18 | public PathSeg this[int idx] { 19 | get => (PathSeg)_path[idx]; 20 | set => _path[idx] = value; 21 | } 22 | 23 | public static implicit operator SvgPath(string s) => new(s); 24 | 25 | public object Clone() => 26 | //we resort to using to/from string rather than writing an efficient clone, for the moment. 27 | new SvgPath(ToString()); 28 | 29 | /// 30 | /// The parsing of the path is not completely perfect yet. You can only have one space between path elements. 31 | /// 32 | /// 33 | public void FromString(string s) { 34 | string[] sa = s.Split(SvgHelpers.CommonSeparators); 35 | 36 | PathSeg ps; 37 | int datasize = 0; 38 | SvgPathSegType pt = SvgPathSegType.SVG_SEGTYPE_UNKNOWN; 39 | bool abs = false; 40 | int i = 0; 41 | char segTypeChar; 42 | _path = []; 43 | 44 | while (i < sa.Length) { 45 | if (sa[i]?.Length == 0) { 46 | i++; 47 | continue; 48 | } 49 | 50 | //check for a segment-type character 51 | 52 | if (char.IsLetter(sa[i][0])) { 53 | segTypeChar = sa[i][0]; 54 | 55 | switch (segTypeChar) { 56 | case 'M': 57 | case 'm': 58 | pt = SvgPathSegType.SVG_SEGTYPE_MOVETO; 59 | abs = segTypeChar == 'M'; 60 | datasize = 2; 61 | break; 62 | case 'Z': 63 | case 'z': 64 | pt = SvgPathSegType.SVG_SEGTYPE_CLOSEPATH; 65 | datasize = 0; 66 | break; 67 | case 'L': 68 | case 'l': 69 | pt = SvgPathSegType.SVG_SEGTYPE_LINETO; 70 | abs = segTypeChar == 'L'; 71 | datasize = 2; 72 | break; 73 | case 'H': 74 | case 'h': 75 | pt = SvgPathSegType.SVG_SEGTYPE_HLINETO; 76 | abs = segTypeChar == 'H'; 77 | datasize = 1; 78 | break; 79 | case 'V': 80 | case 'v': 81 | pt = SvgPathSegType.SVG_SEGTYPE_VLINETO; 82 | abs = segTypeChar == 'V'; 83 | datasize = 1; 84 | break; 85 | case 'C': 86 | case 'c': 87 | pt = SvgPathSegType.SVG_SEGTYPE_CURVETO; 88 | abs = segTypeChar == 'C'; 89 | datasize = 6; 90 | break; 91 | case 'S': 92 | case 's': 93 | pt = SvgPathSegType.SVG_SEGTYPE_SMOOTHCURVETO; 94 | abs = segTypeChar == 'S'; 95 | datasize = 4; 96 | break; 97 | case 'Q': 98 | case 'q': 99 | pt = SvgPathSegType.SVG_SEGTYPE_BEZIERTO; 100 | abs = segTypeChar == 'Q'; 101 | datasize = 4; 102 | break; 103 | case 'T': 104 | case 't': 105 | pt = SvgPathSegType.SVG_SEGTYPE_SMOOTHBEZIERTO; 106 | abs = segTypeChar == 'T'; 107 | datasize = 2; 108 | break; 109 | case 'A': 110 | case 'a': 111 | pt = SvgPathSegType.SVG_SEGTYPE_ARCTO; 112 | abs = segTypeChar == 'A'; 113 | datasize = 7; 114 | break; 115 | default: 116 | throw new SvgException("Invalid SvgPath", s); 117 | } 118 | 119 | //strip off type character 120 | sa[i] = sa[i].SkipFirst(); 121 | 122 | if (sa[i]?.Length == 0) 123 | i++; 124 | } else if (pt == SvgPathSegType.SVG_SEGTYPE_MOVETO) // ensure implicit "lineto" commands are parsed according to SVG 1.1 spec section 8.3.2. 125 | pt = SvgPathSegType.SVG_SEGTYPE_LINETO; 126 | 127 | if (pt == SvgPathSegType.SVG_SEGTYPE_UNKNOWN) 128 | throw new SvgException("Invalid SvgPath", s); 129 | 130 | float[] arr = new float[datasize]; 131 | 132 | for (int j = 0; j < datasize; ++j) arr[j] = float.Parse(sa[i + j], CultureInfo.InvariantCulture); 133 | 134 | ps = new PathSeg(pt, abs, arr); 135 | 136 | _ = _path.Add(ps); 137 | 138 | i += datasize; 139 | } 140 | } 141 | 142 | public override string ToString() { 143 | PathSeg prev = null; 144 | var builder = new StringBuilder(); 145 | foreach (PathSeg seg in _path) { 146 | if (prev == null 147 | || (prev.Type != seg.Type && !(prev.Type == SvgPathSegType.SVG_SEGTYPE_MOVETO && seg.Type == SvgPathSegType.SVG_SEGTYPE_LINETO)) 148 | || prev.Abs != seg.Abs) _ = builder.Append(seg.Char).Append(' '); 149 | foreach (float d in seg.Data) _ = builder.Append(d.ToString(CultureInfo.InvariantCulture)).Append(' '); 150 | prev = seg; 151 | } 152 | return builder.ToString(); 153 | } 154 | 155 | private ArrayList _path; 156 | } 157 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgPathSegType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// The various different types of segment that make up an SVG path, as listed in the SVG Path grammar. 12 | /// 13 | public enum SvgPathSegType { 14 | SVG_SEGTYPE_UNKNOWN = 0, 15 | SVG_SEGTYPE_MOVETO, 16 | SVG_SEGTYPE_CLOSEPATH, 17 | SVG_SEGTYPE_LINETO, 18 | SVG_SEGTYPE_HLINETO, 19 | SVG_SEGTYPE_VLINETO, 20 | SVG_SEGTYPE_CURVETO, 21 | SVG_SEGTYPE_SMOOTHCURVETO, 22 | SVG_SEGTYPE_BEZIERTO, 23 | SVG_SEGTYPE_SMOOTHBEZIERTO, 24 | SVG_SEGTYPE_ARCTO 25 | } 26 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgPoints.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// A list of points, as specified in the SVG 1.1 spec section 9.8. Only used in polygon and polyline elements. 12 | /// 13 | public class SvgPoints : ICloneable { 14 | public SvgPoints(string s) => FromString(s); 15 | 16 | public SvgPoints(PointF[] pts) { 17 | foreach (PointF p in pts) { 18 | _ = _pts.Add(p.X); 19 | _ = _pts.Add(p.Y); 20 | } 21 | } 22 | 23 | /// 24 | /// The array must have an even length 25 | /// 26 | /// 27 | public SvgPoints(float[] pts) { 28 | if (pts.Length % 2 != 0) 29 | throw new SvgException("Invalid SvgPoints", pts.ToString()); 30 | 31 | foreach (float p in pts) _ = _pts.Add(p); 32 | } 33 | 34 | public static implicit operator SvgPoints(string s) => new(s); 35 | 36 | public static implicit operator SvgPoints(PointF[] pts) => new(pts); 37 | 38 | public object Clone() => new SvgPoints((PointF[])_pts.ToArray(typeof(PointF))); 39 | 40 | /// 41 | /// The standard boils down to a list of numbers in any format separated by any amount of wsp and commas; 42 | /// in other words it looks the same as a SvgNumList 43 | /// 44 | /// 45 | public void FromString(string s) { 46 | try { 47 | float[] fa = SvgNumList.String2Floats(s); 48 | foreach (float f in fa) _ = _pts.Add(f); 49 | } catch (Exception) { 50 | throw new SvgException("Invalid SvgPoints", s); 51 | } 52 | 53 | if (_pts.Count % 2 != 0) 54 | throw new SvgException("Invalid SvgPoints", s); 55 | } 56 | 57 | public override string ToString() { 58 | var builder = new StringBuilder(); 59 | foreach (float f in _pts) _ = builder.Append(f.ToString("F", CultureInfo.InvariantCulture)).Append(' '); 60 | return builder.ToString(); 61 | } 62 | 63 | private readonly ArrayList _pts = []; 64 | } 65 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgStyle.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// Represents a CSS2 style, as applied to an SVG element. 12 | /// 13 | public class SvgStyle : ICloneable { 14 | public SvgStyle() { 15 | } 16 | 17 | public SvgStyle(string s) => FromString(s); 18 | 19 | /// 20 | /// Creates a style from a GDI+ pen object. Most properties of the pen are implemented, but GDI+ allows fine control over line-capping which 21 | /// has no equivalent in SVG. 22 | /// 23 | /// 24 | public SvgStyle(Pen pen) { 25 | var strokeCol = new SvgColor(((SolidBrush)pen.Brush).Color); 26 | Set("stroke", strokeCol); 27 | Set("stroke-width", pen.Width); 28 | Set("fill", "none"); 29 | 30 | switch (pen.EndCap) { 31 | case LineCap.Round: 32 | Set("stroke-linecap", "round"); 33 | break; 34 | 35 | case LineCap.Square: 36 | Set("stroke-linecap", "square"); 37 | break; 38 | 39 | case LineCap.Flat: 40 | Set("stroke-linecap", "butt"); 41 | break; 42 | } 43 | 44 | switch (pen.LineJoin) { 45 | case LineJoin.Bevel: 46 | Set("stroke-linejoin", "bevel"); 47 | break; 48 | 49 | case LineJoin.Miter: 50 | Set("stroke-linejoin", "miter"); 51 | break; 52 | 53 | case LineJoin.Round: 54 | Set("stroke-linejoin", "round"); 55 | break; 56 | } 57 | 58 | //converting between adobe and ms miter limits is very hard because adobe have never explained what the value means. 59 | Set("stroke-miterlimit", (pen.MiterLimit / 2) + 4f); 60 | 61 | float[] dashes = null; 62 | 63 | switch (pen.DashStyle) { 64 | case DashStyle.Dash: 65 | dashes = [3, 1]; 66 | break; 67 | 68 | case DashStyle.DashDot: 69 | dashes = [3, 1, 1, 1]; 70 | break; 71 | 72 | case DashStyle.DashDotDot: 73 | dashes = [3, 1, 1, 1, 1]; 74 | break; 75 | 76 | case DashStyle.Dot: 77 | dashes = [1, 1]; 78 | break; 79 | 80 | case DashStyle.Custom: 81 | dashes = pen.DashPattern; 82 | break; 83 | } 84 | 85 | if (dashes != null) { 86 | //MS GDI changes dash pattern to match width of line; svg does not. 87 | for (int i = 0; i < dashes.Length; ++i) dashes[i] *= pen.Width; 88 | 89 | Set("stroke-dasharray", new SvgNumList(dashes)); 90 | } 91 | 92 | Set("opacity", pen.Color.A / 255f); 93 | } 94 | 95 | /// 96 | /// Creates a style based on a GDI brush object. Only works for solid brushes; pattern brushes are not yet emulated. 97 | /// 98 | /// 99 | public SvgStyle(SolidBrush brush) { 100 | var col = new SvgColor(brush.Color); 101 | Set("fill", col); 102 | Set("stroke", "none"); 103 | Set("opacity", brush.Color.A / 255f); 104 | } 105 | 106 | /// 107 | /// Creates a style based on a GDI+ font object. GDI+ allows many subtle specifications which have no SVG equivalent. 108 | /// 109 | /// 110 | public SvgStyle(Font font) { 111 | Set("font-family", font.FontFamily.Name); 112 | 113 | if (font.Bold) 114 | Set("font-weight", "bolder"); 115 | 116 | if (font.Italic) 117 | Set("font-style", "italic"); 118 | 119 | if (font.Underline) 120 | Set("text-decoration", "underline"); 121 | 122 | Set("font-size", font.SizeInPoints.ToString("F", CultureInfo.InvariantCulture) + "pt"); 123 | } 124 | 125 | /// 126 | /// A basic way to enumerate the styles. 127 | /// 128 | public ICollection Keys => _styles.Keys; 129 | 130 | /// 131 | /// A quick way to get and set style elements. 132 | /// 133 | public object this[string attname] { 134 | get => _styles[attname]; 135 | set => _styles[attname] = value; 136 | } 137 | 138 | public static implicit operator SvgStyle(string s) => new(s); 139 | 140 | /// 141 | /// Adds two SvgStyles together, resulting in a new object that contains all the attributes of both styles. 142 | /// Attributes are copied deeply, i.e. cloned if they are ICloneable. 143 | /// 144 | public static SvgStyle operator +(SvgStyle lhs, SvgStyle rhs) { 145 | var res = new SvgStyle(); 146 | 147 | foreach (string key in lhs._styles.Keys) res[key] = lhs[key].CloneIfPossible(); 148 | 149 | foreach (string key in rhs._styles.Keys) res[key] = rhs[key].CloneIfPossible(); 150 | 151 | return res; 152 | } 153 | 154 | /// 155 | /// Creates a new style, but does not do a deep copy on the members in the style. Thus if any of these are 156 | /// not strings, they meay be left referred to by more than one style or element. 157 | /// 158 | /// 159 | public object Clone() => new SvgStyle() + this; 160 | 161 | /// 162 | /// Parses a CSS string representation as used in SVG. 163 | /// 164 | /// 165 | public void FromString(string s) { 166 | try { 167 | string[] pairs = s.Split(';'); 168 | 169 | foreach (string pair in pairs) { 170 | string[] kv = pair.Split(':'); 171 | if (kv.Length == 2) 172 | Set(kv[0].Trim(), kv[1].Trim()); 173 | } 174 | } catch (Exception) { 175 | throw new SvgException("Invalid style string", s); 176 | } 177 | } 178 | 179 | /// 180 | /// Gets the value for a given key. 181 | /// 182 | public object Get(string key) => _styles[key]; 183 | 184 | /// 185 | /// Sets a style. The key must be a string but the value can be anything (e.g. SvgColor). If and when the element that owns this style is written out 186 | /// to XML, ToString will be called on the value. 187 | /// 188 | /// 189 | /// 190 | public void Set(string key, object val) { 191 | if (val == null || val.ToString()?.Length == 0) { 192 | _styles.Remove(key); 193 | return; 194 | } 195 | 196 | _styles[key] = val; 197 | } 198 | 199 | /// 200 | /// Outputs a CSS string representation as used in SVG. 201 | /// 202 | public override string ToString() { 203 | var result = new StringBuilder(); 204 | foreach (string s in _styles.Keys) _ = result.Append(s).Append(':').Append(InvariantCultureToString(_styles[s])).Append(';'); 205 | return result.ToString(); 206 | } 207 | 208 | private readonly Hashtable _styles = []; 209 | 210 | private static string InvariantCultureToString(object styleValue) 211 | => styleValue is float styleAsFloat 212 | ? styleAsFloat.ToString(CultureInfo.InvariantCulture) 213 | : styleValue is double styleAsDouble ? styleAsDouble.ToString(CultureInfo.InvariantCulture) : styleValue.ToString(); 214 | } 215 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgTransform.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// Represents a single element in an SVG transformation list. The transformation is represented internally as a 12 | /// GDI+ Matrix object. 13 | /// 14 | public class SvgTransform : ICloneable { 15 | public SvgTransform() => Matrix = new Matrix(); 16 | 17 | public SvgTransform(string s) => FromString(s); 18 | 19 | public SvgTransform(Matrix m) => Matrix = m; 20 | 21 | public Matrix Matrix { get; set; } 22 | 23 | public object Clone() => new SvgTransform(Matrix.Clone()); 24 | 25 | /// 26 | /// Parse a transformation according to the SVG standard. This is complex enough that it makes 27 | /// me wish it was worth using a real parser, but antlr is so unwieldy. 28 | /// 29 | public void FromString(string s) { 30 | Matrix = new Matrix(); 31 | 32 | if (s.TryParseTransformation(out string name, out float[] points)) 33 | switch (name) { 34 | case "matrix": 35 | if (points.Length == 6) { 36 | Matrix = new Matrix(points[0], points[1], points[2], points[3], points[4], points[5]); 37 | return; 38 | } 39 | break; 40 | case "translate": 41 | if (points.Length == 1) { 42 | Matrix.Translate(points[0], 0); 43 | return; 44 | } 45 | if (points.Length == 2) { 46 | Matrix.Translate(points[0], points[1]); 47 | return; 48 | } 49 | break; 50 | case "scale": 51 | if (points.Length == 1) { 52 | Matrix.Scale(points[0], 0); 53 | return; 54 | } 55 | if (points.Length == 2) { 56 | Matrix.Scale(points[0], points[1]); 57 | return; 58 | } 59 | break; 60 | case "rotate": 61 | if (points.Length == 1) { 62 | Matrix.Rotate(points[0]); 63 | return; 64 | } 65 | if (points.Length == 3) { 66 | Matrix.Translate(points[1], points[2]); 67 | Matrix.Rotate(points[0]); 68 | Matrix.Translate(points[1] * -1, points[2] * -1); 69 | return; 70 | } 71 | break; 72 | case "skewX": 73 | if (points.Length == 1) { 74 | Matrix.Shear(points[0], 0); 75 | return; 76 | } 77 | break; 78 | case "skewY": 79 | if (points.Length == 1) { 80 | Matrix.Shear(0, points[0]); 81 | return; 82 | } 83 | break; 84 | default: 85 | throw new SvgException($"Invalid SvgTransformation: unrecognized name '{name}'", s); 86 | 87 | } 88 | throw new SvgException("Invalid SvgTransformation", s); 89 | } 90 | 91 | /// 92 | /// Currently, we always output as matrix() no matter how the transform was specified. 93 | /// 94 | /// 95 | public override string ToString() { 96 | string result = "matrix("; 97 | 98 | foreach (float f in Matrix.Elements) { 99 | result += f.ToString("F", CultureInfo.InvariantCulture); 100 | result += " "; 101 | } 102 | 103 | result += ")"; 104 | 105 | return result; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgTransformList.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | namespace SvgNet.Types; 10 | /// 11 | /// Represents an SVG transform-list, as specified in section 7.6 of the SVG 1.1 standard. 12 | /// 13 | public class SvgTransformList : ICloneable { 14 | public SvgTransformList() { 15 | } 16 | 17 | public SvgTransformList(string s) => FromString(s); 18 | 19 | public SvgTransformList(Matrix m) { 20 | var tr = new SvgTransform(m); 21 | _ = _t.Add(tr); 22 | } 23 | 24 | public int Count => _t.Count; 25 | 26 | public SvgTransform this[int idx] { 27 | get => (SvgTransform)_t[idx]; 28 | set => _t[idx] = value; 29 | } 30 | 31 | public static implicit operator SvgTransformList(string s) => new(s); 32 | 33 | public static implicit operator SvgTransformList(Matrix m) => new(m); 34 | 35 | public void Add(string trans) => _t.Add(new SvgTransform(trans)); 36 | 37 | public void Add(Matrix m) => _t.Add(new SvgTransform(m)); 38 | 39 | public object Clone() => 40 | //use to/from string as a shortcut 41 | new SvgTransformList(ToString()); 42 | 43 | /// 44 | /// Parse a string containing a whitespace-separated list of transformations as per the SVG 45 | /// standard 46 | /// 47 | public void FromString(string s) { 48 | int start = -1; 49 | 50 | while (true) { 51 | int end = s.IndexOf(')', start + 1); 52 | 53 | if (end == -1) return; 54 | 55 | var trans = new SvgTransform(s.Substring(start + 1, end - start)); 56 | 57 | _ = _t.Add(trans); 58 | 59 | start = end; 60 | } 61 | } 62 | 63 | public override string ToString() { 64 | string result = ""; 65 | 66 | foreach (SvgTransform tr in _t) { 67 | result += tr.ToString(); 68 | result += " "; 69 | } 70 | 71 | return result; 72 | } 73 | 74 | private readonly ArrayList _t = []; 75 | } 76 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgUriReference.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using SvgNet.Elements; 10 | 11 | namespace SvgNet.Types; 12 | /// 13 | /// Represents a URI reference within a style. Local uri references are generally strings of the form 14 | /// url(#elementID). This class should not be confused with which represents 15 | /// the xlink:* properties of, for example, an a element. 16 | /// 17 | public class SvgUriReference : ICloneable { 18 | public SvgUriReference() { 19 | } 20 | 21 | public SvgUriReference(string href) => Href = href; 22 | 23 | public SvgUriReference(SvgElement target) { 24 | Href = "#" + target.Id; 25 | if (target.Id?.Length == 0) throw new SvgException("Uri Reference cannot refer to an element with no id.", target.ToString()); 26 | } 27 | 28 | public string Href { get; set; } 29 | 30 | public object Clone() => new SvgUriReference(Href); 31 | 32 | public override string ToString() => "url(" + Href + ")"; 33 | } 34 | -------------------------------------------------------------------------------- /SvgNet/Types/SvgXRef.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using SvgNet.Elements; 10 | 11 | namespace SvgNet.Types; 12 | /// 13 | /// Represents a URI reference. Unlike most svg types, uri references are represented by more than one attribute 14 | /// of an element. This means special measures are required to get and set uri references. 15 | /// 16 | public class SvgXRef : ICloneable { 17 | public SvgXRef() { 18 | } 19 | 20 | public SvgXRef(string href) => Href = href; 21 | 22 | public SvgXRef(SvgStyledTransformedElement el) => ReadFromElement(el); 23 | 24 | public string Actuate { get; set; } = "onLoad"; 25 | 26 | public string Arcrole { get; set; } 27 | 28 | public string Href { get; set; } 29 | 30 | public string Role { get; set; } 31 | 32 | public string Show { get; set; } 33 | 34 | public string Title { get; set; } 35 | 36 | public string Type { get; set; } = "simple"; 37 | 38 | public object Clone() { 39 | var r = new SvgXRef { 40 | Href = Href, 41 | Type = Type, 42 | Role = Role, 43 | Arcrole = Arcrole, 44 | Title = Title, 45 | Show = Show, 46 | Actuate = Actuate 47 | }; 48 | return r; 49 | } 50 | 51 | public void ReadFromElement(SvgStyledTransformedElement el) { 52 | Href = (string)el["xlink:href"]; 53 | Role = (string)el["xlink:role"]; 54 | Arcrole = (string)el["xlink:arcrole"]; 55 | Title = (string)el["xlink:title"]; 56 | Show = (string)el["xlink:show"]; 57 | 58 | //ignore the possibility of setting type and actuate for now 59 | } 60 | 61 | public override string ToString() => Href; 62 | 63 | public void WriteToElement(SvgStyledTransformedElement el) { 64 | el["xlink:href"] = Href; 65 | //if (_type != "simple") el["xlink:type"] = _type; 66 | el["xlink:role"] = Role; 67 | el["xlink:arcrole"] = Arcrole; 68 | el["xlink:title"] = Title; 69 | el["xlink:show"] = Show; 70 | //if (_type != "onLoad") el["xlink:actuate"] = _actuate; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SvgNet/svgnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/managed-commons/SvgNet/9aa368c6dafc05f61f97ad7b1c8ec5eda316ec1a/SvgNet/svgnet.png -------------------------------------------------------------------------------- /SvgNetUnitTests/Main.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using System.Drawing; 10 | 11 | using NUnit.Framework; 12 | 13 | using SvgNet.Interfaces; 14 | 15 | namespace SvgNet; 16 | 17 | [TestFixture] 18 | public class Main { 19 | [TestCase("Clipping")] 20 | [TestCase("Transforms")] 21 | [TestCase("Lines")] 22 | [TestCase("Curves")] 23 | [TestCase("Transparency")] 24 | [TestCase("Fills")] 25 | [TestCase("Arcs/Pies")] 26 | [TestCase("Text")] 27 | [TestCase("Path")] 28 | [TestCase("Path with Polygon")] 29 | [TestCase("Path (Slow)")] 30 | public void TestCases(string key) { 31 | TestContext.Out.WriteLine($"=== Renderer {key}"); 32 | System.Action value = TestShared.Renderers[key]; 33 | using var ig = new SvgGraphics(Color.WhiteSmoke); 34 | value(ig); 35 | string svgBody = ig.WriteSVGString(640, 480); 36 | key = key.Replace("/", "."); // Arcs/Pies is not file friendly 37 | string dstPath = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, $"{TestContext.CurrentContext.Test.ID}.{key}.svg"); 38 | System.IO.File.WriteAllText(dstPath, svgBody); 39 | TestContext.AddTestAttachment(dstPath, key); 40 | } 41 | 42 | [Test] 43 | public void TestSvgFactory_BuildElementNameDictionary() { 44 | System.Collections.Hashtable dict = SvgFactory.BuildElementNameDictionary(); 45 | Assert.That(dict, Is.Not.Null); 46 | Assert.That(dict, Has.Count.GreaterThanOrEqualTo(25)); 47 | Assert.That(dict.ContainsKey("svg")); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SvgNetUnitTests/SvgColorTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2003 RiskCare Ltd. All rights reserved. 3 | Copyright © 2010 SvgNet & SvgGdi Bridge Project. All rights reserved. 4 | Copyright © 2015-2024 Rafael Teixeira, Mojmír Němeček, Benjamin Peterson and Other Contributors 5 | 6 | Original source code licensed with BSD-2-Clause spirit, treat it thus, see accompanied LICENSE for more 7 | */ 8 | 9 | using NUnit.Framework; 10 | 11 | using SvgNet.Exceptions; 12 | using SvgNet.Types; 13 | 14 | namespace SvgNet; 15 | 16 | [TestFixture] 17 | public class SvgColorTests { 18 | 19 | [TestCase("black", 255, 0, 0, 0)] 20 | [TestCase("Black", 255, 0, 0, 0)] 21 | [TestCase("BLACK", 255, 0, 0, 0)] 22 | [TestCase("white", 255, 255, 255, 255)] 23 | [TestCase("red", 255, 255, 0, 0)] 24 | [TestCase("#F00", 255, 255, 0, 0)] 25 | [TestCase("#C0C0C0", 255, 192, 192, 192)] 26 | [TestCase("#c0c0c0", 255, 192, 192, 192)] 27 | [TestCase("rgb(192,192,192)", 255, 192, 192, 192)] 28 | [TestCase("RGB (192,192,192)", 255, 192, 192, 192)] 29 | [TestCase("RGB (75%,75%,75%)", 255, 191, 191, 191)] 30 | public void TestSvgColor_FromString(string colorAsString, int a, int r, int g, int b) { 31 | var color = new SvgColor(colorAsString); 32 | Assert.That(color, Is.Not.Null); 33 | Assert.Multiple(() => { 34 | Assert.That(color.ToString(), Is.EqualTo(colorAsString)); 35 | Assert.That(a, Is.EqualTo(color.Color.A), "alpha"); 36 | Assert.That(r, Is.EqualTo(color.Color.R), "red"); 37 | Assert.That(g, Is.EqualTo(color.Color.G), "green"); 38 | Assert.That(b, Is.EqualTo(color.Color.B), "blue"); 39 | }); 40 | } 41 | 42 | [TestCase("blackPearl")] 43 | [TestCase("whitish")] 44 | [TestCase("")] 45 | [TestCase("Transparent")] 46 | [TestCase("#F0")] 47 | [TestCase("#F00A")] 48 | [TestCase("#C0C0C")] 49 | [TestCase("#C0C0C0C")] 50 | [TestCase("#C0C0C080")] 51 | [TestCase("#Z0c0c0")] 52 | [TestCase("rgb(1920,192,192)")] 53 | [TestCase("rgb(192,192,-192)")] 54 | [TestCase("RGB (175%,75%,75%)")] 55 | public void TestSvgColor_FromStringException(string colorAsString) { 56 | SvgException ex = Assert.Throws(() => new SvgColor(colorAsString)); 57 | Assert.That(ex.Message, Is.EqualTo("Invalid SvgColor")); 58 | } 59 | } -------------------------------------------------------------------------------- /SvgNetUnitTests/SvgNetUnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | preview 4 | net8.0;net9.0 5 | SvgNet 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.403", 4 | "allowPrerelease": false, 5 | "rollForward": "latestMajor" 6 | } 7 | } 8 | --------------------------------------------------------------------------------