├── .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 | [](https://dev.azure.com/wieslawsoltes/GitHub/_build/latest?definitionId=92&branchName=main)
4 | [](https://github.com/wieslawsoltes/SvgToXaml/actions/workflows/build.yml)
5 |
6 | [](https://www.nuget.org/packages/SvgToXamlConverter)
7 | [](https://www.nuget.org/packages/SvgToXamlConverter)
8 |
9 | [](https://github.com/wieslawsoltes/SvgToXaml)
10 | [](https://github.com/wieslawsoltes/SvgToXaml)
11 | [](https://github.com/wieslawsoltes/SvgToXaml)
12 |
13 | Svg to xaml conveter.
14 |
15 | 
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 |
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 |
51 |
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 |
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 |
--------------------------------------------------------------------------------