Repository: otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET Branch: master Commit: 0eaa8c9e8839 Files: 53 Total size: 358.9 KB Directory structure: gitextract_t5vkdpxu/ ├── .gitattributes ├── .gitignore ├── .gitmodules ├── COPYING ├── README.md ├── SvgFileType/ │ ├── BenchmarkScope.cs │ ├── Export/ │ │ ├── PreviewMode.cs │ │ ├── PropertyNames.cs │ │ ├── ScanMode.cs │ │ └── SvgExport.cs │ ├── Extensions/ │ │ ├── ControlExtensions.cs │ │ ├── DocumentExtensions.cs │ │ ├── StringExtensions.cs │ │ ├── SurfaceExtensions.cs │ │ ├── SvgDocumentExtensions.cs │ │ ├── SvgElementExtensions.cs │ │ ├── SvgUnitExtensions.cs │ │ ├── SvgUseExtensions.cs │ │ ├── SvgViewBoxExtensions.cs │ │ └── SvgVisualElementExtensions.cs │ ├── FodyWeavers.xml │ ├── FodyWeavers.xsd │ ├── Import/ │ │ ├── Direct2DSvgRenderer.cs │ │ ├── GdipSvgRenderer.cs │ │ ├── LayersMode.cs │ │ ├── MyBaseForm.cs │ │ ├── MyBaseForm.resx │ │ ├── ResvgSvgRenderer.cs │ │ ├── StreamTrackerCancellationTokenSource.cs │ │ ├── SvgImport.cs │ │ ├── SvgImportConfig.cs │ │ ├── SvgImportDialog.Designer.cs │ │ ├── SvgImportDialog.cs │ │ ├── SvgImportDialog.resx │ │ └── SvgRenderer2.cs │ ├── Localization/ │ │ ├── InjectResourceManager.ps1 │ │ ├── Localize.cs │ │ ├── SingleAssemblyResourceManager.cs │ │ ├── StringResources.Designer.cs │ │ ├── StringResources.resx │ │ ├── StringResources_de.resx │ │ ├── StringResources_fr.resx │ │ ├── StringResources_ru.resx │ │ └── StringResources_tr.resx │ ├── Logger.cs │ ├── Services.cs │ ├── SvgFileType.cs │ ├── SvgFileTypeFactory.cs │ ├── SvgFileTypePlugin.csproj │ ├── SvgFileTypePluginSupportInfo.cs │ └── UIHelper.cs ├── SvgFileType.ThirdPartyNotices.txt └── SvgFileType.sln ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ [Xx]64/ [Xx]86/ [Bb]uild/ bld/ [Bb]in/ [Oo]bj/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Un-comment the next line if you do not want to checkin # your web deploy settings because they may include unencrypted # passwords #*.pubxml *.publishproj # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Microsoft Azure ApplicationInsights config file ApplicationInsights.config # Windows Store app package directory AppPackages/ BundleArtifacts/ # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ orleans.codegen.cs # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # LightSwitch generated files GeneratedArtifacts/ ModelManifest.xml # Paket dependency manager .paket/paket.exe # FAKE - F# Make .fake/ # CUSTOM *.editorconfig Installer/ SvgFileType/Properties/ ================================================ FILE: .gitmodules ================================================ [submodule "resvg.net"] path = resvg.net url = https://github.com/otuncelli/resvg.net.git [submodule "PaintDotNet.IndirectUI.Fluent"] path = PaintDotNet.IndirectUI.Fluent url = https://github.com/otuncelli/PaintDotNet.IndirectUI.Fluent.git [submodule "BitmapVectorizer"] path = BitmapVectorizer url = https://github.com/otuncelli/BitmapVectorizer.git ================================================ FILE: COPYING ================================================  GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: README.md ================================================ # ![W3C SVG Logo](https://www.w3.org/Icons/SVG/svg-logo-v.png) Scalable Vector Graphics (SVG) Plugin for Paint.NET [![](https://img.shields.io/github/release-pre/otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET.svg?style=flat)](https://github.com/otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET/releases) [![](https://img.shields.io/github/downloads/otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET/total)](https://github.com/otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET/releases) This is a [paint.net](https://getpaint.net) filetype plugin for loading SVG (Scalable Vector Graphics) and its compressed variant SVGZ files. SVG files can be rendered as a flat image or each element/group on a separate layer. This plugin also provides basic image tracing (raster to vector conversion) functionality which works best with black and white drawings. You may export the tracing result as SVG or Paint.NET Shape. ### Prerequisites paint.net 5.1 or later ### Download links Here are the download links for latest release: Installer[^1] (Recommended) | Manual Installation --- | --- :floppy_disk: [SvgFileTypePlugin_setup.exe](https://github.com/otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET/releases/latest/download/SvgFileTypePlugin_setup.exe) | :floppy_disk: [SvgFileTypePlugin.zip](https://github.com/otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET/releases/latest/download/SvgFileTypePlugin.zip) [^1]: The portable version of paint.net is not supported by the installer. ### How to install :warning: **Note: Before install make sure you don't have any other file type plugin installed handling the same file extensions: .svg, .svgz** :point_right: To automatically install the plugin perform the following steps: * Make sure paint.net is not running. * Download and run `SvgFileTypePlugin_setup.exe` * Follow the steps of the setup wizard. :point_right: To manually install the plugin perform the following steps: * Make sure paint.net is not running. * Download and extract `SvgFileTypePlugin.zip` * If you're using Classic version of paint.net: * Copy the `SvgFileTypePlugin` folder into the `\FileTypes` directory (Default location is `C:\Program Files\paint.net\FileTypes`). * If you're using [Microsoft Store version of paint.net](https://www.microsoft.com/store/apps/9NBHCS1LX4R0): * Copy the `SvgFileTypePlugin` folder into the `\paint.net App Files\FileTypes` directory. * Done. You may start paint.net. ================================================ FILE: SvgFileType/BenchmarkScope.cs ================================================ // Copyright 2025 Osman Tunçelli. Allrights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Diagnostics; using System.Text; using System.Threading; namespace SvgFileTypePlugin; internal struct BenchmarkScope : IDisposable { private readonly Stopwatch timer; private readonly string? name; private int disposed; public BenchmarkScope(string? name) : this() { this.name = name; } public BenchmarkScope() { timer = Stopwatch.StartNew(); } public void Dispose() { if (Interlocked.CompareExchange(ref disposed, 1, 0) == 0) { StringBuilder sb = new StringBuilder(nameof(BenchmarkScope)); sb.Append(' '); if (name is not null) { sb.Append($"({name}) "); } sb.Append($"took {timer.ElapsedMilliseconds} ms."); Logger.WriteLine(sb.ToString()); } } } ================================================ FILE: SvgFileType/Export/PreviewMode.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. namespace SvgFileTypePlugin.Export; internal enum PreviewMode { Fast, Slow } ================================================ FILE: SvgFileType/Export/PropertyNames.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. namespace SvgFileTypePlugin.Export; internal enum PropertyNames { Angle, BrightnessCutoff, Color, DiscussionLink, Enclose, FillColor, GitHubLink, GreymapScale, HighpassFilter, Invert, LowpassFilter, Optimize, PdnShape, PdnShapeName, PreviewMode, ScanMode, Scale, SmoothCorners, SuppressSpeckles, Tight, TurnPolicy } ================================================ FILE: SvgFileType/Export/ScanMode.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. namespace SvgFileTypePlugin.Export; internal enum ScanMode { Transparent, Opaque } ================================================ FILE: SvgFileType/Export/SvgExport.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.ComponentModel; using System.IO; using System.Text; using System.Threading; using System.Windows.Forms; using BitmapVectorizer; using PaintDotNet; using PaintDotNet.AppModel; using PaintDotNet.PropertySystem; using PaintDotNet.PropertySystem.Extensions; using SvgFileTypePlugin.Extensions; using SvgFileTypePlugin.Localization; using static PaintDotNet.UserBlendOps; namespace SvgFileTypePlugin.Export; internal static partial class SvgExport { private static Lazy ShapesDirectory => new(() => { try { IFileSystemService fss = Services.Get(); string path = Path.Combine(fss.PerUserAppFilesPath, "Shapes"); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } return path; } catch { return null; } }); public static string? ShowSaveShapeDialog() { if (!UIHelper.IsSaveConfigDialogVisible()) { return null; } return UIHelper.RunOnUIThread(() => { using SaveFileDialog sfd = new SaveFileDialog(); sfd.InitialDirectory = ShapesDirectory.Value; sfd.Filter = $"{StringResources.Shape}|*.xaml"; sfd.OverwritePrompt = true; return sfd.ShowDialog() == DialogResult.OK ? sfd.FileName : null; }); } private static T ThrowIfOverflow(Func action, string message) { try { return action(); } catch (OverflowException ex) { throw new WarningException(message, ex); } } public static void Export(Surface input, Stream output, PropertyCollection props, ProgressEventHandler progressCallback, string shapePath) { ArgumentNullException.ThrowIfNull(input); ArgumentNullException.ThrowIfNull(output); ArgumentNullException.ThrowIfNull(props); ArgumentNullException.ThrowIfNull(progressCallback); ThrowIfOverflow(() => checked(input.Stride * input.Height), StringResources.CanvasIsTooBig); #pragma warning disable format // @formatter:off ScanMode scanMode = props.GetPropertyValue(PropertyNames.ScanMode); PreviewMode previewMode = props.GetPropertyValue(PropertyNames.PreviewMode); double brightnessCutoff = props.GetPropertyValue(PropertyNames.BrightnessCutoff); int highpass = props.GetPropertyValue(PropertyNames.HighpassFilter); int gmScale = props.GetPropertyValue(PropertyNames.GreymapScale); int lowpass = props.GetPropertyValue(PropertyNames.LowpassFilter); int turdsize = props.GetPropertyValue(PropertyNames.SuppressSpeckles); float alphamax = props.GetPropertyValue(PropertyNames.SmoothCorners); float opttolerance = props.GetPropertyValue(PropertyNames.Optimize); TurnPolicy turnpolicy = props.GetPropertyValue(PropertyNames.TurnPolicy); int color = props.GetPropertyValue(PropertyNames.Color); int fillcolor = props.GetPropertyValue(PropertyNames.FillColor); bool invert = props.GetPropertyValue(PropertyNames.Invert); bool tight = props.GetPropertyValue(PropertyNames.Tight); bool enclose = props.GetPropertyValue(PropertyNames.Enclose); float angle = props.GetPropertyValue(PropertyNames.Angle); float scale = props.GetPropertyValue(PropertyNames.Scale); string shapeName = props.GetPropertyValue(PropertyNames.PdnShapeName); #pragma warning restore format // @formatter:on PotraceBitmap bm; TraceResult? trace; ImageInfo imginfo; SvgBackEnd backend = new SvgBackEnd { Color = color, ColumnWidth = int.MaxValue }; backend.Rx /= scale; backend.Ry /= scale; bool isSaveConfigDialogVisible = UIHelper.IsSaveConfigDialogVisible(); Surface surface; const int maxDimForPreview = 1280; if (isSaveConfigDialogVisible && previewMode == PreviewMode.Fast && input.Width * input.Height > maxDimForPreview * maxDimForPreview) { // Preview Only // Image downscaled to speed up preview generation. double ar = input.Width / (double)input.Height; int newWidth = maxDimForPreview; int newHeight = maxDimForPreview; if (input.Width > input.Height) { newHeight = (int)Math.Round(maxDimForPreview / ar); } else { newWidth = (int)Math.Round(maxDimForPreview * ar); } surface = new Surface(newWidth, newHeight); surface.FitSurface(ResamplingAlgorithm.Cubic, input); } else { surface = input.Clone(); } using var _ = surface; surface.BlendOnto(ColorBgra.White); using CancellationTokenSource cts = new CancellationTokenSource(); float full = isSaveConfigDialogVisible ? 90f : 100f; CancellationToken cancellationToken = cts.Token; if (scanMode == ScanMode.Opaque) { backend.Opaque = true; backend.FillColor = fillcolor; } if (highpass == 0) { bm = PotraceBitmap.FromRgbx(surface.Scan0.Pointer, surface.Width, surface.Height, c: brightnessCutoff); } else { using Greymap gm = Greymap.FromRgbx(surface.Scan0.Pointer, surface.Width, surface.Height); gm.HighPassFilter(lambda: highpass); if (lowpass > 0) { gm.LowPassFilter(lambda: lowpass); } if (gmScale > 1) { bm = gm.InterpolateCubicBilevel(gmScale, c: brightnessCutoff); backend.Rx *= gmScale; backend.Ry *= gmScale; } else { bm = gm.Threshold(c: brightnessCutoff); } } using (bm) { if (invert) { bm.Invert(); } if (enclose) { bm.Enclose(); } Progress progress = new Progress((prog) => OnProgress(ConvertProgressValue(prog))); trace = bm.Trace(turdsize, turnpolicy, alphamax, opttolerance, progress, cancellationToken); imginfo = bm.Info; imginfo.Tight = tight; } imginfo.Angle = angle; if (trace is null) { if (UIHelper.IsSaveConfigDialogVisible()) { return; } else { throw new InvalidOperationException(StringResources.NoPath); } } if (isSaveConfigDialogVisible && scanMode == ScanMode.Transparent && shapePath.Length > 0) { PdnShapeBackEnd pdnbackend = new PdnShapeBackEnd { ColumnWidth = int.MaxValue, DisplayName = shapeName }; using (FileStream shapeStream = File.Open(shapePath, FileMode.Create, FileAccess.Write)) { pdnbackend.Save(shapeStream, trace, imginfo, cancellationToken); } StringBuilder msg = new StringBuilder(); msg.AppendFormat(StringResources.ShapeSaved, shapePath); if (ShapesDirectory.Value != null && Path.GetDirectoryName(shapePath)?.StartsWith(ShapesDirectory.Value, StringComparison.OrdinalIgnoreCase) == true) { msg.AppendLine(); msg.AppendLine(); msg.AppendLine(StringResources.ShapeSavedRestart); } UIHelper.RunOnUIThread(() => { MessageBox.Show(msg.ToString(), StringResources.ShapeSavedCaption, MessageBoxButtons.OK, MessageBoxIcon.Information); }); } backend.Save(output, trace, imginfo, cancellationToken); void OnProgress(float prog) { try { progressCallback.Invoke(null, new ProgressEventArgs(prog * full)); } catch (OperationCanceledException) { try { cts.Cancel(); } catch (ObjectDisposedException) { // ignore } } } } public static void Export(Document input, Stream output, PropertyBasedSaveConfigToken token, Surface scratchSurface, ProgressEventHandler progressCallback, string shapePath) { ArgumentNullException.ThrowIfNull(input); ArgumentNullException.ThrowIfNull(output); ArgumentNullException.ThrowIfNull(token); ArgumentNullException.ThrowIfNull(scratchSurface); ArgumentNullException.ThrowIfNull(progressCallback); ThrowIfOverflow(() => checked(scratchSurface.Stride * scratchSurface.Height), StringResources.CanvasIsTooBig); input.Flatten(scratchSurface); Export(scratchSurface, output, token.Properties, progressCallback, shapePath); } private static float ConvertProgressValue(ProgressArgs args) { float progress = args.Progress * .5f; if (args.Level == ProgressLevel.Tracing) { progress += .5f; } return progress; } } ================================================ FILE: SvgFileType/Extensions/ControlExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Generic; using System.Windows.Forms; namespace SvgFileTypePlugin.Extensions; internal static class ControlExtensions { public static IEnumerable Descendants(this Control root) { ArgumentNullException.ThrowIfNull(root); foreach (Control control in root.Controls) { yield return control; if (control.HasChildren) { foreach (Control child in Descendants(control)) { yield return child; } } } } public static object? RunOnUIThread(this Control control, T action, params object[] args) where T : Delegate { ArgumentNullException.ThrowIfNull(control); return control.InvokeRequired ? control.Invoke(action, args) : action.DynamicInvoke(args); } public static object? RunOnUIThread(this Control control, T action) where T : Delegate { ArgumentNullException.ThrowIfNull(control); return control.InvokeRequired ? control.Invoke(action) : action.DynamicInvoke(); } } ================================================ FILE: SvgFileType/Extensions/DocumentExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using PaintDotNet; namespace SvgFileTypePlugin.Extensions; internal static class DocumentExtensions { public static void SetDpu(this Document doc, double dpuX, double dpuY, MeasurementUnit dpuUnit = MeasurementUnit.Inch) { ArgumentNullException.ThrowIfNull(doc); doc.DpuUnit = dpuUnit; doc.DpuX = dpuX; doc.DpuY = dpuY; } public static void SetDpu(this Document doc, double dpu, MeasurementUnit dpuUnit = MeasurementUnit.Inch) { SetDpu(doc, dpu, dpu, dpuUnit); } } ================================================ FILE: SvgFileType/Extensions/StringExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Text.RegularExpressions; namespace SvgFileTypePlugin.Extensions; internal static class StringExtensions { public static string Truncate(this string s, int maxLength, string suffix = "...") { ArgumentNullException.ThrowIfNull(s); ArgumentOutOfRangeException.ThrowIfNegative(maxLength); return s.Length > maxLength ? $"{s[..maxLength]}{suffix}" : s; } public static string SplitIntoLines(this string s, int maximumLineLength) { ArgumentNullException.ThrowIfNull(s); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(maximumLineLength); return Regex.Replace(s, @"(.{1," + maximumLineLength + @"})(?:\s|$)", "$1\n"); } } ================================================ FILE: SvgFileType/Extensions/SurfaceExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; using PaintDotNet; namespace SvgFileTypePlugin.Extensions; internal static class SurfaceExtensions { public static Document CreateSingleLayerDocument(this Surface surface, bool takeOwnership = false) { ArgumentNullException.ThrowIfNull(surface); Document document = new Document(surface.Width, surface.Height); try { BitmapLayer layer = new BitmapLayer(surface, takeOwnership); document.Layers.Add(layer); } catch { document.Dispose(); throw; } return document; } public static unsafe bool IsEmpty(this Surface surface) { ArgumentNullException.ThrowIfNull(surface); const int unrollFactor = 4; int pixcnt = surface.Width * surface.Height; sbyte* ptr = (sbyte*)surface.Scan0.VoidStar; if (Avx2.IsSupported && pixcnt >= Vector256.Count) { Vector256 valphamask = Vector256.Create( 3, 7, 11, 15, 19, 23, 27, 31, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff).AsSByte(); Vector256 zero = Vector256.Zero; while (pixcnt >= Vector256.Count * unrollFactor) { Vector256 v0 = Avx.LoadVector256(ptr); Vector256 v1 = Avx.LoadVector256(ptr + Vector256.Count); Vector256 v2 = Avx.LoadVector256(ptr + 2 * Vector256.Count); Vector256 v3 = Avx.LoadVector256(ptr + 3 * Vector256.Count); v0 = Avx2.Shuffle(v0, valphamask); v1 = Avx2.Shuffle(v1, valphamask); v2 = Avx2.Shuffle(v2, valphamask); v3 = Avx2.Shuffle(v3, valphamask); v0 = Avx2.CompareGreaterThan(v0, zero); v1 = Avx2.CompareGreaterThan(v1, zero); v2 = Avx2.CompareGreaterThan(v2, zero); v3 = Avx2.CompareGreaterThan(v3, zero); int mask = Avx2.MoveMask(v0) | Avx2.MoveMask(v1) | Avx2.MoveMask(v2) | Avx2.MoveMask(v3); if (mask != 0) { return false; } ptr += Vector256.Count * unrollFactor; pixcnt -= Vector256.Count * unrollFactor; } while (pixcnt >= Vector256.Count) { Vector256 v = Avx.LoadVector256(ptr); v = Avx2.Shuffle(v, valphamask); v = Avx2.CompareGreaterThan(v, zero); if (Avx2.MoveMask(v) != 0) { return false; } ptr += Vector256.Count; pixcnt -= Vector256.Count; } } if (Ssse3.IsSupported && pixcnt >= Vector128.Count) { Vector128 valphamask = Vector128.Create( 3, 7, 11, 15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff).AsSByte(); Vector128 zero = Vector128.Zero; while (pixcnt >= Vector128.Count * unrollFactor) { Vector128 v0 = Sse2.LoadVector128(ptr); Vector128 v1 = Sse2.LoadVector128(ptr + Vector128.Count); Vector128 v2 = Sse2.LoadVector128(ptr + 2 * Vector128.Count); Vector128 v3 = Sse2.LoadVector128(ptr + 3 * Vector128.Count); v0 = Ssse3.Shuffle(v0, valphamask); v1 = Ssse3.Shuffle(v1, valphamask); v2 = Ssse3.Shuffle(v2, valphamask); v3 = Ssse3.Shuffle(v3, valphamask); v0 = Sse2.CompareGreaterThan(v0, zero); v1 = Sse2.CompareGreaterThan(v1, zero); v2 = Sse2.CompareGreaterThan(v2, zero); v3 = Sse2.CompareGreaterThan(v3, zero); int mask = Sse2.MoveMask(v0) | Sse2.MoveMask(v1) | Sse2.MoveMask(v2) | Sse2.MoveMask(v3); if (mask != 0) { return false; } ptr += Vector128.Count * unrollFactor; pixcnt -= Vector128.Count * unrollFactor; } while (pixcnt >= Vector128.Count) { Vector128 v = Sse2.LoadVector128(ptr); v = Ssse3.Shuffle(v, valphamask); v = Sse2.CompareGreaterThan(v, zero); if (Sse2.MoveMask(v) != 0) { return false; } ptr += Vector128.Count; pixcnt -= Vector128.Count; } } while (pixcnt > 0) { if (ptr[3] > 0) { return false; } ptr += sizeof(uint); pixcnt--; } return true; } public static void BlendOnto(this Surface surface, ColorBgra backgroundColor) where T : UserBlendOp, new() { ArgumentNullException.ThrowIfNull(surface); using Surface tmp = surface.Clone(); surface.Fill(backgroundColor); T blendOp = new T(); blendOp.Apply(surface, tmp); } } ================================================ FILE: SvgFileType/Extensions/SvgDocumentExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Drawing; using Svg; using SvgFileTypePlugin.Import; namespace SvgFileTypePlugin.Extensions; internal static class SvgDocumentExtensions { public static SvgDocument RemoveInvisibleAndNonTextElements(this SvgDocument svg) { ArgumentNullException.ThrowIfNull(svg); SvgFragment clonedFragment = (SvgFragment)svg.Clone(); clonedFragment.RemoveInvisibleAndNonTextElements(); SvgDocument clonedDocument = new SvgDocument(); clonedDocument.Children.Add(clonedFragment); return clonedDocument; } public static IDisposable UseSetRasterDimensions(this SvgDocument svg, SvgImportConfig config) { ArgumentNullException.ThrowIfNull(svg); ArgumentNullException.ThrowIfNull(config); SvgAspectRatio origAR = svg.AspectRatio; SizeF origSize = svg.GetDimensions(); SizeF rasterSize = origSize; SvgViewBox origViewBox = svg.ViewBox; if (origViewBox.IsEmpty() && !origSize.IsEmpty) { svg.ViewBox = new SvgViewBox(0, 0, origSize.Width, origSize.Height); } svg.RasterizeDimensions(ref rasterSize, config.RasterWidth, config.RasterHeight); svg.Width = new SvgUnit(SvgUnitType.User, rasterSize.Width); svg.Height = new SvgUnit(SvgUnitType.User, rasterSize.Height); svg.AspectRatio = new SvgAspectRatio(config.PreserveAspectRatio ? SvgPreserveAspectRatio.xMinYMin : SvgPreserveAspectRatio.none); return new DisposableAction(() => { // Restore the original values back svg.AspectRatio = origAR; svg.Width = origSize.Width; svg.Height = origSize.Height; svg.ViewBox = origViewBox; }); } private sealed class DisposableAction : IDisposable { private Action? _dispose; public DisposableAction(Action dispose) { ArgumentNullException.ThrowIfNull(dispose); _dispose = dispose; } public void Dispose() { _dispose?.Invoke(); _dispose = null; } } } ================================================ FILE: SvgFileType/Extensions/SvgElementExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Globalization; using System.IO; using System.Reflection; using System.Text; using System.Xml; using Svg; namespace SvgFileTypePlugin.Extensions; internal static class SvgElementExtensions { private delegate SvgAttributeCollection AttributesGetterDelegate(SvgElement element); private delegate string? ElementNameGetterDelegate(SvgElement element); private static readonly AttributesGetterDelegate GetElementAttributes = CreateGetterDelegate("Attributes"); private static readonly ElementNameGetterDelegate GetElementName = CreateGetterDelegate("ElementName"); private static readonly XmlWriterSettings WriterSettings = new() { Encoding = Encoding.UTF8 }; public static string GetName(this SvgElement element) { ArgumentNullException.ThrowIfNull(element); return element.GetType().GetCustomAttribute()?.ElementName ?? GetElementName(element) ?? element.GetType().Name; } public static SvgAttributeCollection GetAttributes(this SvgElement element) { ArgumentNullException.ThrowIfNull(element); return GetElementAttributes(element); } public static void RemoveInvisibleAndNonTextElements(this SvgElement element) { ArgumentNullException.ThrowIfNull(element); for (int i = element.Children.Count - 1; i >= 0; i--) { SvgElement child = element.Children[i]; if (child is not SvgTextBase && child.Visibility != "visible") { element.Children.RemoveAt(i); } else { child.RemoveInvisibleAndNonTextElements(); } } } public static string GetXML_QuotedFuncIRIHack(this SvgElement svg) { ArgumentNullException.ThrowIfNull(svg); using InvariantUtf8StringWriter writer = new InvariantUtf8StringWriter(); using (XmlWriter xmlWriter = new CustomXmlWriter(XmlWriter.Create(writer, WriterSettings))) { svg.Write(xmlWriter); xmlWriter.Flush(); } writer.Flush(); return writer.ToString(); } public static void WriteXML_QuotedFuncIRIHack(this SvgElement svg, Stream output) { ArgumentNullException.ThrowIfNull(svg); using InvariantUtf8StreamWriter writer = new InvariantUtf8StreamWriter(output); using (XmlWriter xmlWriter = new CustomXmlWriter(XmlWriter.Create(writer, WriterSettings))) { svg.Write(xmlWriter); xmlWriter.Flush(); } writer.Flush(); } private static T CreateGetterDelegate(string propertyName) where T : Delegate { MethodInfo getter = typeof(SvgElement) ?.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance) ?.GetGetMethod(true) ?? throw new MissingMemberException(nameof(SvgElement), propertyName); return getter.CreateDelegate(); } #region InvariantUtf8StreamWriter private sealed class InvariantUtf8StreamWriter(Stream stream) : StreamWriter(stream, Encoding.UTF8, leaveOpen: true) { private readonly Stream stream = stream; public override Encoding Encoding => Encoding.UTF8; public override IFormatProvider FormatProvider => CultureInfo.InvariantCulture; protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { stream.Position = 0; } } } #endregion #region InvariantUtf8StringWriter private sealed class InvariantUtf8StringWriter() : StringWriter(CultureInfo.InvariantCulture) { public override Encoding Encoding => Encoding.UTF8; } #endregion #region CustomXmlWriter // Workaround for removing quotes from quoted FuncIRI(s). private sealed class CustomXmlWriter(XmlWriter writer) : XmlWriter { private readonly XmlWriter writer = writer; public override WriteState WriteState => writer.WriteState; public override void Flush() => writer.Flush(); public override string? LookupPrefix(string ns) => writer.LookupPrefix(ns); public override void WriteBase64(byte[] buffer, int index, int count) => writer.WriteBase64(buffer, index, count); public override void WriteCData(string? text) => writer.WriteCData(text); public override void WriteCharEntity(char ch) => writer.WriteCharEntity(ch); public override void WriteChars(char[] buffer, int index, int count) => writer.WriteChars(buffer, index, count); public override void WriteComment(string? text) => writer.WriteComment(text); public override void WriteDocType(string name, string? pubid, string? sysid, string? subset) => writer.WriteDocType(name, pubid, sysid, subset); public override void WriteEndAttribute() => writer.WriteEndAttribute(); public override void WriteEndDocument() => writer.WriteEndDocument(); public override void WriteEndElement() => writer.WriteEndElement(); public override void WriteEntityRef(string name) => writer.WriteEntityRef(name); public override void WriteFullEndElement() => writer.WriteFullEndElement(); public override void WriteProcessingInstruction(string name, string? text) => writer.WriteProcessingInstruction(name, text); public override void WriteRaw(char[] buffer, int index, int count) => writer.WriteRaw(buffer, index, count); public override void WriteRaw(string data) => writer.WriteRaw(data); public override void WriteStartAttribute(string? prefix, string localName, string? ns) => writer.WriteStartAttribute(prefix, localName, ns); public override void WriteStartDocument() => writer.WriteStartDocument(); public override void WriteStartDocument(bool standalone) => writer.WriteStartDocument(standalone); public override void WriteStartElement(string? prefix, string localName, string? ns) => writer.WriteStartElement(prefix, localName, ns); public override void WriteString(string? text) { writer.WriteString(text?.Replace("\"", string.Empty).Replace("\'", string.Empty)); } public override void WriteSurrogateCharEntity(char lowChar, char highChar) => writer.WriteSurrogateCharEntity(lowChar, highChar); public override void WriteWhitespace(string? ws) => writer.WriteWhitespace(ws); } #endregion } ================================================ FILE: SvgFileType/Extensions/SvgUnitExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using Svg; namespace SvgFileTypePlugin.Extensions; internal static class SvgUnitExtensions { public static int ToDeviceValue(this SvgUnit unit, SvgElement owner, UnitRenderingType unitRenderingType = UnitRenderingType.Other) { ArgumentNullException.ThrowIfNull(owner); using ISvgRenderer renderer = SvgRenderer.FromNull(); float value = unit.ToDeviceValue(renderer, unitRenderingType, owner); return checked((int)Math.Round(value)); } } ================================================ FILE: SvgFileType/Extensions/SvgUseExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Generic; using Svg; using Svg.Transforms; namespace SvgFileTypePlugin.Extensions; internal static class SvgUseExtensions { public static SvgElement? GetCopyOfReferencedElement(this SvgUse useElement) { ArgumentNullException.ThrowIfNull(useElement); SvgDocument document = useElement.OwnerDocument ?? throw new ArgumentException("Use element does not have an owner document.", nameof(useElement)); List transforms = []; SvgElement? referencedElement = useElement; while (referencedElement is SvgUse parentUse and { ReferencedElement: Uri uri }) { string href = uri.ToString().TrimStart('#'); if (href.Length < 1) { return null; } if (parentUse.Transforms is { Count: > 0 }) { transforms.AddRange(parentUse.Transforms); } referencedElement = document.GetElementById(href); } if (referencedElement is null) { return null; } SvgElement copiedElement = referencedElement.DeepCopy(); useElement.CopyOverridedAttributes(copiedElement); copiedElement.Transforms ??= []; copiedElement.Transforms.AddRange(transforms); return copiedElement; } private static void CopyOverridedAttributes(this SvgUse useElement, SvgElement targetElement) { SvgAttributeCollection targetAttributes = targetElement.GetAttributes(); SvgAttributeCollection sourceAttributes = useElement.GetAttributes(); foreach (KeyValuePair attribute in sourceAttributes) { string key = attribute.Key.ToLowerInvariant(); // Most attributes on use do not override those already on the element // referenced by use. (This differs from how CSS style attributes override // those set 'earlier' in the cascade). Only the attributes x, y, width, // height and href on the use element will override those set on the // referenced element.However, any other attributes not set on the referenced // element will be applied to the use element. if (key is "x" or "y" or "width" or "height" or "href" or "xlink:href") { targetAttributes[key] = attribute.Value; } } } } ================================================ FILE: SvgFileType/Extensions/SvgViewBoxExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using Svg; namespace SvgFileTypePlugin.Extensions; internal static class SvgViewBoxExtensions { public static bool IsEmpty(this SvgViewBox vb) { return vb.Width < float.Epsilon || vb.Height < float.Epsilon; } } ================================================ FILE: SvgFileType/Extensions/SvgVisualElementExtensions.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Svg; namespace SvgFileTypePlugin.Extensions; internal static class SvgVisualElementExtensions { #region OriginalVisibilityAttribute private const string OriginalVisibilityAttribute = "original_visibility"; public static bool IsOriginallyVisible(this SvgVisualElement element) { ArgumentNullException.ThrowIfNull(element); return element.CustomAttributes.TryGetValue(OriginalVisibilityAttribute, out string? arg) && string.Equals(arg.Trim(), "visible", StringComparison.OrdinalIgnoreCase); } public static void StoreOriginalVisibility(this SvgVisualElement element) { ArgumentNullException.ThrowIfNull(element); SvgCustomAttributeCollection col = element.CustomAttributes; if (col.ContainsKey(OriginalVisibilityAttribute)) { col[OriginalVisibilityAttribute] = element.Visibility; } else { col.Add(OriginalVisibilityAttribute, element.Visibility); } } #endregion #region GroupNameAttribute private const string GroupNameAttribute = "group_name"; public static string? GetGroupName(this SvgVisualElement element) { ArgumentNullException.ThrowIfNull(element); return element.CustomAttributes.TryGetValue(GroupNameAttribute, out string? groupName) ? groupName : null; } public static void SetGroupName(this SvgVisualElement element, string? groupName) { ArgumentNullException.ThrowIfNull(element); SvgCustomAttributeCollection col = element.CustomAttributes; if (col.ContainsKey(GroupNameAttribute)) { if (string.IsNullOrEmpty(groupName)) { col.Remove(GroupNameAttribute); } else { col[GroupNameAttribute] = groupName; } } else if (!string.IsNullOrEmpty(groupName)) { col.Add(GroupNameAttribute, groupName); } } #endregion public static IEnumerable NotOfType(this IEnumerable source) where TElement : SvgVisualElement { ArgumentNullException.ThrowIfNull(source); return source.Where(element => element is not TElement); } public static bool IsDisplayable(this SvgVisualElement element) { ArgumentNullException.ThrowIfNull(element); return !element.Display.Trim().Equals("none", StringComparison.OrdinalIgnoreCase); } public static bool PreRender(this SvgVisualElement element, IReadOnlyCollection elements, bool hiddenElements, CancellationToken ctoken = default) { ArgumentNullException.ThrowIfNull(element); ArgumentNullException.ThrowIfNull(elements); // Turn off visibility of all other elements foreach (SvgVisualElement sibling in elements.Where(e => e != element)) { ctoken.ThrowIfCancellationRequested(); sibling.Visibility = "hidden"; } element.Visibility = "visible"; // Turn on visibility from node to parent for (SvgElement parent = element.Parent; parent != null; parent = parent.Parent) { ctoken.ThrowIfCancellationRequested(); if (parent is not SvgVisualElement visual) { continue; } // Check, maybe parent element was initially hidden // Skip hidden layers. if (!hiddenElements && !visual.IsOriginallyVisible()) { return false; } visual.Visibility = "visible"; } return true; } } ================================================ FILE: SvgFileType/FodyWeavers.xml ================================================  ================================================ FILE: SvgFileType/FodyWeavers.xsd ================================================  A regular expression matching the assembly names to include in merging. A regular expression matching the assembly names to exclude from merging. A regular expression matching the resource names to include in merging. A regular expression matching the resource names to exclude from merging. A switch to control whether the imported types are hidden (made private) or keep their visibility unchanged. Default is 'true' A string that is used as prefix for the namespace of the imported types. A switch to control whether to import the full assemblies or only the referenced types. Default is 'false' A switch to enable compacting of the target assembly by skipping properties, events and unused methods. Default is 'false' A regular expression matching the assembly names to include in merging. A regular expression matching the assembly names to exclude from merging. A regular expression matching the resource names to include in merging. A regular expression matching the resource names to exclude from merging. A switch to control whether the imported types are hidden (made private) or keep their visibility unchanged. Default is 'true' A string that is used as prefix for the namespace of the imported types. A switch to control whether to import the full assemblies or only the referenced types. Default is 'false' A switch to enable compacting of the target assembly by skipping properties, events and unused methods. Default is 'false' 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. A comma-separated list of error codes that can be safely ignored in assembly verification. 'false' to turn off automatic generation of the XML Schema file. ================================================ FILE: SvgFileType/Import/Direct2DSvgRenderer.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Runtime; using System.Threading; using PaintDotNet; using PaintDotNet.Direct2D1; using PaintDotNet.DirectWrite; using PaintDotNet.Imaging; using PaintDotNet.Rendering; using Svg; using SvgFileTypePlugin.Extensions; using FontStyle = PaintDotNet.DirectWrite.FontStyle; using IDeviceContext = PaintDotNet.Direct2D1.IDeviceContext; using SR = SvgFileTypePlugin.Localization.StringResources; namespace SvgFileTypePlugin.Import; internal sealed class Direct2DSvgRenderer() : SvgRenderer2(name: "Direct2D") { protected override Document GetFlatDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(svgdata); ArgumentNullException.ThrowIfNull(config); ctoken.ThrowIfCancellationRequested(); (int width, int height) = (config.RasterWidth, config.RasterHeight); using BenchmarkScope _ = new BenchmarkScope(); using MemoryFailPoint mfp = GetMemoryFailPoint(width, height, 1); ResetProgress(1); Surface surface = new Surface(width, height, SurfaceCreationFlags.DoNotZeroFillHint); try { SvgDocument svgdoc = SvgDocument.FromSvg(svgdata); IDirect2DFactory d2d = Services.Get(); // Don't dispose this! It's singleton. using IBitmap sbitmap = surface.CreateSharedBitmap(); using IBitmap pbitmap = sbitmap.CreatePremultipliedAdapter(PremultipliedAdapterOptions.UnPremultiplyOnDispose); using IDeviceContext dc = d2d.CreateBitmapDeviceContext(pbitmap); using MemoryStream stream = new MemoryStream(); svgdoc.WriteXML_QuotedFuncIRIHack(stream); using ISvgDocument svg = dc.CreateSvgDocument(stream, viewportSize: new SizeFloat(width, height)); using DrawingScope _1 = dc.UseBeginDraw(); dc.Transform = CalculateTransform(svgsize: new SizeF(svgdoc.Width, svgdoc.Height), config); dc.Clear(); dc.DrawSvgDocument(svg); } catch (Exception) { surface.Dispose(); throw; } Document document = surface.CreateSingleLayerDocument(takeOwnership: true); document.SetDpu(config.Ppi); IncrementProgress(); return document; } protected override Document GetLayeredDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(svgdata); ArgumentNullException.ThrowIfNull(config); using BenchmarkScope _ = new BenchmarkScope(); SvgDocument svg = SvgDocument.FromSvg(svgdata); using IDisposable _1 = svg.UseSetRasterDimensions(config); List elements = GetSvgVisualElements(svg, config, ctoken); ctoken.ThrowIfCancellationRequested(); using MemoryFailPoint _2 = GetMemoryFailPoint(config.RasterWidth, config.RasterHeight, elements.Count); ResetProgress(elements.Count); List layers = []; IDirect2DFactory d2d = Services.Get(); // Don't dispose this! It's singleton. using Surface surface = new Surface(config.RasterWidth, config.RasterHeight); using IBitmap sharedBitmap = surface.CreateSharedBitmap(); using IBitmap premultiplied = sharedBitmap.CreatePremultipliedAdapter(PremultipliedAdapterOptions.None); using IDeviceContext dc = d2d.CreateBitmapDeviceContext(premultiplied); // Render all visual elements that are passed here. foreach (SvgVisualElement element in elements) { ctoken.ThrowIfCancellationRequested(); BitmapLayer? layer; if (element is GroupBoundary boundaryNode) { // Render empty group boundary and continue layer = boundaryNode.ToEmptyLayer(config.RasterWidth, config.RasterHeight); layers.Add(layer); IncrementProgress(); continue; } if (!element.PreRender(elements, config.ImportHiddenElements, ctoken)) { IncrementProgress(); continue; } RenderSvgDocument(element, dc); if (surface.IsEmpty()) { IncrementProgress(); continue; } layer = new BitmapLayer(surface, takeOwnership: false); try { layer.Name = GetLayerTitle(element); if (config.RespectElementOpacity) { layer.Opacity = ToByteOpacity(element.Opacity); } if (config.ImportHiddenElements && !element.IsOriginallyVisible()) { layer.Visible = false; } } catch (Exception) { layer?.Dispose(); layers.ForEach(layer => layer.Dispose()); throw; } IncrementProgress(); layers.Add(layer); } return GetDocument(layers, config); } public override Document GetNoPathDocument() { IDirectWriteFactory dw = Services.Get(); IDirect2DFactory d2d = Services.Get(); using ITextFormat textFormat = dw.CreateTextFormat("Arial", null, FontWeight.Bold, FontStyle.Normal, FontStretch.Normal, UIScaleFactor.ConvertFontPointsToDips(24)); using ITextLayout textLayout = dw.CreateTextLayout(SR.NoPath, textFormat); textLayout.WordWrapping = WordWrapping.NoWrap; TextMeasurement textMeasurement = textLayout.Measure(); int width = (int)textMeasurement.Width; int height = (int)textMeasurement.Height; //StrokeStyleProperties strokeStyleProperties = StrokeStyleProperties.Default; //strokeStyleProperties.LineJoin = LineJoin.Bevel; //strokeStyleProperties.MiterLimit = 0; Surface surface = new Surface(width, height, SurfaceCreationFlags.DoNotZeroFillHint); using (IBitmap shared = surface.CreateSharedBitmap()) using (IBitmap premltd = shared.CreatePremultipliedAdapter(PremultipliedAdapterOptions.UnPremultiplyOnDispose)) using (IDeviceContext dc = d2d.CreateBitmapDeviceContext(premltd)) using (ISolidColorBrush textBrush = dc.CreateSolidColorBrush(Color.Black)) //using (ISolidColorBrush strokeBrush = dc.CreateSolidColorBrush(Color.Red)) //using (IGeometry textGeometry = d2d.CreateGeometryFromTextLayout(textLayout)) //using (IStrokeStyle strokeStyle = d2d.CreateStrokeStyle(strokeStyleProperties)) //using (IGeometry widenedGeometry = textGeometry.Widen(1, strokeStyle)) //using (IGeometry outerStrokeGeometry = widenedGeometry.CombineWithGeometry(textGeometry, GeometryCombineMode.Exclude)) using (dc.UseTextRenderingMode(TextRenderingMode.Default)) using (dc.UseTextAntialiasMode(TextAntialiasMode.Grayscale)) using (dc.UseBeginDraw()) { dc.Clear(Color.LightGray); dc.DrawTextLayout(0, 0, textLayout, textBrush, DrawTextOptions.EnableColorFont); //dc.FillGeometry(textGeometry, textBrush); //dc.FillGeometry(outerStrokeGeometry, strokeBrush); } return surface.CreateSingleLayerDocument(takeOwnership: true); } private void RenderSvgDocument(SvgElement element, IDeviceContext dc) { if (element is SvgUse use) { RenderSvgUseElement(use, e => RenderSvgDocument(e, dc)); } else { SvgDocument clone = element.OwnerDocument.RemoveInvisibleAndNonTextElements(); using MemoryStream stream = new MemoryStream(); clone.WriteXML_QuotedFuncIRIHack(stream); using ISvgDocument partial = dc.CreateSvgDocument(stream, dc.Size); using DrawingScope _ = dc.UseBeginDraw(); dc.Clear(); dc.DrawSvgDocument(partial); } } private static Matrix3x2Float CalculateTransform(SizeF svgsize, SvgImportConfig config) { float ratioX = config.RasterWidth / svgsize.Width; float ratioY = config.PreserveAspectRatio ? ratioX : config.RasterHeight / svgsize.Height; return new Matrix3x2Float() { M11 = ratioX, M22 = ratioY }; } } ================================================ FILE: SvgFileType/Import/GdipSvgRenderer.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Generic; using System.Drawing; using System.Runtime; using System.Threading; using PaintDotNet; using Svg; using SvgFileTypePlugin.Extensions; namespace SvgFileTypePlugin.Import; internal sealed class GdipSvgRenderer() : SvgRenderer2(name: "GDI+") { protected override Document GetFlatDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(svgdata); ArgumentNullException.ThrowIfNull(config); ctoken.ThrowIfCancellationRequested(); using BenchmarkScope _ = new BenchmarkScope(); SvgDocument svg = SvgDocument.FromSvg(svgdata); using IDisposable _1 = svg.UseSetRasterDimensions(config); List elements = GetSvgVisualElements(svg, config, ctoken); using MemoryFailPoint _2 = GetMemoryFailPoint(config.RasterWidth, config.RasterHeight, 2); ResetProgress(elements.Count); using Bitmap bmp = new Bitmap(config.RasterWidth, config.RasterHeight); using (Graphics g = Graphics.FromImage(bmp)) { // Render all visual elements that are passed here. foreach (SvgVisualElement element in elements) { ctoken.ThrowIfCancellationRequested(); if (!element.PreRender(elements, hiddenElements: false, ctoken)) { IncrementProgress(); continue; } RenderSvgDocument(element, g, config); IncrementProgress(); } } Document document = Document.FromImage(bmp); document.SetDpu(config.Ppi); return document; } protected override Document GetLayeredDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(svgdata); ArgumentNullException.ThrowIfNull(config); using BenchmarkScope _ = new BenchmarkScope(); SvgDocument svg = SvgDocument.FromSvg(svgdata); using IDisposable _1 = svg.UseSetRasterDimensions(config); List elements = GetSvgVisualElements(svg, config, ctoken); ctoken.ThrowIfCancellationRequested(); using MemoryFailPoint _2 = GetMemoryFailPoint(config.RasterWidth, config.RasterHeight, elements.Count); ResetProgress(elements.Count); List layers = []; using Bitmap bmp = new Bitmap(config.RasterWidth, config.RasterHeight); using Graphics g = Graphics.FromImage(bmp); // Render all visual elements that are passed here. foreach (SvgVisualElement element in elements) { ctoken.ThrowIfCancellationRequested(); BitmapLayer? layer; if (element is GroupBoundary boundaryNode) { // Render empty group boundary and continue layer = boundaryNode.ToEmptyLayer(config.RasterWidth, config.RasterHeight); layers.Add(layer); IncrementProgress(); continue; } if (!element.PreRender(elements, config.ImportHiddenElements, ctoken)) { IncrementProgress(); continue; } g.Clear(default); RenderSvgDocument(element, g, config); Surface surface = Surface.CopyFromBitmap(bmp, detectDishonestAlpha: false); if (surface.IsEmpty()) { surface.Dispose(); IncrementProgress(); continue; } layer = new BitmapLayer(surface, takeOwnership: true); try { layer.Name = GetLayerTitle(element); if (config.RespectElementOpacity) layer.Opacity = ToByteOpacity(element.Opacity); if (config.ImportHiddenElements && !element.IsOriginallyVisible()) layer.Visible = false; } catch (Exception) { surface.Dispose(); layer?.Dispose(); layers.ForEach(layer => layer.Dispose()); throw; } IncrementProgress(); layers.Add(layer); } return GetDocument(layers, config); } private void RenderSvgDocument(SvgElement element, Graphics graphics, SvgImportConfig config) { if (element is SvgUse use) { RenderSvgUseElement(use, e => RenderSvgDocument(e, graphics, config)); } else { SvgDocument clone = element.OwnerDocument.RemoveInvisibleAndNonTextElements(); clone.Draw(graphics); } } } ================================================ FILE: SvgFileType/Import/LayersMode.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. namespace SvgFileTypePlugin.Import; internal enum LayersMode { Flat, Groups, All } ================================================ FILE: SvgFileType/Import/MyBaseForm.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.ComponentModel; using System.Drawing; using System.Runtime.InteropServices; using System.Security; using System.Windows.Forms; using PaintDotNet; using SvgFileTypePlugin.Extensions; namespace SvgFileTypePlugin.Import; internal partial class MyBaseForm : Form { #region Properties [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool UseAppThemeColors { get; private set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public virtual bool EnableImmersiveDarkMode => false; #endregion #region Constructor protected MyBaseForm(Form? owner) { Owner = owner; FormBorderStyle = FormBorderStyle.FixedDialog; StartPosition = FormStartPosition.CenterParent; MaximizeBox = false; MinimizeBox = false; ShowIcon = false; } protected MyBaseForm() : this(null) { } #endregion #region Public Instance Methods public void SetUseAppThemeColors(bool enable = true) { if (enable == UseAppThemeColors) { return; } using (PdnBaseForm dummy = new PdnBaseForm()) { dummy.UseAppThemeColors = enable; BackColor = dummy.BackColor; ForeColor = dummy.ForeColor; } UpdateControlColorsRecursive(); UseAppThemeColors = enable; } #endregion #region Private Instance Methods private void UpdateControlColorsRecursive() { bool isDark = ForeColor.GetBrightness() > BackColor.GetBrightness(); if (EnableImmersiveDarkMode) { // Native.SetPreferredAppMode(true); Native.UseImmersiveDarkModeColors(this, isDark); Native.UseImmersiveDarkMode(this, isDark); } foreach (Control control in this.Descendants()) { control.ForeColor = ForeColor; control.BackColor = BackColor; switch (control) { case LinkLabel linkLabel when isDark: linkLabel.LinkColor = Color.Orange; linkLabel.VisitedLinkColor = Color.Orange; break; case LinkLabel linkLabel when !isDark: linkLabel.LinkColor = Color.Empty; linkLabel.VisitedLinkColor = Color.Empty; break; case Label label: label.FlatStyle = FlatStyle.Standard; SubscribeOnPaint(label, Label_Paint); break; case CheckBox _: case RadioButton _: ((ButtonBase)control).FlatStyle = FlatStyle.Standard; SubscribeOnPaint(control, CheckRadio_Paint); break; case Button button: button.FlatStyle = FlatStyle.System; break; case GroupBox groupBox: groupBox.FlatStyle = FlatStyle.Standard; SubscribeOnPaint(groupBox, GroupBox_Paint); break; case ComboBox _ when !isDark: case TextBox _ when !isDark: control.BackColor = SystemColors.Window; break; } if (EnableImmersiveDarkMode) { if (control.IsHandleCreated) { Native.ControlSetDarkMode(control, isDark); } control.HandleCreated -= Control_HandleCreated; control.HandleCreated -= Control_HandleCreated; control.HandleCreated += Control_HandleCreated; } } } #endregion #region Private Static Methods private static void DrawBackground(Graphics g, Color backColor, Rectangle rect) { using SolidBrush brush = new SolidBrush(backColor); g.FillRectangle(brush, rect); } #region Control Event Handlers private static void Control_HandleCreated(object? sender, EventArgs e) { if (sender is not Control ctrl) { return; } bool isDark = ctrl.ForeColor.GetBrightness() > ctrl.BackColor.GetBrightness(); Native.ControlSetDarkMode(ctrl, isDark); } private static void SubscribeOnPaint(Control control, PaintEventHandler handler) { control.Paint -= handler; control.Paint -= handler; control.Paint += handler; } private static void GroupBox_Paint(object? sender, PaintEventArgs e) { if (sender is not GroupBox ctrl || ctrl.Enabled) { return; } Color foreColor = SystemColors.GrayText; bool rtl = ctrl.RightToLeft == RightToLeft.Yes; TextFormatFlags tff = ConvertAlignment(ContentAlignment.MiddleLeft, rtl) | TextFormatFlags.WordBreak; DrawBackground(e.Graphics, ctrl.BackColor, e.ClipRectangle); GroupBoxRenderer.DrawGroupBox(e.Graphics, e.ClipRectangle, ctrl.Text, ctrl.Font, foreColor, tff, System.Windows.Forms.VisualStyles.GroupBoxState.Disabled); } private static void Label_Paint(object? sender, PaintEventArgs e) { if (sender is not Label ctrl || ctrl.Enabled) { return; } Color foreColor = SystemColors.GrayText; bool rtl = ctrl.RightToLeft == RightToLeft.Yes; TextFormatFlags tff = ConvertAlignment(ctrl.TextAlign, rtl) | TextFormatFlags.WordBreak; DrawBackground(e.Graphics, ctrl.BackColor, e.ClipRectangle); TextRenderer.DrawText(e.Graphics, ctrl.Text, ctrl.Font, e.ClipRectangle, foreColor, tff); } private static void CheckRadio_Paint(object? sender, PaintEventArgs e) { if (sender is not ButtonBase ctrl || ctrl.Enabled) { return; } Color foreColor = SystemColors.GrayText; Size glyphSize = CheckBoxRenderer.GetGlyphSize(e.Graphics, System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal); bool rtl = ctrl.RightToLeft == RightToLeft.Yes; TextFormatFlags tff = ConvertAlignment(ctrl.TextAlign, rtl) | TextFormatFlags.LeftAndRightPadding | TextFormatFlags.Internal; Rectangle rect = e.ClipRectangle; Size size = new Size(rect.Width - 1 - glyphSize.Width, rect.Height); Point point = rtl ? new Point(rect.X - 1, rect.Y) : new Point(rect.X + glyphSize.Width + 1, rect.Y + 1); rect = new Rectangle(point, size); DrawBackground(e.Graphics, ctrl.BackColor, rect); TextRenderer.DrawText(e.Graphics, ctrl.Text, ctrl.Font, rect, foreColor, tff); } #endregion #region ContentAlignment<->TextFormatting Conversions public static TextFormatFlags ConvertAlignment(ContentAlignment alignment, bool rtl) { TextFormatFlags tff = TextFormatFlags.Default; // Vertical alignment tff |= alignment switch { ContentAlignment.TopLeft or ContentAlignment.TopRight or ContentAlignment.TopCenter => TextFormatFlags.Top, ContentAlignment.BottomLeft or ContentAlignment.BottomRight or ContentAlignment.BottomCenter => TextFormatFlags.Bottom, ContentAlignment.MiddleLeft or ContentAlignment.MiddleRight or ContentAlignment.MiddleCenter => TextFormatFlags.VerticalCenter, _ => TextFormatFlags.Default }; // Horizontal alignment tff |= alignment switch { ContentAlignment.TopLeft or ContentAlignment.BottomLeft or ContentAlignment.MiddleLeft when rtl => TextFormatFlags.Right, ContentAlignment.TopLeft or ContentAlignment.BottomLeft or ContentAlignment.MiddleLeft => TextFormatFlags.Left, ContentAlignment.TopRight or ContentAlignment.BottomRight or ContentAlignment.MiddleRight when rtl => TextFormatFlags.Left, ContentAlignment.TopRight or ContentAlignment.BottomRight or ContentAlignment.MiddleRight => TextFormatFlags.Right, _ => TextFormatFlags.HorizontalCenter }; return tff; } #endregion #endregion #region Native [SuppressUnmanagedCodeSecurity] private static partial class Native { #region Scaling Factor [LibraryImport("gdi32.dll", SetLastError = true)] private static partial int GetDeviceCaps(nint hdc, int nIndex); public static float GetScalingFactor() { const int VERTRES = 10; const int DESKTOPVERTRES = 117; using Graphics g = Graphics.FromHwnd(IntPtr.Zero); IntPtr desktop = g.GetHdc(); int logicalScreenHeight = GetDeviceCaps(desktop, VERTRES); int physicalScreenHeight = GetDeviceCaps(desktop, DESKTOPVERTRES); return physicalScreenHeight / (float)logicalScreenHeight; // 1.25 = 125% } #endregion #region Experimental Dark Mode private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; private const int PREFFERED_APP_MODE__ALLOW_DARK = 1; private const int PREFFERED_APP_MODE__DEFAULT = 0; private const int S_OK = 0; public static bool IsDarkModeSupported { get; } = IsWindows10OrGreater(17763); public static bool ControlSetDarkMode(IWin32Window window, bool enable) { if (!IsDarkModeSupported) { return false; } int error = SetWindowTheme(window.Handle, enable ? "darkmode_explorer" : "explorer", null); return Check(error); } public static bool UseImmersiveDarkModeColors(IWin32Window window, bool enable) { if (!IsDarkModeSupported) { return false; } WindowCompositionAttributeData data = new() { Attribute = WindowCompositionAttribute.WCA_USEDARKMODECOLORS, Data = enable ? 1 : 0, SizeOfData = Marshal.SizeOf() }; return SetWindowCompositionAttribute(window.Handle, data); } public static bool SetPreferredAppMode(bool dark) { if (!IsDarkModeSupported) { return false; } int error = SetPreferredAppMode(dark ? 1 : 0); return Check(error); } public static bool UseImmersiveDarkMode(IWin32Window window, bool enabled) { if (!IsDarkModeSupported) { return false; } int attr = IsWindows10OrGreater(18985) ? DWMWA_USE_IMMERSIVE_DARK_MODE : DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; int attrValue = enabled ? 1 : 0; int error = DwmSetWindowAttribute(window.Handle, attr, ref attrValue, sizeof(int)); return Check(error); } private static bool Check(int error) { return error == S_OK ? true : throw new Win32Exception(error); } private static bool IsWindows10OrGreater(int build = -1) { Version version = Environment.OSVersion.Version; return version.Major >= 10 && version.Build >= build; } [LibraryImport("uxtheme.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] private static partial int SetWindowTheme(nint hWnd, string? pszSubAppName, string? pszSubIdList); [LibraryImport("uxtheme.dll", EntryPoint = "#133", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool AllowDarkModeForWindow(nint hWnd, [MarshalAs(UnmanagedType.Bool)] bool allow); [LibraryImport("uxtheme.dll", EntryPoint = "#135", SetLastError = true)] private static partial int SetPreferredAppMode(int i); [LibraryImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static partial bool SetWindowCompositionAttribute(nint hwnd, WindowCompositionAttributeData data); [LibraryImport("dwmapi.dll", SetLastError = true)] private static partial int DwmSetWindowAttribute(nint hwnd, int attr, ref int attrValue, int attrSize); private enum WindowCompositionAttribute { WCA_ACCENT_POLICY = 19, WCA_USEDARKMODECOLORS = 26 } [StructLayout(LayoutKind.Sequential)] private ref struct WindowCompositionAttributeData { public WindowCompositionAttribute Attribute; public int Data; public int SizeOfData; } #endregion } #endregion } ================================================ FILE: SvgFileType/Import/MyBaseForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: SvgFileType/Import/ResvgSvgRenderer.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Generic; using System.Drawing; using System.Runtime; using System.Threading; using PaintDotNet; using resvg.net; using Svg; using SvgFileTypePlugin.Extensions; namespace SvgFileTypePlugin.Import; internal sealed class ResvgSvgRenderer() : SvgRenderer2("resvg") { protected override Document GetFlatDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(svgdata); ArgumentNullException.ThrowIfNull(config); ctoken.ThrowIfCancellationRequested(); (int width, int height) = (config.RasterWidth, config.RasterHeight); using BenchmarkScope _ = new BenchmarkScope(); using MemoryFailPoint mfp = GetMemoryFailPoint(width, height, 1); ResetProgress(1); Surface surface = new Surface(width, height); try { using Resvg resvg = Resvg.FromData(svgdata, loadSystemFonts: true); ResvgTransform transform = CalculateTransform(resvg.Size, config); resvg.Render(surface.Scan0.Pointer, transform, width, height, PixelOpFlags.RgbaToBgra | PixelOpFlags.UnPremultiplyAlpha); } catch (Exception) { surface.Dispose(); throw; } Document document = surface.CreateSingleLayerDocument(takeOwnership: true); document.SetDpu(config.Ppi); IncrementProgress(); return document; } protected override Document GetLayeredDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(svgdata); ArgumentNullException.ThrowIfNull(config); using BenchmarkScope _ = new BenchmarkScope(); SvgDocument svg = SvgDocument.FromSvg(svgdata); using IDisposable _1 = svg.UseSetRasterDimensions(config); List elements = GetSvgVisualElements(svg, config, ctoken); ctoken.ThrowIfCancellationRequested(); using MemoryFailPoint _2 = GetMemoryFailPoint(config.RasterWidth, config.RasterHeight, elements.Count); ResetProgress(elements.Count); List layers = []; using ResvgOptions options = new ResvgOptions(); options.LoadSystemFonts(); using Surface surface = new Surface(config.RasterWidth, config.RasterHeight, SurfaceCreationFlags.DoNotZeroFillHint); // Render all visual elements that are passed here. foreach (SvgVisualElement element in elements) { ctoken.ThrowIfCancellationRequested(); BitmapLayer? layer; if (element is GroupBoundary boundaryNode) { // Render empty group boundary and continue layer = boundaryNode.ToEmptyLayer(config.RasterWidth, config.RasterHeight); layers.Add(layer); IncrementProgress(); continue; } if (!element.PreRender(elements, config.ImportHiddenElements, ctoken)) { IncrementProgress(); continue; } surface.Clear(); RenderSvgDocument(element, surface, options, config); if (surface.IsEmpty()) { IncrementProgress(); continue; } layer = new BitmapLayer(surface, takeOwnership: false); try { layer.Name = GetLayerTitle(element); if (config.RespectElementOpacity) { layer.Opacity = ToByteOpacity(element.Opacity); } if (config.ImportHiddenElements && !element.IsOriginallyVisible()) { layer.Visible = false; } } catch (Exception) { layer?.Dispose(); layers.ForEach(layer => layer.Dispose()); throw; } IncrementProgress(); layers.Add(layer); } return GetDocument(layers, config); } private void RenderSvgDocument(SvgElement element, Surface surface, ResvgOptions options, SvgImportConfig config) { if (element is SvgUse use) { RenderSvgUseElement(use, e => RenderSvgDocument(e, surface, options, config)); } else { SvgDocument clone = element.OwnerDocument.RemoveInvisibleAndNonTextElements(); (int width, int height) = (surface.Width, surface.Height); using Resvg resvg = Resvg.FromData(clone.GetXML_QuotedFuncIRIHack(), options); resvg.Render(surface.Scan0.Pointer, width, height, PixelOpFlags.RgbaToBgra | PixelOpFlags.UnPremultiplyAlpha); } } private static ResvgTransform CalculateTransform(SizeF svgsize, SvgImportConfig config) { float ratioX = config.RasterWidth / svgsize.Width; float ratioY = config.PreserveAspectRatio ? ratioX : config.RasterHeight / svgsize.Height; return new ResvgTransform() { M11 = ratioX, M22 = ratioY }; } } ================================================ FILE: SvgFileType/Import/StreamTrackerCancellationTokenSource.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.IO; using System.Threading; namespace SvgFileTypePlugin.Import; /// /// Paint.NET input stream can throw internally. /// This class helps us to track these cancellation events. Provides us ability to gracefully end /// the ongoing operation. ///
/// ! Note: Must be used after reading the whole stream. ///
internal sealed class StreamTrackerCancellationTokenSource : CancellationTokenSource { private bool disposed; private readonly Timer? timer; private readonly Func? action; public StreamTrackerCancellationTokenSource(Stream stream, int period = 1000) { ArgumentNullException.ThrowIfNull(stream); ArgumentOutOfRangeException.ThrowIfNegative(period); if (!stream.CanSeek) { return; } action = () => IsStreamCanceled(stream); timer = new Timer(OnTimer, null, 0, period); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (!disposed) { disposed = true; timer?.Dispose(); } } private void OnTimer(object? state) { try { if (action!.Invoke()) { Cancel(); timer!.Dispose(); } } catch (ObjectDisposedException) { // ignore } } private static bool IsStreamCanceled(Stream stream) { ArgumentNullException.ThrowIfNull(stream); try { stream.Position = 0; return false; } catch (ObjectDisposedException) { Logger.WriteLine("Completed!"); return true; } catch (OperationCanceledException) { Logger.WriteLine("Canceled!"); return true; } } } ================================================ FILE: SvgFileType/Import/SvgImport.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Threading; using System.Windows.Forms; using PaintDotNet; using Svg; using SvgFileTypePlugin.Extensions; namespace SvgFileTypePlugin.Import; internal static class SvgImport { public static Document Load(Stream stream) { ArgumentNullException.ThrowIfNull(stream); if (!stream.CanRead) { throw new IOException("Input stream is not readable."); } if (stream.Length <= 0) { return new Direct2DSvgRenderer().GetNoPathDocument(); } // We are using resvg which gives the best result in my tests. // For testing purposes, I also have implemented GDI+ and Direct2D based SVG renderers as well. // Renderers: resvg, gdip or gdiplus or gdi+, direct2d or d2d const string rendererName = "resvg"; string svgdata = Open(stream); using StreamTrackerCancellationTokenSource cts = new(stream); CancellationToken ctoken = cts.Token; if (UIHelper.IsSaveConfigDialogVisible()) { // This is workaround for Saving functionality. // We don't want the dialog appear if Paint.NET is generating previews. SvgDocument svg = SvgDocument.FromSvg(svgdata); SvgRenderer2 svgRenderer = SvgRenderer2.Create(rendererName); SvgImportConfig config = new SvgImportConfig { LayersMode = LayersMode.Flat, RasterWidth = svg.Width.ToDeviceValue(svg), RasterHeight = svg.Height.ToDeviceValue(svg), Ppi = 96 }; return svgRenderer.Rasterize(svgdata, config, ctoken); } Document? document = UIHelper.RunOnUIThread(() => { using SvgImportDialog dialog = new SvgImportDialog(svgdata, rendererName, ctoken); DialogResult dialogResult = dialog.ShowDialog(); return dialogResult == DialogResult.OK ? dialog.Result : throw dialog.Error ?? new InvalidOperationException(); }); Debug.Assert(document is not null); return document; } private static string Open(Stream input) { using MemoryStream ms = new(); Span buf = new byte[3]; if (input.Read(buf) < 3) { throw new IOException("Input stream is not a valid SVG."); } ms.Write(buf); input.CopyTo(ms); // Do not close the source stream. // It also can be used to track cancellation. ms.Position = 0; ReadOnlySpan gzipMagicBytes = [0x1F, 0x8B, 0x8]; input = buf.SequenceEqual(gzipMagicBytes) ? new GZipStream(ms, CompressionMode.Decompress, leaveOpen: false) : ms; using StreamReader reader = new StreamReader(input, leaveOpen: false); return reader.ReadToEnd(); } } ================================================ FILE: SvgFileType/Import/SvgImportConfig.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. namespace SvgFileTypePlugin.Import; internal sealed class SvgImportConfig { public required int RasterWidth { get; init; } public required int RasterHeight { get; init; } public bool PreserveAspectRatio { get; set; } = true; public int Ppi { get; set; } = 96; public LayersMode LayersMode { get; set; } = LayersMode.Flat; public bool GroupBoundariesAsLayers { get; set; } public bool RespectElementOpacity { get; set; } public bool ImportHiddenElements { get; set; } } ================================================ FILE: SvgFileType/Import/SvgImportDialog.Designer.cs ================================================ namespace SvgFileTypePlugin.Import; partial class SvgImportDialog { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { components = new System.ComponentModel.Container(); ToolTip1 = new System.Windows.Forms.ToolTip(components); PbWarning = new System.Windows.Forms.PictureBox(); CbGroupBoundaries = new System.Windows.Forms.CheckBox(); RootPanel = new System.Windows.Forms.Panel(); StatusStrip = new System.Windows.Forms.StatusStrip(); ProgressBar = new System.Windows.Forms.ToolStripProgressBar(); ProgressLabel = new System.Windows.Forms.ToolStripStatusLabel(); PlaceHolderLabel = new System.Windows.Forms.ToolStripStatusLabel(); UpdateAvailLabel = new System.Windows.Forms.ToolStripStatusLabel(); tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); LnkGitHub = new System.Windows.Forms.LinkLabel(); LnkForum = new System.Windows.Forms.LinkLabel(); BtnOk = new System.Windows.Forms.Button(); BtnCancel = new System.Windows.Forms.Button(); GbLayers = new System.Windows.Forms.GroupBox(); tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); RbFlatten = new System.Windows.Forms.RadioButton(); RbGroups = new System.Windows.Forms.RadioButton(); CbHiddenLayers = new System.Windows.Forms.CheckBox(); RbAllElements = new System.Windows.Forms.RadioButton(); CbOpacity = new System.Windows.Forms.CheckBox(); GbSizeSelection = new System.Windows.Forms.GroupBox(); CbKeepAR = new System.Windows.Forms.CheckBox(); tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); LblResolution = new System.Windows.Forms.Label(); LblCanvasWH = new System.Windows.Forms.Label(); NudCanvasW = new System.Windows.Forms.NumericUpDown(); NudCanvasH = new System.Windows.Forms.NumericUpDown(); label9 = new System.Windows.Forms.Label(); NudDpi = new System.Windows.Forms.NumericUpDown(); GbInfo = new System.Windows.Forms.GroupBox(); tableLayoutPanel9 = new System.Windows.Forms.TableLayoutPanel(); LnkUseSvgSettings = new System.Windows.Forms.Button(); tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); LblViewport = new System.Windows.Forms.Label(); TbViewboxH = new System.Windows.Forms.TextBox(); label6 = new System.Windows.Forms.Label(); TbViewportW = new System.Windows.Forms.TextBox(); label3 = new System.Windows.Forms.Label(); TbViewboxW = new System.Windows.Forms.TextBox(); TbViewportH = new System.Windows.Forms.TextBox(); LblViewboxWH = new System.Windows.Forms.Label(); LblViewboxXY = new System.Windows.Forms.Label(); TbViewboxY = new System.Windows.Forms.TextBox(); label4 = new System.Windows.Forms.Label(); TbViewboxX = new System.Windows.Forms.TextBox(); ((System.ComponentModel.ISupportInitialize)PbWarning).BeginInit(); RootPanel.SuspendLayout(); StatusStrip.SuspendLayout(); tableLayoutPanel3.SuspendLayout(); GbLayers.SuspendLayout(); tableLayoutPanel1.SuspendLayout(); GbSizeSelection.SuspendLayout(); tableLayoutPanel4.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)NudCanvasW).BeginInit(); ((System.ComponentModel.ISupportInitialize)NudCanvasH).BeginInit(); ((System.ComponentModel.ISupportInitialize)NudDpi).BeginInit(); GbInfo.SuspendLayout(); tableLayoutPanel9.SuspendLayout(); tableLayoutPanel2.SuspendLayout(); SuspendLayout(); // // ToolTip1 // ToolTip1.AutomaticDelay = 0; ToolTip1.AutoPopDelay = 0; ToolTip1.InitialDelay = 0; ToolTip1.IsBalloon = true; ToolTip1.ReshowDelay = 0; ToolTip1.ShowAlways = true; ToolTip1.UseAnimation = false; // // PbWarning // PbWarning.Dock = System.Windows.Forms.DockStyle.Fill; PbWarning.Location = new System.Drawing.Point(52, 0); PbWarning.Margin = new System.Windows.Forms.Padding(0); PbWarning.MinimumSize = new System.Drawing.Size(23, 23); PbWarning.Name = "PbWarning"; PbWarning.Size = new System.Drawing.Size(23, 29); PbWarning.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; PbWarning.TabIndex = 2; PbWarning.TabStop = false; ToolTip1.SetToolTip(PbWarning, "Please make sure you've enough memory, especially if you're importing with layers."); PbWarning.Visible = false; // // CbGroupBoundaries // CbGroupBoundaries.AutoSize = true; CbGroupBoundaries.Dock = System.Windows.Forms.DockStyle.Fill; CbGroupBoundaries.Enabled = false; CbGroupBoundaries.Location = new System.Drawing.Point(99, 53); CbGroupBoundaries.Name = "CbGroupBoundaries"; CbGroupBoundaries.RightToLeft = System.Windows.Forms.RightToLeft.Yes; CbGroupBoundaries.Size = new System.Drawing.Size(256, 19); CbGroupBoundaries.TabIndex = 2; CbGroupBoundaries.Text = "Group boundaries"; ToolTip1.SetToolTip(CbGroupBoundaries, "Imports group boundaries as empty layers."); CbGroupBoundaries.UseVisualStyleBackColor = true; // // RootPanel // RootPanel.AutoSize = true; RootPanel.Controls.Add(StatusStrip); RootPanel.Controls.Add(tableLayoutPanel3); RootPanel.Controls.Add(GbLayers); RootPanel.Controls.Add(GbSizeSelection); RootPanel.Controls.Add(GbInfo); RootPanel.Dock = System.Windows.Forms.DockStyle.Top; RootPanel.Location = new System.Drawing.Point(0, 0); RootPanel.Name = "RootPanel"; RootPanel.Padding = new System.Windows.Forms.Padding(10, 10, 10, 0); RootPanel.Size = new System.Drawing.Size(384, 421); RootPanel.TabIndex = 0; // // StatusStrip // StatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { ProgressBar, ProgressLabel, PlaceHolderLabel, UpdateAvailLabel }); StatusStrip.Location = new System.Drawing.Point(10, 399); StatusStrip.Name = "StatusStrip"; StatusStrip.Size = new System.Drawing.Size(364, 22); StatusStrip.SizingGrip = false; StatusStrip.TabIndex = 0; StatusStrip.Text = "statusStrip1"; // // ProgressBar // ProgressBar.Name = "ProgressBar"; ProgressBar.Size = new System.Drawing.Size(100, 16); ProgressBar.Visible = false; // // ProgressLabel // ProgressLabel.BackColor = System.Drawing.Color.Transparent; ProgressLabel.Name = "ProgressLabel"; ProgressLabel.Size = new System.Drawing.Size(42, 17); ProgressLabel.Text = "Ready!"; // // PlaceHolderLabel // PlaceHolderLabel.BackColor = System.Drawing.Color.Transparent; PlaceHolderLabel.Name = "PlaceHolderLabel"; PlaceHolderLabel.Size = new System.Drawing.Size(307, 17); PlaceHolderLabel.Spring = true; // // UpdateAvailLabel // UpdateAvailLabel.BackColor = System.Drawing.Color.Transparent; UpdateAvailLabel.ForeColor = System.Drawing.Color.Red; UpdateAvailLabel.Name = "UpdateAvailLabel"; UpdateAvailLabel.Size = new System.Drawing.Size(0, 17); // // tableLayoutPanel3 // tableLayoutPanel3.AutoSize = true; tableLayoutPanel3.ColumnCount = 5; tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel3.Controls.Add(LnkGitHub, 0, 0); tableLayoutPanel3.Controls.Add(LnkForum, 1, 0); tableLayoutPanel3.Controls.Add(BtnOk, 3, 0); tableLayoutPanel3.Controls.Add(BtnCancel, 4, 0); tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Top; tableLayoutPanel3.Location = new System.Drawing.Point(10, 350); tableLayoutPanel3.Name = "tableLayoutPanel3"; tableLayoutPanel3.Padding = new System.Windows.Forms.Padding(10); tableLayoutPanel3.RowCount = 1; tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel3.Size = new System.Drawing.Size(364, 49); tableLayoutPanel3.TabIndex = 1; // // LnkGitHub // LnkGitHub.Dock = System.Windows.Forms.DockStyle.Fill; LnkGitHub.Location = new System.Drawing.Point(13, 10); LnkGitHub.Name = "LnkGitHub"; LnkGitHub.Size = new System.Drawing.Size(45, 29); LnkGitHub.TabIndex = 2; LnkGitHub.TabStop = true; LnkGitHub.Text = "GitHub"; LnkGitHub.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // LnkForum // LnkForum.AutoSize = true; LnkForum.Dock = System.Windows.Forms.DockStyle.Fill; LnkForum.Location = new System.Drawing.Point(64, 10); LnkForum.Name = "LnkForum"; LnkForum.Size = new System.Drawing.Size(42, 29); LnkForum.TabIndex = 3; LnkForum.TabStop = true; LnkForum.Text = "Forum"; LnkForum.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // BtnOk // BtnOk.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; BtnOk.Location = new System.Drawing.Point(195, 13); BtnOk.Name = "BtnOk"; BtnOk.Size = new System.Drawing.Size(75, 23); BtnOk.TabIndex = 0; BtnOk.Text = "OK"; BtnOk.UseVisualStyleBackColor = true; // // BtnCancel // BtnCancel.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; BtnCancel.Location = new System.Drawing.Point(276, 13); BtnCancel.Name = "BtnCancel"; BtnCancel.Size = new System.Drawing.Size(75, 23); BtnCancel.TabIndex = 1; BtnCancel.Text = "Cancel"; BtnCancel.UseVisualStyleBackColor = true; // // GbLayers // GbLayers.AutoSize = true; GbLayers.Controls.Add(tableLayoutPanel1); GbLayers.Dock = System.Windows.Forms.DockStyle.Top; GbLayers.Location = new System.Drawing.Point(10, 253); GbLayers.Name = "GbLayers"; GbLayers.Size = new System.Drawing.Size(364, 97); GbLayers.TabIndex = 2; GbLayers.TabStop = false; GbLayers.Text = "Layers"; // // tableLayoutPanel1 // tableLayoutPanel1.AutoSize = true; tableLayoutPanel1.ColumnCount = 2; tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel1.Controls.Add(RbFlatten, 0, 0); tableLayoutPanel1.Controls.Add(CbGroupBoundaries, 1, 2); tableLayoutPanel1.Controls.Add(RbGroups, 0, 1); tableLayoutPanel1.Controls.Add(CbHiddenLayers, 1, 1); tableLayoutPanel1.Controls.Add(RbAllElements, 0, 2); tableLayoutPanel1.Controls.Add(CbOpacity, 1, 0); tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; tableLayoutPanel1.Location = new System.Drawing.Point(3, 19); tableLayoutPanel1.Name = "tableLayoutPanel1"; tableLayoutPanel1.RowCount = 3; tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel1.Size = new System.Drawing.Size(358, 75); tableLayoutPanel1.TabIndex = 0; // // RbFlatten // RbFlatten.AutoSize = true; RbFlatten.Checked = true; RbFlatten.Dock = System.Windows.Forms.DockStyle.Fill; RbFlatten.Location = new System.Drawing.Point(3, 3); RbFlatten.Name = "RbFlatten"; RbFlatten.Size = new System.Drawing.Size(90, 19); RbFlatten.TabIndex = 3; RbFlatten.TabStop = true; RbFlatten.Text = "Flatten"; RbFlatten.UseVisualStyleBackColor = true; // // RbGroups // RbGroups.AutoSize = true; RbGroups.Dock = System.Windows.Forms.DockStyle.Fill; RbGroups.Location = new System.Drawing.Point(3, 28); RbGroups.Name = "RbGroups"; RbGroups.Size = new System.Drawing.Size(90, 19); RbGroups.TabIndex = 4; RbGroups.Text = "Groups"; RbGroups.UseVisualStyleBackColor = true; // // CbHiddenLayers // CbHiddenLayers.AutoSize = true; CbHiddenLayers.Checked = true; CbHiddenLayers.CheckState = System.Windows.Forms.CheckState.Checked; CbHiddenLayers.Dock = System.Windows.Forms.DockStyle.Fill; CbHiddenLayers.Enabled = false; CbHiddenLayers.Location = new System.Drawing.Point(99, 28); CbHiddenLayers.Name = "CbHiddenLayers"; CbHiddenLayers.RightToLeft = System.Windows.Forms.RightToLeft.Yes; CbHiddenLayers.Size = new System.Drawing.Size(256, 19); CbHiddenLayers.TabIndex = 1; CbHiddenLayers.Text = "Hidden layers"; CbHiddenLayers.UseVisualStyleBackColor = true; // // RbAllElements // RbAllElements.AutoSize = true; RbAllElements.Dock = System.Windows.Forms.DockStyle.Fill; RbAllElements.Location = new System.Drawing.Point(3, 53); RbAllElements.Name = "RbAllElements"; RbAllElements.Size = new System.Drawing.Size(90, 19); RbAllElements.TabIndex = 5; RbAllElements.Text = "All Elements"; RbAllElements.UseVisualStyleBackColor = true; // // CbOpacity // CbOpacity.AutoSize = true; CbOpacity.Checked = true; CbOpacity.CheckState = System.Windows.Forms.CheckState.Checked; CbOpacity.Dock = System.Windows.Forms.DockStyle.Fill; CbOpacity.Enabled = false; CbOpacity.Location = new System.Drawing.Point(99, 3); CbOpacity.Name = "CbOpacity"; CbOpacity.RightToLeft = System.Windows.Forms.RightToLeft.Yes; CbOpacity.Size = new System.Drawing.Size(256, 19); CbOpacity.TabIndex = 0; CbOpacity.Text = "Opacity as layer property"; CbOpacity.UseVisualStyleBackColor = true; // // GbSizeSelection // GbSizeSelection.AutoSize = true; GbSizeSelection.Controls.Add(CbKeepAR); GbSizeSelection.Controls.Add(tableLayoutPanel4); GbSizeSelection.Dock = System.Windows.Forms.DockStyle.Top; GbSizeSelection.Location = new System.Drawing.Point(10, 154); GbSizeSelection.Name = "GbSizeSelection"; GbSizeSelection.Size = new System.Drawing.Size(364, 99); GbSizeSelection.TabIndex = 3; GbSizeSelection.TabStop = false; GbSizeSelection.Text = "Size selection by user"; // // CbKeepAR // CbKeepAR.AutoSize = true; CbKeepAR.Checked = true; CbKeepAR.CheckState = System.Windows.Forms.CheckState.Checked; CbKeepAR.Dock = System.Windows.Forms.DockStyle.Top; CbKeepAR.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; CbKeepAR.Location = new System.Drawing.Point(3, 77); CbKeepAR.Name = "CbKeepAR"; CbKeepAR.RightToLeft = System.Windows.Forms.RightToLeft.Yes; CbKeepAR.Size = new System.Drawing.Size(358, 19); CbKeepAR.TabIndex = 0; CbKeepAR.Text = "Keep aspect ratio"; CbKeepAR.UseVisualStyleBackColor = true; // // tableLayoutPanel4 // tableLayoutPanel4.AutoSize = true; tableLayoutPanel4.ColumnCount = 7; tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 23F)); tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); tableLayoutPanel4.Controls.Add(LblResolution, 2, 1); tableLayoutPanel4.Controls.Add(LblCanvasWH, 2, 0); tableLayoutPanel4.Controls.Add(PbWarning, 1, 0); tableLayoutPanel4.Controls.Add(NudCanvasW, 3, 0); tableLayoutPanel4.Controls.Add(NudCanvasH, 5, 0); tableLayoutPanel4.Controls.Add(label9, 4, 0); tableLayoutPanel4.Controls.Add(NudDpi, 3, 1); tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Top; tableLayoutPanel4.Location = new System.Drawing.Point(3, 19); tableLayoutPanel4.Name = "tableLayoutPanel4"; tableLayoutPanel4.RowCount = 2; tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel4.Size = new System.Drawing.Size(358, 58); tableLayoutPanel4.TabIndex = 1; // // LblResolution // LblResolution.AutoSize = true; LblResolution.Dock = System.Windows.Forms.DockStyle.Fill; LblResolution.Location = new System.Drawing.Point(78, 29); LblResolution.Name = "LblResolution"; LblResolution.Size = new System.Drawing.Size(92, 29); LblResolution.TabIndex = 0; LblResolution.Text = "Resolution (DPI)"; LblResolution.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // LblCanvasWH // LblCanvasWH.AutoSize = true; LblCanvasWH.Dock = System.Windows.Forms.DockStyle.Fill; LblCanvasWH.Location = new System.Drawing.Point(78, 0); LblCanvasWH.Name = "LblCanvasWH"; LblCanvasWH.Size = new System.Drawing.Size(92, 29); LblCanvasWH.TabIndex = 1; LblCanvasWH.Text = "Canvas (W×H)"; LblCanvasWH.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // NudCanvasW // NudCanvasW.Dock = System.Windows.Forms.DockStyle.Fill; NudCanvasW.Location = new System.Drawing.Point(176, 3); NudCanvasW.Maximum = new decimal(new int[] { 100000, 0, 0, 0 }); NudCanvasW.Minimum = new decimal(new int[] { 1, 0, 0, 0 }); NudCanvasW.MinimumSize = new System.Drawing.Size(50, 0); NudCanvasW.Name = "NudCanvasW"; NudCanvasW.Size = new System.Drawing.Size(50, 23); NudCanvasW.TabIndex = 0; NudCanvasW.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; NudCanvasW.Value = new decimal(new int[] { 512, 0, 0, 0 }); // // NudCanvasH // NudCanvasH.Dock = System.Windows.Forms.DockStyle.Fill; NudCanvasH.Location = new System.Drawing.Point(253, 3); NudCanvasH.Maximum = new decimal(new int[] { 100000, 0, 0, 0 }); NudCanvasH.Minimum = new decimal(new int[] { 1, 0, 0, 0 }); NudCanvasH.MinimumSize = new System.Drawing.Size(50, 0); NudCanvasH.Name = "NudCanvasH"; NudCanvasH.Size = new System.Drawing.Size(50, 23); NudCanvasH.TabIndex = 1; NudCanvasH.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; NudCanvasH.Value = new decimal(new int[] { 512, 0, 0, 0 }); // // label9 // label9.AutoSize = true; label9.Dock = System.Windows.Forms.DockStyle.Fill; label9.Location = new System.Drawing.Point(232, 0); label9.Name = "label9"; label9.Size = new System.Drawing.Size(15, 29); label9.TabIndex = 5; label9.Text = "×"; label9.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // NudDpi // NudDpi.Dock = System.Windows.Forms.DockStyle.Fill; NudDpi.Location = new System.Drawing.Point(176, 32); NudDpi.Maximum = new decimal(new int[] { 32767, 0, 0, 0 }); NudDpi.Minimum = new decimal(new int[] { 1, 0, 0, 0 }); NudDpi.MinimumSize = new System.Drawing.Size(50, 0); NudDpi.Name = "NudDpi"; NudDpi.Size = new System.Drawing.Size(50, 23); NudDpi.TabIndex = 2; NudDpi.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; NudDpi.Value = new decimal(new int[] { 96, 0, 0, 0 }); // // GbInfo // GbInfo.AutoSize = true; GbInfo.Controls.Add(tableLayoutPanel9); GbInfo.Controls.Add(tableLayoutPanel2); GbInfo.Dock = System.Windows.Forms.DockStyle.Top; GbInfo.Location = new System.Drawing.Point(10, 10); GbInfo.Name = "GbInfo"; GbInfo.Size = new System.Drawing.Size(364, 144); GbInfo.TabIndex = 4; GbInfo.TabStop = false; GbInfo.Text = "Size settings given in SVG file"; // // tableLayoutPanel9 // tableLayoutPanel9.AutoSize = true; tableLayoutPanel9.ColumnCount = 3; tableLayoutPanel9.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); tableLayoutPanel9.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel9.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); tableLayoutPanel9.Controls.Add(LnkUseSvgSettings, 1, 0); tableLayoutPanel9.Dock = System.Windows.Forms.DockStyle.Top; tableLayoutPanel9.Location = new System.Drawing.Point(3, 106); tableLayoutPanel9.Name = "tableLayoutPanel9"; tableLayoutPanel9.RowCount = 1; tableLayoutPanel9.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel9.Size = new System.Drawing.Size(358, 35); tableLayoutPanel9.TabIndex = 0; // // LnkUseSvgSettings // LnkUseSvgSettings.AutoSize = true; LnkUseSvgSettings.Dock = System.Windows.Forms.DockStyle.Top; LnkUseSvgSettings.Location = new System.Drawing.Point(80, 5); LnkUseSvgSettings.Margin = new System.Windows.Forms.Padding(5); LnkUseSvgSettings.Name = "LnkUseSvgSettings"; LnkUseSvgSettings.Size = new System.Drawing.Size(197, 25); LnkUseSvgSettings.TabIndex = 0; LnkUseSvgSettings.Text = "▼ Use size settings given in SVG ▼"; // // tableLayoutPanel2 // tableLayoutPanel2.AutoSize = true; tableLayoutPanel2.ColumnCount = 6; tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); tableLayoutPanel2.Controls.Add(LblViewport, 1, 0); tableLayoutPanel2.Controls.Add(TbViewboxH, 4, 2); tableLayoutPanel2.Controls.Add(label6, 3, 2); tableLayoutPanel2.Controls.Add(TbViewportW, 2, 0); tableLayoutPanel2.Controls.Add(label3, 3, 0); tableLayoutPanel2.Controls.Add(TbViewboxW, 2, 2); tableLayoutPanel2.Controls.Add(TbViewportH, 4, 0); tableLayoutPanel2.Controls.Add(LblViewboxWH, 1, 2); tableLayoutPanel2.Controls.Add(LblViewboxXY, 1, 1); tableLayoutPanel2.Controls.Add(TbViewboxY, 4, 1); tableLayoutPanel2.Controls.Add(label4, 3, 1); tableLayoutPanel2.Controls.Add(TbViewboxX, 2, 1); tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Top; tableLayoutPanel2.Location = new System.Drawing.Point(3, 19); tableLayoutPanel2.Name = "tableLayoutPanel2"; tableLayoutPanel2.RowCount = 3; tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); tableLayoutPanel2.Size = new System.Drawing.Size(358, 87); tableLayoutPanel2.TabIndex = 1; // // LblViewport // LblViewport.AutoSize = true; LblViewport.Dock = System.Windows.Forms.DockStyle.Fill; LblViewport.Location = new System.Drawing.Point(60, 0); LblViewport.Name = "LblViewport"; LblViewport.Size = new System.Drawing.Size(93, 29); LblViewport.TabIndex = 0; LblViewport.Text = "Viewport (W×H)"; LblViewport.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // TbViewboxH // TbViewboxH.Dock = System.Windows.Forms.DockStyle.Fill; TbViewboxH.Enabled = false; TbViewboxH.Location = new System.Drawing.Point(242, 61); TbViewboxH.MinimumSize = new System.Drawing.Size(50, 4); TbViewboxH.Name = "TbViewboxH"; TbViewboxH.Size = new System.Drawing.Size(56, 23); TbViewboxH.TabIndex = 5; TbViewboxH.TabStop = false; TbViewboxH.Text = "-"; TbViewboxH.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // label6 // label6.AutoSize = true; label6.Dock = System.Windows.Forms.DockStyle.Fill; label6.Location = new System.Drawing.Point(221, 58); label6.Name = "label6"; label6.Size = new System.Drawing.Size(15, 29); label6.TabIndex = 2; label6.Text = "×"; label6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // TbViewportW // TbViewportW.Dock = System.Windows.Forms.DockStyle.Fill; TbViewportW.Enabled = false; TbViewportW.Location = new System.Drawing.Point(159, 3); TbViewportW.MinimumSize = new System.Drawing.Size(50, 4); TbViewportW.Name = "TbViewportW"; TbViewportW.Size = new System.Drawing.Size(56, 23); TbViewportW.TabIndex = 0; TbViewportW.TabStop = false; TbViewportW.Text = "-"; TbViewportW.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // label3 // label3.AutoSize = true; label3.Dock = System.Windows.Forms.DockStyle.Fill; label3.Location = new System.Drawing.Point(221, 0); label3.Name = "label3"; label3.Size = new System.Drawing.Size(15, 29); label3.TabIndex = 4; label3.Text = "×"; label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // TbViewboxW // TbViewboxW.Dock = System.Windows.Forms.DockStyle.Fill; TbViewboxW.Enabled = false; TbViewboxW.Location = new System.Drawing.Point(159, 61); TbViewboxW.MinimumSize = new System.Drawing.Size(50, 4); TbViewboxW.Name = "TbViewboxW"; TbViewboxW.Size = new System.Drawing.Size(56, 23); TbViewboxW.TabIndex = 4; TbViewboxW.TabStop = false; TbViewboxW.Text = "-"; TbViewboxW.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // TbViewportH // TbViewportH.Dock = System.Windows.Forms.DockStyle.Fill; TbViewportH.Enabled = false; TbViewportH.Location = new System.Drawing.Point(242, 3); TbViewportH.MinimumSize = new System.Drawing.Size(50, 4); TbViewportH.Name = "TbViewportH"; TbViewportH.Size = new System.Drawing.Size(56, 23); TbViewportH.TabIndex = 1; TbViewportH.TabStop = false; TbViewportH.Text = "-"; TbViewportH.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // LblViewboxWH // LblViewboxWH.AutoSize = true; LblViewboxWH.Dock = System.Windows.Forms.DockStyle.Fill; LblViewboxWH.Location = new System.Drawing.Point(60, 58); LblViewboxWH.Name = "LblViewboxWH"; LblViewboxWH.Size = new System.Drawing.Size(93, 29); LblViewboxWH.TabIndex = 7; LblViewboxWH.Text = "ViewBox (W×H)"; LblViewboxWH.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // LblViewboxXY // LblViewboxXY.AutoSize = true; LblViewboxXY.Dock = System.Windows.Forms.DockStyle.Fill; LblViewboxXY.Location = new System.Drawing.Point(60, 29); LblViewboxXY.Name = "LblViewboxXY"; LblViewboxXY.Size = new System.Drawing.Size(93, 29); LblViewboxXY.TabIndex = 8; LblViewboxXY.Text = "ViewBox (X,Y)"; LblViewboxXY.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // TbViewboxY // TbViewboxY.Dock = System.Windows.Forms.DockStyle.Fill; TbViewboxY.Enabled = false; TbViewboxY.Location = new System.Drawing.Point(242, 32); TbViewboxY.MinimumSize = new System.Drawing.Size(50, 4); TbViewboxY.Name = "TbViewboxY"; TbViewboxY.Size = new System.Drawing.Size(56, 23); TbViewboxY.TabIndex = 3; TbViewboxY.TabStop = false; TbViewboxY.Text = "-"; TbViewboxY.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // label4 // label4.AutoSize = true; label4.Dock = System.Windows.Forms.DockStyle.Fill; label4.Location = new System.Drawing.Point(221, 29); label4.Name = "label4"; label4.Size = new System.Drawing.Size(15, 29); label4.TabIndex = 10; label4.Text = ","; label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // TbViewboxX // TbViewboxX.Dock = System.Windows.Forms.DockStyle.Fill; TbViewboxX.Enabled = false; TbViewboxX.Location = new System.Drawing.Point(159, 32); TbViewboxX.MinimumSize = new System.Drawing.Size(50, 4); TbViewboxX.Name = "TbViewboxX"; TbViewboxX.Size = new System.Drawing.Size(56, 23); TbViewboxX.TabIndex = 2; TbViewboxX.TabStop = false; TbViewboxX.Text = "-"; TbViewboxX.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // SvgImportDialog // AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; AutoSize = true; ClientSize = new System.Drawing.Size(384, 341); Controls.Add(RootPanel); Name = "SvgImportDialog"; ((System.ComponentModel.ISupportInitialize)PbWarning).EndInit(); RootPanel.ResumeLayout(false); RootPanel.PerformLayout(); StatusStrip.ResumeLayout(false); StatusStrip.PerformLayout(); tableLayoutPanel3.ResumeLayout(false); tableLayoutPanel3.PerformLayout(); GbLayers.ResumeLayout(false); GbLayers.PerformLayout(); tableLayoutPanel1.ResumeLayout(false); tableLayoutPanel1.PerformLayout(); GbSizeSelection.ResumeLayout(false); GbSizeSelection.PerformLayout(); tableLayoutPanel4.ResumeLayout(false); tableLayoutPanel4.PerformLayout(); ((System.ComponentModel.ISupportInitialize)NudCanvasW).EndInit(); ((System.ComponentModel.ISupportInitialize)NudCanvasH).EndInit(); ((System.ComponentModel.ISupportInitialize)NudDpi).EndInit(); GbInfo.ResumeLayout(false); GbInfo.PerformLayout(); tableLayoutPanel9.ResumeLayout(false); tableLayoutPanel9.PerformLayout(); tableLayoutPanel2.ResumeLayout(false); tableLayoutPanel2.PerformLayout(); ResumeLayout(false); PerformLayout(); } #endregion private System.Windows.Forms.ToolTip ToolTip1; private System.Windows.Forms.Panel RootPanel; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; private System.Windows.Forms.LinkLabel LnkGitHub; private System.Windows.Forms.LinkLabel LnkForum; private System.Windows.Forms.Button BtnOk; private System.Windows.Forms.Button BtnCancel; private System.Windows.Forms.GroupBox GbLayers; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.RadioButton RbFlatten; private System.Windows.Forms.CheckBox CbGroupBoundaries; private System.Windows.Forms.RadioButton RbGroups; private System.Windows.Forms.CheckBox CbHiddenLayers; private System.Windows.Forms.RadioButton RbAllElements; private System.Windows.Forms.CheckBox CbOpacity; private System.Windows.Forms.GroupBox GbSizeSelection; private System.Windows.Forms.CheckBox CbKeepAR; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; private System.Windows.Forms.Label LblResolution; private System.Windows.Forms.Label LblCanvasWH; private System.Windows.Forms.PictureBox PbWarning; private System.Windows.Forms.NumericUpDown NudCanvasW; private System.Windows.Forms.NumericUpDown NudCanvasH; private System.Windows.Forms.Label label9; private System.Windows.Forms.NumericUpDown NudDpi; private System.Windows.Forms.GroupBox GbInfo; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; private System.Windows.Forms.Label LblViewport; private System.Windows.Forms.TextBox TbViewboxH; private System.Windows.Forms.Label label6; private System.Windows.Forms.TextBox TbViewportW; private System.Windows.Forms.Label label3; private System.Windows.Forms.TextBox TbViewboxW; private System.Windows.Forms.TextBox TbViewportH; private System.Windows.Forms.Label LblViewboxWH; private System.Windows.Forms.Label LblViewboxXY; private System.Windows.Forms.TextBox TbViewboxY; private System.Windows.Forms.Label label4; private System.Windows.Forms.TextBox TbViewboxX; private System.Windows.Forms.StatusStrip StatusStrip; private System.Windows.Forms.ToolStripStatusLabel ProgressLabel; private System.Windows.Forms.ToolStripProgressBar ProgressBar; private System.Windows.Forms.ToolStripStatusLabel PlaceHolderLabel; private System.Windows.Forms.ToolStripStatusLabel UpdateAvailLabel; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel9; private System.Windows.Forms.Button LnkUseSvgSettings; } ================================================ FILE: SvgFileType/Import/SvgImportDialog.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using PaintDotNet; using Svg; using SvgFileTypePlugin.Extensions; using SR = SvgFileTypePlugin.Localization.StringResources; namespace SvgFileTypePlugin.Import; internal partial class SvgImportDialog : MyBaseForm { private const string NotAvailable = "-"; private const int CanvasSizeWarningThreshold = 1280; private const int DefaultFallbackSize = 512; private object? lastModifiedNud; private bool dontUpdate; private bool isRunning; private readonly CancellationTokenSource cts; private readonly Size docSize; private readonly int docPpi; private readonly string svgdata; private Size rasterSize; private readonly Rectangle docViewbox; private readonly string renderer; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Document? Result { get; private set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Exception? Error { get; private set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override bool EnableImmersiveDarkMode => true; public SvgImportDialog(string svgdata, string renderer = "resvg", CancellationToken ctoken = default) : base(UIHelper.GetMainForm()) { ArgumentNullException.ThrowIfNull(svgdata); ArgumentException.ThrowIfNullOrWhiteSpace(renderer); InitializeComponent(); SetUseAppThemeColors(); this.renderer = renderer; this.svgdata = svgdata; cts = CancellationTokenSource.CreateLinkedTokenSource(ctoken); SvgDocument svg = SvgDocument.FromSvg(svgdata); docSize = svg.GetDimensions().ToSize(); docViewbox = Rectangle.Truncate(svg.ViewBox); docPpi = svg.Ppi; PopulateContols(); HookEvents(); } protected override void OnLoad(EventArgs e) { ClientSize = RootPanel.Size; base.OnLoad(e); } protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); if (!isRunning) { Error = new WarningException(SR.CanceledUponYourRequest); } if (!cts.IsCancellationRequested) { cts.Cancel(); } } protected override void OnClosed(EventArgs e) { base.OnClosed(e); cts.Dispose(); } #region Private Methods private void PopulateContols() { Text = $"{SvgFileTypePluginSupportInfo.Instance.DisplayName} v{SvgFileTypePluginSupportInfo.Instance.Version}"; PbWarning.Image = SystemIcons.Warning.ToBitmap(); GbInfo.Text = SR.SizeSettingsGivenInSvgFile; LnkUseSvgSettings.Text = SR.UseSizeSettingsGivenInSvg.SplitIntoLines(maximumLineLength: 60); GbSizeSelection.Text = SR.SizeSelectionByUser; LblResolution.Text = SR.Resolution; LblCanvasWH.Text = SR.Canvas; LblViewport.Text = SR.Viewport; LblViewboxWH.Text = SR.ViewBoxWH; LblViewboxXY.Text = SR.ViewBoxXY; CbKeepAR.Text = SR.KeepAspectRatio; GbLayers.Text = SR.Layers; RbFlatten.Text = SR.Flatten; RbGroups.Text = SR.Groups; RbAllElements.Text = SR.AllElements; CbOpacity.Text = SR.OpacityAsLayerProperty.SplitIntoLines(maximumLineLength: 36); CbHiddenLayers.Text = SR.ImportHiddenElements.SplitIntoLines(maximumLineLength: 36); CbGroupBoundaries.Text = SR.GroupBoundaries.SplitIntoLines(maximumLineLength: 36); BtnOk.Text = SR.OK; BtnCancel.Text = SR.Cancel; ProgressLabel.Text = SR.Ready; ToolTip1.SetToolTip(PbWarning, SR.MemoryWarningText); TbViewportW.Text = docSize.Width > 0 ? docSize.Width.ToString() : NotAvailable; TbViewportH.Text = docSize.Height > 0 ? docSize.Height.ToString() : NotAvailable; if (!docViewbox.IsEmpty) { TbViewboxX.Text = docViewbox.X.ToString(); TbViewboxY.Text = docViewbox.Y.ToString(); TbViewboxW.Text = docViewbox.Width.ToString(); TbViewboxH.Text = docViewbox.Height.ToString(); } else { TbViewboxX.Text = NotAvailable; TbViewboxY.Text = NotAvailable; TbViewboxW.Text = NotAvailable; TbViewboxH.Text = NotAvailable; } rasterSize = docSize.Width == 0 || docSize.Height == 0 ? new Size(DefaultFallbackSize, DefaultFallbackSize) : docSize; if (rasterSize.Width * rasterSize.Height > CanvasSizeWarningThreshold * CanvasSizeWarningThreshold) { double ratio = rasterSize.Width / (double)rasterSize.Height; if (rasterSize.Width > rasterSize.Height) { NudCanvasW.Value = CanvasSizeWarningThreshold; NudCanvasH.Value = (int)Math.Round(CanvasSizeWarningThreshold / ratio); } else { NudCanvasH.Value = CanvasSizeWarningThreshold; NudCanvasW.Value = (int)Math.Round(CanvasSizeWarningThreshold * ratio); } } else { NudCanvasW.Value = rasterSize.Width; NudCanvasH.Value = rasterSize.Height; } UpdateLayersPanel(); } private void HookEvents() { RbAllElements.CheckedChanged += Rb_CheckedChanged; RbFlatten.CheckedChanged += Rb_CheckedChanged; RbGroups.CheckedChanged += Rb_CheckedChanged; NudCanvasW.Accelerations.Add(new NumericUpDownAcceleration(3, 10)); NudCanvasW.KeyUp += NudCanvas_KeyUp; NudCanvasH.Accelerations.Add(new NumericUpDownAcceleration(3, 10)); NudCanvasH.KeyUp += NudCanvas_KeyUp; NudCanvasW.LostFocus += NudCanvas_LostFocus; NudCanvasH.LostFocus += NudCanvas_LostFocus; NudCanvasW.ValueChanged += NudCanvas_ValueChanged; NudCanvasH.ValueChanged += NudCanvas_ValueChanged; CbKeepAR.CheckedChanged += CbKeepAR_CheckedChanged; LnkUseSvgSettings.Click += LnkUseSvgSettings_Click; LnkGitHub.LinkClicked += LnkGitHub_LinkClicked; LnkForum.LinkClicked += LnkForum_LinkClicked; BtnOk.Click += BtnOk_Click; BtnCancel.Click += BtnCancel_Click; CancelButton = BtnCancel; } private void UpdateLayersPanel() { CbOpacity.Enabled = CbHiddenLayers.Enabled = CbGroupBoundaries.Enabled = !RbFlatten.Checked; CbGroupBoundaries.Enabled = RbAllElements.Checked; ShowCanvasSizeWarningIfNeeded(); } private void ShowCanvasSizeWarningIfNeeded() { PbWarning.Visible = !RbFlatten.Checked && NudCanvasH.Value * NudCanvasW.Value > CanvasSizeWarningThreshold * CanvasSizeWarningThreshold; } private void UpdateTheOtherNud() { lastModifiedNud ??= NudCanvasW.Value > NudCanvasH.Value ? NudCanvasW : NudCanvasH; if (ReferenceEquals(lastModifiedNud, NudCanvasW)) { decimal newHeight = CbKeepAR.Checked ? NudCanvasW.Value * rasterSize.Height / rasterSize.Width : NudCanvasH.Value; newHeight = Math.Clamp(newHeight, 1, Math.Min(NudCanvasH.Maximum, int.MaxValue / (NudCanvasW.Value * 4))); NudCanvasH.Value = newHeight; } else { decimal newWidth = CbKeepAR.Checked ? NudCanvasH.Value * rasterSize.Width / rasterSize.Height : NudCanvasW.Value; newWidth = Math.Clamp(newWidth, 1, Math.Min(NudCanvasW.Maximum, int.MaxValue / (NudCanvasH.Value * 4))); NudCanvasW.Value = newWidth; } ShowCanvasSizeWarningIfNeeded(); } private SvgImportConfig GetSvgImportConfig() { SvgImportConfig config = new SvgImportConfig { RasterWidth = (int)NudCanvasW.Value, RasterHeight = (int)NudCanvasH.Value, PreserveAspectRatio = CbKeepAR.Checked, Ppi = (int)NudDpi.Value, GroupBoundariesAsLayers = CbGroupBoundaries.Checked, ImportHiddenElements = CbHiddenLayers.Checked, RespectElementOpacity = CbOpacity.Checked, LayersMode = RbAllElements.Checked ? LayersMode.All : RbFlatten.Checked ? LayersMode.Flat : LayersMode.Groups }; return config; } private void DisableControlsExceptCancelButton() { this.Descendants().OfType().ToList().ForEach(groupbox => groupbox.Enabled = false); BtnOk.Enabled = false; ProgressBar.Visible = true; } #endregion #region Events private void NudCanvas_ValueChanged(object? sender, EventArgs e) { if (dontUpdate) { return; } lastModifiedNud = sender; UpdateTheOtherNud(); } private void CbKeepAR_CheckedChanged(object? sender, EventArgs e) { if (dontUpdate) { return; } lastModifiedNud = null; UpdateTheOtherNud(); } private void NudCanvas_KeyUp(object? sender, KeyEventArgs e) { if (dontUpdate) { return; } lastModifiedNud = sender; if (e.KeyValue >= '0' || e.KeyValue <= '9' || e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back) { // Handle key events like digits, delete and backspace UpdateTheOtherNud(); } else { // We ignore any other key event on these controls e.SuppressKeyPress = true; e.Handled = true; } } private void NudCanvas_LostFocus(object? sender, EventArgs e) { if (sender is not NumericUpDown nud) { return; } TextBox? textbox = nud.Controls.OfType().FirstOrDefault(); if (textbox is not null) { textbox.Text = Math.Round(nud.Value, nud.DecimalPlaces).ToString(); } } private void Rb_CheckedChanged(object? sender, EventArgs e) { UpdateLayersPanel(); } private void LnkUseSvgSettings_Click(object? sender, EventArgs e) { // Restore the original sizes and show the size warning if needed dontUpdate = true; NudCanvasW.Value = rasterSize.Width; NudCanvasH.Value = rasterSize.Height; dontUpdate = false; NudDpi.Value = docPpi; ShowCanvasSizeWarningIfNeeded(); } private void LnkGitHub_LinkClicked(object? sender, LinkLabelLinkClickedEventArgs e) { if (e.Button == MouseButtons.Left) { LaunchUrl(SvgFileTypePluginSupportInfo.Instance.WebsiteUri); } } private void LnkForum_LinkClicked(object? sender, LinkLabelLinkClickedEventArgs e) { if (e.Button == MouseButtons.Left) { LaunchUrl(SvgFileTypePluginSupportInfo.Instance.ForumUri); } } private async void BtnOk_Click(object? sender, EventArgs e) { isRunning = true; try { DisableControlsExceptCancelButton(); CancellationToken ctoken = cts.Token; SvgImportConfig config = GetSvgImportConfig(); SvgRenderer2 svgRenderer = SvgRenderer2.Create(renderer); svgRenderer.ProgressChanged += OnProgressChanged; Result = await Task.Run(() => svgRenderer.Rasterize(svgdata, config, ctoken), ctoken).ConfigureAwait(false); } catch (Exception ex) when (ex is OperationCanceledException) { Error = new WarningException(SR.CanceledUponYourRequest, ex); } catch (Exception ex) { Error = ex; } finally { DialogResult = Result is null ? DialogResult.Cancel : DialogResult.OK; } } private void OnProgressChanged(object? sender, ProgressChangedEventArgs e) { if (IsDisposed) { return; } try { if (InvokeRequired) { Invoke(OnProgressChanged, sender, e); return; } int total = (int)e.UserState!; if (e.ProgressPercentage == 0) { if (total == 1) { ProgressBar.Style = ProgressBarStyle.Marquee; ProgressLabel.Text = SR.Working; } else { ProgressBar.Style = ProgressBarStyle.Blocks; ProgressBar.Maximum = total; ProgressLabel.Text = SR.Ready; } } if (total > 1) { ProgressBar.Value = e.ProgressPercentage; float percentage = e.ProgressPercentage / (float)total; ProgressLabel.Text = percentage.ToString("P2"); } } catch (ObjectDisposedException) { // ignore } } private void BtnCancel_Click(object? sender, EventArgs e) { BtnCancel.Enabled = false; try { cts.Cancel(); } catch (ObjectDisposedException) { // This should never happen } finally { Error = new WarningException(SR.CanceledUponYourRequest); } } #endregion #region Static private static void LaunchUrl(Uri uri) { Process.Start("explorer", $@"""{uri}"""); } #endregion } ================================================ FILE: SvgFileType/Import/SvgImportDialog.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 True True True 115, 17 True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True True ================================================ FILE: SvgFileType/Import/SvgRenderer2.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Frozen; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Linq; using System.Runtime; using System.Threading; using PaintDotNet; using Svg; using SvgFileTypePlugin.Extensions; using SR = SvgFileTypePlugin.Localization.StringResources; namespace SvgFileTypePlugin.Import; internal abstract class SvgRenderer2(string name) { private static readonly FrozenSet TitleAttributes = ["label", "title", "inkscape:label"]; private int step, total; public string Name { get; } = name; public event ProgressChangedEventHandler? ProgressChanged; #region Public public Document Rasterize(string svgdata, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentNullException.ThrowIfNull(config); Logger.WriteLine($"Using '{Name}' renderer with '{config.LayersMode}' layers mode."); return config.LayersMode == LayersMode.Flat ? GetFlatDocument(svgdata, config, ctoken) : GetLayeredDocument(svgdata, config, ctoken); } #endregion #region Public Static public static SvgRenderer2 Create(string name) { ArgumentNullException.ThrowIfNull(name); return name.ToLowerInvariant() switch { "resvg" => new ResvgSvgRenderer(), "gdip" or "gdiplus" or "gdi+" => new GdipSvgRenderer(), "direct2d" or "d2d" => new Direct2DSvgRenderer(), _ => throw new ArgumentOutOfRangeException(nameof(name), $"Unknown SVG renderer: {name}") }; } #endregion #region Protected protected abstract Document GetFlatDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default); protected abstract Document GetLayeredDocument(string svgdata, SvgImportConfig config, CancellationToken ctoken = default); public virtual Document GetNoPathDocument() { Graphics g; Size size; using Font font = new Font("Arial", 24, FontStyle.Bold); using (g = Graphics.FromHwnd(nint.Zero)) { size = g.MeasureString(SR.NoPath, font).ToSize(); } using Bitmap bmp = new Bitmap(size.Width, size.Height, PixelFormat.Format24bppRgb); g = Graphics.FromImage(bmp); using var _ = g; g.Clear(Color.LightGray); Rectangle layoutRectangle = new Rectangle(Point.Empty, bmp.Size); using StringFormat format = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }; g.DrawString(SR.NoPath, font, Brushes.Black, layoutRectangle, format); return Document.FromImage(bmp); } protected void IncrementProgress() { if (ProgressChanged is null) { return; } int value = Interlocked.Increment(ref step); value = Math.Clamp(value, 0, total); ProgressChangedEventArgs args = new ProgressChangedEventArgs(value, total); ProgressChanged.Invoke(this, args); } /// protected void ResetProgress(int total) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(total); if (ProgressChanged is null) { return; } this.total = total; int value = Interlocked.Exchange(ref step, 0); ProgressChangedEventArgs args = new ProgressChangedEventArgs(value, total); ProgressChanged.Invoke(this, args); } #endregion #region Protected Static /// protected static List GetSvgVisualElements(SvgDocument svg, SvgImportConfig config, CancellationToken ctoken = default) { ArgumentNullException.ThrowIfNull(svg); ArgumentNullException.ThrowIfNull(config); IEnumerable elements = GetPreparedElements(svg.Children, ctoken); if (config.LayersMode == LayersMode.Groups) { elements = FilterByGroups(elements, ctoken); } else { elements = FilterByNonGroups(elements); } if (config.LayersMode == LayersMode.Flat || !config.GroupBoundariesAsLayers) { elements = elements.NotOfType(); } if (config.LayersMode != LayersMode.Flat) { // Skip group boundaries for hidden layers. elements = elements.Where(e => config.ImportHiddenElements || (e is GroupBoundary groupBoundary ? groupBoundary.IsOriginallyVisible : e.IsOriginallyVisible())); } return elements.ToList(); } /// protected static string GetLayerTitle(SvgElement element, bool prependElementName = true) { ArgumentNullException.ThrowIfNull(element); string elementName = element.GetName(); string? layerName = null; if (element.ID is not null) { layerName = element.ID; } if (string.IsNullOrEmpty(layerName) && element.CustomAttributes != null) { // get custom title attributes. foreach (string attr in TitleAttributes) { if (element.CustomAttributes.TryGetValue(attr, out string? title) && !string.IsNullOrEmpty(title)) { // found a title candidate layerName = title; break; } } } if (string.IsNullOrEmpty(layerName) && element.Children != null) { // Get child title tag SvgTitle? title = element.Children.OfType().FirstOrDefault(); if (!string.IsNullOrEmpty(title?.Content)) { layerName = title.Content; } } if (string.IsNullOrEmpty(layerName)) { // Generate more meanfull name for a svg use node. Add reference element name in a case if it's local document. if (element is SvgUse useElement and { ReferencedElement.OriginalString.Length: > 1 }) { string str = useElement.ReferencedElement.OriginalString.Trim(); if (str.StartsWith('#')) { layerName = str[1..]; } prependElementName = true; } // Generate more meanfull name for a svg text. else if (element is SvgTextBase text and { Text.Length: > 0}) { layerName = text.Text.Truncate(maxLength: 32); } // Generate more meanfull name for a svg path. else if (element is SvgPath path and { PathData.Count: > 0 }) { layerName = path.PathData.ToString().Truncate(maxLength: 32); } } return layerName is null ? elementName : prependElementName ? $"{elementName}: {layerName}" : layerName; } /// /// Generates a Paint.NET document with given layers and configuration. /// /// /// /// /// /// protected static Document GetDocument(List layers, SvgImportConfig config) { ArgumentNullException.ThrowIfNull(layers); ArgumentNullException.ThrowIfNull(config); if (layers.Count == 0) { throw new ArgumentException("There aren't any layers in the collection.", nameof(layers)); } Debug.Assert(layers[0].Width == config.RasterWidth); Debug.Assert(layers[0].Height == config.RasterHeight); Document document = new Document(config.RasterWidth, config.RasterHeight); try { layers.ForEach(document.Layers.Add); document.SetDpu(config.Ppi); return document; } catch (Exception) { document.Dispose(); layers.ForEach(layer => layer.Dispose()); throw; } } /// protected static void RenderSvgUseElement(SvgUse useElement, Action renderAction) { ArgumentNullException.ThrowIfNull(useElement); ArgumentNullException.ThrowIfNull(renderAction); if (useElement.GetCopyOfReferencedElement() is not SvgElement referencedElement) { return; } referencedElement.Visibility = "visible"; useElement.Visibility = "hidden"; SvgElementCollection children = useElement.Parent.Children; children.AddAndForceUniqueID(referencedElement); try { renderAction(referencedElement); } finally { children.Remove(referencedElement); } } /// protected static byte ToByteOpacity(float opacity) { ArgumentOutOfRangeException.ThrowIfNegative(opacity); ArgumentOutOfRangeException.ThrowIfGreaterThan(opacity, 1f); int value = (int)MathF.Round(opacity * 255f); value = Math.Clamp(value, byte.MinValue, byte.MaxValue); return (byte)value; } /// /// /// protected static MemoryFailPoint GetMemoryFailPoint(int width, int height, int count = 1) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(width); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(height); ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count); int sizeInMegabytes = checked((int)(height * 4L * width * count / (1024 * 1024))); sizeInMegabytes = Math.Max(sizeInMegabytes, 1); return new MemoryFailPoint(sizeInMegabytes); } #endregion #region Private Static private static IEnumerable GetPreparedElements(IEnumerable elements, CancellationToken ctoken = default) { return GetPreparedElements(elements, groupName: null, ctoken); } private static IEnumerable GetPreparedElements(IEnumerable elements, string? groupName, CancellationToken ctoken = default) { // Prepare a collection of elements that about to be rendered. // Don't prepare for a separate parsing def lists. foreach (SvgVisualElement visual in elements.OfType()) { ctoken.ThrowIfCancellationRequested(); // Fix problem that SVG visual element lib style "display:none" is not recognized as visible state. if (visual.Visible && !visual.IsDisplayable()) { visual.Visibility = "hidden"; visual.Display = string.Empty; // null throws exception } // Store visibility visual.StoreOriginalVisibility(); // Save current group to indicate that elements inside a group. if (!string.IsNullOrEmpty(groupName)) { visual.SetGroupName(groupName); // Store group info } SvgGroup? group = visual as SvgGroup; if (group is not null) { groupName = GetLayerTitle(group, prependElementName: false); // Return fake node to indicate group end. (order is reversed) yield return new GroupBoundary(group, groupName, isStart: false); } if (GetPreparedElements(visual.Children, groupName, ctoken) is IEnumerable preparedElements) { foreach (SvgVisualElement element in preparedElements) { yield return element; } } if (group is not null) { Debug.Assert(groupName is not null); // Return fake node to indicate group start. yield return new GroupBoundary(group, groupName, isStart: true); } // Skip text with empty content. But keep all children nodes. if (visual is SvgTextBase textNode and { Text.Length: > 0 }) { continue; } yield return visual; } } private static IEnumerable FilterByNonGroups(IEnumerable elements) { return elements.NotOfType(); } private static IEnumerable FilterByGroups(IEnumerable elements, CancellationToken ctoken = default) { HashSet groups = []; foreach (SvgVisualElement element in elements.NotOfType()) { ctoken.ThrowIfCancellationRequested(); SvgVisualElement? visual = null; if (element.GetGroupName() is not null) { // Get only root level for (SvgElement parent = element; parent is not null; parent = parent.Parent) { // TODO: render more groups. In most cases svg has only few root groups. if (parent is SvgGroup group) { visual = group; } } } visual ??= element; if (groups.Add(visual)) { yield return visual; } } } #endregion #region GroupBoundary /* a private type to determine boundaries of a group. */ protected sealed class GroupBoundary(SvgGroup group, string name, bool isStart) : SvgVisualElement { private readonly SvgGroup group = group; public override bool Visible => group.Visible; public override float Opacity => group.Opacity; public bool IsOriginallyVisible => group.IsOriginallyVisible(); public string Name { get; } = string.Format(isStart ? SR.LayerGroup : SR.EndLayerGroup, name); public override RectangleF Bounds => throw new NotImplementedException(); public override SvgElement DeepCopy() { throw new NotImplementedException(); } public override GraphicsPath Path(ISvgRenderer renderer) { throw new NotImplementedException(); } public BitmapLayer ToEmptyLayer(int width, int height) { // Render empty group boundary and continue BitmapLayer layer = new BitmapLayer(width, height) { Name = Name, // Store related group opacity and visibility. Opacity = ToByteOpacity(Opacity), Visible = Visible }; return layer; } } #endregion } ================================================ FILE: SvgFileType/Localization/InjectResourceManager.ps1 ================================================ (Get-Content 'Localization\StringResources.Designer.cs').replace('new global::System.Resources.ResourceManager', 'new SingleAssemblyResourceManager') | Set-Content 'Localization\StringResources.Designer.cs' -Encoding utf8 ================================================ FILE: SvgFileType/Localization/Localize.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Text.RegularExpressions; using SR = SvgFileTypePlugin.Localization.StringResources; namespace SvgFileTypePlugin.Localization; internal static partial class Localize { public static string GetDisplayName(TEnum @enum) where TEnum : struct, Enum { string name = Enum.GetName(@enum) ?? throw new ArgumentException("Unknown enum constant.", nameof(@enum)); string key = string.Concat(typeof(TEnum).Name, name); return SR.ResourceManager.GetString(key) ?? SplitCamelCase(name); } private static string SplitCamelCase(string str) { return CamelCaseRegex().Replace(str, " $1"); } [GeneratedRegex("(\\B[A-Z])")] private static partial Regex CamelCaseRegex(); } ================================================ FILE: SvgFileType/Localization/SingleAssemblyResourceManager.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Collections.Concurrent; using System.Globalization; using System.IO; using System.Reflection; using System.Resources; using System.Text; namespace SvgFileTypePlugin.Localization; internal sealed class SingleAssemblyResourceManager : ResourceManager { private readonly ConcurrentDictionary> resourceSets = new(); private const string DefaultCultureName = "en-US"; public SingleAssemblyResourceManager() : base() { } public SingleAssemblyResourceManager(Type resourceSource) : base(resourceSource) { } public SingleAssemblyResourceManager(string baseName, Assembly assembly) : base(baseName, assembly) { } public SingleAssemblyResourceManager(string baseName, Assembly assembly, Type usingResourceSet) : base(baseName, assembly, usingResourceSet) { } protected override ResourceSet? InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) { /* If you call GetOrAdd simultaneously on different threads, addValueFactory may be called * multiple times, but its key/value pair might not be added to the dictionary for every call.*/ return resourceSets.GetOrAdd(culture, c => new Lazy(() => CustomGetResourceSet(c, createIfNotExists, tryParents))).Value; } private ResourceSet? CustomGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) { string filename = GetResourceFileName(culture); using Stream? stream = MainAssembly?.GetManifestResourceStream(filename); Logger.WriteLineIf(stream != null, $"Translations loaded for '{culture}'."); ResourceSet? resourceSet = stream is null ? base.InternalGetResourceSet(culture, createIfNotExists, tryParents) : new ResourceSet(stream); return resourceSet; } public override void ReleaseAllResources() { base.ReleaseAllResources(); foreach (Lazy resourceSet in resourceSets.Values) { if (resourceSet.IsValueCreated && resourceSet.Value is not null) { resourceSet.Value.Close(); resourceSet.Value.Dispose(); } } resourceSets.Clear(); } protected override string GetResourceFileName(CultureInfo culture) { StringBuilder sb = new StringBuilder(BaseName, capacity: 255); // If this is the neutral culture, don't append culture name. if (culture.Name != CultureInfo.InvariantCulture.Name && culture.Name != DefaultCultureName) { VerifyCultureName(culture.Name, throwException: true); sb.Append('_'); sb.Append(culture.Name); } sb.Append(".resources"); return sb.ToString(); } private static bool VerifyCultureName(string cultureName, bool throwException) { // This function is used by ResourceManager.GetResourceFileName(). // ResourceManager searches for resource using CultureInfo.Name, // so we should check against CultureInfo.Name. for (int i = 0; i < cultureName.Length; i++) { char c = cultureName[i]; // TODO: Names can only be RFC4646 names (ie: a-zA-Z0-9) while this allows any unicode letter/digit if (char.IsLetterOrDigit(c) || c == '-' || c == '_') { continue; } if (throwException) { throw new ArgumentException($"The given culture name '{cultureName}' cannot be used to locate a resource file. " + "Resource filenames must consist of only letters, numbers, hyphens or underscores."); } return false; } return true; } } ================================================ FILE: SvgFileType/Localization/StringResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace SvgFileTypePlugin.Localization { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class StringResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal StringResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new SingleAssemblyResourceManager("SvgFileTypePlugin.Localization.StringResources", typeof(StringResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to All Elements. /// internal static string AllElements { get { return ResourceManager.GetString("AllElements", resourceCulture); } } /// /// Looks up a localized string similar to Angle. /// internal static string Angle { get { return ResourceManager.GetString("Angle", resourceCulture); } } /// /// Looks up a localized string similar to An update is available!. /// internal static string AnUpdateIsAvailable { get { return ResourceManager.GetString("AnUpdateIsAvailable", resourceCulture); } } /// /// Looks up a localized string similar to Brightness Cutoff. /// internal static string BrightnessCutoff { get { return ResourceManager.GetString("BrightnessCutoff", resourceCulture); } } /// /// Looks up a localized string similar to Detects areas that are darker than the threshold value and creates a path enclosing them.. /// internal static string BrightnessCutoffDesc { get { return ResourceManager.GetString("BrightnessCutoffDesc", resourceCulture); } } /// /// Looks up a localized string similar to Cancel. /// internal static string Cancel { get { return ResourceManager.GetString("Cancel", resourceCulture); } } /// /// Looks up a localized string similar to Canceled upon your request.. /// internal static string CanceledUponYourRequest { get { return ResourceManager.GetString("CanceledUponYourRequest", resourceCulture); } } /// /// Looks up a localized string similar to Canvas (W×H). /// internal static string Canvas { get { return ResourceManager.GetString("Canvas", resourceCulture); } } /// /// Looks up a localized string similar to Canvas is too big.. /// internal static string CanvasIsTooBig { get { return ResourceManager.GetString("CanvasIsTooBig", resourceCulture); } } /// /// Looks up a localized string similar to Color. /// internal static string Color { get { return ResourceManager.GetString("Color", resourceCulture); } } /// /// Looks up a localized string similar to Discussion. /// internal static string DiscussionLink { get { return ResourceManager.GetString("DiscussionLink", resourceCulture); } } /// /// Looks up a localized string similar to Edge Detection Mode. /// internal static string EdgeDetectionMode { get { return ResourceManager.GetString("EdgeDetectionMode", resourceCulture); } } /// /// Looks up a localized string similar to Enclose. /// internal static string Enclose { get { return ResourceManager.GetString("Enclose", resourceCulture); } } /// /// Looks up a localized string similar to End Layer Group: {0}. /// internal static string EndLayerGroup { get { return ResourceManager.GetString("EndLayerGroup", resourceCulture); } } /// /// Looks up a localized string similar to Export as Shape. /// internal static string ExportAsPdnShape { get { return ResourceManager.GetString("ExportAsPdnShape", resourceCulture); } } /// /// Looks up a localized string similar to Fill Color. /// internal static string FillColor { get { return ResourceManager.GetString("FillColor", resourceCulture); } } /// /// Looks up a localized string similar to Flatten. /// internal static string Flatten { get { return ResourceManager.GetString("Flatten", resourceCulture); } } /// /// Looks up a localized string similar to GitHub. /// internal static string GitHubLink { get { return ResourceManager.GetString("GitHubLink", resourceCulture); } } /// /// Looks up a localized string similar to Group boundaries as empty layers. /// internal static string GroupBoundaries { get { return ResourceManager.GetString("GroupBoundaries", resourceCulture); } } /// /// Looks up a localized string similar to Groups. /// internal static string Groups { get { return ResourceManager.GetString("Groups", resourceCulture); } } /// /// Looks up a localized string similar to Highpass Filter Radius. /// internal static string HighpassFilterRadius { get { return ResourceManager.GetString("HighpassFilterRadius", resourceCulture); } } /// /// Looks up a localized string similar to Suppresses large-scale irregularities such as background variations, while preserving small-scale detail such as lines.. /// internal static string HighpassFilterRadiusDesc { get { return ResourceManager.GetString("HighpassFilterRadiusDesc", resourceCulture); } } /// /// Looks up a localized string similar to Hidden elements. /// internal static string ImportHiddenElements { get { return ResourceManager.GetString("ImportHiddenElements", resourceCulture); } } /// /// Looks up a localized string similar to Invert. /// internal static string Invert { get { return ResourceManager.GetString("Invert", resourceCulture); } } /// /// Looks up a localized string similar to Keep Aspect Ratio. /// internal static string KeepAspectRatio { get { return ResourceManager.GetString("KeepAspectRatio", resourceCulture); } } /// /// Looks up a localized string similar to Layer Group: {0}. /// internal static string LayerGroup { get { return ResourceManager.GetString("LayerGroup", resourceCulture); } } /// /// Looks up a localized string similar to Layers. /// internal static string Layers { get { return ResourceManager.GetString("Layers", resourceCulture); } } /// /// Looks up a localized string similar to Lowpass Filter Radius. /// internal static string LowpassFilterRadius { get { return ResourceManager.GetString("LowpassFilterRadius", resourceCulture); } } /// /// Looks up a localized string similar to Smooths foreground details.. /// internal static string LowpassFilterRadiusDesc { get { return ResourceManager.GetString("LowpassFilterRadiusDesc", resourceCulture); } } /// /// Looks up a localized string similar to Make sure you've enough memory!. /// internal static string MemoryWarningText { get { return ResourceManager.GetString("MemoryWarningText", resourceCulture); } } /// /// Looks up a localized string similar to NO PATH. /// internal static string NoPath { get { return ResourceManager.GetString("NoPath", resourceCulture); } } /// /// Looks up a localized string similar to OK. /// internal static string OK { get { return ResourceManager.GetString("OK", resourceCulture); } } /// /// Looks up a localized string similar to Element opacity as layer property. /// internal static string OpacityAsLayerProperty { get { return ResourceManager.GetString("OpacityAsLayerProperty", resourceCulture); } } /// /// Looks up a localized string similar to Optimize. /// internal static string Optimize { get { return ResourceManager.GetString("Optimize", resourceCulture); } } /// /// Looks up a localized string similar to Try to optimize paths by joining adjacent Bezier curve segments.. /// internal static string OptimizeDesc { get { return ResourceManager.GetString("OptimizeDesc", resourceCulture); } } /// /// Looks up a localized string similar to Paint.NET Shape Export Options. /// internal static string PdnShapeExportOptions { get { return ResourceManager.GetString("PdnShapeExportOptions", resourceCulture); } } /// /// Looks up a localized string similar to * Enter a display name for the shape. /// internal static string PdnShapeExportOptionsDesc { get { return ResourceManager.GetString("PdnShapeExportOptionsDesc", resourceCulture); } } /// /// Looks up a localized string similar to Plugin Version: {0}. /// internal static string PluginVersion { get { return ResourceManager.GetString("PluginVersion", resourceCulture); } } /// /// Looks up a localized string similar to Preview Mode. /// internal static string PreviewMode { get { return ResourceManager.GetString("PreviewMode", resourceCulture); } } /// /// Looks up a localized string similar to Fast. /// internal static string PreviewModeFast { get { return ResourceManager.GetString("PreviewModeFast", resourceCulture); } } /// /// Looks up a localized string similar to Slow. /// internal static string PreviewModeSlow { get { return ResourceManager.GetString("PreviewModeSlow", resourceCulture); } } /// /// Looks up a localized string similar to Ready!. /// internal static string Ready { get { return ResourceManager.GetString("Ready", resourceCulture); } } /// /// Looks up a localized string similar to Resolution (DPI). /// internal static string Resolution { get { return ResourceManager.GetString("Resolution", resourceCulture); } } /// /// Looks up a localized string similar to Scale. /// internal static string Scale { get { return ResourceManager.GetString("Scale", resourceCulture); } } /// /// Looks up a localized string similar to Scale before threshold. /// internal static string ScaleBeforeThreshold { get { return ResourceManager.GetString("ScaleBeforeThreshold", resourceCulture); } } /// /// Looks up a localized string similar to Scan Mode. /// internal static string ScanMode { get { return ResourceManager.GetString("ScanMode", resourceCulture); } } /// /// Looks up a localized string similar to Opaque. /// internal static string ScanModeOpaque { get { return ResourceManager.GetString("ScanModeOpaque", resourceCulture); } } /// /// Looks up a localized string similar to Transparent. /// internal static string ScanModeTransparent { get { return ResourceManager.GetString("ScanModeTransparent", resourceCulture); } } /// /// Looks up a localized string similar to Shape. /// internal static string Shape { get { return ResourceManager.GetString("Shape", resourceCulture); } } /// /// Looks up a localized string similar to The image tracing result is saved as Paint.NET Shape. ///----- ///{0} ///-----. /// internal static string ShapeSaved { get { return ResourceManager.GetString("ShapeSaved", resourceCulture); } } /// /// Looks up a localized string similar to Success. /// internal static string ShapeSavedCaption { get { return ResourceManager.GetString("ShapeSavedCaption", resourceCulture); } } /// /// Looks up a localized string similar to You must restart Paint.NET to use it.. /// internal static string ShapeSavedRestart { get { return ResourceManager.GetString("ShapeSavedRestart", resourceCulture); } } /// /// Looks up a localized string similar to Size selection by user. /// internal static string SizeSelectionByUser { get { return ResourceManager.GetString("SizeSelectionByUser", resourceCulture); } } /// /// Looks up a localized string similar to Size settings given in SVG file. /// internal static string SizeSettingsGivenInSvgFile { get { return ResourceManager.GetString("SizeSettingsGivenInSvgFile", resourceCulture); } } /// /// Looks up a localized string similar to Smooth Corners. /// internal static string SmoothCorners { get { return ResourceManager.GetString("SmoothCorners", resourceCulture); } } /// /// Looks up a localized string similar to Smooth out sharp corners of the trace.. /// internal static string SmoothCornersDesc { get { return ResourceManager.GetString("SmoothCornersDesc", resourceCulture); } } /// /// Looks up a localized string similar to Suppress Speckles. /// internal static string SuppressSpeckles { get { return ResourceManager.GetString("SuppressSpeckles", resourceCulture); } } /// /// Looks up a localized string similar to Ignore small spots (speckles) in the bitmap.. /// internal static string SuppressSpecklesDesc { get { return ResourceManager.GetString("SuppressSpecklesDesc", resourceCulture); } } /// /// Looks up a localized string similar to Tight. /// internal static string Tight { get { return ResourceManager.GetString("Tight", resourceCulture); } } /// /// Looks up a localized string similar to Turn Policy. /// internal static string TurnPolicy { get { return ResourceManager.GetString("TurnPolicy", resourceCulture); } } /// /// Looks up a localized string similar to Black. /// internal static string TurnPolicyBlack { get { return ResourceManager.GetString("TurnPolicyBlack", resourceCulture); } } /// /// Looks up a localized string similar to Determines how to resolve ambiguities during decomposition of bitmaps into paths.. /// internal static string TurnPolicyDesc { get { return ResourceManager.GetString("TurnPolicyDesc", resourceCulture); } } /// /// Looks up a localized string similar to Left. /// internal static string TurnPolicyLeft { get { return ResourceManager.GetString("TurnPolicyLeft", resourceCulture); } } /// /// Looks up a localized string similar to Majority. /// internal static string TurnPolicyMajority { get { return ResourceManager.GetString("TurnPolicyMajority", resourceCulture); } } /// /// Looks up a localized string similar to Minority. /// internal static string TurnPolicyMinority { get { return ResourceManager.GetString("TurnPolicyMinority", resourceCulture); } } /// /// Looks up a localized string similar to Random. /// internal static string TurnPolicyRandom { get { return ResourceManager.GetString("TurnPolicyRandom", resourceCulture); } } /// /// Looks up a localized string similar to Right. /// internal static string TurnPolicyRight { get { return ResourceManager.GetString("TurnPolicyRight", resourceCulture); } } /// /// Looks up a localized string similar to White. /// internal static string TurnPolicyWhite { get { return ResourceManager.GetString("TurnPolicyWhite", resourceCulture); } } /// /// Looks up a localized string similar to Untitled. /// internal static string Untitled { get { return ResourceManager.GetString("Untitled", resourceCulture); } } /// /// Looks up a localized string similar to ▼ Use size settings given in SVG ▼. /// internal static string UseSizeSettingsGivenInSvg { get { return ResourceManager.GetString("UseSizeSettingsGivenInSvg", resourceCulture); } } /// /// Looks up a localized string similar to ViewBox (W×H). /// internal static string ViewBoxWH { get { return ResourceManager.GetString("ViewBoxWH", resourceCulture); } } /// /// Looks up a localized string similar to ViewBox (X,Y). /// internal static string ViewBoxXY { get { return ResourceManager.GetString("ViewBoxXY", resourceCulture); } } /// /// Looks up a localized string similar to Viewport (W×H). /// internal static string Viewport { get { return ResourceManager.GetString("Viewport", resourceCulture); } } /// /// Looks up a localized string similar to Working.... /// internal static string Working { get { return ResourceManager.GetString("Working", resourceCulture); } } } } ================================================ FILE: SvgFileType/Localization/StringResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 All Elements Import Angle Export An update is available! Import Brightness Cutoff Export Detects areas that are darker than the threshold value and creates a path enclosing them. Export Cancel Import Canceled upon your request. Generic Canvas (W×H) Import Canvas is too big. Generic Color Export Discussion Export Edge Detection Mode Export Enclose Export End Layer Group: {0} Import Export as Shape Export Fill Color Export Flatten Import GitHub Export Group boundaries as empty layers Import Groups Import Highpass Filter Radius Export Suppresses large-scale irregularities such as background variations, while preserving small-scale detail such as lines. Export Hidden elements Import Invert Export Keep Aspect Ratio Import Layer Group: {0} Import Layers Import Lowpass Filter Radius Export Smooths foreground details. Export Make sure you've enough memory! Import NO PATH Export OK Import Element opacity as layer property Import Optimize Export Try to optimize paths by joining adjacent Bezier curve segments. Export Paint.NET Shape Export Options Export * Enter a display name for the shape Export Plugin Version: {0} Export Preview Mode Export Fast Export Slow Export Ready! Import Resolution (DPI) Import Scale Export Scale before threshold Export Scan Mode Export Opaque Export Transparent Export Shape Export The image tracing result is saved as Paint.NET Shape. ----- {0} ----- Export Success Export You must restart Paint.NET to use it. Export Size selection by user Import Size settings given in SVG file Import Smooth Corners Export Smooth out sharp corners of the trace. Export Suppress Speckles Export Ignore small spots (speckles) in the bitmap. Export Tight Export Turn Policy Export Black Export Determines how to resolve ambiguities during decomposition of bitmaps into paths. Export Left Export Majority Export Minority Export Random Export Right Export White Export Untitled Export ▼ Use size settings given in SVG ▼ Import ViewBox (W×H) Import ViewBox (X,Y) Import Viewport (W×H) Import Working... Import ================================================ FILE: SvgFileType/Localization/StringResources_de.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Alle Elemente Import Winkel Export Ein Update ist verfügbar! Import Helligkeitsabschaltung Export Erkennt Bereiche, die dunkler als der Schwellenwert sind, und erstellt einen Pfad, der sie einschließt. Export Abbruch Import Auf Ihren Wunsch abgebrochen. Generic Leinwand (B×H) Import Leinwand ist zu groß. Generic Farbe Export Diskussion Export Kantenerkennungsmodus Export Einschließen Export Ebenengruppe beenden: {0} Import Exportieren als Form Export Füllfarbe Export Glätten Import GitHub Export Gruppenbegrenzungen als leere Ebenen Import Gruppen Import Radius des Hochpassfilters Export Unterdrückt großflächige Unregelmäßigkeiten wie Hintergrundvariationen, während kleinräumige Details wie Linien erhalten bleiben. Export Versteckte Elemente Import Invertieren Export Seitenverhältnis beibehalten Import Ebenengruppen: {0} Import Ebenen Import Radius des Lowpassfilters Export Glättet Details im Vordergrund. Export Stelle sicher, dass genügend Speicherplatz vorhanden ist! Import KEIN PFAD Export OK Import Elementdeckkraft als Ebeneneigenschaft Import Optimieren Export Versuche Pfade zu optimieren, indem benachbarte Bezier-Kurvensegmente verbunden werden. Export Paint.NET Form Exporteinstellungen Export * Geben Sie einen Anzeigenamen für die Form ein. Export Plugin Version: {0} Export Vorschaumodus Export Schnell Export Langsam Export Bereit! Import Auflösung (DPI) Import Maßstab Export Maßstab vor Schwellenwert Export Scanmodus Export Undurchsichtig Export Transparent Export Form Export Das Ergebnis der Nachverfolgung wird als Paint.NET Form gespeichert. ----- {0} ----- Export Erfolg Export Paint.NET muß neu gestartet werden, um es zu verwenden. Export Größenauswahl durch den Benutzer Import Größeneinstellungen aus der SVG-Datei Import Kanten glätten Export Glättet scharfe Kanten einer Kurve. Export Flecken unterdrücken Export Kleine Flecken (Sprenkel) in der Bitmap werden ignoriert. Export Dicht Export Richtlinie ändern Export Schwarz Export Legt fest, wie Mehrdeutigkeiten bei der Vektorisierung von Bitmaps in Pfade aufgelöst werden sollen. Export Links Export Mehrheit Export Minderheit Export Zufällig Export Rechts Export Weiss Export Unbenannt Export ▼ Größeneinstellungen aus der SVG-Datei verwenden ▼ Import ViewBox (B×H) Import ViewBox (X,Y) Import Viewport (B×H) Import Arbeite... Import ================================================ FILE: SvgFileType/Localization/StringResources_fr.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Tous les éléments Import Angle Export Une mise à jour est disponible ! Import Extinction de la luminosité Export Détecte les zones plus foncées que le seuil et crée un chemin les entourant. Export Annuler Import Annulée à votre demande. Generic Canevas (L × H) Import Le canevas est trop grand. Generic Couleur Export Discussion Export Mode de détection des bords Export Joindre Export Groupe de calques finaux : {0} Import Exporter en tant que forme Export Couleur de remplissage Export Aplatir Import GitHub Export Limites de groupe comme calques vides Import Groupes Import Rayon du filtre passe-haut Export Supprime les irrégularités à grande échelle comme les variations d'arrière-plan, tout en préservant les détails à petite échelle comme les lignes. Export Éléments masqués Import Inverser Export Conserver le rapport d'aspect Import Groupe de calques : {0} Import Calques Import Rayon du filtre passe-bas Export Adoucit les détails du premier plan. Export Assurez-vous d'avoir assez de mémoire ! Import AUCUN CHEMIN Export OK Import Opacité de l'élément comme propriété de calque Import Optimiser Export Tenter d'optimiser les chemins en joignant des segments de courbe de Bézier adjacents. Export Options d'exportation de formes Paint.NET Export * Entrez un nom d'affichage pour la forme Export Version du plug-in : {0} Export Mode aperçu Export Rapide Export Lent Export Prêt ! Import Résolution (DPI) Import Échelle Export Échelle avant le seuil Export Mode de numérisation Export Opaque Export Transparent Export Forme Export Le résultat du traçage de l'image est enregistré en tant que Forme Paint.NET. ----- {0} ----- Export Réussi Export Vous devez redémarrer Paint.NET pour l'utiliser. Export Sélection de la taille par l'utilisateur Import Paramètres de taille indiqués dans le fichier SVG Import Lisser les coins Export Lisser les angles pointus du traçage. Export Enlever les salissures Export Ignorer les petites taches (salissures) dans l'image bitmap. Export Resserrer Export Stratégie de direction Export Noir Export Détermine comment résoudre les ambiguïtés lors de la décomposition des bitmaps en chemins. Export Gauche Export Majorité Export Minorité Export Aléatoire Export Droite Export Blanc Export Sans titre Export ▼ Utiliser les paramètres de taille dans le SVG ▼ Import ViewBox (L x H) Import ViewBox (X, Y) Import Viewport (L x H) Import Traitement en cours… Import ================================================ FILE: SvgFileType/Localization/StringResources_ru.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Все элементы Import Угол Export Доступно обновление! Import Порог яркости Export Обнаруживает области, темнее порогового значения, и создаёт путь, их охватывающий. Export Отмена Import Отменено по вашему запросу. Generic Холст (Ш×В) Import Холст слишком большой. Generic Цвет Export Обсуждение Export Режим обнаружения границ Export Охватить Export Конец группы слоёв: {0} Import Экспорт как форму Export Цвет заливки Export Сплющить Import GitHub Export Группировать границы как пустые слои Import Группы Import Радиус высокочастотного фильтра Export Подавляет крупномасштабные искажения, такие как фоновая неоднородность, сохраняя мелкие детали, например линии. Export Скрытые элементы Import Инвертировать Export Сохранять пропорции Import Группа слоёв: {0} Import Слои Import Радиус низкочастотного фильтра Export Сглаживает детали переднего плана. Export Убедитесь, что у вас достаточно памяти! Import НЕТ ПУТИ Export ОК Import Прозрачность элемента как свойство слоя Import Оптимизировать Export Пробует оптимизировать пути, объединяя соседние сегменты кривых Безье. Export Параметры экспорта формы Paint.NET Export * Введите отображаемое имя для формы Export Версия плагина: {0} Export Режим предпросмотра Export Быстрый Export Медленный Export Готово! Import Разрешение (DPI) Import Масштаб Export Масштабировать до применения порога Export Режим сканирования Export Непрозрачный Export Прозрачный Export Форма Export Результат трассировки изображения сохранён как форма Paint.NET. ----- {0} ----- Export Успех Export Для использования необходимо перезапустить Paint.NET. Export Выбор размера пользователем Import Параметры размера указаны в файле SVG Import Сглаживание углов Export Сглаживает резкие углы трассировки. Export Подавление пятен Export Игнорирует мелкие пятна (помехи) на изображении. Export Плотно Export Политика поворота Export Чёрный Export Определяет, как разрешать неоднозначности при разложении битмапов на пути. Export Влево Export Большинство Export Меньшинство Export Случайно Export Вправо Export Белый Export Без названия Export ▼ Использовать параметры размера из SVG ▼ Import ViewBox (Ш×В) Import ViewBox (X,Y) Import Область просмотра (Ш×В) Import Обработка... Import ================================================ FILE: SvgFileType/Localization/StringResources_tr.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Tüm Öğeler Import Döndür Export Bir güncelleme mevcut! Import Parlaklık Kesme Export Eşik değerinden daha koyu olan alanları algılar ve onları çevreleyen bir yol oluşturur. Export İptal Import Talebiniz üzerine iptal edildi. Generic Tuval (G×Y) Import Tuval çok büyük. Generic Renk Export Tartışma Export Kenar Algılama Modu Export Çevrele Export Bitiş Katman Grubu: {0} Import Şekil olarak kaydet Export Dolgu Rengi Export Düzleştirilmiş Import GitHub Export Grup sınırlarını boş katman olarak içe aktar Import Gruplar Import Yüksek Geçiş Export Arka plan varyasyonları gibi büyük ölçekli düzensizlikleri bastırırken çizgiler gibi küçük ölçekli ayrıntıları korur. Export Gizli elementler Import Ters çevir Export En-boy oranını koru Import Başlangıç Katman Grubu: {0} Import Katmanlar Import Düşük Geçiş Export Ön plan ayrıntılarını yumuşatır. Export Yeterli miktarda kullanılabilir bellek olduğundan emin olunuz! Import YOL YOK Export TAMAM Import Element saydamlığı katman niteliği olarak Import Optimize et Export Bitişik Bezier eğrilerini birleştirerek yolları optimize etmeye çalışır. Export Paint.NET Şekil Dışa Aktarma Seçenekleri Export * Şekil için bir isim giriniz Export Eklenti Sürümü: {0} Export Önizleme Modu Export Hızlı Export Yavaş Export Hazır Import Çözünürlük (DPI) Import Ölçekle Export Eşikten önce ölçeklendir Export Tarama Modu Export Opak Export Saydam Export Şekil Export Resim taraması Paint.NET Şekli olarak kaydedildi. ----- {0} ----- Export Başarılı Export Kullanmak için Paint.NET'i yeniden başlatmalısınız. Export Kullanıcı tarafından istenen boyutlar Import SVG dosyasında belirtilen boyut değerleri Import Pürüzsüz Köşeler Export Keskin köşeleri yumuşatır. Export Benekleri Yoksay Export Biteşlemdeki küçük noktaları (benekleri) yoksay. Export Sıkı Export Dönüş Politikası Export Siyah Export Bit eşlemlerin yollara ayrıştırılması sırasında belirsizliklerin nasıl çözüleceğini belirler. Export Sol Export Çoğunluk Export Azınlık Export Rastgele Export Sağ Export Beyaz Export İsimsiz Export ▼ SVG'de belirtilen boyut değerlerini kullan ▼ Import ViewBox (G×Y) Import Viewport (G×Y) Import Çalışıyor... Import ================================================ FILE: SvgFileType/Logger.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System.Diagnostics; namespace SvgFileTypePlugin; internal static class Logger { private const string Category = $"[{nameof(SvgFileTypePlugin)}]"; public static void WriteLine(string message) { Trace.WriteLine(message, Category); } public static void WriteLineIf(bool condition, string message) { Trace.WriteLineIf(condition, message, Category); } } ================================================ FILE: SvgFileType/Services.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using PaintDotNet; namespace SvgFileTypePlugin; internal static class Services { private static IServiceProvider? Provider; public static void Init(IServiceProvider provider) { ArgumentNullException.ThrowIfNull(provider); Provider = provider; } public static TService Get() where TService : class { if (Provider is null) { throw new InvalidOperationException("No service provider has been configured. Call Init(IServiceProvider) first."); } TService? service = Provider.GetService(); return service ?? throw new InvalidOperationException($"Cannot find the service: '{typeof(TService)}'"); } } ================================================ FILE: SvgFileType/SvgFileType.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.IO; using System.Threading; using BitmapVectorizer; using PaintDotNet; using PaintDotNet.IndirectUI; using PaintDotNet.IndirectUI.Extensions; using PaintDotNet.PropertySystem; using SvgFileTypePlugin.Export; using SvgFileTypePlugin.Import; using SvgFileTypePlugin.Localization; using SR = SvgFileTypePlugin.Localization.StringResources; namespace SvgFileTypePlugin; [PluginSupportInfo(typeof(SvgFileTypePluginSupportInfo))] public sealed class SvgFileType() : PropertyBasedFileType("SVG Plugin", BaseOptions) { private string shapePath = string.Empty; #region OnLoad protected override Document OnLoad(Stream input) { return SvgImport.Load(input); } #endregion #region OnSave protected override void OnSaveT(Document input, Stream output, PropertyBasedSaveConfigToken token, Surface scratchSurface, ProgressEventHandler progressCallback) { SvgExport.Export(input, output, token, scratchSurface, progressCallback, Interlocked.Exchange(ref shapePath, string.Empty)); } private void PdnShape_Click(object? sender, ValueEventArgs args) { if ((int)args.Value != int.MinValue) { Interlocked.Exchange(ref shapePath, SvgExport.ShowSaveShapeDialog() ?? string.Empty); } } public override ControlInfo OnCreateSaveConfigUI(PropertyCollection props) { ArgumentNullException.ThrowIfNull(props); PropertyControlInfo CommonSettingsF(PropertyControlInfo p) => p.SliderSmallChange(.01).SliderLargeChange(.01).UpDownIncrement(.01).DecimalPlaces(2).ShowResetButton(); PropertyControlInfo CommonSettings(PropertyControlInfo p) => p.SliderSmallChange(1).SliderLargeChange(1).UpDownIncrement(1).ShowResetButton(); PropertyControlInfoCollection pcic = new PropertyControlInfoCollection(props); pcic.Configure(PropertyNames.ScanMode, SR.ScanMode, p => p .ValueDisplayNameCallback(Localize.GetDisplayName)) .Configure(PropertyNames.PreviewMode, SR.PreviewMode, p => p .ValueDisplayNameCallback(Localize.GetDisplayName)) .Configure(PropertyNames.PdnShapeName, SR.PdnShapeExportOptions, SR.PdnShapeExportOptionsDesc) .Configure(PropertyNames.PdnShape, string.Empty, p => p .ControlType(PropertyControlType.IncrementButton) .ButtonText(SR.ExportAsPdnShape) .OnValueChanged(PdnShape_Click)) .Configure(PropertyNames.BrightnessCutoff, SR.BrightnessCutoff, SR.BrightnessCutoffDesc, CommonSettingsF) .Configure(PropertyNames.HighpassFilter, SR.HighpassFilterRadius, SR.HighpassFilterRadiusDesc, CommonSettings) .Configure(PropertyNames.GreymapScale, SR.ScaleBeforeThreshold, CommonSettings) .Configure(PropertyNames.LowpassFilter, SR.LowpassFilterRadius, SR.LowpassFilterRadiusDesc, CommonSettings) .Configure(PropertyNames.SuppressSpeckles, SR.SuppressSpeckles, SR.SuppressSpecklesDesc, CommonSettings) .Configure(PropertyNames.SmoothCorners, SR.SmoothCorners, SR.SmoothCornersDesc, CommonSettingsF) .Configure(PropertyNames.Optimize, SR.Optimize, SR.OptimizeDesc, p => p .ShowResetButton()) .Configure(PropertyNames.TurnPolicy, SR.TurnPolicy, SR.TurnPolicyDesc, p => p .ValueDisplayNameCallback(Localize.GetDisplayName)) .Configure(PropertyNames.Color, SR.Color, p => p .ControlType(PropertyControlType.ColorWheel) .ShowResetButton()) .Configure(PropertyNames.FillColor, SR.FillColor, p => p .ControlType(PropertyControlType.ColorWheel) .ShowResetButton()) .Configure(PropertyNames.Invert, string.Empty, SR.Invert) .Configure(PropertyNames.Tight, string.Empty, SR.Tight) .Configure(PropertyNames.Enclose, string.Empty, SR.Enclose) .Configure(PropertyNames.Scale, SR.Scale, CommonSettingsF) .Configure(PropertyNames.Angle, SR.Angle, p => p .ControlType(PropertyControlType.AngleChooser) .ShowResetButton()) .Configure(PropertyNames.GitHubLink, string.Format(SR.PluginVersion, SvgFileTypePluginSupportInfo.Instance.Version), SR.GitHubLink) .Configure(PropertyNames.DiscussionLink, string.Empty, SR.DiscussionLink); PanelControlInfo panel = pcic.CreatePanel(); return panel; } public override PropertyCollection OnCreateSavePropertyCollection() { PropertyName[] targets1 = [ PropertyNames.PdnShape, PropertyNames.PdnShapeName ]; PropertyName[] targets2 = [ PropertyNames.LowpassFilter, PropertyNames.GreymapScale ]; FluentPropertyCollection properties = new FluentPropertyCollection() .AddStaticListChoice(PropertyNames.ScanMode, ScanMode.Transparent) .AddStaticListChoice(PropertyNames.PreviewMode, PreviewMode.Fast) .AddString(PropertyNames.PdnShapeName, SR.Untitled) .AddInt32(PropertyNames.PdnShape, int.MinValue) .AddDouble(PropertyNames.BrightnessCutoff, 0.45, 0.01, 1) .AddInt32(PropertyNames.HighpassFilter, 0, 0, 5) .AddInt32(PropertyNames.GreymapScale, 1, 1, 4) .AddInt32(PropertyNames.LowpassFilter, 0, 0, 5) .AddInt32(PropertyNames.SuppressSpeckles, PotraceBitmap.TurdSizeDef, PotraceBitmap.TurdSizeMin, PotraceBitmap.TurdSizeMax) .AddDouble(PropertyNames.SmoothCorners, Potrace.AlphaMaxDef, Potrace.AlphaMaxMin, Potrace.AlphaMaxMax) .AddDouble(PropertyNames.Optimize, Potrace.OptToleranceDef, Potrace.OptToleranceMin, Potrace.OptToleranceMax) .AddStaticListChoice(PropertyNames.TurnPolicy, TurnPolicy.Minority) .AddInt32(PropertyNames.Color, ColorBgra.Black) .AddInt32(PropertyNames.FillColor, ColorBgra.White) .AddBoolean(PropertyNames.Invert) .AddBoolean(PropertyNames.Tight) .AddBoolean(PropertyNames.Enclose) .AddDouble(PropertyNames.Scale, 1, 0.01, 4) .AddDouble(PropertyNames.Angle, 0, 0, 360) .AddUri(PropertyNames.GitHubLink, SvgFileTypePluginSupportInfo.Instance.WebsiteUri) .AddUri(PropertyNames.DiscussionLink, SvgFileTypePluginSupportInfo.Instance.ForumUri) .WithReadOnlyRule(targets1, PropertyNames.ScanMode, ScanMode.Transparent, inverse: true) .WithReadOnlyRule(targets2, PropertyNames.HighpassFilter, 0) .WithReadOnlyRule(PropertyNames.FillColor, PropertyNames.ScanMode, ScanMode.Opaque, inverse: true); return properties; } protected override bool ShouldSerializeTokenProperty(Property property) { return IsSerializable(property); } #endregion #region Static Private private static readonly FileTypeOptions BaseOptions = new() { LoadExtensions = [".svg", ".svgz"], SupportsCancellation = true, SupportsLayers = false, SaveExtensions = [".svg"] }; private static bool IsSerializable(Property property) { return Enum.TryParse(property.Name, out PropertyNames prop) && prop is not PropertyNames.GitHubLink and not PropertyNames.DiscussionLink and not PropertyNames.PdnShape and not PropertyNames.PdnShapeName and not PropertyNames.PreviewMode; } #endregion } ================================================ FILE: SvgFileType/SvgFileTypeFactory.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using PaintDotNet; namespace SvgFileTypePlugin; public sealed class SvgFileTypeFactory : IFileTypeFactory2 { public FileType[] GetFileTypeInstances(IFileTypeHost host) { ArgumentNullException.ThrowIfNull(host); Services.Init(host.Services); return [new SvgFileType()]; } } ================================================ FILE: SvgFileType/SvgFileTypePlugin.csproj ================================================  net9.0-windows true TRACE; FodyPackageReference;MSB3277 enable Library false SvgFileTypePlugin true true SvgFileType SVG File Type Plugin for Paint.NET Copyright © 2025 Osman Tunçelli $(Copyright) $(AssemblyTitle) 1.0.7.3 FileType C:\Program Files\paint.net $(USERPROFILE)\Documents\paint.net App Files\$(PluginType)s\$(RootNamespace)\ false $(PdnRoot)\PaintDotNet.Base.dll false $(PdnRoot)\PaintDotNet.Core.dll false $(PdnRoot)\PaintDotNet.Data.dll false $(PdnRoot)\PaintDotNet.Effects.dll false $(PdnRoot)\PaintDotNet.PropertySystem.dll false $(PdnRoot)\PaintDotNet.ComponentModel.dll false $(PdnRoot)\PaintDotNet.Fundamentals.dll false $(PdnRoot)\PaintDotNet.ObjectModel.dll false $(PdnRoot)\PaintDotNet.Primitives.dll false $(PdnRoot)\PaintDotNet.PropertySystem.dll false $(PdnRoot)\PaintDotNet.Windows.dll false $(PdnRoot)\PaintDotNet.Windows.Core.dll false $(PdnRoot)\PaintDotNet.Windows.Framework.dll false $(PdnRoot)\PaintDotNet.Effects.Core.dll false $(PdnRoot)\PaintDotNet.Effects.Gpu.dll Form True True StringResources.resx Designer ResXFileCodeGenerator StringResources.Designer.cs all runtime; build; native; contentfiles; analyzers; buildtransitive ================================================ FILE: SvgFileType/SvgFileTypePluginSupportInfo.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Reflection; using PaintDotNet; namespace SvgFileTypePlugin; public sealed class SvgFileTypePluginSupportInfo : IPluginSupportInfo, IPluginSupportInfoProvider { #region Properties internal static SvgFileTypePluginSupportInfo Instance { get; } = new SvgFileTypePluginSupportInfo(); #region IPluginSupportInfo public string Author { get; } = "Osman Tunçelli"; public string Copyright { get; } = GetCustomAttribute()?.Copyright ?? string.Empty; public string DisplayName { get; } = GetCustomAttribute()?.Product ?? string.Empty; public Version Version { get; } = GetAssembly().GetName().Version ?? new Version(1, 0); public Uri WebsiteUri { get; } = new Uri("https://github.com/otuncelli/Scalable-Vector-Graphics-Plugin-for-Paint.NET"); #endregion internal Uri ForumUri { get; } = new Uri("https://forums.getpaint.net/index.php?showtopic=117086"); #endregion #region IPluginSupportInfoProvider public IPluginSupportInfo GetPluginSupportInfo() { return new SvgFileTypePluginSupportInfo(); } #endregion #region Static Methods private static T? GetCustomAttribute() where T : Attribute { return GetAssembly().GetCustomAttribute(); } private static Assembly GetAssembly() { return typeof(SvgFileTypePluginSupportInfo).Assembly; } #endregion } ================================================ FILE: SvgFileType/UIHelper.cs ================================================ // Copyright 2025 Osman Tunçelli. All rights reserved. // Use of this source code is governed by GNU General Public License (GPL-2.0) that can be found in the COPYING file. using System; using System.Diagnostics; using System.Linq; using System.Threading; using System.Windows.Forms; using PaintDotNet; using PaintDotNet.AppModel; using SvgFileTypePlugin.Extensions; namespace SvgFileTypePlugin; internal static partial class UIHelper { public static Form? GetMainForm() { nint handle = Process.GetCurrentProcess().MainWindowHandle; return (Form?)Control.FromHandle(handle) ?? Application.OpenForms["MainForm"] ?? Application.OpenForms[0]; } public static bool IsSaveConfigDialogVisible() { Form? main = GetMainForm(); return Application.OpenForms .OfType() .Where(form => form != main) .Reverse() .SelectMany(form => form.Descendants()) .AsParallel() .OfType() .Any(); } public static TResult? RunOnUIThread(Func action) { IUISynchronizationContext ctx = Services.Get(); if (ctx.IsOnUIThread) { return action(); } TResult? result = default; Exception? error = null; ctx.Send(new SendOrPostCallback(state => { try { result = action(); } catch (Exception ex) { error = ex; } }), state: null); if (error is not null) { throw error; } return result; } public static void RunOnUIThread(Action action) { IUISynchronizationContext ctx = Services.Get(); if (ctx.IsOnUIThread) { action(); return; } Exception? error = null; ctx.Send(new SendOrPostCallback(state => { try { action(); } catch (Exception ex) { error = ex; } }), state: null); if (error is not null) { throw error; } } } ================================================ FILE: SvgFileType.ThirdPartyNotices.txt ================================================ Scalable-Vector-Graphics-Plugin-for-Paint.NET also uses third-party projects that may be distributed under different licenses. Please see below for details. 1. SVG.NET (https://github.com/svg-net/SVG) 2. ExCSS (https://github.com/TylerBrinks/ExCSS) 3. BitmapVectorizer (https://github.com/otuncelli/BitmapVectorizer) 4. resvg (https://github.com/linebender/resvg) 5. resvg.net (https://github.com/otuncelli/resvg.net) %% SVG.NET NOTICES AND INFORMATION BEGIN HERE ========================================= Microsoft Public License (Ms-PL) This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 1. Definitions The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. 2. Grant of Rights (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 3. Conditions and Limitations (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. ========================================= END OF SVG.NET NOTICES AND INFORMATION %% ExCSS NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) Copyright (c) [year] [fullname] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF ExCSS NOTICES AND INFORMATION %% BitmapVectorizer NOTICES AND INFORMATION BEGIN HERE ========================================= GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ========================================= END OF BitmapVectorizer NOTICES AND INFORMATION %% resvg NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright 2017 the Resvg Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF resvg NOTICES AND INFORMATION %% resvg.net NOTICES AND INFORMATION BEGIN HERE ========================================= Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ========================================= END OF resvg.net NOTICES AND INFORMATION ================================================ FILE: SvgFileType.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgFileTypePlugin", "SvgFileType\SvgFileTypePlugin.csproj", "{C32235C8-90B3-4C43-B589-B821C84839E9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BitmapVectorizer", "BitmapVectorizer\BitmapVectorizer\BitmapVectorizer.csproj", "{2C7479CE-42CF-4D90-9650-6ACDF868B8D2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "resvg.net", "..\resvg.net\resvg.net\resvg.net.csproj", "{AC700075-39F4-4DB8-B65A-8B0C9264225C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PaintDotNet.IndirectUI.Fluent", "..\PaintDotNet.IndirectUI.Fluent\PaintDotNet.IndirectUI.Fluent\PaintDotNet.IndirectUI.Fluent.csproj", "{0A612B08-8FC5-4068-9437-FC38476EA808}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "resvg.net.SourceGenerator", "..\resvg.net\resvg.net.SourceGenerator\resvg.net.SourceGenerator.csproj", "{829DA38A-DC43-4A9F-AEB6-C2208390B6B0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C32235C8-90B3-4C43-B589-B821C84839E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C32235C8-90B3-4C43-B589-B821C84839E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {C32235C8-90B3-4C43-B589-B821C84839E9}.Release|Any CPU.ActiveCfg = Release|Any CPU {C32235C8-90B3-4C43-B589-B821C84839E9}.Release|Any CPU.Build.0 = Release|Any CPU {2C7479CE-42CF-4D90-9650-6ACDF868B8D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2C7479CE-42CF-4D90-9650-6ACDF868B8D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {2C7479CE-42CF-4D90-9650-6ACDF868B8D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {2C7479CE-42CF-4D90-9650-6ACDF868B8D2}.Release|Any CPU.Build.0 = Release|Any CPU {AC700075-39F4-4DB8-B65A-8B0C9264225C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AC700075-39F4-4DB8-B65A-8B0C9264225C}.Debug|Any CPU.Build.0 = Debug|Any CPU {AC700075-39F4-4DB8-B65A-8B0C9264225C}.Release|Any CPU.ActiveCfg = Release|Any CPU {AC700075-39F4-4DB8-B65A-8B0C9264225C}.Release|Any CPU.Build.0 = Release|Any CPU {0A612B08-8FC5-4068-9437-FC38476EA808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0A612B08-8FC5-4068-9437-FC38476EA808}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A612B08-8FC5-4068-9437-FC38476EA808}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A612B08-8FC5-4068-9437-FC38476EA808}.Release|Any CPU.Build.0 = Release|Any CPU {829DA38A-DC43-4A9F-AEB6-C2208390B6B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {829DA38A-DC43-4A9F-AEB6-C2208390B6B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {829DA38A-DC43-4A9F-AEB6-C2208390B6B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {829DA38A-DC43-4A9F-AEB6-C2208390B6B0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1ACF9C3B-BF8E-4939-88D4-BA7E2CE114E5} EndGlobalSection EndGlobal