├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── stale.yml └── workflows │ ├── build.yml │ └── pages.yml ├── .gitignore ├── .nuke ├── build.schema.json └── parameters.json ├── LICENSE ├── README.md ├── SvgToXaml.sln ├── azure-pipelines.yml ├── build.cmd ├── build.ps1 ├── build.sh ├── build ├── Avalonia.Controls.Skia.props ├── Avalonia.Desktop.props ├── Avalonia.Diagnostics.props ├── Avalonia.Markup.Xaml.Loader.props ├── Avalonia.Skia.props ├── Avalonia.ThemeManager.props ├── Avalonia.Themes.Fluent.props ├── Avalonia.Web.props ├── Avalonia.Xaml.Behaviors.props ├── Avalonia.props ├── Base.props ├── CommunityToolkit.Mvvm.props ├── ReactiveMarbles.PropertyChanged.props ├── ReferenceAssemblies.props ├── SignAssembly.props ├── SkiaSharp.Linux.props ├── SkiaSharp.props ├── SourceLink.props ├── Svg.Skia.props ├── SvgToXaml.public.snk ├── System.CommandLine.props ├── System.Text.Json.props ├── XUnit.props └── build │ ├── Build.cs │ └── _build.csproj ├── global.json ├── nuget.config ├── src ├── SvgToXaml.Base │ ├── App.axaml │ ├── App.axaml.cs │ ├── Assets │ │ └── Icon.ico │ ├── Icons.axaml │ ├── SvgToXaml.Base.csproj │ ├── ViewLocator.cs │ ├── ViewModels │ │ ├── FileItemViewModel.cs │ │ ├── MainWindowViewModel.cs │ │ ├── ProjectViewModel.cs │ │ ├── SettingsViewModel.cs │ │ ├── StorageService.cs │ │ ├── SvgViewModel.cs │ │ └── ViewModelBase.cs │ └── Views │ │ ├── ActionsView.axaml │ │ ├── ActionsView.axaml.cs │ │ ├── ItemsView.axaml │ │ ├── ItemsView.axaml.cs │ │ ├── LeftPaneView.axaml │ │ ├── LeftPaneView.axaml.cs │ │ ├── MainView.axaml │ │ ├── MainView.axaml.cs │ │ ├── MainWindow.axaml │ │ ├── MainWindow.axaml.cs │ │ ├── PictureView.axaml │ │ ├── PictureView.axaml.cs │ │ ├── PreviewView.axaml │ │ ├── PreviewView.axaml.cs │ │ ├── ProjectActionsView.axaml │ │ ├── ProjectActionsView.axaml.cs │ │ ├── RightPaneView.axaml │ │ ├── RightPaneView.axaml.cs │ │ ├── Sandbox │ │ ├── SandBox.axaml │ │ ├── SandBox.axaml.cs │ │ └── Styles.axaml │ │ ├── SettingsView.axaml │ │ └── SettingsView.axaml.cs ├── SvgToXaml.Converter │ ├── SvgToXaml.Converter.csproj │ ├── SvgToXamlConverter.cs │ ├── System │ │ └── Runtime │ │ │ └── CompilerServices │ │ │ └── IsExternalInit.cs │ ├── XamlGenerator.cs │ └── XamlGeneratorSettings.cs ├── SvgToXaml.Desktop │ ├── Program.cs │ └── SvgToXaml.Desktop.csproj ├── SvgToXaml.Model │ ├── Containers │ │ ├── Image.cs │ │ └── Styles.cs │ ├── Drawing │ │ ├── Drawing.cs │ │ ├── DrawingGroup.cs │ │ ├── DrawingImage.cs │ │ └── GeometryDrawing.cs │ ├── Factory.cs │ ├── InputItem.cs │ ├── Paint │ │ ├── Brush.cs │ │ ├── Dashes.cs │ │ ├── GradientBrush.cs │ │ ├── GradientStop.cs │ │ ├── LinearGradientBrush.cs │ │ ├── Pen.cs │ │ ├── PictureBrush.cs │ │ ├── RadialGradientBrush.cs │ │ ├── SolidColorBrush.cs │ │ └── TwoPointConicalGradientBrush.cs │ ├── Resources │ │ ├── Resource.cs │ │ └── ResourceDictionary.cs │ ├── SvgToXaml.Model.csproj │ └── System │ │ └── Runtime │ │ └── CompilerServices │ │ └── IsExternalInit.cs ├── SvgToXaml.Web │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Roots.xml │ ├── SvgToXaml.Web.csproj │ ├── runtimeconfig.template.json │ └── wwwroot │ │ ├── app.css │ │ ├── favicon.ico │ │ ├── index.html │ │ └── main.js └── svgxaml │ ├── Icon.ico │ ├── Program.cs │ └── svgxaml.csproj └── tests └── SvgToXamlConverter.UnitTests └── SvgToXamlConverter.UnitTests.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Default settings: 7 | # A newline ending every file 8 | # Use 4 spaces as indentation 9 | [*] 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | # C# files 15 | [*.cs] 16 | # New line preferences 17 | csharp_new_line_before_open_brace = all 18 | csharp_new_line_before_else = true 19 | csharp_new_line_before_catch = true 20 | csharp_new_line_before_finally = true 21 | csharp_new_line_before_members_in_object_initializers = true 22 | csharp_new_line_before_members_in_anonymous_types = true 23 | csharp_new_line_between_query_expression_clauses = true 24 | 25 | # Indentation preferences 26 | csharp_indent_block_contents = true 27 | csharp_indent_braces = false 28 | csharp_indent_case_contents = true 29 | csharp_indent_switch_labels = true 30 | csharp_indent_labels = one_less_than_current 31 | 32 | # avoid this. unless absolutely necessary 33 | dotnet_style_qualification_for_field = false:suggestion 34 | dotnet_style_qualification_for_property = false:suggestion 35 | dotnet_style_qualification_for_method = false:suggestion 36 | dotnet_style_qualification_for_event = false:suggestion 37 | 38 | # prefer var 39 | csharp_style_var_for_built_in_types = true 40 | csharp_style_var_when_type_is_apparent = true 41 | csharp_style_var_elsewhere = true:suggestion 42 | 43 | # use language keywords instead of BCL types 44 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 45 | dotnet_style_predefined_type_for_member_access = true:suggestion 46 | 47 | # name all constant fields using PascalCase 48 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion 49 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields 50 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style 51 | 52 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 53 | dotnet_naming_symbols.constant_fields.required_modifiers = const 54 | 55 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 56 | 57 | # static fields should have s_ prefix 58 | dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion 59 | dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields 60 | dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style 61 | 62 | dotnet_naming_symbols.static_fields.applicable_kinds = field 63 | dotnet_naming_symbols.static_fields.required_modifiers = static 64 | 65 | dotnet_naming_style.static_prefix_style.required_prefix = s_ 66 | dotnet_naming_style.static_prefix_style.capitalization = camel_case 67 | 68 | # internal and private fields should be _camelCase 69 | dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion 70 | dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields 71 | dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style 72 | 73 | dotnet_naming_symbols.private_internal_fields.applicable_kinds = field 74 | dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal 75 | 76 | dotnet_naming_style.camel_case_underscore_style.required_prefix = _ 77 | dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case 78 | 79 | # use accessibility modifiers 80 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion 81 | 82 | # Code style defaults 83 | dotnet_sort_system_directives_first = true 84 | csharp_preserve_single_line_blocks = true 85 | csharp_preserve_single_line_statements = false 86 | 87 | # Expression-level preferences 88 | dotnet_style_object_initializer = true:suggestion 89 | dotnet_style_collection_initializer = true:suggestion 90 | dotnet_style_explicit_tuple_names = true:suggestion 91 | dotnet_style_coalesce_expression = true:suggestion 92 | dotnet_style_null_propagation = true:suggestion 93 | 94 | # Expression-bodied members 95 | csharp_style_expression_bodied_methods = false:none 96 | csharp_style_expression_bodied_constructors = false:none 97 | csharp_style_expression_bodied_operators = false:none 98 | csharp_style_expression_bodied_properties = true:none 99 | csharp_style_expression_bodied_indexers = true:none 100 | csharp_style_expression_bodied_accessors = true:none 101 | 102 | # Pattern matching 103 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 104 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 105 | csharp_style_inlined_variable_declaration = true:suggestion 106 | 107 | # Null checking preferences 108 | csharp_style_throw_expression = true:suggestion 109 | csharp_style_conditional_delegate_call = true:suggestion 110 | 111 | # Space preferences 112 | csharp_space_after_cast = false 113 | csharp_space_after_colon_in_inheritance_clause = true 114 | csharp_space_after_comma = true 115 | csharp_space_after_dot = false 116 | csharp_space_after_keywords_in_control_flow_statements = true 117 | csharp_space_after_semicolon_in_for_statement = true 118 | csharp_space_around_binary_operators = before_and_after 119 | csharp_space_around_declaration_statements = do_not_ignore 120 | csharp_space_before_colon_in_inheritance_clause = true 121 | csharp_space_before_comma = false 122 | csharp_space_before_dot = false 123 | csharp_space_before_open_square_brackets = false 124 | csharp_space_before_semicolon_in_for_statement = false 125 | csharp_space_between_empty_square_brackets = false 126 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 127 | csharp_space_between_method_call_name_and_opening_parenthesis = false 128 | csharp_space_between_method_call_parameter_list_parentheses = false 129 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 130 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 131 | csharp_space_between_method_declaration_parameter_list_parentheses = false 132 | csharp_space_between_parentheses = false 133 | csharp_space_between_square_brackets = false 134 | 135 | # Xaml files 136 | [*.{xaml,axaml}] 137 | indent_style = space 138 | indent_size = 2 139 | 140 | # Xml project files 141 | [*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] 142 | indent_size = 2 143 | 144 | # Xml build files 145 | [*.builds] 146 | indent_size = 2 147 | 148 | # Xml files 149 | [*.{xml,stylecop,resx,ruleset}] 150 | indent_size = 2 151 | 152 | # Xml config files 153 | [*.{props,targets,config,nuspec}] 154 | indent_size = 2 155 | 156 | # Shell scripts 157 | [*.sh] 158 | end_of_line = lf 159 | [*.{cmd, bat}] 160 | end_of_line = crlf -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [wieslawsoltes] 2 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # https://probot.github.io/apps/stale/ 2 | 3 | # Number of days of inactivity before an issue becomes stale 4 | daysUntilStale: 60 5 | 6 | # Number of days of inactivity before a stale issue is closed 7 | daysUntilClose: 7 8 | 9 | # Issues with these labels will never be considered stale 10 | exemptLabels: 11 | - pinned 12 | - security 13 | 14 | # Label to use when marking an issue as stale 15 | staleLabel: stale 16 | 17 | # Comment to post when marking an issue as stale. Set to `false` to disable 18 | markComment: > 19 | This issue has been automatically marked as stale because it has not had 20 | recent activity. It will be closed if no further activity occurs. Thank you 21 | for your contributions. 22 | 23 | # Comment to post when closing a stale issue. Set to `false` to disable 24 | closeComment: true 25 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release/* 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, windows-latest, macos-latest] 17 | name: Build ${{ matrix.os }} 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - uses: actions/checkout@v1 22 | - name: Setup .NET Core 23 | uses: actions/setup-dotnet@v1 24 | - name: Install wasm-tools 25 | run: dotnet workload install wasm-tools wasm-experimental 26 | - name: Build Release 27 | run: dotnet build --configuration Release 28 | - name: Test Release 29 | run: dotnet test --configuration Release 30 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | env: 4 | PROJECT_PATH: src/SvgToXaml.Web/SvgToXaml.Web.csproj 5 | OUTPUT_PATH: src/SvgToXaml.Web/bin/Release/net8.0-browser/browser-wasm/AppBundle 6 | on: 7 | push: 8 | branches: [ main ] 9 | 10 | jobs: 11 | deploy-to-github-pages: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup .NET Core SDK 17 | uses: actions/setup-dotnet@v1.9.0 18 | 19 | - name: Install wasm-tools 20 | run: dotnet workload install wasm-tools wasm-experimental 21 | 22 | - name: Install DotNetCompress 23 | run: dotnet tool install --global DotNetCompress --version 3.0.0 --no-cache 24 | 25 | - name: Publish .NET Project 26 | run: dotnet publish $PROJECT_PATH -c Release -o release --nologo 27 | 28 | - name: Change base-tag in index.html 29 | run: sed -i 's///g' $OUTPUT_PATH/index.html 30 | 31 | - name: copy index.html to 404.html 32 | run: cp $OUTPUT_PATH/index.html $OUTPUT_PATH/404.html 33 | 34 | - name: Compress Output using Brotli 35 | run: DotNetCompress -d $OUTPUT_PATH/ -p "*.dll" "*.js" "*.wasm" --format br --threads 4 36 | 37 | - name: Compress Output using GZip 38 | run: DotNetCompress -d $OUTPUT_PATH -p "*.dll" "*.js" "*.wasm" --format gz --threads 4 39 | 40 | - name: Add .nojekyll file 41 | run: touch $OUTPUT_PATH/.nojekyll 42 | 43 | - name: Commit wwwroot to GitHub Pages 44 | uses: JamesIves/github-pages-deploy-action@4.1.7 45 | with: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | BRANCH: gh-pages 48 | FOLDER: ${{ env.OUTPUT_PATH }} 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | 290 | # macOS 291 | .DS_Store 292 | -------------------------------------------------------------------------------- /.nuke/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Build Schema", 4 | "$ref": "#/definitions/build", 5 | "definitions": { 6 | "build": { 7 | "type": "object", 8 | "properties": { 9 | "Configuration": { 10 | "type": "string", 11 | "description": "configuration" 12 | }, 13 | "Continue": { 14 | "type": "boolean", 15 | "description": "Indicates to continue a previously failed build attempt" 16 | }, 17 | "Help": { 18 | "type": "boolean", 19 | "description": "Shows the help text for this build assembly" 20 | }, 21 | "Host": { 22 | "type": "string", 23 | "description": "Host for execution. Default is 'automatic'", 24 | "enum": [ 25 | "AppVeyor", 26 | "AzurePipelines", 27 | "Bamboo", 28 | "Bitrise", 29 | "GitHubActions", 30 | "GitLab", 31 | "Jenkins", 32 | "Rider", 33 | "SpaceAutomation", 34 | "TeamCity", 35 | "Terminal", 36 | "TravisCI", 37 | "VisualStudio", 38 | "VSCode" 39 | ] 40 | }, 41 | "NoLogo": { 42 | "type": "boolean", 43 | "description": "Disables displaying the NUKE logo" 44 | }, 45 | "Partition": { 46 | "type": "string", 47 | "description": "Partition to use on CI" 48 | }, 49 | "Plan": { 50 | "type": "boolean", 51 | "description": "Shows the execution plan (HTML)" 52 | }, 53 | "Profile": { 54 | "type": "array", 55 | "description": "Defines the profiles to load", 56 | "items": { 57 | "type": "string" 58 | } 59 | }, 60 | "PublishFramework": { 61 | "type": "string", 62 | "description": "publish-framework" 63 | }, 64 | "PublishProject": { 65 | "type": "string", 66 | "description": "publish-project" 67 | }, 68 | "PublishRuntime": { 69 | "type": "string", 70 | "description": "publish-runtime" 71 | }, 72 | "PublishSelfContained": { 73 | "type": "boolean", 74 | "description": "publish-self-contained" 75 | }, 76 | "Root": { 77 | "type": "string", 78 | "description": "Root directory during build execution" 79 | }, 80 | "Skip": { 81 | "type": "array", 82 | "description": "List of targets to be skipped. Empty list skips all dependencies", 83 | "items": { 84 | "type": "string", 85 | "enum": [ 86 | "Clean", 87 | "Compile", 88 | "Pack", 89 | "Publish", 90 | "Restore", 91 | "Test" 92 | ] 93 | } 94 | }, 95 | "Solution": { 96 | "type": "string", 97 | "description": "Path to a solution file that is automatically loaded" 98 | }, 99 | "Target": { 100 | "type": "array", 101 | "description": "List of targets to be invoked. Default is '{default_target}'", 102 | "items": { 103 | "type": "string", 104 | "enum": [ 105 | "Clean", 106 | "Compile", 107 | "Pack", 108 | "Publish", 109 | "Restore", 110 | "Test" 111 | ] 112 | } 113 | }, 114 | "Verbosity": { 115 | "type": "string", 116 | "description": "Logging verbosity during build execution. Default is 'Normal'", 117 | "enum": [ 118 | "Minimal", 119 | "Normal", 120 | "Quiet", 121 | "Verbose" 122 | ] 123 | }, 124 | "VersionSuffix": { 125 | "type": "string", 126 | "description": "version-suffix" 127 | } 128 | } 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /.nuke/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./build.schema.json", 3 | "Solution": "SvgToXaml.sln" 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Wiesław Šoltés 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SvgToXaml 2 | 3 | [![Build Status](https://dev.azure.com/wieslawsoltes/GitHub/_apis/build/status/wieslawsoltes.SvgToXaml?branchName=main)](https://dev.azure.com/wieslawsoltes/GitHub/_build/latest?definitionId=92&branchName=main) 4 | [![CI](https://github.com/wieslawsoltes/SvgToXaml/actions/workflows/build.yml/badge.svg)](https://github.com/wieslawsoltes/SvgToXaml/actions/workflows/build.yml) 5 | 6 | [![NuGet](https://img.shields.io/nuget/v/SvgToXamlConverter.svg)](https://www.nuget.org/packages/SvgToXamlConverter) 7 | [![NuGet](https://img.shields.io/nuget/dt/SvgToXamlConverter.svg)](https://www.nuget.org/packages/SvgToXamlConverter) 8 | 9 | [![GitHub release](https://img.shields.io/github/release/wieslawsoltes/SvgToXaml.svg)](https://github.com/wieslawsoltes/SvgToXaml) 10 | [![Github All Releases](https://img.shields.io/github/downloads/wieslawsoltes/SvgToXaml/total.svg)](https://github.com/wieslawsoltes/SvgToXaml) 11 | [![Github Releases](https://img.shields.io/github/downloads/wieslawsoltes/SvgToXaml/latest/total.svg)](https://github.com/wieslawsoltes/SvgToXaml) 12 | 13 | Svg to xaml conveter. 14 | 15 | ![image](https://user-images.githubusercontent.com/2297442/130685251-185cc489-8724-408b-8965-955f9bc77177.png) 16 | 17 | ## License 18 | 19 | SvgToXaml is licensed under the [MIT license](LICENSE). 20 | -------------------------------------------------------------------------------- /SvgToXaml.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # 4 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7F50DE02-D1D9-48CF-B810-2F036D022B34}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgToXaml.Converter", "src\SvgToXaml.Converter\SvgToXaml.Converter.csproj", "{3066AB6A-F627-491B-A187-AE1DAB5C73C5}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgToXaml.Base", "src\SvgToXaml.Base\SvgToXaml.Base.csproj", "{FF9A2CC2-C733-421E-8F6D-39442041C86B}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "svgxaml", "src\svgxaml\svgxaml.csproj", "{3C542F9C-446C-4B77-A4B6-FD0D74DCC9E7}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{3DA1182D-FE58-4135-83B6-12612E71F8E9}" 13 | ProjectSection(SolutionItems) = preProject 14 | .editorconfig = .editorconfig 15 | build.ps1 = build.ps1 16 | build.sh = build.sh 17 | global.json = global.json 18 | build\SvgToXaml.public.snk = build\SvgToXaml.public.snk 19 | azure-pipelines.yml = azure-pipelines.yml 20 | EndProjectSection 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{038CD179-37B9-4AD8-B94E-D077723F45D7}" 23 | ProjectSection(SolutionItems) = preProject 24 | LICENSE = LICENSE 25 | README.md = README.md 26 | EndProjectSection 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0A02A9B3-8AF1-4AAD-93B8-2E18438C9A3F}" 29 | EndProject 30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgToXamlConverter.UnitTests", "tests\SvgToXamlConverter.UnitTests\SvgToXamlConverter.UnitTests.csproj", "{A9A6981E-12E8-4553-AA63-0EF5F1E1B575}" 31 | EndProject 32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "git", "git", "{AEF545DD-DD4E-454A-91E6-A1D82B6382F6}" 33 | ProjectSection(SolutionItems) = preProject 34 | .gitattributes = .gitattributes 35 | .gitignore = .gitignore 36 | EndProjectSection 37 | EndProject 38 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{5B9F5688-208D-449D-A2D3-021834123C78}" 39 | ProjectSection(SolutionItems) = preProject 40 | nuget.config = nuget.config 41 | EndProjectSection 42 | EndProject 43 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{D4B713E6-64FD-411D-93FD-462D35E4A3A1}" 44 | ProjectSection(SolutionItems) = preProject 45 | build\Avalonia.Desktop.props = build\Avalonia.Desktop.props 46 | build\Avalonia.Diagnostics.props = build\Avalonia.Diagnostics.props 47 | build\Avalonia.props = build\Avalonia.props 48 | build\Avalonia.Skia.props = build\Avalonia.Skia.props 49 | build\Base.props = build\Base.props 50 | build\ReferenceAssemblies.props = build\ReferenceAssemblies.props 51 | build\SignAssembly.props = build\SignAssembly.props 52 | build\SkiaSharp.Linux.props = build\SkiaSharp.Linux.props 53 | build\SkiaSharp.props = build\SkiaSharp.props 54 | build\SourceLink.props = build\SourceLink.props 55 | build\System.CommandLine.props = build\System.CommandLine.props 56 | build\XUnit.props = build\XUnit.props 57 | build\Avalonia.Controls.Skia.props = build\Avalonia.Controls.Skia.props 58 | build\Avalonia.Markup.Xaml.Loader.props = build\Avalonia.Markup.Xaml.Loader.props 59 | build\Avalonia.Xaml.Behaviors.props = build\Avalonia.Xaml.Behaviors.props 60 | build\Svg.Skia.props = build\Svg.Skia.props 61 | build\System.Text.Json.props = build\System.Text.Json.props 62 | build\Avalonia.Themes.Fluent.props = build\Avalonia.Themes.Fluent.props 63 | build\CommunityToolkit.Mvvm.props = build\CommunityToolkit.Mvvm.props 64 | build\ReactiveMarbles.PropertyChanged.props = build\ReactiveMarbles.PropertyChanged.props 65 | build\Avalonia.ThemeManager.props = build\Avalonia.ThemeManager.props 66 | build\Avalonia.Web.props = build\Avalonia.Web.props 67 | EndProjectSection 68 | EndProject 69 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "_build", "build\build\_build.csproj", "{E9A88CB0-B853-44C3-8A7E-F67DAD2A7F9B}" 70 | EndProject 71 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgToXaml.Desktop", "src\SvgToXaml.Desktop\SvgToXaml.Desktop.csproj", "{F5405E7E-2C76-406F-B81D-159CC9F712A4}" 72 | EndProject 73 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgToXaml.Web", "src\SvgToXaml.Web\SvgToXaml.Web.csproj", "{26D44BDD-F30E-42C5-AF90-B93BD53C67AC}" 74 | EndProject 75 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgToXaml.Model", "src\SvgToXaml.Model\SvgToXaml.Model.csproj", "{67FC1B34-6B44-47EB-8D70-28A50B9B7F7C}" 76 | EndProject 77 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{2500DF4E-966C-4DDF-8F48-2D2E96A12F1F}" 78 | EndProject 79 | Global 80 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 81 | Debug|Any CPU = Debug|Any CPU 82 | Release|Any CPU = Release|Any CPU 83 | EndGlobalSection 84 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 85 | {3066AB6A-F627-491B-A187-AE1DAB5C73C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 86 | {3066AB6A-F627-491B-A187-AE1DAB5C73C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 87 | {3066AB6A-F627-491B-A187-AE1DAB5C73C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 88 | {3066AB6A-F627-491B-A187-AE1DAB5C73C5}.Release|Any CPU.Build.0 = Release|Any CPU 89 | {FF9A2CC2-C733-421E-8F6D-39442041C86B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 90 | {FF9A2CC2-C733-421E-8F6D-39442041C86B}.Debug|Any CPU.Build.0 = Debug|Any CPU 91 | {FF9A2CC2-C733-421E-8F6D-39442041C86B}.Release|Any CPU.ActiveCfg = Release|Any CPU 92 | {FF9A2CC2-C733-421E-8F6D-39442041C86B}.Release|Any CPU.Build.0 = Release|Any CPU 93 | {3C542F9C-446C-4B77-A4B6-FD0D74DCC9E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 94 | {3C542F9C-446C-4B77-A4B6-FD0D74DCC9E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 95 | {3C542F9C-446C-4B77-A4B6-FD0D74DCC9E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 96 | {3C542F9C-446C-4B77-A4B6-FD0D74DCC9E7}.Release|Any CPU.Build.0 = Release|Any CPU 97 | {A9A6981E-12E8-4553-AA63-0EF5F1E1B575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 98 | {A9A6981E-12E8-4553-AA63-0EF5F1E1B575}.Debug|Any CPU.Build.0 = Debug|Any CPU 99 | {A9A6981E-12E8-4553-AA63-0EF5F1E1B575}.Release|Any CPU.ActiveCfg = Release|Any CPU 100 | {A9A6981E-12E8-4553-AA63-0EF5F1E1B575}.Release|Any CPU.Build.0 = Release|Any CPU 101 | {E9A88CB0-B853-44C3-8A7E-F67DAD2A7F9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 102 | {E9A88CB0-B853-44C3-8A7E-F67DAD2A7F9B}.Debug|Any CPU.Build.0 = Debug|Any CPU 103 | {E9A88CB0-B853-44C3-8A7E-F67DAD2A7F9B}.Release|Any CPU.ActiveCfg = Release|Any CPU 104 | {E9A88CB0-B853-44C3-8A7E-F67DAD2A7F9B}.Release|Any CPU.Build.0 = Release|Any CPU 105 | {F5405E7E-2C76-406F-B81D-159CC9F712A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 106 | {F5405E7E-2C76-406F-B81D-159CC9F712A4}.Debug|Any CPU.Build.0 = Debug|Any CPU 107 | {F5405E7E-2C76-406F-B81D-159CC9F712A4}.Release|Any CPU.ActiveCfg = Release|Any CPU 108 | {F5405E7E-2C76-406F-B81D-159CC9F712A4}.Release|Any CPU.Build.0 = Release|Any CPU 109 | {26D44BDD-F30E-42C5-AF90-B93BD53C67AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 110 | {26D44BDD-F30E-42C5-AF90-B93BD53C67AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 111 | {26D44BDD-F30E-42C5-AF90-B93BD53C67AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 112 | {26D44BDD-F30E-42C5-AF90-B93BD53C67AC}.Release|Any CPU.Build.0 = Release|Any CPU 113 | {67FC1B34-6B44-47EB-8D70-28A50B9B7F7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 114 | {67FC1B34-6B44-47EB-8D70-28A50B9B7F7C}.Debug|Any CPU.Build.0 = Debug|Any CPU 115 | {67FC1B34-6B44-47EB-8D70-28A50B9B7F7C}.Release|Any CPU.ActiveCfg = Release|Any CPU 116 | {67FC1B34-6B44-47EB-8D70-28A50B9B7F7C}.Release|Any CPU.Build.0 = Release|Any CPU 117 | EndGlobalSection 118 | GlobalSection(NestedProjects) = preSolution 119 | {3066AB6A-F627-491B-A187-AE1DAB5C73C5} = {7F50DE02-D1D9-48CF-B810-2F036D022B34} 120 | {A9A6981E-12E8-4553-AA63-0EF5F1E1B575} = {0A02A9B3-8AF1-4AAD-93B8-2E18438C9A3F} 121 | {AEF545DD-DD4E-454A-91E6-A1D82B6382F6} = {3DA1182D-FE58-4135-83B6-12612E71F8E9} 122 | {5B9F5688-208D-449D-A2D3-021834123C78} = {3DA1182D-FE58-4135-83B6-12612E71F8E9} 123 | {D4B713E6-64FD-411D-93FD-462D35E4A3A1} = {3DA1182D-FE58-4135-83B6-12612E71F8E9} 124 | {E9A88CB0-B853-44C3-8A7E-F67DAD2A7F9B} = {3DA1182D-FE58-4135-83B6-12612E71F8E9} 125 | {67FC1B34-6B44-47EB-8D70-28A50B9B7F7C} = {7F50DE02-D1D9-48CF-B810-2F036D022B34} 126 | {F5405E7E-2C76-406F-B81D-159CC9F712A4} = {2500DF4E-966C-4DDF-8F48-2D2E96A12F1F} 127 | {26D44BDD-F30E-42C5-AF90-B93BD53C67AC} = {2500DF4E-966C-4DDF-8F48-2D2E96A12F1F} 128 | {FF9A2CC2-C733-421E-8F6D-39442041C86B} = {2500DF4E-966C-4DDF-8F48-2D2E96A12F1F} 129 | {3C542F9C-446C-4B77-A4B6-FD0D74DCC9E7} = {2500DF4E-966C-4DDF-8F48-2D2E96A12F1F} 130 | EndGlobalSection 131 | EndGlobal 132 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | name: $(date:yyyyMMdd)$(rev:-rr) 2 | 3 | resources: 4 | repositories: 5 | - repository: templates 6 | endpoint: wieslawsoltes 7 | type: github 8 | name: wieslawsoltes/BuildTemplates 9 | ref: refs/tags/v2.0.0 10 | 11 | variables: 12 | BuildConfiguration: 'Release' 13 | BuildPlatform: 'Any CPU' 14 | PublishFramework: 'net8.0' 15 | PublishProject: 'SvgToXaml.Desktop' 16 | PublishRuntime: '' 17 | Workloads: 'wasm-tools wasm-experimental' 18 | 19 | jobs: 20 | - template: Test-PowerShell.yml@templates 21 | parameters: 22 | name: 'Test_Windows' 23 | vmImage: 'windows-2022' 24 | BuildConfiguration: ${{ variables.BuildConfiguration }} 25 | Workloads: ${{ variables.Workloads }} 26 | 27 | - template: Test-Bash.yml@templates 28 | parameters: 29 | name: 'Test_Linux' 30 | vmImage: 'ubuntu-20.04' 31 | BuildConfiguration: ${{ variables.BuildConfiguration }} 32 | Workloads: ${{ variables.Workloads }} 33 | 34 | - template: Test-Bash.yml@templates 35 | parameters: 36 | name: 'Test_macOS' 37 | vmImage: 'macOS-14' 38 | BuildConfiguration: ${{ variables.BuildConfiguration }} 39 | Workloads: ${{ variables.Workloads }} 40 | 41 | - template: Pack-MyGet.yml@templates 42 | parameters: 43 | name: 'Pack_MyGet' 44 | vmImage: 'windows-2022' 45 | BuildConfiguration: ${{ variables.BuildConfiguration }} 46 | Workloads: ${{ variables.Workloads }} 47 | 48 | - template: Pack-NuGet.yml@templates 49 | parameters: 50 | name: 'Pack_NuGet' 51 | vmImage: 'windows-2022' 52 | BuildConfiguration: ${{ variables.BuildConfiguration }} 53 | Workloads: ${{ variables.Workloads }} 54 | 55 | - template: Publish-PowerShell.yml@templates 56 | parameters: 57 | name: 'Publish_Windows' 58 | vmImage: 'windows-2022' 59 | BuildConfiguration: ${{ variables.BuildConfiguration }} 60 | Workloads: ${{ variables.Workloads }} 61 | PublishFramework: ${{ variables.PublishFramework }} 62 | PublishProject: ${{ variables.PublishProject }} 63 | PublishRuntime: 'win-x64' 64 | 65 | - template: Publish-Bash.yml@templates 66 | parameters: 67 | name: 'Publish_Linux' 68 | vmImage: 'ubuntu-20.04' 69 | BuildConfiguration: ${{ variables.BuildConfiguration }} 70 | Workloads: ${{ variables.Workloads }} 71 | PublishFramework: ${{ variables.PublishFramework }} 72 | PublishProject: ${{ variables.PublishProject }} 73 | PublishRuntime: 'linux-x64' 74 | 75 | - template: Publish-Bash.yml@templates 76 | parameters: 77 | name: 'Publish_macOS' 78 | vmImage: 'macOS-14' 79 | BuildConfiguration: ${{ variables.BuildConfiguration }} 80 | Workloads: ${{ variables.Workloads }} 81 | PublishFramework: ${{ variables.PublishFramework }} 82 | PublishProject: ${{ variables.PublishProject }} 83 | PublishRuntime: 'osx-x64' 84 | 85 | - template: Publish-Bash.yml@templates 86 | parameters: 87 | name: 'Publish_macOS_arm64' 88 | vmImage: 'macOS-14' 89 | BuildConfiguration: ${{ variables.BuildConfiguration }} 90 | Workloads: ${{ variables.Workloads }} 91 | PublishFramework: ${{ variables.PublishFramework }} 92 | PublishProject: ${{ variables.PublishProject }} 93 | PublishRuntime: 'osx-arm64' 94 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 3 | :; ${SCRIPT_DIR}/build.sh "$@" 4 | :; exit $? 5 | 6 | @ECHO OFF 7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* 8 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 4 | [string[]]$BuildArguments 5 | ) 6 | 7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" 8 | 9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } 10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 11 | 12 | ########################################################################### 13 | # CONFIGURATION 14 | ########################################################################### 15 | 16 | $BuildProjectFile = "$PSScriptRoot\build\build\_build.csproj" 17 | $TempDirectory = "$PSScriptRoot\\.nuke\temp" 18 | 19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json" 20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" 21 | $DotNetChannel = "Current" 22 | 23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 25 | $env:DOTNET_MULTILEVEL_LOOKUP = 0 26 | 27 | ########################################################################### 28 | # EXECUTION 29 | ########################################################################### 30 | 31 | function ExecSafe([scriptblock] $cmd) { 32 | & $cmd 33 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 34 | } 35 | 36 | # If dotnet CLI is installed globally and it matches requested version, use for execution 37 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` 38 | $(dotnet --version) -and $LASTEXITCODE -eq 0) { 39 | $env:DOTNET_EXE = (Get-Command "dotnet").Path 40 | } 41 | else { 42 | # Download install script 43 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" 44 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null 45 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 46 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) 47 | 48 | # If global.json exists, load expected version 49 | if (Test-Path $DotNetGlobalFile) { 50 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) 51 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { 52 | $DotNetVersion = $DotNetGlobal.sdk.version 53 | } 54 | } 55 | 56 | # Install by channel or version 57 | $DotNetDirectory = "$TempDirectory\dotnet-win" 58 | if (!(Test-Path variable:DotNetVersion)) { 59 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } 60 | } else { 61 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } 62 | } 63 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" 64 | } 65 | 66 | Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" 67 | 68 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } 69 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 70 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bash --version 2>&1 | head -n 1 4 | 5 | set -eo pipefail 6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 7 | 8 | ########################################################################### 9 | # CONFIGURATION 10 | ########################################################################### 11 | 12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/build/_build.csproj" 13 | TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" 14 | 15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" 16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" 17 | DOTNET_CHANNEL="Current" 18 | 19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 21 | export DOTNET_MULTILEVEL_LOOKUP=0 22 | 23 | ########################################################################### 24 | # EXECUTION 25 | ########################################################################### 26 | 27 | function FirstJsonValue { 28 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" 29 | } 30 | 31 | # If dotnet CLI is installed globally and it matches requested version, use for execution 32 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then 33 | export DOTNET_EXE="$(command -v dotnet)" 34 | else 35 | # Download install script 36 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 37 | mkdir -p "$TEMP_DIRECTORY" 38 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 39 | chmod +x "$DOTNET_INSTALL_FILE" 40 | 41 | # If global.json exists, load expected version 42 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 43 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") 44 | if [[ "$DOTNET_VERSION" == "" ]]; then 45 | unset DOTNET_VERSION 46 | fi 47 | fi 48 | 49 | # Install by channel or version 50 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 51 | if [[ -z ${DOTNET_VERSION+x} ]]; then 52 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 53 | else 54 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 55 | fi 56 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 57 | fi 58 | 59 | echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" 60 | 61 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet 62 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 63 | -------------------------------------------------------------------------------- /build/Avalonia.Controls.Skia.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.Desktop.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.Diagnostics.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.Markup.Xaml.Loader.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.Skia.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.ThemeManager.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.Themes.Fluent.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /build/Avalonia.Web.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.Xaml.Behaviors.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Avalonia.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/Base.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11.2.0 5 | 6 | Wiesław Šoltés 7 | Wiesław Šoltés 8 | Copyright © Wiesław Šoltés 2024 9 | https://github.com/wieslawsoltes/SvgToXaml 10 | 11 | 12 | latest 13 | latest 14 | 15 | 16 | -------------------------------------------------------------------------------- /build/CommunityToolkit.Mvvm.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/ReactiveMarbles.PropertyChanged.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/ReferenceAssemblies.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/SignAssembly.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | True 5 | $(MSBuildThisFileDirectory)\SvgToXaml.public.snk 6 | false 7 | true 8 | 9 | 10 | -------------------------------------------------------------------------------- /build/SkiaSharp.Linux.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/SkiaSharp.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/SourceLink.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | true 6 | embedded 7 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 8 | 9 | 10 | true 11 | 12 | 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /build/Svg.Skia.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/SvgToXaml.public.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wieslawsoltes/SvgToXaml/23ac52e18fa4d5b8b1ded09fb3378c7cae68e228/build/SvgToXaml.public.snk -------------------------------------------------------------------------------- /build/System.CommandLine.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/System.Text.Json.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/XUnit.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /build/build/Build.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Nuke.Common; 3 | using Nuke.Common.Git; 4 | using Nuke.Common.ProjectModel; 5 | using Nuke.Common.Tools.DotNet; 6 | using Nuke.Common.IO; 7 | using static Nuke.Common.IO.FileSystemTasks; 8 | using static Nuke.Common.IO.PathConstruction; 9 | using static Nuke.Common.Tools.DotNet.DotNetTasks; 10 | 11 | class Build : NukeBuild 12 | { 13 | public static int Main() => Execute(x => x.Compile); 14 | 15 | [Solution] 16 | readonly Solution Solution; 17 | 18 | [GitRepository] 19 | readonly GitRepository GitRepository; 20 | 21 | [Parameter("configuration")] 22 | public string Configuration { get; set; } 23 | 24 | [Parameter("version-suffix")] 25 | public string VersionSuffix { get; set; } 26 | 27 | [Parameter("publish-framework")] 28 | public string PublishFramework { get; set; } 29 | 30 | [Parameter("publish-runtime")] 31 | public string PublishRuntime { get; set; } 32 | 33 | [Parameter("publish-project")] 34 | public string PublishProject { get; set; } 35 | 36 | [Parameter("publish-self-contained")] 37 | public bool PublishSelfContained { get; set; } = true; 38 | 39 | AbsolutePath SourceDirectory => RootDirectory / "src"; 40 | 41 | AbsolutePath TestsDirectory => RootDirectory / "tests"; 42 | 43 | AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; 44 | 45 | protected override void OnBuildInitialized() 46 | { 47 | Configuration = Configuration ?? "Release"; 48 | VersionSuffix = VersionSuffix ?? ""; 49 | } 50 | 51 | private void DeleteDirectories(IReadOnlyCollection directories) 52 | { 53 | foreach (var directory in directories) 54 | { 55 | DeleteDirectory(directory); 56 | } 57 | } 58 | 59 | Target Clean => _ => _ 60 | .Executes(() => 61 | { 62 | DeleteDirectories(GlobDirectories(SourceDirectory, "**/bin", "**/obj")); 63 | DeleteDirectories(GlobDirectories(TestsDirectory, "**/bin", "**/obj")); 64 | EnsureCleanDirectory(ArtifactsDirectory); 65 | }); 66 | 67 | Target Restore => _ => _ 68 | .DependsOn(Clean) 69 | .Executes(() => 70 | { 71 | DotNetRestore(s => s 72 | .SetProjectFile(Solution)); 73 | }); 74 | 75 | Target Compile => _ => _ 76 | .DependsOn(Restore) 77 | .Executes(() => 78 | { 79 | DotNetBuild(s => s 80 | .SetProjectFile(Solution) 81 | .SetConfiguration(Configuration) 82 | .SetVersionSuffix(VersionSuffix) 83 | .EnableNoRestore()); 84 | }); 85 | 86 | Target Test => _ => _ 87 | .DependsOn(Compile) 88 | .Executes(() => 89 | { 90 | DotNetTest(s => s 91 | .SetProjectFile(Solution) 92 | .SetConfiguration(Configuration) 93 | .SetLoggers("trx") 94 | .SetResultsDirectory(ArtifactsDirectory / "TestResults") 95 | .EnableNoBuild() 96 | .EnableNoRestore()); 97 | }); 98 | 99 | Target Pack => _ => _ 100 | .DependsOn(Test) 101 | .Executes(() => 102 | { 103 | DotNetPack(s => s 104 | .SetProject(Solution) 105 | .SetConfiguration(Configuration) 106 | .SetVersionSuffix(VersionSuffix) 107 | .SetOutputDirectory(ArtifactsDirectory / "NuGet") 108 | .EnableNoBuild() 109 | .EnableNoRestore()); 110 | }); 111 | 112 | Target Publish => _ => _ 113 | .DependsOn(Test) 114 | .Requires(() => PublishRuntime) 115 | .Requires(() => PublishFramework) 116 | .Requires(() => PublishProject) 117 | .Executes(() => 118 | { 119 | DotNetPublish(s => s 120 | .SetProject(Solution.GetProject(PublishProject)) 121 | .SetConfiguration(Configuration) 122 | .SetVersionSuffix(VersionSuffix) 123 | .SetFramework(PublishFramework) 124 | .SetRuntime(PublishRuntime) 125 | .SetSelfContained(PublishSelfContained) 126 | .SetOutput(ArtifactsDirectory / "Publish" / PublishProject + "-" + PublishFramework + "-" + PublishRuntime)); 127 | }); 128 | } 129 | -------------------------------------------------------------------------------- /build/build/_build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | false 7 | False 8 | CS0649;CS0169 9 | 1 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.403", 4 | "rollForward": "latestMinor", 5 | "allowPrerelease": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/App.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Text.Json; 4 | using Avalonia; 5 | using Avalonia.Controls.ApplicationLifetimes; 6 | using Avalonia.Markup.Xaml; 7 | using Avalonia.ThemeManager; 8 | using SvgToXaml.ViewModels; 9 | using SvgToXaml.Views; 10 | 11 | namespace SvgToXaml; 12 | 13 | public class App : Application 14 | { 15 | public static IThemeManager? ThemeManager; 16 | 17 | private const string ProjectFileName = "project.json"; 18 | 19 | public override void Initialize() 20 | { 21 | #if true 22 | ThemeManager = new FluentThemeManager(); 23 | #else 24 | ThemeManager = new SimpleThemeManager(); 25 | #endif 26 | ThemeManager.Initialize(this); 27 | ThemeManager.Switch(1); 28 | 29 | AvaloniaXamlLoader.Load(this); 30 | } 31 | 32 | public override void OnFrameworkInitializationCompleted() 33 | { 34 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 35 | { 36 | var mainViewModel = new MainWindowViewModel(); 37 | 38 | var mainWindow = new MainWindow 39 | { 40 | DataContext = mainViewModel 41 | }; 42 | 43 | desktop.MainWindow = mainWindow; 44 | 45 | desktop.Startup += (_, _) => 46 | { 47 | if (File.Exists(ProjectFileName)) 48 | { 49 | var json = File.ReadAllText(ProjectFileName); 50 | var project = JsonSerializer.Deserialize(json); 51 | if (project is { }) 52 | { 53 | mainViewModel.Project = project; 54 | 55 | foreach (var fileItemViewModel in mainViewModel.Project.Items) 56 | { 57 | mainViewModel.Initialize(fileItemViewModel); 58 | } 59 | } 60 | } 61 | }; 62 | 63 | desktop.Exit += (_, _) => 64 | { 65 | var json = JsonSerializer.Serialize(mainViewModel.Project); 66 | File.WriteAllText(ProjectFileName, json); 67 | }; 68 | } 69 | else if (ApplicationLifetime is ISingleViewApplicationLifetime single) 70 | { 71 | var mainViewModel = new MainWindowViewModel(); 72 | 73 | var mainView = new MainView() 74 | { 75 | DataContext = mainViewModel 76 | }; 77 | 78 | single.MainView = mainView; 79 | } 80 | 81 | base.OnFrameworkInitializationCompleted(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Assets/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wieslawsoltes/SvgToXaml/23ac52e18fa4d5b8b1ded09fb3378c7cae68e228/src/SvgToXaml.Base/Assets/Icon.ico -------------------------------------------------------------------------------- /src/SvgToXaml.Base/SvgToXaml.Base.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library 5 | net8.0 6 | False 7 | enable 8 | latest 9 | Assets\Icon.ico 10 | SvgToXaml 11 | 12 | 13 | 14 | An Svg to Xaml conveter. 15 | SvgToXaml.Base 16 | MIT 17 | svg;xaml;axaml;avalonia;avaloniaui;convert;converter;vector graphics;rendering;2d;graphics;geometry;shapes;skiasharp;skia 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using SvgToXaml.ViewModels; 5 | 6 | namespace SvgToXaml; 7 | 8 | public class ViewLocator : IDataTemplate 9 | { 10 | public bool SupportsRecycling => false; 11 | 12 | public Control Build(object? data) 13 | { 14 | var name = data?.GetType().FullName!.Replace("ViewModel", "View"); 15 | var type = name is null ? null : Type.GetType(name); 16 | 17 | if (type != null) 18 | { 19 | return (Control) Activator.CreateInstance(type)!; 20 | } 21 | else 22 | { 23 | return new TextBlock {Text = "Not Found: " + name}; 24 | } 25 | } 26 | 27 | public bool Match(object? data) 28 | { 29 | return data is ViewModelBase; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewModels/FileItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json.Serialization; 3 | using System.Threading.Tasks; 4 | using System.Windows.Input; 5 | using CommunityToolkit.Mvvm.Input; 6 | using Svg.Model; 7 | 8 | namespace SvgToXaml.ViewModels; 9 | 10 | public class FileItemViewModel : ViewModelBase 11 | { 12 | private bool _isLoading; 13 | private string _name; 14 | private string _content; 15 | private SvgViewModel? _svg; 16 | private SkiaSharp.SKPicture? _picture; 17 | 18 | [JsonInclude] 19 | public string Name 20 | { 21 | get => _name; 22 | private set => SetProperty(ref _name, value); 23 | } 24 | 25 | [JsonInclude] 26 | public string Content 27 | { 28 | get => _content; 29 | private set => SetProperty(ref _content, value); 30 | } 31 | 32 | [JsonIgnore] 33 | public SvgViewModel? Svg 34 | { 35 | get => _svg; 36 | private set => SetProperty(ref _svg, value); 37 | } 38 | 39 | [JsonIgnore] 40 | public SkiaSharp.SKPicture? Picture 41 | { 42 | get => _picture; 43 | private set => SetProperty(ref _picture, value); 44 | } 45 | 46 | [JsonIgnore] 47 | public ICommand? RemoveCommand { get; private set; } 48 | 49 | [JsonConstructor] 50 | public FileItemViewModel(string name, string content) 51 | { 52 | _name = name; 53 | _content = content; 54 | } 55 | 56 | public FileItemViewModel(string name, string content, Func remove) 57 | : this(name, content) 58 | { 59 | Initialize(remove); 60 | } 61 | 62 | public void Initialize(Func remove) 63 | { 64 | RemoveCommand = new AsyncRelayCommand(async () => await remove(this)); 65 | } 66 | 67 | public async Task Load(DrawAttributes ignoreAttribute) 68 | { 69 | if (_isLoading) 70 | { 71 | return; 72 | } 73 | 74 | _isLoading = true; 75 | 76 | if (Picture is null) 77 | { 78 | await Task.Run(() => 79 | { 80 | Svg = new SvgViewModel(); 81 | Picture = Svg.FromSvg(Content, ignoreAttribute); 82 | }); 83 | } 84 | 85 | _isLoading = false; 86 | } 87 | 88 | public void Clean() 89 | { 90 | if (Picture is not null) 91 | { 92 | Picture?.Dispose(); 93 | Svg?.Dispose(); 94 | Picture = null; 95 | Svg = null; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.IO.Compression; 6 | using System.Linq; 7 | using System.Reactive.Linq; 8 | using System.Text; 9 | using System.Text.Json; 10 | using System.Text.Json.Serialization; 11 | using System.Threading.Tasks; 12 | using System.Windows.Input; 13 | using Avalonia; 14 | using Avalonia.Controls; 15 | using Avalonia.Controls.ApplicationLifetimes; 16 | using Avalonia.Input.Platform; 17 | using Avalonia.Platform.Storage; 18 | using Avalonia.Threading; 19 | using Avalonia.VisualTree; 20 | using CommunityToolkit.Mvvm.Input; 21 | using ReactiveMarbles.PropertyChanged; 22 | using SvgToXaml.Converter; 23 | using SvgToXaml.Model; 24 | 25 | namespace SvgToXaml.ViewModels; 26 | 27 | public class MainWindowViewModel : ViewModelBase 28 | { 29 | private ProjectViewModel _project; 30 | 31 | [JsonInclude] 32 | public ProjectViewModel Project 33 | { 34 | get => _project; 35 | set => SetProperty(ref _project, value); 36 | } 37 | 38 | [JsonIgnore] 39 | public ICommand ClearCommand { get; } 40 | 41 | [JsonIgnore] 42 | public ICommand OpenCommand { get; } 43 | 44 | [JsonIgnore] 45 | public ICommand SaveCommand { get; } 46 | 47 | [JsonIgnore] 48 | public ICommand AddCommand { get; } 49 | 50 | [JsonIgnore] 51 | public ICommand CopySelectedCommand { get; } 52 | 53 | [JsonIgnore] 54 | public ICommand CopyAllCommand { get; } 55 | 56 | [JsonIgnore] 57 | public ICommand ExportSelectedCommand { get; } 58 | 59 | [JsonIgnore] 60 | public ICommand ExportAllCommand { get; } 61 | 62 | [JsonIgnore] 63 | public ICommand ClipboardCommand { get; } 64 | 65 | [JsonConstructor] 66 | public MainWindowViewModel() 67 | { 68 | _project = new ProjectViewModel(); 69 | 70 | ClearCommand = new RelayCommand(Clear); 71 | 72 | OpenCommand = new AsyncRelayCommand(async () => await Open()); 73 | 74 | SaveCommand = new AsyncRelayCommand(async () => await Save()); 75 | 76 | AddCommand = new AsyncRelayCommand(async () => await Add()); 77 | 78 | CopySelectedCommand = new AsyncRelayCommand(async format => await CopySelected(format)); 79 | 80 | CopyAllCommand = new AsyncRelayCommand(async format => await CopyAll(format)); 81 | 82 | ExportSelectedCommand = new AsyncRelayCommand(async format => await ExportSelected(format)); 83 | 84 | ExportAllCommand = new AsyncRelayCommand(async format => await ExportAll(format)); 85 | 86 | ClipboardCommand = new AsyncRelayCommand(async format => await Clipboard(format)); 87 | 88 | // ReSharper disable once AsyncVoidLambda 89 | this.WhenChanged(x => x.Project.SelectedItem).DistinctUntilChanged().Subscribe(async x => 90 | { 91 | if (x is { }) 92 | { 93 | await x.Load(Project.GetIgnoreAttributes()); 94 | } 95 | }); 96 | 97 | // ReSharper disable once AsyncVoidLambda 98 | this.WhenChanged(x => x.Project.Settings.UseCompatMode).DistinctUntilChanged().Subscribe(async _ => 99 | { 100 | await Reload(); 101 | }); 102 | 103 | // ReSharper disable once AsyncVoidLambda 104 | this.WhenChanged(x => x.Project.Settings.IgnoreOpacity).DistinctUntilChanged().Subscribe(async _ => 105 | { 106 | await Reload(); 107 | }); 108 | 109 | // ReSharper disable once AsyncVoidLambda 110 | this.WhenChanged(x => x.Project.Settings.IgnoreFilter).DistinctUntilChanged().Subscribe(async _ => 111 | { 112 | await Reload(); 113 | }); 114 | 115 | // ReSharper disable once AsyncVoidLambda 116 | this.WhenChanged(x => x.Project.Settings.IgnoreClipPath).DistinctUntilChanged().Subscribe(async _ => 117 | { 118 | await Reload(); 119 | }); 120 | 121 | // ReSharper disable once AsyncVoidLambda 122 | this.WhenChanged(x => x.Project.Settings.IgnoreMask).DistinctUntilChanged().Subscribe(async _ => 123 | { 124 | await Reload(); 125 | }); 126 | } 127 | 128 | private void Clear() 129 | { 130 | Project.SelectedItem = null; 131 | Project.Items.Clear(); 132 | } 133 | 134 | private List GetOpenFileTypes() 135 | { 136 | return new List 137 | { 138 | StorageService.Json, 139 | StorageService.All 140 | }; 141 | } 142 | 143 | private static List GetSaveFileTypes() 144 | { 145 | return new List 146 | { 147 | StorageService.Json, 148 | StorageService.All 149 | }; 150 | } 151 | 152 | private static List GetImportFileTypes() 153 | { 154 | return new List 155 | { 156 | StorageService.ImageSvg, 157 | StorageService.ImageSvgz, 158 | StorageService.All 159 | }; 160 | } 161 | 162 | private static List GetExportFileTypes() 163 | { 164 | return new List 165 | { 166 | StorageService.Axaml, 167 | StorageService.Xaml, 168 | StorageService.All 169 | }; 170 | } 171 | 172 | private static IClipboard? GetClipboard() 173 | { 174 | if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } window }) 175 | { 176 | return window.Clipboard; 177 | } 178 | 179 | if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime { MainView: { } mainView }) 180 | { 181 | if (mainView.GetVisualRoot() is TopLevel topLevel) 182 | { 183 | return topLevel.Clipboard; 184 | } 185 | } 186 | 187 | return null; 188 | } 189 | 190 | private async Task Open() 191 | { 192 | var storageProvider = StorageService.GetStorageProvider(); 193 | if (storageProvider is null) 194 | { 195 | return; 196 | } 197 | 198 | var result = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions 199 | { 200 | Title = "Open project", 201 | FileTypeFilter = GetOpenFileTypes(), 202 | AllowMultiple = false 203 | }); 204 | 205 | var file = result.FirstOrDefault(); 206 | 207 | if (file is not null) 208 | { 209 | try 210 | { 211 | await using var stream = await file.OpenReadAsync(); 212 | using var reader = new StreamReader(stream); 213 | var json = await reader.ReadToEndAsync(); 214 | var project = JsonSerializer.Deserialize(json); 215 | if (project is { }) 216 | { 217 | Project = project; 218 | 219 | await Task.Run(() => 220 | { 221 | foreach (var fileItemViewModel in Project.Items) 222 | { 223 | Initialize(fileItemViewModel); 224 | } 225 | }); 226 | } 227 | } 228 | catch (Exception ex) 229 | { 230 | Debug.WriteLine(ex.Message); 231 | Debug.WriteLine(ex.StackTrace); 232 | } 233 | } 234 | } 235 | 236 | private async Task Save() 237 | { 238 | var storageProvider = StorageService.GetStorageProvider(); 239 | if (storageProvider is null) 240 | { 241 | return; 242 | } 243 | 244 | var file = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions 245 | { 246 | Title = "Save project", 247 | FileTypeChoices = GetSaveFileTypes(), 248 | SuggestedFileName = Path.GetFileNameWithoutExtension("project"), 249 | DefaultExtension = "json", 250 | ShowOverwritePrompt = true 251 | }); 252 | 253 | if (file is not null) 254 | { 255 | try 256 | { 257 | var json = await Task.Run(() => JsonSerializer.Serialize(Project)); 258 | await using var stream = await file.OpenWriteAsync(); 259 | await using var writer = new StreamWriter(stream); 260 | await writer.WriteAsync(json); 261 | } 262 | catch (Exception ex) 263 | { 264 | Debug.WriteLine(ex.Message); 265 | Debug.WriteLine(ex.StackTrace); 266 | } 267 | } 268 | } 269 | 270 | private async Task LoadFromStream(Stream stream, string name) 271 | { 272 | var extension = Path.GetExtension(name); 273 | var memoryStream = new MemoryStream(); 274 | 275 | if (extension == "svgz") 276 | { 277 | await using var gzipStream = new GZipStream(stream, CompressionMode.Decompress); 278 | await gzipStream.CopyToAsync(memoryStream); 279 | } 280 | else 281 | { 282 | await stream.CopyToAsync(memoryStream); 283 | } 284 | 285 | memoryStream.Position = 0; 286 | return memoryStream; 287 | } 288 | 289 | private async Task Add() 290 | { 291 | var storageProvider = StorageService.GetStorageProvider(); 292 | if (storageProvider is null) 293 | { 294 | return; 295 | } 296 | 297 | var result = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions 298 | { 299 | Title = "Import svgs", 300 | FileTypeFilter = GetImportFileTypes(), 301 | AllowMultiple = true 302 | }); 303 | 304 | foreach (var file in result) 305 | { 306 | try 307 | { 308 | await using var stream = await file.OpenReadAsync(); 309 | var ms = await LoadFromStream(stream, file.Name); 310 | await Add(ms, Path.GetFileName(file.Name)); 311 | } 312 | catch (Exception ex) 313 | { 314 | Debug.WriteLine(ex.Message); 315 | Debug.WriteLine(ex.StackTrace); 316 | } 317 | } 318 | } 319 | 320 | private async Task CopySelected(string? format) 321 | { 322 | if (Project.SelectedItem is null || string.IsNullOrWhiteSpace(format)) 323 | { 324 | return; 325 | } 326 | 327 | var xaml = await ToXaml(Project.SelectedItem, Project.Settings.EnableGenerateImage); 328 | 329 | await SetClipboard(xaml); 330 | } 331 | 332 | private async Task CopyAll(string? format) 333 | { 334 | if (string.IsNullOrWhiteSpace(format)) 335 | { 336 | return; 337 | } 338 | 339 | var inputItems = Project.Items.Select(x => new InputItem(x.Name, x.Content)).ToList(); 340 | var xaml = await ToXamlStyles(inputItems); 341 | await SetClipboard(xaml); 342 | } 343 | 344 | private async Task ExportSelected(string? format) 345 | { 346 | if (Project.SelectedItem is null || string.IsNullOrWhiteSpace(format)) 347 | { 348 | return; 349 | } 350 | 351 | var storageProvider = StorageService.GetStorageProvider(); 352 | if (storageProvider is null) 353 | { 354 | return; 355 | } 356 | 357 | var file = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions 358 | { 359 | Title = "Save xaml", 360 | FileTypeChoices = GetExportFileTypes(), 361 | SuggestedFileName = Path.GetFileNameWithoutExtension(Project.SelectedItem.Name) + ".axaml", 362 | DefaultExtension = "axaml", 363 | ShowOverwritePrompt = true 364 | }); 365 | 366 | if (file is not null) 367 | { 368 | try 369 | { 370 | var xaml = await ToXaml(Project.SelectedItem, Project.Settings.EnableGenerateImage); 371 | await using var stream = await file.OpenWriteAsync(); 372 | await using var writer = new StreamWriter(stream); 373 | await writer.WriteAsync(xaml); 374 | } 375 | catch (Exception ex) 376 | { 377 | Debug.WriteLine(ex.Message); 378 | Debug.WriteLine(ex.StackTrace); 379 | } 380 | } 381 | } 382 | 383 | private async Task ExportAll(string? format) 384 | { 385 | if (string.IsNullOrWhiteSpace(format)) 386 | { 387 | return; 388 | } 389 | 390 | var storageProvider = StorageService.GetStorageProvider(); 391 | if (storageProvider is null) 392 | { 393 | return; 394 | } 395 | 396 | var file = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions 397 | { 398 | Title = "Save xaml", 399 | FileTypeChoices = GetExportFileTypes(), 400 | SuggestedFileName = "Styles.axaml", 401 | DefaultExtension = "axaml", 402 | ShowOverwritePrompt = true 403 | }); 404 | 405 | if (file is not null) 406 | { 407 | try 408 | { 409 | var paths = Project.Items.Select(x => new InputItem(x.Name, x.Content)).ToList(); 410 | if (paths.Count > 0) 411 | { 412 | var xaml = await ToXamlStyles(paths); 413 | await using var stream = await file.OpenWriteAsync(); 414 | await using var writer = new StreamWriter(stream); 415 | await writer.WriteAsync(xaml); 416 | } 417 | } 418 | catch (Exception ex) 419 | { 420 | Debug.WriteLine(ex.Message); 421 | Debug.WriteLine(ex.StackTrace); 422 | } 423 | } 424 | } 425 | 426 | private async Task Clipboard(string? format) 427 | { 428 | if (string.IsNullOrWhiteSpace(format)) 429 | { 430 | return; 431 | } 432 | 433 | var svg = await Dispatcher.UIThread.InvokeAsync(async () => 434 | { 435 | try 436 | { 437 | if (GetClipboard() is {} clipboard) 438 | { 439 | return await clipboard.GetTextAsync(); 440 | } 441 | } 442 | catch 443 | { 444 | // ignored 445 | } 446 | 447 | return ""; 448 | }); 449 | 450 | var skSvg = new SvgViewModel(); 451 | 452 | try 453 | { 454 | skSvg.FromSvg(svg, Project.GetIgnoreAttributes()); 455 | } 456 | catch 457 | { 458 | // ignored 459 | } 460 | 461 | var text = await Task.Run(() => 462 | { 463 | if (Project.Settings.EnableGenerateImage) 464 | { 465 | var converter = new SvgToXamlConverter() 466 | { 467 | UseCompatMode = Project.Settings.UseCompatMode, 468 | ReuseExistingResources = Project.Settings.ReuseExistingResources, 469 | TransformGeometry = Project.Settings.TransformGeometry, 470 | Resources = Project.Settings.UseResources ? new SvgToXaml.Model.Resources.ResourceDictionary() : null 471 | }; 472 | 473 | var xaml = converter.ToXamlImage(skSvg.Model); 474 | return converter.Format(xaml); 475 | } 476 | else 477 | { 478 | var converter = new SvgToXamlConverter() 479 | { 480 | UseCompatMode = Project.Settings.UseCompatMode, 481 | ReuseExistingResources = Project.Settings.ReuseExistingResources, 482 | TransformGeometry = Project.Settings.TransformGeometry, 483 | Resources = Project.Settings.UseResources ? new SvgToXaml.Model.Resources.ResourceDictionary() : null 484 | }; 485 | 486 | var xaml = converter.ToXamlDrawingGroup(skSvg.Model); 487 | return converter.Format(xaml); 488 | } 489 | }); 490 | 491 | await SetClipboard(text); 492 | } 493 | 494 | private async Task SetClipboard(string? xaml) 495 | { 496 | if (xaml is not { }) 497 | { 498 | return; 499 | } 500 | 501 | await Dispatcher.UIThread.InvokeAsync(async () => 502 | { 503 | try 504 | { 505 | if (GetClipboard() is {} clipboard) 506 | { 507 | await clipboard.SetTextAsync(xaml); 508 | } 509 | } 510 | catch 511 | { 512 | // ignored 513 | } 514 | }); 515 | } 516 | 517 | private async Task Reload() 518 | { 519 | var items = Project.Items; 520 | if (items.Count == 0) 521 | { 522 | return; 523 | } 524 | 525 | await Task.Run(() => 526 | { 527 | foreach (var fileItemViewModel in items) 528 | { 529 | fileItemViewModel.Clean(); 530 | } 531 | }); 532 | 533 | if (Project.SelectedItem is { } selectedItem) 534 | { 535 | await selectedItem.Load(Project.GetIgnoreAttributes()); 536 | } 537 | } 538 | 539 | private async Task ToXaml(FileItemViewModel fileItemViewModel, bool enableGenerateImage) 540 | { 541 | return await Task.Run(async () => 542 | { 543 | if (fileItemViewModel.Picture is null) 544 | { 545 | await fileItemViewModel.Load(Project.GetIgnoreAttributes()); 546 | } 547 | 548 | if (fileItemViewModel.Svg is { }) 549 | { 550 | if (enableGenerateImage) 551 | { 552 | var converter = new SvgToXamlConverter() 553 | { 554 | UseCompatMode = Project.Settings.UseCompatMode, 555 | AddTransparentBackground = Project.Settings.AddTransparentBackground, 556 | ReuseExistingResources = Project.Settings.ReuseExistingResources, 557 | TransformGeometry = Project.Settings.TransformGeometry, 558 | Resources = Project.Settings.UseResources ? new SvgToXaml.Model.Resources.ResourceDictionary() : null 559 | }; 560 | 561 | var xaml = converter.ToXamlImage(fileItemViewModel.Svg.Model); 562 | return converter.Format(xaml); 563 | } 564 | else 565 | { 566 | var converter = new SvgToXamlConverter() 567 | { 568 | UseCompatMode = Project.Settings.UseCompatMode, 569 | AddTransparentBackground = Project.Settings.AddTransparentBackground, 570 | ReuseExistingResources = Project.Settings.ReuseExistingResources, 571 | TransformGeometry = Project.Settings.TransformGeometry, 572 | Resources = Project.Settings.UseResources ? new SvgToXaml.Model.Resources.ResourceDictionary() : null 573 | }; 574 | 575 | var xaml = converter.ToXamlDrawingGroup(fileItemViewModel.Svg.Model); 576 | return converter.Format(xaml); 577 | } 578 | } 579 | 580 | return ""; 581 | }); 582 | } 583 | 584 | private async Task ToXamlStyles(List inputItems) 585 | { 586 | return await Task.Run(() => 587 | { 588 | var converter = new SvgToXamlConverter() 589 | { 590 | UseCompatMode = Project.Settings.UseCompatMode, 591 | ReuseExistingResources = Project.Settings.ReuseExistingResources, 592 | TransformGeometry = Project.Settings.TransformGeometry, 593 | Resources = Project.Settings.UseResources ? new SvgToXaml.Model.Resources.ResourceDictionary() : null 594 | }; 595 | 596 | var xaml = converter.ToXamlStyles(inputItems, Project.Settings.EnableGenerateImage, Project.Settings.EnableGeneratePreview); 597 | return converter.Format(xaml); 598 | }); 599 | } 600 | 601 | public async void Drop(IEnumerable paths) 602 | { 603 | foreach (var path in paths) 604 | { 605 | if (File.GetAttributes(path).HasFlag(FileAttributes.Directory)) 606 | { 607 | var svgPaths = Directory.EnumerateFiles(path, "*.svg", SearchOption.AllDirectories); 608 | var svgzPaths = Directory.EnumerateFiles(path, "*.svgz", SearchOption.AllDirectories); 609 | Drop(svgPaths); 610 | Drop(svgzPaths); 611 | continue; 612 | } 613 | 614 | var extension = Path.GetExtension(path); 615 | switch (extension.ToLower()) 616 | { 617 | case ".svg": 618 | case ".svgz": 619 | { 620 | await using var stream = File.OpenRead(path); 621 | var ms = await LoadFromStream(stream, path); 622 | var name = Path.GetFileName(path); 623 | await Add(ms, name); 624 | break; 625 | } 626 | } 627 | } 628 | } 629 | 630 | private async Task Add(Stream stream, string name) 631 | { 632 | using var reader = new StreamReader(stream); 633 | var content = await reader.ReadToEndAsync(); 634 | var item = await Task.Run(() => new FileItemViewModel(name, content, Remove)); 635 | Project.Items.Add(item); 636 | } 637 | 638 | public record PreviewItem(string Image, string TabControl); 639 | 640 | public async Task GetPreview(FileItemViewModel item) 641 | { 642 | if (item.Svg is null) 643 | { 644 | await item.Load(Project.GetIgnoreAttributes()); 645 | } 646 | 647 | if (item.Svg is null) 648 | { 649 | return null; 650 | } 651 | 652 | try 653 | { 654 | var converter = new SvgToXamlConverter() 655 | { 656 | UseCompatMode = Project.Settings.UseCompatMode, 657 | ReuseExistingResources = Project.Settings.ReuseExistingResources, 658 | TransformGeometry = Project.Settings.TransformGeometry, 659 | Resources = Project.Settings.UseResources ? new SvgToXaml.Model.Resources.ResourceDictionary() : null 660 | }; 661 | 662 | var image = converter.ToXamlImage(item.Svg.Model); 663 | 664 | image = converter.Format(image); 665 | 666 | var sb = new StringBuilder(); 667 | 668 | sb.Append($""); 669 | 670 | sb.Append($""); 671 | sb.Append($""); 672 | sb.Append($""); 673 | sb.Append($""); 674 | sb.Append($""); 675 | sb.Append($""); 676 | sb.Append($""); 677 | sb.Append($""); 678 | sb.Append($""); 679 | sb.Append($""); 680 | sb.Append($""); 681 | sb.Append($""); 682 | sb.Append($""); 683 | sb.Append($"{image}"); 684 | sb.Append($""); 685 | sb.Append($""); 686 | sb.Append($""); 687 | 688 | sb.Append($""); 689 | sb.Append($""); 690 | sb.Append($""); 691 | 692 | sb.Append($""); 693 | 694 | return new PreviewItem(image, sb.ToString()); 695 | } 696 | catch (Exception exception) 697 | { 698 | Debug.WriteLine(exception); 699 | } 700 | 701 | return null; 702 | } 703 | 704 | private async Task Remove(FileItemViewModel item) 705 | { 706 | await Task.Run(() => 707 | { 708 | Project.Items.Remove(item); 709 | }); 710 | } 711 | 712 | public void Initialize(FileItemViewModel item) 713 | { 714 | item.Initialize(Remove); 715 | } 716 | } 717 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewModels/ProjectViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Text.Json.Serialization; 3 | using Svg.Model; 4 | 5 | namespace SvgToXaml.ViewModels; 6 | 7 | public class ProjectViewModel : ViewModelBase 8 | { 9 | private FileItemViewModel? _selectedItem; 10 | private ObservableCollection _items; 11 | private SettingsViewModel _settings; 12 | 13 | [JsonIgnore] 14 | public FileItemViewModel? SelectedItem 15 | { 16 | get => _selectedItem; 17 | set => SetProperty(ref _selectedItem, value); 18 | } 19 | 20 | [JsonInclude] 21 | public ObservableCollection Items 22 | { 23 | get => _items; 24 | set => SetProperty(ref _items, value); 25 | } 26 | 27 | [JsonInclude] 28 | public SettingsViewModel Settings 29 | { 30 | get => _settings; 31 | set => SetProperty(ref _settings, value); 32 | } 33 | 34 | [JsonConstructor] 35 | public ProjectViewModel() 36 | { 37 | _items = new ObservableCollection(); 38 | 39 | _settings = new SettingsViewModel() 40 | { 41 | EnableGenerateImage = true, 42 | EnableGeneratePreview = true, 43 | UseResources = true, 44 | ReuseExistingResources = true, 45 | TransformGeometry = false, 46 | UseCompatMode = false, 47 | AddTransparentBackground = true, 48 | IgnoreOpacity = false, 49 | IgnoreFilter = false, 50 | IgnoreClipPath = false, 51 | IgnoreMask = false, 52 | }; 53 | } 54 | 55 | public DrawAttributes GetIgnoreAttributes() 56 | { 57 | var ignoreAttribute = DrawAttributes.None; 58 | 59 | if (_settings.IgnoreOpacity) 60 | { 61 | ignoreAttribute |= DrawAttributes.Opacity; 62 | } 63 | 64 | if (_settings.IgnoreFilter) 65 | { 66 | ignoreAttribute |= DrawAttributes.Filter; 67 | } 68 | 69 | if (_settings.IgnoreClipPath) 70 | { 71 | ignoreAttribute |= DrawAttributes.ClipPath; 72 | } 73 | 74 | if (_settings.IgnoreMask) 75 | { 76 | ignoreAttribute |= DrawAttributes.Mask; 77 | } 78 | 79 | return ignoreAttribute; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewModels/SettingsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace SvgToXaml.ViewModels; 4 | 5 | public class SettingsViewModel : ViewModelBase 6 | { 7 | private bool _enableGenerateImage; 8 | private bool _enableGeneratePreview; 9 | private bool _useResources; 10 | private bool _reuseExistingResources; 11 | private bool _transformGeometry; 12 | private bool _useCompatMode; 13 | private bool _addTransparentBackground; 14 | private bool _ignoreOpacity; 15 | private bool _ignoreFilter; 16 | private bool _ignoreClipPath; 17 | private bool _ignoreMask; 18 | 19 | [JsonInclude] 20 | public bool EnableGenerateImage 21 | { 22 | get => _enableGenerateImage; 23 | set => SetProperty(ref _enableGenerateImage, value); 24 | } 25 | 26 | [JsonInclude] 27 | public bool EnableGeneratePreview 28 | { 29 | get => _enableGeneratePreview; 30 | set => SetProperty(ref _enableGeneratePreview, value); 31 | } 32 | 33 | [JsonInclude] 34 | public bool UseResources 35 | { 36 | get => _useResources; 37 | set => SetProperty(ref _useResources, value); 38 | } 39 | 40 | [JsonInclude] 41 | public bool ReuseExistingResources 42 | { 43 | get => _reuseExistingResources; 44 | set => SetProperty(ref _reuseExistingResources, value); 45 | } 46 | 47 | [JsonInclude] 48 | public bool TransformGeometry 49 | { 50 | get => _transformGeometry; 51 | set => SetProperty(ref _transformGeometry, value); 52 | } 53 | 54 | [JsonInclude] 55 | public bool UseCompatMode 56 | { 57 | get => _useCompatMode; 58 | set => SetProperty(ref _useCompatMode, value); 59 | } 60 | 61 | [JsonInclude] 62 | public bool AddTransparentBackground 63 | { 64 | get => _addTransparentBackground; 65 | set => SetProperty(ref _addTransparentBackground, value); 66 | } 67 | 68 | [JsonInclude] 69 | public bool IgnoreOpacity 70 | { 71 | get => _ignoreOpacity; 72 | set => SetProperty(ref _ignoreOpacity, value); 73 | } 74 | 75 | [JsonInclude] 76 | public bool IgnoreFilter 77 | { 78 | get => _ignoreFilter; 79 | set => SetProperty(ref _ignoreFilter, value); 80 | } 81 | 82 | [JsonInclude] 83 | public bool IgnoreClipPath 84 | { 85 | get => _ignoreClipPath; 86 | set => SetProperty(ref _ignoreClipPath, value); 87 | } 88 | 89 | [JsonInclude] 90 | public bool IgnoreMask 91 | { 92 | get => _ignoreMask; 93 | set => SetProperty(ref _ignoreMask, value); 94 | } 95 | 96 | [JsonConstructor] 97 | public SettingsViewModel() 98 | { 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewModels/StorageService.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.ApplicationLifetimes; 4 | using Avalonia.Platform.Storage; 5 | using Avalonia.VisualTree; 6 | 7 | namespace SvgToXaml.ViewModels; 8 | 9 | internal static class StorageService 10 | { 11 | public static FilePickerFileType All { get; } = new("All") 12 | { 13 | Patterns = new[] { "*.*" }, 14 | MimeTypes = new[] { "*/*" } 15 | }; 16 | 17 | public static FilePickerFileType Json { get; } = new("Json") 18 | { 19 | Patterns = new[] { "*.json" }, 20 | AppleUniformTypeIdentifiers = new[] { "public.json" }, 21 | MimeTypes = new[] { "application/json" } 22 | }; 23 | 24 | public static FilePickerFileType ImageSvg { get; } = new("Svg") 25 | { 26 | Patterns = new[] { "*.svg" }, 27 | AppleUniformTypeIdentifiers = new[] { "public.svg-image" }, 28 | MimeTypes = new[] { "image/svg+xml" } 29 | }; 30 | 31 | public static FilePickerFileType ImageSvgz { get; } = new("Svgz") 32 | { 33 | Patterns = new[] { "*.svgz" }, 34 | // TODO: 35 | AppleUniformTypeIdentifiers = new[] { "public.svg-image" }, 36 | // TODO: 37 | MimeTypes = new[] { "image/svg+xml" } 38 | }; 39 | 40 | public static FilePickerFileType Xaml { get; } = new("Xaml") 41 | { 42 | Patterns = new[] { "*.xaml" }, 43 | // TODO: 44 | AppleUniformTypeIdentifiers = new[] { "public.xaml" }, 45 | // TODO: 46 | MimeTypes = new[] { "application/xaml" } 47 | }; 48 | 49 | public static FilePickerFileType Axaml { get; } = new("Axaml") 50 | { 51 | Patterns = new[] { "*.axaml" }, 52 | // TODO: 53 | AppleUniformTypeIdentifiers = new[] { "public.axaml" }, 54 | // TODO: 55 | MimeTypes = new[] { "application/axaml" } 56 | }; 57 | 58 | public static IStorageProvider? GetStorageProvider() 59 | { 60 | if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } window }) 61 | { 62 | return window.StorageProvider; 63 | } 64 | 65 | if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime { MainView: { } mainView }) 66 | { 67 | var visualRoot = mainView.GetVisualRoot(); 68 | if (visualRoot is TopLevel topLevel) 69 | { 70 | return topLevel.StorageProvider; 71 | } 72 | } 73 | 74 | return null; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewModels/SvgViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Svg.Model; 3 | using ShimSkiaSharp; 4 | using Svg.Skia; 5 | 6 | namespace SvgToXaml.ViewModels; 7 | 8 | public class SvgViewModel : ViewModelBase 9 | { 10 | private static readonly SkiaModel s_model; 11 | private static readonly IAssetLoader s_assetLoader; 12 | 13 | public SKDrawable? Drawable { get; private set; } 14 | 15 | public SKPicture? Model { get; private set; } 16 | 17 | public SkiaSharp.SKPicture? Picture { get; private set; } 18 | 19 | static SvgViewModel() 20 | { 21 | s_model = new SkiaModel(new SKSvgSettings()); 22 | s_assetLoader = new SkiaAssetLoader(new SkiaModel(new SKSvgSettings())); 23 | } 24 | 25 | private void Reset() 26 | { 27 | Model = null; 28 | Drawable = null; 29 | 30 | Picture?.Dispose(); 31 | Picture = null; 32 | } 33 | 34 | public void Dispose() 35 | { 36 | Reset(); 37 | } 38 | 39 | public SkiaSharp.SKPicture? Load(Stream stream, DrawAttributes ignoreAttributes) 40 | { 41 | Reset(); 42 | var svgDocument = SvgExtensions.Open(stream); 43 | if (svgDocument is { }) 44 | { 45 | Model = SvgExtensions.ToModel(svgDocument, s_assetLoader, out var drawable, out _, ignoreAttributes); 46 | Drawable = drawable; 47 | Picture = s_model.ToSKPicture(Model); 48 | return Picture; 49 | } 50 | return null; 51 | } 52 | 53 | public SkiaSharp.SKPicture? FromSvg(string svg, DrawAttributes ignoreAttributes) 54 | { 55 | Reset(); 56 | var svgDocument = SvgExtensions.FromSvg(svg); 57 | if (svgDocument is { }) 58 | { 59 | Model = SvgExtensions.ToModel(svgDocument, s_assetLoader, out var drawable, out _, ignoreAttributes); 60 | Drawable = drawable; 61 | Picture = s_model.ToSKPicture(Model); 62 | return Picture; 63 | } 64 | return null; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace SvgToXaml.ViewModels; 4 | 5 | public class ViewModelBase : ObservableObject 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/ActionsView.axaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 33 | 34 | 48 | 49 | 54 | 55 | 73 | 74 | 79 | 80 | 94 | 95 | 109 | 110 | 115 | 116 | 130 | 131 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/ActionsView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class ActionsView : UserControl 6 | { 7 | public ActionsView() 8 | { 9 | InitializeComponent(); 10 | InitializeThemes(); 11 | } 12 | 13 | private void InitializeThemes() 14 | { 15 | var dark = true; 16 | 17 | ThemeButton.Click += (_, _) => 18 | { 19 | dark = !dark; 20 | App.ThemeManager?.Switch(dark ? 1 : 0); 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/ItemsView.axaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/ItemsView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class ItemsView : UserControl 6 | { 7 | public ItemsView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/LeftPaneView.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 43 | 44 | 45 | 52 | 53 | 54 | 55 | 56 | 61 | 62 | 63 | 64 | 65 | 67 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/LeftPaneView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class LeftPane : UserControl 6 | { 7 | public LeftPane() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/MainView.axaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/MainView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class MainView : UserControl 6 | { 7 | public MainView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Input; 5 | using SvgToXaml.ViewModels; 6 | 7 | namespace SvgToXaml.Views; 8 | 9 | public partial class MainWindow : Window 10 | { 11 | public MainWindow() 12 | { 13 | InitializeComponent(); 14 | #if DEBUG 15 | this.AttachDevTools(); 16 | #endif 17 | AddHandler(DragDrop.DropEvent, Drop); 18 | AddHandler(DragDrop.DragOverEvent, DragOver); 19 | } 20 | 21 | private void DragOver(object? sender, DragEventArgs e) 22 | { 23 | e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link); 24 | 25 | if (!e.Data.Contains(DataFormats.Files)) 26 | { 27 | e.DragEffects = DragDropEffects.None; 28 | } 29 | } 30 | 31 | private void Drop(object? sender, DragEventArgs e) 32 | { 33 | if (e.Data.Contains(DataFormats.Files)) 34 | { 35 | var paths = e.Data.GetFileNames(); 36 | if (paths is { }) 37 | { 38 | if (DataContext is MainWindowViewModel vm) 39 | { 40 | try 41 | { 42 | vm.Drop(paths); 43 | } 44 | catch (Exception) 45 | { 46 | // ignored 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/PictureView.axaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 24 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/PictureView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class PictureView : UserControl 6 | { 7 | public PictureView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/PreviewView.axaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/PreviewView.axaml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Markup.Xaml; 5 | using Avalonia.Threading; 6 | using SvgToXaml.ViewModels; 7 | 8 | namespace SvgToXaml.Views; 9 | 10 | public partial class PreviewView : UserControl 11 | { 12 | public PreviewView() 13 | { 14 | InitializeComponent(); 15 | } 16 | 17 | protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) 18 | { 19 | base.OnAttachedToVisualTree(e); 20 | 21 | if (DataContext is MainWindowViewModel vm && vm.Project.SelectedItem is { }) 22 | { 23 | Task.Run(async () => 24 | { 25 | await Dispatcher.UIThread.InvokeAsync(async () => 26 | { 27 | var previewItem = await vm.GetPreview(vm.Project.SelectedItem); 28 | if (previewItem is { }) 29 | { 30 | var content = AvaloniaRuntimeXamlLoader.Parse(previewItem.TabControl); 31 | 32 | content.DataContext = previewItem.Image; 33 | 34 | Content = content; 35 | } 36 | }); 37 | }); 38 | } 39 | } 40 | 41 | protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) 42 | { 43 | base.OnDetachedFromVisualTree(e); 44 | 45 | Content = null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/ProjectActionsView.axaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 31 | 32 | 45 | 46 | 59 | 60 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/ProjectActionsView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class ProjectActionsView : UserControl 6 | { 7 | public ProjectActionsView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/RightPaneView.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 28 | 29 | 33 | 34 | 37 | 38 | 39 | 40 | 43 | 44 | 57 | 66 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/RightPaneView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class RightPaneView : UserControl 6 | { 7 | public RightPaneView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/Sandbox/SandBox.axaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/Sandbox/SandBox.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class SandBox : UserControl 6 | { 7 | public SandBox() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/Sandbox/Styles.axaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/SettingsView.axaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 27 | 28 | 29 | 30 | 35 | 36 | 41 | 42 | 47 | 48 | 53 | 54 | 55 | 56 | 61 | 62 | 63 | 64 | 69 | 70 | 75 | 76 | 81 | 82 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/SvgToXaml.Base/Views/SettingsView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SvgToXaml.Views; 4 | 5 | public partial class SettingsView : UserControl 6 | { 7 | public SettingsView() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Converter/SvgToXaml.Converter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | netstandard2.0;net8.0 6 | True 7 | enable 8 | 9 | 10 | 11 | An Svg drawing model. 12 | SvgToXaml.Converter 13 | MIT 14 | svg;xaml;axaml;avalonia;avaloniaui;convert;converter;vector graphics;rendering;2d;graphics;geometry;shapes;skiasharp;skia 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/SvgToXaml.Converter/SvgToXamlConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Xml; 7 | 8 | namespace SvgToXaml.Converter; 9 | 10 | public class SvgToXamlConverter 11 | { 12 | public string NewLine { get; set; } = "\r\n"; 13 | 14 | public bool UseCompatMode { get; set; } 15 | 16 | public bool AddTransparentBackground { get; set; } 17 | 18 | public bool ReuseExistingResources { get; set; } 19 | 20 | public bool TransformGeometry { get; set; } 21 | 22 | public SvgToXaml.Model.Resources.ResourceDictionary? Resources { get; set; } 23 | 24 | public string ToXamlDrawingGroup(ShimSkiaSharp.SKPicture? skPicture, string? key = null) 25 | { 26 | var drawingGroup = new SvgToXaml.Model.Drawing.DrawingGroup(skPicture, Resources, key); 27 | 28 | var context = new XamlGeneratorSettings 29 | { 30 | NewLine = NewLine, 31 | UseCompatMode = UseCompatMode, 32 | AddTransparentBackground = AddTransparentBackground, 33 | ReuseExistingResources = ReuseExistingResources, 34 | TransformGeometry = TransformGeometry, 35 | WriteResources = false, 36 | Resources = Resources 37 | }; 38 | 39 | return new XamlGenerator().GenerateDrawingGroup(drawingGroup, context); 40 | } 41 | 42 | public string ToXamlImage(ShimSkiaSharp.SKPicture? skPicture, string? key = null) 43 | { 44 | var drawingGroup = new SvgToXaml.Model.Drawing.DrawingGroup(skPicture, Resources); 45 | var drawingImage = new SvgToXaml.Model.Drawing.DrawingImage(drawingGroup); 46 | var image = new SvgToXaml.Model.Containers.Image(drawingImage, key); 47 | 48 | var context = new XamlGeneratorSettings 49 | { 50 | NewLine = NewLine, 51 | UseCompatMode = UseCompatMode, 52 | AddTransparentBackground = AddTransparentBackground, 53 | ReuseExistingResources = ReuseExistingResources, 54 | TransformGeometry = TransformGeometry, 55 | WriteResources = true, 56 | Resources = Resources 57 | }; 58 | 59 | return new XamlGenerator().GenerateImage(image, context, null); 60 | } 61 | 62 | public string ToXamlStyles(List inputItems, bool generateImage = false, bool generatePreview = true) 63 | { 64 | var results = new List<(string Path, string Key, SvgToXaml.Model.Resources.Resource Resource)>(); 65 | 66 | foreach (var inputItem in inputItems) 67 | { 68 | try 69 | { 70 | var svg = new Svg.Skia.SKSvg(); 71 | svg.FromSvg(inputItem.Content); 72 | if (svg.Model is null) 73 | { 74 | continue; 75 | } 76 | 77 | var key = $"_{CreateKey(inputItem.Name)}"; 78 | if (generateImage) 79 | { 80 | var drawingGroup = new SvgToXaml.Model.Drawing.DrawingGroup(svg.Model, Resources); 81 | var drawingImage = new SvgToXaml.Model.Drawing.DrawingImage(drawingGroup); 82 | var image = new SvgToXaml.Model.Containers.Image(drawingImage, key); 83 | results.Add((inputItem.Name, key, image)); 84 | } 85 | else 86 | { 87 | var drawingGroup = new SvgToXaml.Model.Drawing.DrawingGroup(svg.Model, Resources, key); 88 | results.Add((inputItem.Name, key, drawingGroup)); 89 | } 90 | } 91 | catch 92 | { 93 | // ignored 94 | } 95 | } 96 | 97 | var resources = results.Select(x => x.Resource).ToList(); 98 | var styles = new SvgToXaml.Model.Containers.Styles(resources, generateImage, generatePreview); 99 | 100 | var context = new XamlGeneratorSettings 101 | { 102 | NewLine = NewLine, 103 | UseCompatMode = UseCompatMode, 104 | AddTransparentBackground = AddTransparentBackground, 105 | ReuseExistingResources = ReuseExistingResources, 106 | TransformGeometry = TransformGeometry, 107 | WriteResources = false, 108 | Resources = Resources 109 | }; 110 | 111 | return new XamlGenerator().GenerateStyles(styles, context); 112 | } 113 | 114 | public virtual string CreateKey(string path) 115 | { 116 | string name = Path.GetFileNameWithoutExtension(path); 117 | string key = name.Replace("-", "_"); 118 | return $"_{key}"; 119 | } 120 | 121 | public virtual string Format(string xml) 122 | { 123 | try 124 | { 125 | var sb = new StringBuilder(); 126 | sb.Append($""); 132 | sb.Append(xml); 133 | sb.Append($""); 134 | 135 | using var ms = new MemoryStream(); 136 | using var writer = new XmlTextWriter(ms, Encoding.UTF8); 137 | var document = new XmlDocument(); 138 | document.LoadXml(sb.ToString()); 139 | writer.Formatting = Formatting.Indented; 140 | writer.Indentation = 2; 141 | writer.IndentChar = ' '; 142 | document.WriteContentTo(writer); 143 | writer.Flush(); 144 | ms.Flush(); 145 | ms.Position = 0; 146 | using var sReader = new StreamReader(ms); 147 | var formatted = sReader.ReadToEnd(); 148 | 149 | var lines = formatted.Split(NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); 150 | var inner = lines.Skip(1).Take(lines.Length - 2).Select(x => x.Substring(2, x.Length - 2)); 151 | return string.Join(NewLine, inner); 152 | } 153 | catch 154 | { 155 | // ignored 156 | } 157 | 158 | return ""; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/SvgToXaml.Converter/System/Runtime/CompilerServices/IsExternalInit.cs: -------------------------------------------------------------------------------- 1 | #if NET461 || NETSTANDARD 2 | namespace System.Runtime.CompilerServices 3 | { 4 | internal static class IsExternalInit {} 5 | } 6 | #endif 7 | -------------------------------------------------------------------------------- /src/SvgToXaml.Converter/XamlGeneratorSettings.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Resources; 2 | 3 | namespace SvgToXaml.Converter; 4 | 5 | public record XamlGeneratorSettings 6 | { 7 | public string NewLine { get; init; } = "\r\n"; 8 | 9 | public bool UseCompatMode { get; init; } = false; 10 | 11 | public bool AddTransparentBackground { get; init; } = false; 12 | 13 | public bool ReuseExistingResources { get; init; } = false; 14 | 15 | public bool TransformGeometry { get; init; } = false; 16 | 17 | public bool WriteResources { get; init; } = false; 18 | 19 | public ResourceDictionary? Resources { get; init; } 20 | } 21 | -------------------------------------------------------------------------------- /src/SvgToXaml.Desktop/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls.Skia; 4 | using Avalonia.Xaml.Interactivity; 5 | 6 | namespace SvgToXaml; 7 | 8 | class Program 9 | { 10 | [STAThread] 11 | public static void Main(string[] args) 12 | { 13 | BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); 14 | } 15 | 16 | public static AppBuilder BuildAvaloniaApp() 17 | { 18 | GC.KeepAlive(typeof(SKPictureControl).Assembly); 19 | GC.KeepAlive(typeof(Behavior).Assembly); 20 | GC.KeepAlive(typeof(ComparisonConditionType).Assembly); 21 | 22 | return AppBuilder.Configure() 23 | .UsePlatformDetect() 24 | .LogToTrace() 25 | .WithInterFont() 26 | .UseSkia(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/SvgToXaml.Desktop/SvgToXaml.Desktop.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0 6 | False 7 | enable 8 | latest 9 | ..\SvgToXaml.Base\Assets\Icon.ico 10 | SvgToXaml 11 | 12 | 13 | 14 | False 15 | False 16 | True 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Containers/Image.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Drawing; 2 | using SvgToXaml.Model.Resources; 3 | 4 | namespace SvgToXaml.Model.Containers; 5 | 6 | public record Image : Resource 7 | { 8 | public DrawingImage? Source { get; } 9 | 10 | public Image(DrawingImage? source = null, string? key = null) 11 | { 12 | Key = key; 13 | Source = source; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Containers/Styles.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SvgToXaml.Model.Resources; 3 | 4 | namespace SvgToXaml.Model.Containers; 5 | 6 | public record Styles 7 | { 8 | public bool GenerateImage { get; init; } 9 | 10 | public bool GeneratePreview { get; init; } 11 | 12 | public List? Resources { get; init; } 13 | 14 | public Styles(List? resources, bool generateImage = false, bool generatePreview = true) 15 | { 16 | GenerateImage = generateImage; 17 | GeneratePreview = generatePreview; 18 | Resources = resources; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Drawing/Drawing.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Resources; 2 | 3 | namespace SvgToXaml.Model.Drawing; 4 | 5 | public abstract record Drawing : Resource; 6 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Drawing/DrawingGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SvgToXaml.Model.Containers; 3 | using SvgToXaml.Model.Paint; 4 | using SvgToXaml.Model.Resources; 5 | 6 | namespace SvgToXaml.Model.Drawing; 7 | 8 | public record DrawingGroup : Drawing 9 | { 10 | private const byte OpaqueAlpha = 255; 11 | 12 | private static readonly ShimSkiaSharp.SKColor s_transparentBlack = new(0, 0, 0, 255); 13 | 14 | public ShimSkiaSharp.SKPicture? Picture { get; } 15 | 16 | public double? Opacity { get; set; } 17 | 18 | public SkiaSharp.SKMatrix? Transform { get; set; } 19 | 20 | public SkiaSharp.SKPath? ClipGeometry { get; set; } 21 | 22 | public Brush? OpacityMask { get; set; } 23 | 24 | public List Children { get; } = new(); 25 | 26 | public DrawingGroup(ShimSkiaSharp.SKPicture? picture = null, ResourceDictionary? resources = null, string? key = null) 27 | { 28 | Key = key; 29 | Picture = picture; 30 | Initialize(Picture, resources); 31 | } 32 | 33 | private bool IsMaskGroupLayer(ShimSkiaSharp.SKPaint skPaint) 34 | { 35 | var isMaskGroup = skPaint.Shader is null 36 | && skPaint.ColorFilter is null 37 | && skPaint.ImageFilter is null 38 | && skPaint.Color is { } skMaskStartColor 39 | && skMaskStartColor.Equals(s_transparentBlack); 40 | return isMaskGroup; 41 | } 42 | 43 | private bool IsMaskBrushLayer(ShimSkiaSharp.SKPaint skPaint) 44 | { 45 | var isMaskBrush = skPaint.Shader is null 46 | && skPaint.ColorFilter is { } 47 | && skPaint.ImageFilter is null 48 | && skPaint.Color is { } skMaskEndColor 49 | && skMaskEndColor.Equals(s_transparentBlack); 50 | return isMaskBrush; 51 | } 52 | 53 | private bool IsOpacityGroupLayer(ShimSkiaSharp.SKPaint skPaint) 54 | { 55 | var isOpacityGroup = skPaint.Shader is null 56 | && skPaint.ColorFilter is null 57 | && skPaint.ImageFilter is null 58 | && skPaint.Color is { Alpha: < OpaqueAlpha }; 59 | return isOpacityGroup; 60 | } 61 | 62 | private bool IsFilterGroupLayer(ShimSkiaSharp.SKPaint skPaint) 63 | { 64 | var isFilterGroup = skPaint.Shader is null 65 | && skPaint.ColorFilter is null 66 | && skPaint.ImageFilter is { } 67 | && skPaint.Color is { } skFilterColor 68 | && skFilterColor.Equals(s_transparentBlack); 69 | return isFilterGroup; 70 | } 71 | 72 | private void Initialize(ShimSkiaSharp.SKPicture? picture, ResourceDictionary? resources = null) 73 | { 74 | if (picture?.Commands is null) 75 | { 76 | return; 77 | } 78 | 79 | var totalMatrixStack = new Stack(); 80 | var clipPathStack = new Stack(); 81 | var paintStack = new Stack(); 82 | var layerStack = new Stack(); 83 | var currentTotalMatrix = default(SkiaSharp.SKMatrix?); 84 | var currentClipPath = default(SkiaSharp.SKPath?); 85 | 86 | layerStack.Push(this); 87 | 88 | foreach (var canvasCommand in picture.Commands) 89 | { 90 | switch (canvasCommand) 91 | { 92 | case ShimSkiaSharp.ClipPathCanvasCommand(var clipPath, _, _): 93 | { 94 | var path = Factory.s_model.ToSKPath(clipPath); 95 | if (path is null) 96 | { 97 | break; 98 | } 99 | 100 | var newLayer = new DrawingGroup 101 | { 102 | ClipGeometry = path 103 | }; 104 | 105 | var currentLayer = layerStack.Peek(); 106 | currentLayer.Children.Add(newLayer); 107 | layerStack.Push(newLayer); 108 | currentClipPath = path; 109 | 110 | break; 111 | } 112 | case ShimSkiaSharp.ClipRectCanvasCommand(var skRect, _, _): 113 | { 114 | var rect = Factory.s_model.ToSKRect(skRect); 115 | var path = new SkiaSharp.SKPath(); 116 | path.AddRect(rect); 117 | 118 | var newLayer = new DrawingGroup 119 | { 120 | ClipGeometry = path 121 | }; 122 | 123 | var currentLayer = layerStack.Peek(); 124 | currentLayer.Children.Add(newLayer); 125 | layerStack.Push(newLayer); 126 | currentClipPath = path; 127 | 128 | break; 129 | } 130 | case ShimSkiaSharp.SetMatrixCanvasCommand(_, var skTotalMatrix): 131 | { 132 | var matrix = Factory.s_model.ToSKMatrix(skTotalMatrix); 133 | if (matrix.IsIdentity) 134 | { 135 | break; 136 | } 137 | 138 | var previousMatrixList = new List(); 139 | 140 | foreach (var totalMatrixList in totalMatrixStack) 141 | { 142 | if (totalMatrixList is { } totalMatrix) 143 | { 144 | previousMatrixList.Add(totalMatrix); 145 | } 146 | } 147 | 148 | previousMatrixList.Reverse(); 149 | 150 | foreach (var previousMatrix in previousMatrixList) 151 | { 152 | var inverted = previousMatrix.Invert(); 153 | matrix = inverted.PreConcat(matrix); 154 | } 155 | 156 | var newLayer = new DrawingGroup 157 | { 158 | Transform = matrix 159 | }; 160 | 161 | var currentLayer = layerStack.Peek(); 162 | currentLayer.Children.Add(newLayer); 163 | layerStack.Push(newLayer); 164 | currentTotalMatrix = matrix; 165 | 166 | break; 167 | } 168 | case ShimSkiaSharp.SaveLayerCanvasCommand(_, var skPaint): 169 | { 170 | if (skPaint is null) 171 | { 172 | break; 173 | } 174 | 175 | totalMatrixStack.Push(currentTotalMatrix); 176 | currentTotalMatrix = default; 177 | 178 | clipPathStack.Push(currentClipPath); 179 | currentClipPath = default; 180 | 181 | var newLayer = new DrawingGroup(); 182 | layerStack.Push(newLayer); 183 | paintStack.Push(skPaint); 184 | 185 | break; 186 | } 187 | case ShimSkiaSharp.SaveCanvasCommand(_): 188 | { 189 | totalMatrixStack.Push(currentTotalMatrix); 190 | currentTotalMatrix = default; 191 | 192 | clipPathStack.Push(currentClipPath); 193 | currentClipPath = default; 194 | 195 | paintStack.Push(default); 196 | 197 | break; 198 | } 199 | case ShimSkiaSharp.RestoreCanvasCommand(_): 200 | { 201 | if (paintStack.Count > 0) 202 | { 203 | var currentPaint = paintStack.Pop(); 204 | if (currentPaint is { }) 205 | { 206 | var content = layerStack.Pop(); 207 | var currentLayer = layerStack.Peek(); 208 | var skPaint = currentPaint; 209 | 210 | var isMaskGroup = IsMaskGroupLayer(skPaint); 211 | if (isMaskGroup) 212 | { 213 | if (content is { }) 214 | { 215 | currentLayer.Children.Add(content); 216 | } 217 | } 218 | 219 | var isMaskBrush = IsMaskBrushLayer(skPaint); 220 | if (isMaskBrush) 221 | { 222 | var drawing = new DrawingGroup 223 | { 224 | OpacityMask = new PictureBrush 225 | { 226 | Picture = new Image(new DrawingImage(content)), 227 | TileMode = ShimSkiaSharp.SKShaderTileMode.Clamp 228 | } 229 | }; 230 | 231 | currentLayer.Children.Add(drawing); 232 | } 233 | 234 | var isOpacityGroup = IsOpacityGroupLayer(skPaint); 235 | if (isOpacityGroup) 236 | { 237 | if (skPaint.Color is { } skColor) 238 | { 239 | if (content is { }) 240 | { 241 | content.Opacity = skColor.Alpha / 255.0; 242 | currentLayer.Children.Add(content); 243 | } 244 | } 245 | } 246 | 247 | var isFilterGroup = IsFilterGroupLayer(skPaint); 248 | if (isFilterGroup) 249 | { 250 | if (content is { }) 251 | { 252 | var drawing = new DrawingGroup(); 253 | 254 | drawing.Children.Add(content); 255 | 256 | currentLayer.Children.Add(drawing); 257 | } 258 | } 259 | 260 | if (!isMaskGroup && !isMaskBrush && !isOpacityGroup && !isFilterGroup) 261 | { 262 | if (content is { }) 263 | { 264 | currentLayer.Children.Add(content); 265 | } 266 | } 267 | } 268 | } 269 | 270 | if (currentClipPath is { }) 271 | { 272 | layerStack.Pop(); 273 | } 274 | currentClipPath = default; 275 | if (clipPathStack.Count > 0) 276 | { 277 | currentClipPath = clipPathStack.Pop(); 278 | } 279 | 280 | if (currentTotalMatrix is { }) 281 | { 282 | layerStack.Pop(); 283 | } 284 | currentTotalMatrix = default; 285 | if (totalMatrixStack.Count > 0) 286 | { 287 | currentTotalMatrix = totalMatrixStack.Pop(); 288 | } 289 | 290 | break; 291 | } 292 | case ShimSkiaSharp.DrawPathCanvasCommand(var skPath, var skPaint): 293 | { 294 | if (skPath is null) 295 | { 296 | break; 297 | } 298 | var path = Factory.s_model.ToSKPath(skPath); 299 | if (path.IsEmpty) 300 | { 301 | break; 302 | } 303 | 304 | var geometryDrawing = new GeometryDrawing(skPaint, path, resources); 305 | 306 | var currentLayer = layerStack.Peek(); 307 | currentLayer.Children.Add(geometryDrawing); 308 | 309 | break; 310 | } 311 | case ShimSkiaSharp.DrawTextCanvasCommand(var text, var x, var y, var skPaint): 312 | { 313 | if (skPaint is null) 314 | { 315 | break; 316 | } 317 | var paint = Factory.s_model.ToSKPaint(skPaint); 318 | if (paint is null) 319 | { 320 | break; 321 | } 322 | var path = paint.GetTextPath(text, x, y); 323 | if (path.IsEmpty) 324 | { 325 | break; 326 | } 327 | 328 | if (skPaint.TextAlign == ShimSkiaSharp.SKTextAlign.Center) 329 | { 330 | path.Transform(SkiaSharp.SKMatrix.CreateTranslation(-path.Bounds.Width / 2f, 0f)); 331 | } 332 | 333 | if (skPaint.TextAlign == ShimSkiaSharp.SKTextAlign.Right) 334 | { 335 | path.Transform(SkiaSharp.SKMatrix.CreateTranslation(-path.Bounds.Width, 0f)); 336 | } 337 | 338 | var geometryDrawing = new GeometryDrawing(skPaint, path, resources); 339 | 340 | var currentLayer = layerStack.Peek(); 341 | currentLayer.Children.Add(geometryDrawing); 342 | 343 | break; 344 | } 345 | // ReSharper disable UnusedVariable 346 | case ShimSkiaSharp.DrawTextOnPathCanvasCommand(var text, var skPath, var hOffset, var vOffset, var skPaint): 347 | { 348 | // TODO: 349 | break; 350 | } 351 | case ShimSkiaSharp.DrawTextBlobCanvasCommand(var skTextBlob, var x, var y, var skPaint): 352 | { 353 | // TODO: 354 | break; 355 | } 356 | case ShimSkiaSharp.DrawImageCanvasCommand(var skImage, var skRect, var dest, var skPaint): 357 | { 358 | // TODO: 359 | break; 360 | } 361 | // ReSharper restore UnusedVariable 362 | } 363 | } 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Drawing/DrawingImage.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model.Drawing; 2 | 3 | public record DrawingImage : Drawing 4 | { 5 | public Drawing? Drawing { get; } 6 | 7 | public DrawingImage(Drawing? drawing = null) 8 | { 9 | Drawing = drawing; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Drawing/GeometryDrawing.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Paint; 2 | using SvgToXaml.Model.Resources; 3 | 4 | namespace SvgToXaml.Model.Drawing; 5 | 6 | public record GeometryDrawing : Drawing 7 | { 8 | public ShimSkiaSharp.SKPaint? Paint { get; } 9 | 10 | public SkiaSharp.SKPath? Geometry { get; } 11 | 12 | public Brush? Brush { get; } 13 | 14 | public Pen? Pen { get; } 15 | 16 | public GeometryDrawing( 17 | ShimSkiaSharp.SKPaint? paint = null, 18 | SkiaSharp.SKPath? geometry = null, 19 | ResourceDictionary? resources = null) 20 | { 21 | Paint = paint; 22 | Geometry = geometry; 23 | 24 | if (Paint is { } && Geometry is { }) 25 | { 26 | var isFilled = Paint.Style is ShimSkiaSharp.SKPaintStyle.StrokeAndFill or ShimSkiaSharp.SKPaintStyle.Fill; 27 | 28 | if (isFilled && Paint.Shader is { }) 29 | { 30 | var resourceKey = resources is { } ? $"Brush{resources.BrushCounter++}" : null; 31 | 32 | Brush = Factory.CreateBrush(Paint.Shader, Geometry.Bounds, resourceKey); 33 | 34 | if (resources is { } && Brush?.Key is { }) 35 | { 36 | resources.Brushes.Add(Brush.Key, (Paint, Brush)); 37 | } 38 | } 39 | 40 | var isStroked = Paint.Style is ShimSkiaSharp.SKPaintStyle.StrokeAndFill or ShimSkiaSharp.SKPaintStyle.Stroke; 41 | 42 | if (isStroked && Paint.Shader is { }) 43 | { 44 | var resourceKey = resources is { } ? $"Pen{resources.PenCounter++}" : null; 45 | 46 | Pen = Factory.CreatePen(Paint, Geometry.Bounds, resourceKey); 47 | 48 | if (resources is { } && Pen?.Key is { }) 49 | { 50 | resources.Pens.Add(Pen.Key, (Paint, Pen)); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Factory.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Containers; 2 | using SvgToXaml.Model.Drawing; 3 | using SvgToXaml.Model.Paint; 4 | 5 | namespace SvgToXaml.Model; 6 | 7 | internal static class Factory 8 | { 9 | internal static Svg.Skia.SkiaModel s_model = new Svg.Skia.SkiaModel(new Svg.Skia.SKSvgSettings()); 10 | 11 | public static Brush CreateBrush(ShimSkiaSharp.ColorShader colorShader, SkiaSharp.SKRect skBounds, string? key = null) 12 | { 13 | var brush = new SolidColorBrush 14 | { 15 | Key = key, 16 | Bounds = skBounds, 17 | LocalMatrix = null, 18 | Color = colorShader.Color 19 | }; 20 | 21 | return brush; 22 | } 23 | 24 | public static LinearGradientBrush CreateBrush(ShimSkiaSharp.LinearGradientShader linearGradientShader, SkiaSharp.SKRect skBounds, string? key) 25 | { 26 | var brush = new LinearGradientBrush 27 | { 28 | Key = key, 29 | Bounds = skBounds, 30 | LocalMatrix = linearGradientShader.LocalMatrix is null 31 | ? null 32 | : s_model.ToSKMatrix(linearGradientShader.LocalMatrix.Value), 33 | Start = s_model.ToSKPoint(linearGradientShader.Start), 34 | End = s_model.ToSKPoint(linearGradientShader.End), 35 | Mode = linearGradientShader.Mode 36 | }; 37 | 38 | if (linearGradientShader.Colors is { } && linearGradientShader.ColorPos is { }) 39 | { 40 | for (var i = 0; i < linearGradientShader.Colors.Length; i++) 41 | { 42 | var color = linearGradientShader.Colors[i]; 43 | var offset = linearGradientShader.ColorPos[i]; 44 | brush.GradientStops.Add(new GradientStop { Color = color, Offset = offset }); 45 | } 46 | } 47 | 48 | return brush; 49 | } 50 | 51 | public static Brush CreateBrush(ShimSkiaSharp.RadialGradientShader radialGradientShader, SkiaSharp.SKRect skBounds, string? key) 52 | { 53 | var brush = new RadialGradientBrush 54 | { 55 | Key = key, 56 | Bounds = skBounds, 57 | LocalMatrix = radialGradientShader.LocalMatrix is null 58 | ? null 59 | : s_model.ToSKMatrix(radialGradientShader.LocalMatrix.Value), 60 | Center = s_model.ToSKPoint(radialGradientShader.Center), 61 | Radius = radialGradientShader.Radius, 62 | Mode = radialGradientShader.Mode 63 | }; 64 | 65 | if (radialGradientShader.Colors is { } && radialGradientShader.ColorPos is { }) 66 | { 67 | for (var i = 0; i < radialGradientShader.Colors.Length; i++) 68 | { 69 | var color = radialGradientShader.Colors[i]; 70 | var offset = radialGradientShader.ColorPos[i]; 71 | brush.GradientStops.Add(new GradientStop { Color = color, Offset = offset }); 72 | } 73 | } 74 | 75 | return brush; 76 | } 77 | 78 | public static Brush CreateBrush(ShimSkiaSharp.TwoPointConicalGradientShader twoPointConicalGradientShader, SkiaSharp.SKRect skBounds, string? key) 79 | { 80 | var brush = new TwoPointConicalGradientBrush() 81 | { 82 | Key = key, 83 | Bounds = skBounds, 84 | LocalMatrix = twoPointConicalGradientShader.LocalMatrix is null 85 | ? null 86 | : s_model.ToSKMatrix(twoPointConicalGradientShader.LocalMatrix.Value), 87 | Start = s_model.ToSKPoint(twoPointConicalGradientShader.Start), 88 | End = s_model.ToSKPoint(twoPointConicalGradientShader.End), 89 | StartRadius = twoPointConicalGradientShader.StartRadius, 90 | EndRadius = twoPointConicalGradientShader.EndRadius, 91 | Mode = twoPointConicalGradientShader.Mode 92 | }; 93 | 94 | if (twoPointConicalGradientShader.Colors is { } && twoPointConicalGradientShader.ColorPos is { }) 95 | { 96 | for (var i = 0; i < twoPointConicalGradientShader.Colors.Length; i++) 97 | { 98 | var color = twoPointConicalGradientShader.Colors[i]; 99 | var offset = twoPointConicalGradientShader.ColorPos[i]; 100 | brush.GradientStops.Add(new GradientStop { Color = color, Offset = offset }); 101 | } 102 | } 103 | 104 | return brush; 105 | } 106 | 107 | public static Brush? CreateBrush(ShimSkiaSharp.PictureShader pictureShader, SkiaSharp.SKRect skBounds, string? key = null) 108 | { 109 | var brush = new PictureBrush 110 | { 111 | Key = key, 112 | Bounds = skBounds, 113 | LocalMatrix = s_model.ToSKMatrix(pictureShader.LocalMatrix), 114 | Picture = new Image(new DrawingImage(new DrawingGroup(pictureShader.Src))), 115 | CullRect = pictureShader.Src?.CullRect ?? ShimSkiaSharp.SKRect.Empty, 116 | Tile = pictureShader.Tile, 117 | TileMode = pictureShader.TmX 118 | }; 119 | 120 | return brush; 121 | } 122 | 123 | public static Brush? CreateBrush(ShimSkiaSharp.SKShader skShader, SkiaSharp.SKRect skBounds, string? key = null) 124 | { 125 | return skShader switch 126 | { 127 | ShimSkiaSharp.ColorShader colorShader => CreateBrush(colorShader, skBounds, key), 128 | ShimSkiaSharp.LinearGradientShader linearGradientShader => CreateBrush(linearGradientShader, skBounds, key), 129 | ShimSkiaSharp.RadialGradientShader radialGradientShader => CreateBrush(radialGradientShader, skBounds, key), 130 | ShimSkiaSharp.TwoPointConicalGradientShader twoPointConicalGradientShader => CreateBrush(twoPointConicalGradientShader, skBounds, key), 131 | ShimSkiaSharp.PictureShader pictureShader => CreateBrush(pictureShader, skBounds, key), 132 | _ => null 133 | }; 134 | } 135 | 136 | public static Pen? CreatePen(ShimSkiaSharp.SKPaint skPaint, SkiaSharp.SKRect skBounds, string? key) 137 | { 138 | if (skPaint.Shader is null) 139 | { 140 | return null; 141 | } 142 | 143 | var pen = new Pen 144 | { 145 | Key = key, 146 | Bounds = skBounds, 147 | Brush = CreateBrush(skPaint.Shader, skBounds), 148 | StrokeWidth = skPaint.StrokeWidth, 149 | StrokeCap = skPaint.StrokeCap, 150 | StrokeJoin = skPaint.StrokeJoin, 151 | StrokeMiter = skPaint.StrokeMiter, 152 | Dashes = skPaint.PathEffect is ShimSkiaSharp.DashPathEffect(var intervals, var phase) { Intervals: { } } 153 | ? new Dashes { Intervals = intervals, Phase = phase } 154 | : null 155 | }; 156 | 157 | return pen; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/InputItem.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model; 2 | 3 | public record InputItem(string Name, string Content); 4 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/Brush.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Resources; 2 | 3 | namespace SvgToXaml.Model.Paint; 4 | 5 | public abstract record Brush : Resource 6 | { 7 | public SkiaSharp.SKRect Bounds { get; init; } 8 | 9 | public SkiaSharp.SKMatrix? LocalMatrix { get; init; } 10 | 11 | public SkiaSharp.SKMatrix WithTransXY(SkiaSharp.SKMatrix matrix, float x, float y) 12 | { 13 | return new SkiaSharp.SKMatrix( 14 | matrix.ScaleX, 15 | matrix.SkewX, 16 | matrix.TransX - x, 17 | matrix.SkewY, 18 | matrix.ScaleY, 19 | matrix.TransY - y, 20 | matrix.Persp0, 21 | matrix.Persp1, 22 | matrix.Persp2); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/Dashes.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model.Paint; 2 | 3 | public record Dashes 4 | { 5 | public float[]? Intervals { get; init; } 6 | 7 | public float Phase { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/GradientBrush.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SvgToXaml.Model.Paint; 4 | 5 | public abstract record GradientBrush : Brush 6 | { 7 | public ShimSkiaSharp.SKShaderTileMode Mode { get; init; } 8 | 9 | public List GradientStops { get; init; } = new (); 10 | } 11 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/GradientStop.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Resources; 2 | 3 | namespace SvgToXaml.Model.Paint; 4 | 5 | public record GradientStop : Resource 6 | { 7 | public float Offset { get; init; } 8 | 9 | public ShimSkiaSharp.SKColor Color { get; init; } 10 | } 11 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/LinearGradientBrush.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model.Paint; 2 | 3 | public record LinearGradientBrush : GradientBrush 4 | { 5 | public SkiaSharp.SKPoint Start { get; init; } 6 | 7 | public SkiaSharp.SKPoint End { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/Pen.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Resources; 2 | 3 | namespace SvgToXaml.Model.Paint; 4 | 5 | public record Pen : Resource 6 | { 7 | public SkiaSharp.SKRect Bounds { get; init; } 8 | 9 | public Brush? Brush { get; init; } 10 | 11 | public float StrokeWidth { get; init; } 12 | 13 | public ShimSkiaSharp.SKStrokeCap StrokeCap { get; init; } 14 | 15 | public ShimSkiaSharp.SKStrokeJoin StrokeJoin { get; init; } 16 | 17 | public float StrokeMiter { get; init; } 18 | 19 | public Dashes? Dashes { get; init; } 20 | } 21 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/PictureBrush.cs: -------------------------------------------------------------------------------- 1 | using SvgToXaml.Model.Containers; 2 | 3 | namespace SvgToXaml.Model.Paint; 4 | 5 | public record PictureBrush : GradientBrush 6 | { 7 | public Image? Picture { get; init; } 8 | 9 | public ShimSkiaSharp.SKRect CullRect { get; init; } 10 | 11 | public ShimSkiaSharp.SKRect Tile { get; init; } 12 | 13 | public ShimSkiaSharp.SKShaderTileMode TileMode { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/RadialGradientBrush.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model.Paint; 2 | 3 | public record RadialGradientBrush : GradientBrush 4 | { 5 | public SkiaSharp.SKPoint Center { get; init; } 6 | 7 | public float Radius { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/SolidColorBrush.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model.Paint; 2 | 3 | public record SolidColorBrush : Brush 4 | { 5 | public ShimSkiaSharp.SKColor Color { get; init; } 6 | } 7 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Paint/TwoPointConicalGradientBrush.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model.Paint; 2 | 3 | public record TwoPointConicalGradientBrush : GradientBrush 4 | { 5 | public float StartRadius { get; init; } 6 | 7 | public float EndRadius { get; init; } 8 | 9 | public SkiaSharp.SKPoint Start { get; init; } 10 | 11 | public SkiaSharp.SKPoint End { get; init; } 12 | } 13 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Resources/Resource.cs: -------------------------------------------------------------------------------- 1 | namespace SvgToXaml.Model.Resources; 2 | 3 | public abstract record Resource 4 | { 5 | public string? Key { get; init; } 6 | } 7 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/Resources/ResourceDictionary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SvgToXaml.Model.Paint; 3 | 4 | namespace SvgToXaml.Model.Resources; 5 | 6 | public record ResourceDictionary 7 | { 8 | public Dictionary Brushes { get; init; } = new(); 9 | 10 | public Dictionary Pens { get; init; } = new(); 11 | 12 | public int BrushCounter { get; set; } 13 | 14 | public int PenCounter { get; set; } 15 | 16 | public HashSet UseBrushes { get; init; } = new(); 17 | 18 | public HashSet UsePens { get; init; } = new(); 19 | } 20 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/SvgToXaml.Model.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | netstandard2.0;net8.0 6 | True 7 | enable 8 | 9 | 10 | 11 | An Svg drawing model. 12 | SvgToXaml.Model 13 | MIT 14 | svg;xaml;axaml;avalonia;avaloniaui;convert;converter;vector graphics;rendering;2d;graphics;geometry;shapes;skiasharp;skia 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/SvgToXaml.Model/System/Runtime/CompilerServices/IsExternalInit.cs: -------------------------------------------------------------------------------- 1 | #if NET461 || NETSTANDARD 2 | namespace System.Runtime.CompilerServices 3 | { 4 | internal static class IsExternalInit {} 5 | } 6 | #endif 7 | -------------------------------------------------------------------------------- /src/SvgToXaml.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using System.Threading.Tasks; 3 | using Avalonia; 4 | using Avalonia.Browser; 5 | using SvgToXaml; 6 | 7 | [assembly:SupportedOSPlatform("browser")] 8 | 9 | internal sealed partial class Program 10 | { 11 | private static async Task Main(string[] args) 12 | => await BuildAvaloniaApp().StartBrowserAppAsync("out"); 13 | 14 | public static AppBuilder BuildAvaloniaApp() 15 | => AppBuilder 16 | .Configure() 17 | .WithInterFont() 18 | .UseSkia(); 19 | } 20 | -------------------------------------------------------------------------------- /src/SvgToXaml.Web/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Runtime.Versioning.SupportedOSPlatform("browser")] -------------------------------------------------------------------------------- /src/SvgToXaml.Web/Roots.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/SvgToXaml.Web/SvgToXaml.Web.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0-browser 5 | Exe 6 | true 7 | browser-wasm 8 | wwwroot\main.js 9 | False 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/SvgToXaml.Web/runtimeconfig.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasmHostProperties": { 3 | "perHostConfig": [ 4 | { 5 | "name": "browser", 6 | "host": "browser" 7 | } 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /src/SvgToXaml.Web/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | /* HTML styles for the splash screen */ 2 | .avalonia-splash { 3 | position: absolute; 4 | height: 100%; 5 | width: 100%; 6 | background: white; 7 | font-family: 'Outfit', sans-serif; 8 | justify-content: center; 9 | align-items: center; 10 | display: flex; 11 | pointer-events: none; 12 | } 13 | 14 | /* Light theme styles */ 15 | @media (prefers-color-scheme: light) { 16 | .avalonia-splash { 17 | background: white; 18 | } 19 | 20 | .avalonia-splash h2 { 21 | color: #1b2a4e; 22 | } 23 | 24 | .avalonia-splash a { 25 | color: #0D6EFD; 26 | } 27 | } 28 | 29 | @media (prefers-color-scheme: dark) { 30 | .avalonia-splash { 31 | background: #1b2a4e; 32 | } 33 | 34 | .avalonia-splash h2 { 35 | color: white; 36 | } 37 | 38 | .avalonia-splash a { 39 | color: white; 40 | } 41 | } 42 | 43 | .avalonia-splash h2 { 44 | font-weight: 400; 45 | font-size: 1.5rem; 46 | } 47 | 48 | .avalonia-splash a { 49 | text-decoration: none; 50 | font-size: 2.5rem; 51 | display: block; 52 | } 53 | 54 | .avalonia-splash.splash-close { 55 | transition: opacity 200ms, display 200ms; 56 | display: none; 57 | opacity: 0; 58 | } 59 | -------------------------------------------------------------------------------- /src/SvgToXaml.Web/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wieslawsoltes/SvgToXaml/23ac52e18fa4d5b8b1ded09fb3378c7cae68e228/src/SvgToXaml.Web/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/SvgToXaml.Web/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SvgToXaml 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 33 |
34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/SvgToXaml.Web/wwwroot/main.js: -------------------------------------------------------------------------------- 1 | import { dotnet } from './_framework/dotnet.js' 2 | 3 | const is_browser = typeof window != "undefined"; 4 | if (!is_browser) throw new Error(`Expected to be running in a browser`); 5 | 6 | const dotnetRuntime = await dotnet 7 | .withDiagnosticTracing(false) 8 | .withApplicationArgumentsFromQuery() 9 | .create(); 10 | 11 | const config = dotnetRuntime.getConfig(); 12 | 13 | await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]); 14 | -------------------------------------------------------------------------------- /src/svgxaml/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wieslawsoltes/SvgToXaml/23ac52e18fa4d5b8b1ded09fb3378c7cae68e228/src/svgxaml/Icon.ico -------------------------------------------------------------------------------- /src/svgxaml/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Linq; 6 | using Svg.Skia; 7 | using SvgToXaml.Converter; 8 | using SvgToXaml.Model; 9 | 10 | if (args.Length != 1 && args.Length != 2) 11 | { 12 | Console.WriteLine("Usage: svgxaml [OutputPath]"); 13 | return; 14 | } 15 | 16 | try 17 | { 18 | var inputPath = args[0]; 19 | 20 | if (File.GetAttributes(inputPath).HasFlag(FileAttributes.Directory)) 21 | { 22 | var paths = new List(); 23 | 24 | GetFiles(inputPath, paths); 25 | 26 | paths.Sort(); 27 | 28 | if (paths.Count == 0) 29 | { 30 | return; 31 | } 32 | 33 | var converter = new SvgToXamlConverter() 34 | { 35 | UseCompatMode = false, 36 | ReuseExistingResources = false, 37 | TransformGeometry = false, 38 | Resources = null 39 | }; 40 | 41 | var xaml = converter.ToXamlStyles(paths.Select(x => 42 | { 43 | using var stream = File.OpenRead(x); 44 | var ms = LoadFromStream(stream, x); 45 | using var reader = new StreamReader(ms); 46 | var content = reader.ReadToEnd(); 47 | return new InputItem(Path.GetFileName(x), content); 48 | }).ToList()); 49 | 50 | if (args.Length == 1) 51 | { 52 | Console.WriteLine(converter.Format(xaml)); 53 | return; 54 | } 55 | 56 | if (args.Length == 2) 57 | { 58 | var outputPath = args[1]; 59 | File.WriteAllText(outputPath, converter.Format(xaml)); 60 | } 61 | } 62 | else 63 | { 64 | var converter = new SvgToXamlConverter() 65 | { 66 | UseCompatMode = false, 67 | ReuseExistingResources = false, 68 | TransformGeometry = false, 69 | Resources = null 70 | }; 71 | 72 | var skSvg = new SKSvg(); 73 | skSvg.Load(inputPath); 74 | 75 | var xaml = converter.ToXamlImage(skSvg.Model); 76 | 77 | if (args.Length == 1) 78 | { 79 | Console.WriteLine(converter.Format(xaml)); 80 | return; 81 | } 82 | 83 | if (args.Length == 2) 84 | { 85 | var outputPath = args[1]; 86 | File.WriteAllText(outputPath, converter.Format(xaml)); 87 | } 88 | } 89 | } 90 | catch (Exception ex) 91 | { 92 | Console.WriteLine($"{ex.Message}"); 93 | Console.WriteLine($"{ex.StackTrace}"); 94 | } 95 | 96 | static void GetFiles(string path, List paths) 97 | { 98 | if (File.GetAttributes(path).HasFlag(FileAttributes.Directory)) 99 | { 100 | var svgPaths = Directory.EnumerateFiles(path, "*.svg", new EnumerationOptions {RecurseSubdirectories = true}); 101 | var svgzPaths = Directory.EnumerateFiles(path, "*.svgz", new EnumerationOptions {RecurseSubdirectories = true}); 102 | svgPaths.ToList().ForEach(x => GetFiles(x, paths)); 103 | svgzPaths.ToList().ForEach(x => GetFiles(x, paths)); 104 | return; 105 | } 106 | 107 | var extension = Path.GetExtension(path); 108 | switch (extension.ToLower()) 109 | { 110 | case ".svg": 111 | case ".svgz": 112 | paths.Add(path); 113 | break; 114 | } 115 | } 116 | 117 | static Stream LoadFromStream(Stream stream, string name) 118 | { 119 | var extension = Path.GetExtension(name); 120 | var memoryStream = new MemoryStream(); 121 | 122 | if (extension == "svgz") 123 | { 124 | using var gzipStream = new GZipStream(stream, CompressionMode.Decompress); 125 | gzipStream.CopyTo(memoryStream); 126 | } 127 | else 128 | { 129 | stream.CopyTo(memoryStream); 130 | } 131 | 132 | memoryStream.Position = 0; 133 | return memoryStream; 134 | } 135 | -------------------------------------------------------------------------------- /src/svgxaml/svgxaml.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | True 7 | True 8 | True 9 | svgxaml 10 | enable 11 | Icon.ico 12 | 13 | 14 | 15 | An Svg to Xaml conveter. 16 | svgxaml 17 | MIT 18 | svg;xaml;axaml;avalonia;avaloniaui;convert;converter;vector graphics;rendering;2d;graphics;geometry;shapes;skiasharp;skia 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/SvgToXamlConverter.UnitTests/SvgToXamlConverter.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | Library 6 | False 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------