-
Notifications
You must be signed in to change notification settings - Fork 3
/
data.dart
121 lines (104 loc) · 3.36 KB
/
data.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:path_drawing/path_drawing.dart';
import 'package:xml/xml.dart';
import 'svg_parsing.dart';
/// Path icon data represents the icon as a [Path] and a [viewBox] which
/// specify a rectangle in user space which is mapped to the bounds of the viewport.
class PathIconData {
/// Create data from the given [path] and its [viewBox] describing the effective area.
const PathIconData({
required this.path,
required this.viewBox,
this.id,
});
/// Create data from the given [path].
///
/// If no [viewBox] is provided, then the bounds of the path are used and
/// the path is translated at origin.
///
/// A [fillType] can override the given [path]'s one.
factory PathIconData.sanitized({
required Path path,
Rect? viewBox,
PathFillType? fillType,
}) {
final bounds = path.getBounds();
if (viewBox == null) {
path = Path()..addPath(path, -bounds.topLeft);
if (fillType != null) {
path.fillType = fillType;
}
} else if (fillType != null) {
path = Path()..addPath(path, Offset.zero);
path.fillType = fillType;
}
return PathIconData(
viewBox: viewBox ?? (Offset.zero & bounds.size),
path: path,
);
}
/// Parse the [data] (as SVG path's `d` data) and create a path.
///
/// If no [viewBox] is provided, then the bounds of the path are used and
/// the path is translated at origin.
///
/// A [fillType] can be given, else `PathFillType.evenOdd`.
factory PathIconData.fromData(
String data, {
Rect? viewBox,
PathFillType? fillType,
}) {
final path = parseSvgPathData(data);
return PathIconData.sanitized(
path: path,
viewBox: viewBox,
fillType: fillType ?? PathFillType.evenOdd,
);
}
/// Parse the [data] SVG document and merges all the shapes found in the document as a single
/// path.
///
/// A [fillType] can be given, else `PathFillType.evenOdd`.
///
/// A [semanticLabel] is `null` and the document has an `id` attribute, then its value is used instead.
factory PathIconData.fromSvg(
String data, {
PathFillType fillType = PathFillType.evenOdd,
}) {
final document = XmlDocument.parse(data);
if (document.rootElement.name.local != 'svg') {
throw Exception('The provided is not an SVG content');
}
final viewBoxData = document.rootElement.getAttribute('viewBox');
final viewBox = viewBoxData == null ? Rect.zero : parseViewBox(viewBoxData);
final path = parseSvgElements(document.rootElement);
if (path == null) {
throw Exception('Empty path');
}
return PathIconData.sanitized(
path: path,
fillType: fillType,
viewBox: viewBox,
);
}
/// The fill path data that represents the icon shape.
final Path path;
/// The area of the path that is effective.
final Rect viewBox;
/// An optional identifier that may help for debug.
final String? id;
@override
String toString() {
return 'PathIconData(${id != null ? id : 'viewBox: $viewBox, path: $path'})';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is PathIconData &&
viewBox == other.viewBox &&
path == other.path);
}
@override
int get hashCode => runtimeType.hashCode ^ viewBox.hashCode ^ path.hashCode;
}