sf_symbol_converter 0.1.0 → 1.0.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +21 -0
- data/README.md +68 -0
- data/bin/sf-symbol-converter +3 -35
- data/lib/sf_symbol_converter/cli/sf_symbol_converter.rb +43 -0
- data/lib/sf_symbol_converter/converter.rb +117 -0
- data/lib/sf_symbol_converter/version.rb +5 -0
- data/lib/sf_symbol_converter.rb +6 -119
- metadata +39 -7
- /data/lib/{trimmer → sf_symbol_converter/trimmer}/template_trimmer.rb +0 -0
- /data/lib/{utils.rb → sf_symbol_converter/utils/get_guide_value.rb} +0 -0
- /data/lib/{validators → sf_symbol_converter/validators}/icon_validator.rb +0 -0
- /data/lib/{validators → sf_symbol_converter/validators}/template_validator.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0e0b93f63a04053685cf2a51ebe9b02a56d3a9e333c9f9e63ed1935e1b7449fb
|
|
4
|
+
data.tar.gz: 836bffc5c81f0f9950071375b0f6f03a4d0e7717a1b6c266247bc9b8da09e1ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37bcddbbfa459d27137591ddc501ebefbbeebaa980ba6502c2211ed64ef9f8197663046061e3bde34025f54abbf8f8970d178f1378c1fc560faa5d9f45d1faa2
|
|
7
|
+
data.tar.gz: ad89602632caab227db154c7e3c71216402b7dedc6cb8489d0e7ff5eae02c57c3e5c47b8035c4e0bc605b9680be6e40ba51d547a0ba60b9e6b1cdecc6ef1fd1a
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Yang Liu and Tycho Tatitscheff
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# SFSymbol Icon Convertor
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
Given a SVG icon with viewBox that respect [material icon principles](https://m3.material.io/styles/icons/designing-icons), generate appropriate SFSymbol.
|
|
5
|
+
|
|
6
|
+
## Longer rational
|
|
7
|
+
|
|
8
|
+
Designers often use the **material guidelines** to produce icon.
|
|
9
|
+
|
|
10
|
+
<img src="docs/images/material_viewbox.png" alt="Material Viewbox" width=200>
|
|
11
|
+
|
|
12
|
+
Inside this viewbox of 24x24px, there is a mandatory margin of 2px.
|
|
13
|
+
The icon is supposed to respect a guideline.
|
|
14
|
+
|
|
15
|
+
<img src="docs/images/material_guideline.png" alt="Material Guideline" width=200>
|
|
16
|
+
|
|
17
|
+
For instance theese icons respect material guidelines:
|
|
18
|
+
|
|
19
|
+
<img src="docs/images/material_example1.png" alt="Material Example" width=150>
|
|
20
|
+
<img src="docs/images/material_example2.png" alt="Material Example" width=150>
|
|
21
|
+
<img src="docs/images/material_example3.png" alt="Material Example" width=150>
|
|
22
|
+
<img src="docs/images/material_example4.png" alt="Material Example" width=150>
|
|
23
|
+
|
|
24
|
+
More info here: https://m3.material.io/styles/icons/designing-icons
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
On the other side, and while it is still possible to use `svg`, Apple pushes for the use of SfSymbol.
|
|
29
|
+
|
|
30
|
+
SFSymbols have multiple benefits:
|
|
31
|
+
- performance
|
|
32
|
+
- ability to mix text and image in a proper way
|
|
33
|
+
- ability to do bi (or multi) color icon
|
|
34
|
+
|
|
35
|
+
<img src="docs/images/sfsymbol_bicolor.png" alt="Material Example" width=400>
|
|
36
|
+
|
|
37
|
+
More info here:
|
|
38
|
+
- https://developer.apple.com/documentation/uikit/uiimage/creating_custom_symbol_images_for_your_app
|
|
39
|
+
- https://developer.apple.com/design/human-interface-guidelines/sf-symbols
|
|
40
|
+
|
|
41
|
+
It require the developper to take a template, and insert the icon inside, while respecting the size and the margin.
|
|
42
|
+
|
|
43
|
+
<img src="docs/images/sfsymbol_template.png" alt="SfSymbol Template" width=650>
|
|
44
|
+
|
|
45
|
+
Since a few version, you can provide only three variations:
|
|
46
|
+
- ultralight-S
|
|
47
|
+
- regular-S
|
|
48
|
+
- black-S
|
|
49
|
+
|
|
50
|
+
And the system (UIKit, or SwiftUI) will interpolate the rest.
|
|
51
|
+
|
|
52
|
+
<img src="docs/images/sfsymbol_interpolation.png" alt="SfSymbol Template" width=500>
|
|
53
|
+
|
|
54
|
+
We provide the small, but to respect the material system, you are supposed to use the `.medium`
|
|
55
|
+
|
|
56
|
+
<img src="docs/images/sfsymbol_size.png" alt="SfSymbol Template" width=500>
|
|
57
|
+
|
|
58
|
+
## Instalation and usage
|
|
59
|
+
|
|
60
|
+
`gem install sf_symbol_converter`
|
|
61
|
+
|
|
62
|
+
Then convert icon one by one:
|
|
63
|
+
|
|
64
|
+
`sf_symbol_converter convert ./input.svg output.svg`
|
|
65
|
+
|
|
66
|
+
Or batch convert:
|
|
67
|
+
|
|
68
|
+
`sf_symbol_converter batch_convert ./input_dir/ ./output_dir`
|
data/bin/sf-symbol-converter
CHANGED
|
@@ -1,41 +1,9 @@
|
|
|
1
1
|
#! /usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
require './lib/sf_symbol_converter'
|
|
7
|
-
|
|
8
|
-
# CLI for converting SF Symbols
|
|
9
|
-
class SFSymbolCli < Thor
|
|
10
|
-
include Thor::Actions
|
|
11
|
-
|
|
12
|
-
TEMPLATE_PATH = 'assets/template.svg'
|
|
13
|
-
|
|
14
|
-
desc 'convert ICON_SVG [OUTPUT_SVG]', 'Converts an SF Symbol SVG to a template SVG'
|
|
15
|
-
def convert(icon_svg_path, output_path = 'output.svg')
|
|
16
|
-
template_svg = Nokogiri::XML(File.open(TEMPLATE_PATH))
|
|
17
|
-
icon_svg = Nokogiri::XML(File.open(icon_svg_path))
|
|
18
|
-
|
|
19
|
-
output_path = output_path || 'output.svg'
|
|
20
|
-
|
|
21
|
-
converter = SFSymbolConverter.new(template_svg, icon_svg)
|
|
22
|
-
converted_svg = converter.convert
|
|
23
|
-
|
|
24
|
-
pretty_printed_svg = Nokogiri::XML(converted_svg.to_s, &:noblanks).to_xml(indent: 2)
|
|
25
|
-
|
|
26
|
-
File.open(output_path, 'w') { |file| file.write(pretty_printed_svg) }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
desc 'batch_convert INPUT_DIR OUTPUT_DIR', 'Converts all SVGs in a directory to SFSymbols'
|
|
30
|
-
def batch_convert(input_dir, output_dir)
|
|
31
|
-
Dir.glob("#{input_dir}/*.svg").each do |icon_svg_path|
|
|
32
|
-
icon_svg = Nokogiri::XML(File.open(icon_svg_path))
|
|
33
|
-
|
|
34
|
-
output_path = "#{output_dir}/#{File.basename(icon_svg_path)}"
|
|
35
|
-
convert(icon_svg_path, output_path)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
4
|
+
# Add the lib directory to the load path
|
|
5
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
39
6
|
|
|
7
|
+
require 'sf_symbol_converter'
|
|
40
8
|
|
|
41
9
|
SFSymbolCli.start(ARGV)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require "nokogiri"
|
|
5
|
+
|
|
6
|
+
require "sf_symbol_converter"
|
|
7
|
+
|
|
8
|
+
# CLI for converting SF Symbols
|
|
9
|
+
class SFSymbolCli < Thor
|
|
10
|
+
include Thor::Actions
|
|
11
|
+
|
|
12
|
+
# Determine the root directory of the gem
|
|
13
|
+
GEM_ROOT = File.expand_path("../../..", __dir__)
|
|
14
|
+
|
|
15
|
+
TEMPLATE_PATH = File.join(GEM_ROOT, "assets", "template.svg")
|
|
16
|
+
|
|
17
|
+
desc "convert ICON_SVG [OUTPUT_SVG]", "Converts an SF Symbol SVG to a template SVG"
|
|
18
|
+
|
|
19
|
+
def convert(icon_svg_path, output_path = "output.svg")
|
|
20
|
+
template_svg = Nokogiri::XML(File.open(TEMPLATE_PATH))
|
|
21
|
+
icon_svg = Nokogiri::XML(File.open(icon_svg_path))
|
|
22
|
+
|
|
23
|
+
output_path = output_path || "output.svg"
|
|
24
|
+
|
|
25
|
+
converter = SFSymbolConverter.new(template_svg, icon_svg)
|
|
26
|
+
converted_svg = converter.convert
|
|
27
|
+
|
|
28
|
+
pretty_printed_svg = Nokogiri::XML(converted_svg.to_s, &:noblanks).to_xml(indent: 2)
|
|
29
|
+
|
|
30
|
+
File.open(output_path, "w") { |file| file.write(pretty_printed_svg) }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
desc "batch_convert INPUT_DIR OUTPUT_DIR", "Converts all SVGs in a directory to SFSymbols"
|
|
34
|
+
|
|
35
|
+
def batch_convert(input_dir, output_dir)
|
|
36
|
+
Dir.glob("#{input_dir}/*.svg").each do |icon_svg_path|
|
|
37
|
+
icon_svg = Nokogiri::XML(File.open(icon_svg_path))
|
|
38
|
+
|
|
39
|
+
output_path = "#{output_dir}/#{File.basename(icon_svg_path)}"
|
|
40
|
+
convert(icon_svg_path, output_path)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
# Given a source SVG and a SFSymbol template, generate a SFSymbol
|
|
6
|
+
class SFSymbolConverter
|
|
7
|
+
SOURCE_ICON_VIEWBOX_SIZE = 24
|
|
8
|
+
|
|
9
|
+
REFERENCE_SFSYMBOL_FONT_CAPS_HEIGHT = 14
|
|
10
|
+
TARGET_SYMBOL_HEIGHT_MEDIUM = 16
|
|
11
|
+
|
|
12
|
+
SFSYMBOL_MEDIUM_TO_SMALL_SCALE = 0.783
|
|
13
|
+
|
|
14
|
+
MARGIN_LINE_WIDTH = 0.5
|
|
15
|
+
|
|
16
|
+
attr_accessor :template_svg
|
|
17
|
+
attr_reader :icon_svg, :icon_validator, :template_validator
|
|
18
|
+
|
|
19
|
+
def initialize(template_svg, icon_svg)
|
|
20
|
+
@template_svg = template_svg
|
|
21
|
+
@icon_svg = icon_svg
|
|
22
|
+
|
|
23
|
+
@icon_validator = IconValidator.new(SOURCE_ICON_VIEWBOX_SIZE)
|
|
24
|
+
@template_validator = TemplateValidator.new
|
|
25
|
+
|
|
26
|
+
@template_trimmer = TemplateTrimmer.new
|
|
27
|
+
|
|
28
|
+
@icon_validator.validate(icon_svg)
|
|
29
|
+
@template_validator.validate(template_svg)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def convert
|
|
33
|
+
@template_svg = @template_trimmer.trim(template_svg)
|
|
34
|
+
replace_template_symbols_with_source_icons
|
|
35
|
+
adjust_guidelines
|
|
36
|
+
@template_svg
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def cap_height_small
|
|
42
|
+
baseline_y = get_guide_value(template_svg, :y, "Baseline-S")
|
|
43
|
+
capline_y = get_guide_value(template_svg, :y, "Capline-S")
|
|
44
|
+
|
|
45
|
+
(baseline_y - capline_y).abs
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def scale_factor_small
|
|
49
|
+
cap_height_small / REFERENCE_SFSYMBOL_FONT_CAPS_HEIGHT * SFSYMBOL_MEDIUM_TO_SMALL_SCALE
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def scaled_size_small
|
|
53
|
+
SOURCE_ICON_VIEWBOX_SIZE * scale_factor_small
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def vertical_center_small
|
|
57
|
+
baseline_y = get_guide_value(template_svg, :y, "Baseline-S")
|
|
58
|
+
capline_y = get_guide_value(template_svg, :y, "Capline-S")
|
|
59
|
+
|
|
60
|
+
(baseline_y + capline_y) / 2
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def horizontal_center(symbol)
|
|
64
|
+
left_margin = get_guide_value(template_svg, :x, "left-margin-#{symbol}")
|
|
65
|
+
right_margin = get_guide_value(template_svg, :x, "right-margin-#{symbol}")
|
|
66
|
+
|
|
67
|
+
(left_margin + right_margin) / 2
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def adjusted_left_margin(symbol)
|
|
71
|
+
# TODO: is it really (MARGIN_LINE_WIDTH / 2) or just MARGIN_LINE_WIDTH
|
|
72
|
+
horizontal_center(symbol) - scaled_size_small / 2 - (MARGIN_LINE_WIDTH / 2)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def adjusted_right_margin(symbol)
|
|
76
|
+
# TODO: is it really (MARGIN_LINE_WIDTH / 2) or just MARGIN_LINE_WIDTH
|
|
77
|
+
horizontal_center(symbol) + scaled_size_small / 2 + (MARGIN_LINE_WIDTH / 2)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def transform_matrix(symbol)
|
|
81
|
+
translation_x = horizontal_center(symbol) - scaled_size_small / 2
|
|
82
|
+
translation_y = vertical_center_small - scaled_size_small / 2
|
|
83
|
+
|
|
84
|
+
matrix_text = "%<scale>.6f 0 0 %<scale>.6f %<trans_x>.6f %<trans_y>.6f"
|
|
85
|
+
parameters = { scale: scale_factor_small, trans_x: translation_x, trans_y: translation_y }
|
|
86
|
+
|
|
87
|
+
format(matrix_text, parameters)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
IDS_TO_REPLACE = %w[Ultralight-S Regular-S Black-S].freeze
|
|
91
|
+
|
|
92
|
+
def replace_template_symbols_with_source_icons
|
|
93
|
+
IDS_TO_REPLACE.each do |symbol|
|
|
94
|
+
template_symbol_node = template_svg.at_css("##{symbol}")
|
|
95
|
+
|
|
96
|
+
template_symbol_node["transform"] = "matrix(#{transform_matrix(symbol)})"
|
|
97
|
+
|
|
98
|
+
paths = icon_svg.dup.root.children
|
|
99
|
+
paths.each do |node|
|
|
100
|
+
node.delete("fill")
|
|
101
|
+
node["class"] = "SFSymbolsPreviewWireframe"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
template_symbol_node.children = paths
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def adjust_guidelines
|
|
109
|
+
IDS_TO_REPLACE.each do |symbol|
|
|
110
|
+
left_margin_node = template_svg.at_css("#left-margin-#{symbol}")
|
|
111
|
+
left_margin_node["x1"] = left_margin_node["x2"] = adjusted_left_margin(symbol).to_s
|
|
112
|
+
|
|
113
|
+
right_margin_node = template_svg.at_css("#right-margin-#{symbol}")
|
|
114
|
+
right_margin_node["x1"] = right_margin_node["x2"] = adjusted_right_margin(symbol).to_s
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
data/lib/sf_symbol_converter.rb
CHANGED
|
@@ -1,122 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
require_relative 'sf_symbol_converter/utils/get_guide_value'
|
|
4
|
+
require_relative 'sf_symbol_converter/validators/icon_validator'
|
|
5
|
+
require_relative 'sf_symbol_converter/validators/template_validator'
|
|
6
|
+
require_relative 'sf_symbol_converter/trimmer/template_trimmer'
|
|
7
|
+
require_relative 'sf_symbol_converter/converter'
|
|
8
|
+
require_relative 'sf_symbol_converter/cli/sf_symbol_converter'
|
|
5
9
|
|
|
6
|
-
require './lib/validators/icon_validator'
|
|
7
|
-
require './lib/validators/template_validator'
|
|
8
|
-
require './lib/trimmer/template_trimmer'
|
|
9
|
-
|
|
10
|
-
# Given a source SVG and a SFSymbol template, generate a SFSymbol
|
|
11
|
-
class SFSymbolConverter
|
|
12
|
-
SOURCE_ICON_VIEWBOX_SIZE = 24
|
|
13
|
-
|
|
14
|
-
REFERENCE_SFSYMBOL_FONT_CAPS_HEIGHT = 14
|
|
15
|
-
TARGET_SYMBOL_HEIGHT_MEDIUM = 16
|
|
16
|
-
|
|
17
|
-
SFSYMBOL_MEDIUM_TO_SMALL_SCALE = 0.783
|
|
18
|
-
|
|
19
|
-
MARGIN_LINE_WIDTH = 0.5
|
|
20
|
-
|
|
21
|
-
attr_accessor :template_svg
|
|
22
|
-
attr_reader :icon_svg, :icon_validator, :template_validator
|
|
23
|
-
|
|
24
|
-
def initialize(template_svg, icon_svg)
|
|
25
|
-
@template_svg = template_svg
|
|
26
|
-
@icon_svg = icon_svg
|
|
27
|
-
|
|
28
|
-
@icon_validator = IconValidator.new(SOURCE_ICON_VIEWBOX_SIZE)
|
|
29
|
-
@template_validator = TemplateValidator.new
|
|
30
|
-
|
|
31
|
-
@template_trimmer = TemplateTrimmer.new
|
|
32
|
-
|
|
33
|
-
@icon_validator.validate(icon_svg)
|
|
34
|
-
@template_validator.validate(template_svg)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def convert
|
|
38
|
-
@template_svg = @template_trimmer.trim(template_svg)
|
|
39
|
-
replace_template_symbols_with_source_icons
|
|
40
|
-
adjust_guidelines
|
|
41
|
-
@template_svg
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def cap_height_small
|
|
47
|
-
baseline_y = get_guide_value(template_svg, :y, 'Baseline-S')
|
|
48
|
-
capline_y = get_guide_value(template_svg, :y, 'Capline-S')
|
|
49
|
-
|
|
50
|
-
(baseline_y - capline_y).abs
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def scale_factor_small
|
|
54
|
-
cap_height_small / REFERENCE_SFSYMBOL_FONT_CAPS_HEIGHT * SFSYMBOL_MEDIUM_TO_SMALL_SCALE
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def scaled_size_small
|
|
58
|
-
SOURCE_ICON_VIEWBOX_SIZE * scale_factor_small
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def vertical_center_small
|
|
62
|
-
baseline_y = get_guide_value(template_svg, :y, 'Baseline-S')
|
|
63
|
-
capline_y = get_guide_value(template_svg, :y, 'Capline-S')
|
|
64
|
-
|
|
65
|
-
(baseline_y + capline_y) / 2
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def horizontal_center(symbol)
|
|
69
|
-
left_margin = get_guide_value(template_svg, :x, "left-margin-#{symbol}")
|
|
70
|
-
right_margin = get_guide_value(template_svg, :x, "right-margin-#{symbol}")
|
|
71
|
-
|
|
72
|
-
(left_margin + right_margin) / 2
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def adjusted_left_margin(symbol)
|
|
76
|
-
# TODO: is it really (MARGIN_LINE_WIDTH / 2) or just MARGIN_LINE_WIDTH
|
|
77
|
-
horizontal_center(symbol) - scaled_size_small / 2 - (MARGIN_LINE_WIDTH / 2)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def adjusted_right_margin(symbol)
|
|
81
|
-
# TODO: is it really (MARGIN_LINE_WIDTH / 2) or just MARGIN_LINE_WIDTH
|
|
82
|
-
horizontal_center(symbol) + scaled_size_small / 2 + (MARGIN_LINE_WIDTH / 2)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def transform_matrix(symbol)
|
|
86
|
-
translation_x = horizontal_center(symbol) - scaled_size_small / 2
|
|
87
|
-
translation_y = vertical_center_small - scaled_size_small / 2
|
|
88
|
-
|
|
89
|
-
matrix_text = '%<scale>.6f 0 0 %<scale>.6f %<trans_x>.6f %<trans_y>.6f'
|
|
90
|
-
parameters = { scale: scale_factor_small, trans_x: translation_x, trans_y: translation_y }
|
|
91
|
-
|
|
92
|
-
format(matrix_text, parameters)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
IDS_TO_REPLACE = %w[Ultralight-S Regular-S Black-S].freeze
|
|
96
|
-
|
|
97
|
-
def replace_template_symbols_with_source_icons
|
|
98
|
-
IDS_TO_REPLACE.each do |symbol|
|
|
99
|
-
template_symbol_node = template_svg.at_css("##{symbol}")
|
|
100
|
-
|
|
101
|
-
template_symbol_node['transform'] = "matrix(#{transform_matrix(symbol)})"
|
|
102
|
-
|
|
103
|
-
paths = icon_svg.dup.root.children
|
|
104
|
-
paths.each do |node|
|
|
105
|
-
node.delete('fill')
|
|
106
|
-
node['class'] = 'SFSymbolsPreviewWireframe'
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
template_symbol_node.children = paths
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def adjust_guidelines
|
|
114
|
-
IDS_TO_REPLACE.each do |symbol|
|
|
115
|
-
left_margin_node = template_svg.at_css("#left-margin-#{symbol}")
|
|
116
|
-
left_margin_node['x1'] = left_margin_node['x2'] = adjusted_left_margin(symbol).to_s
|
|
117
|
-
|
|
118
|
-
right_margin_node = template_svg.at_css("#right-margin-#{symbol}")
|
|
119
|
-
right_margin_node['x1'] = right_margin_node['x2'] = adjusted_right_margin(symbol).to_s
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sf_symbol_converter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 1.0.0.alpha.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yang Liu
|
|
@@ -51,8 +51,35 @@ dependencies:
|
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: 1.1.0
|
|
54
|
-
description:
|
|
55
|
-
|
|
54
|
+
description: "# SFSymbol Icon Convertor\n\n\nGiven a SVG icon with viewBox that respect
|
|
55
|
+
[material icon principles](https://m3.material.io/styles/icons/designing-icons),
|
|
56
|
+
generate appropriate SFSymbol.\n\n## Longer rational\n\nDesigners often use the
|
|
57
|
+
**material guidelines** to produce icon.\n\n<img src=\"docs/images/material_viewbox.png\"
|
|
58
|
+
alt=\"Material Viewbox\" width=200>\n\nInside this viewbox of 24x24px, there is
|
|
59
|
+
a mandatory margin of 2px. \nThe icon is supposed to respect a guideline.\n\n<img
|
|
60
|
+
src=\"docs/images/material_guideline.png\" alt=\"Material Guideline\" width=200>\n\nFor
|
|
61
|
+
instance theese icons respect material guidelines:\n\n<img src=\"docs/images/material_example1.png\"
|
|
62
|
+
alt=\"Material Example\" width=150>\n<img src=\"docs/images/material_example2.png\"
|
|
63
|
+
alt=\"Material Example\" width=150>\n<img src=\"docs/images/material_example3.png\"
|
|
64
|
+
alt=\"Material Example\" width=150>\n<img src=\"docs/images/material_example4.png\"
|
|
65
|
+
alt=\"Material Example\" width=150>\n\nMore info here: https://m3.material.io/styles/icons/designing-icons\n\n---\n\nOn
|
|
66
|
+
the other side, and while it is still possible to use `svg`, Apple pushes for the
|
|
67
|
+
use of SfSymbol.\n\nSFSymbols have multiple benefits:\n- performance\n- ability
|
|
68
|
+
to mix text and image in a proper way\n- ability to do bi (or multi) color icon\n\n<img
|
|
69
|
+
src=\"docs/images/sfsymbol_bicolor.png\" alt=\"Material Example\" width=400>\n\nMore
|
|
70
|
+
info here: \n- https://developer.apple.com/documentation/uikit/uiimage/creating_custom_symbol_images_for_your_app\n-
|
|
71
|
+
https://developer.apple.com/design/human-interface-guidelines/sf-symbols\n\nIt require
|
|
72
|
+
the developper to take a template, and insert the icon inside, while respecting
|
|
73
|
+
the size and the margin.\n\n<img src=\"docs/images/sfsymbol_template.png\" alt=\"SfSymbol
|
|
74
|
+
Template\" width=650>\n\nSince a few version, you can provide only three variations:\n-
|
|
75
|
+
ultralight-S\n- regular-S\n- black-S\n\nAnd the system (UIKit, or SwiftUI) will
|
|
76
|
+
interpolate the rest.\n\n<img src=\"docs/images/sfsymbol_interpolation.png\" alt=\"SfSymbol
|
|
77
|
+
Template\" width=500>\n\nWe provide the small, but to respect the material system,
|
|
78
|
+
you are supposed to use the `.medium`\n\n<img src=\"docs/images/sfsymbol_size.png\"
|
|
79
|
+
alt=\"SfSymbol Template\" width=500>\n\n## Instalation and usage\n\n`gem install
|
|
80
|
+
sf_symbol_converter`\n\nThen convert icon one by one:\n\n`sf_symbol_converter convert
|
|
81
|
+
./input.svg output.svg`\n\nOr batch convert:\n\n`sf_symbol_converter batch_convert
|
|
82
|
+
./input_dir/ ./output_dir`"
|
|
56
83
|
email:
|
|
57
84
|
- yangl@bam.tech
|
|
58
85
|
- tychot@bam.tech
|
|
@@ -61,13 +88,18 @@ executables:
|
|
|
61
88
|
extensions: []
|
|
62
89
|
extra_rdoc_files: []
|
|
63
90
|
files:
|
|
91
|
+
- LICENSE
|
|
92
|
+
- README.md
|
|
64
93
|
- assets/template.svg
|
|
65
94
|
- bin/sf-symbol-converter
|
|
66
95
|
- lib/sf_symbol_converter.rb
|
|
67
|
-
- lib/
|
|
68
|
-
- lib/
|
|
69
|
-
- lib/
|
|
70
|
-
- lib/
|
|
96
|
+
- lib/sf_symbol_converter/cli/sf_symbol_converter.rb
|
|
97
|
+
- lib/sf_symbol_converter/converter.rb
|
|
98
|
+
- lib/sf_symbol_converter/trimmer/template_trimmer.rb
|
|
99
|
+
- lib/sf_symbol_converter/utils/get_guide_value.rb
|
|
100
|
+
- lib/sf_symbol_converter/validators/icon_validator.rb
|
|
101
|
+
- lib/sf_symbol_converter/validators/template_validator.rb
|
|
102
|
+
- lib/sf_symbol_converter/version.rb
|
|
71
103
|
homepage: https://github.com/tychota/SfSymbolExporter
|
|
72
104
|
licenses:
|
|
73
105
|
- MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|