Skip to content

Commit cf38f25

Browse files
committed
Cleanup, start image
1 parent 882a2f6 commit cf38f25

File tree

5 files changed

+137
-93
lines changed

5 files changed

+137
-93
lines changed

lib/src/svg_parser.dart

+1-6
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,7 @@ Drawable parseSvgElement(
138138
} else if (el.name.local == 'svg') {
139139
throw UnsupportedError('Nested SVGs not supported in this implementation.');
140140
} else if (el.name.local == 'use') {
141-
final String xlinkHref = getAttribute(
142-
el,
143-
'href',
144-
namespace: 'http://www.w3.org/1999/xlink',
145-
def: getAttribute(el, 'href'),
146-
);
141+
final String xlinkHref = getHrefAttribute(el);
147142
final DrawableStyle style = parseStyle(
148143
el,
149144
definitions,

lib/src/utilities/xml.dart

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
import 'package:xml/xml.dart';
22

3+
/// The namespace for xlink from the SVG 1.1 spec.
4+
const String kXlinkNamespace = 'http://www.w3.org/1999/xlink';
5+
6+
/// Get the `xlink:href` or `href` attribute, preferring `xlink`.
7+
///
8+
/// SVG 1.1 specifies that these attributes should be in the xlink namespace.
9+
/// SVG 2 deprecates that namespace.
10+
String getHrefAttribute(XmlElement el) => getAttribute(
11+
el,
12+
'href',
13+
namespace: kXlinkNamespace,
14+
def: getAttribute(el, 'href'),
15+
);
16+
317
/// Gets the attribute, trims it, and returns the attribute or default if the attribute
418
/// is null or ''.
519
///

lib/src/vector_drawable.dart

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'dart:typed_data';
22
import 'dart:ui';
33

4-
import 'package:flutter/widgets.dart' hide TextStyle;
54
import 'package:meta/meta.dart';
65
import 'package:path_drawing/path_drawing.dart';
76

@@ -637,7 +636,8 @@ class DrawableNoop implements Drawable {
637636
void draw(Canvas canvas, ColorFilter colorFilter) {}
638637
}
639638

640-
/// Represents a group of drawing elements that may share a common `transform`, `stroke`, or `fill`.
639+
/// Represents a group of drawing elements that may share a common `transform`,
640+
/// `stroke`, or `fill`.
641641
class DrawableGroup implements DrawableStyleable {
642642
const DrawableGroup(this.children, this.style);
643643

@@ -721,6 +721,24 @@ class DrawableGroup implements DrawableStyleable {
721721
}
722722
}
723723

724+
/// A raster image (e.g. PNG, JPEG, or GIF) embedded in the drawable.
725+
class DrawableRasterImage implements Drawable {
726+
const DrawableRasterImage(this.image, this.offset)
727+
: assert(image != null),
728+
assert(offset != null);
729+
730+
final Image image;
731+
final Offset offset;
732+
733+
@override
734+
void draw(Canvas canvas, ColorFilter colorFilter) {
735+
canvas.drawImage(image, offset, Paint());
736+
}
737+
738+
@override
739+
bool get hasDrawableContent => image.height > 0 && image.width > 0;
740+
}
741+
724742
/// Represents a drawing element that will be rendered to the canvas.
725743
class DrawableShape implements Drawable, DrawableStyleable {
726744
const DrawableShape(this.path, this.style)

lib/svg.dart

+86-82
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,13 @@ class Svg {
111111

112112
final List<Drawable> children = svg.children
113113
.whereType<XmlElement>()
114-
.map(
115-
(XmlNode child) => parseSvgElement(
116-
child,
117-
definitions,
118-
viewBox.viewBoxRect,
119-
style,
120-
key,
121-
),
122-
)
114+
.map((XmlNode child) => parseSvgElement(
115+
child,
116+
definitions,
117+
viewBox.viewBoxRect,
118+
style,
119+
key,
120+
))
123121
.toList();
124122
return DrawableRoot(
125123
viewBox,
@@ -153,16 +151,17 @@ class SvgPicture extends StatefulWidget {
153151
///
154152
/// A custom `placeholderBuilder` can be specified for cases where decoding or
155153
/// acquiring data may take a noticeably long time, e.g. for a network picture.
156-
const SvgPicture(this.pictureProvider,
157-
{Key key,
158-
this.width,
159-
this.height,
160-
this.fit = BoxFit.contain,
161-
this.alignment = Alignment.center,
162-
this.matchTextDirection = false,
163-
this.allowDrawingOutsideViewBox = false,
164-
this.placeholderBuilder})
165-
: super(key: key);
154+
const SvgPicture(
155+
this.pictureProvider, {
156+
Key key,
157+
this.width,
158+
this.height,
159+
this.fit = BoxFit.contain,
160+
this.alignment = Alignment.center,
161+
this.matchTextDirection = false,
162+
this.allowDrawingOutsideViewBox = false,
163+
this.placeholderBuilder,
164+
}) : super(key: key);
166165

167166
/// Instantiates a widget that renders an SVG picture from an [AssetBundle].
168167
///
@@ -239,20 +238,21 @@ class SvgPicture extends StatefulWidget {
239238
/// scale is present.
240239
/// * <https://flutter.io/assets-and-images/>, an introduction to assets in
241240
/// Flutter.
242-
SvgPicture.asset(String assetName,
243-
{Key key,
244-
this.matchTextDirection = false,
245-
AssetBundle bundle,
246-
String package,
247-
this.width,
248-
this.height,
249-
this.fit = BoxFit.contain,
250-
this.alignment = Alignment.center,
251-
this.allowDrawingOutsideViewBox = false,
252-
this.placeholderBuilder,
253-
Color color,
254-
BlendMode colorBlendMode = BlendMode.srcIn})
255-
: pictureProvider = ExactAssetPicture(
241+
SvgPicture.asset(
242+
String assetName, {
243+
Key key,
244+
this.matchTextDirection = false,
245+
AssetBundle bundle,
246+
String package,
247+
this.width,
248+
this.height,
249+
this.fit = BoxFit.contain,
250+
this.alignment = Alignment.center,
251+
this.allowDrawingOutsideViewBox = false,
252+
this.placeholderBuilder,
253+
Color color,
254+
BlendMode colorBlendMode = BlendMode.srcIn,
255+
}) : pictureProvider = ExactAssetPicture(
256256
allowDrawingOutsideViewBox == true
257257
? svgByteDecoderOutsideViewBox
258258
: svgByteDecoder,
@@ -289,19 +289,20 @@ class SvgPicture extends StatefulWidget {
289289
///
290290
/// An optional `headers` argument can be used to send custom HTTP headers
291291
/// with the image request.
292-
SvgPicture.network(String url,
293-
{Key key,
294-
Map<String, String> headers,
295-
this.width,
296-
this.height,
297-
this.fit = BoxFit.contain,
298-
this.alignment = Alignment.center,
299-
this.matchTextDirection = false,
300-
this.allowDrawingOutsideViewBox = false,
301-
this.placeholderBuilder,
302-
Color color,
303-
BlendMode colorBlendMode = BlendMode.srcIn})
304-
: pictureProvider = NetworkPicture(
292+
SvgPicture.network(
293+
String url, {
294+
Key key,
295+
Map<String, String> headers,
296+
this.width,
297+
this.height,
298+
this.fit = BoxFit.contain,
299+
this.alignment = Alignment.center,
300+
this.matchTextDirection = false,
301+
this.allowDrawingOutsideViewBox = false,
302+
this.placeholderBuilder,
303+
Color color,
304+
BlendMode colorBlendMode = BlendMode.srcIn,
305+
}) : pictureProvider = NetworkPicture(
305306
allowDrawingOutsideViewBox == true
306307
? svgByteDecoderOutsideViewBox
307308
: svgByteDecoder,
@@ -335,18 +336,19 @@ class SvgPicture extends StatefulWidget {
335336
///
336337
/// On Android, this may require the
337338
/// `android.permission.READ_EXTERNAL_STORAGE` permission.
338-
SvgPicture.file(File file,
339-
{Key key,
340-
this.width,
341-
this.height,
342-
this.fit = BoxFit.contain,
343-
this.alignment = Alignment.center,
344-
this.matchTextDirection = false,
345-
this.allowDrawingOutsideViewBox = false,
346-
this.placeholderBuilder,
347-
Color color,
348-
BlendMode colorBlendMode = BlendMode.srcIn})
349-
: pictureProvider = FilePicture(
339+
SvgPicture.file(
340+
File file, {
341+
Key key,
342+
this.width,
343+
this.height,
344+
this.fit = BoxFit.contain,
345+
this.alignment = Alignment.center,
346+
this.matchTextDirection = false,
347+
this.allowDrawingOutsideViewBox = false,
348+
this.placeholderBuilder,
349+
Color color,
350+
BlendMode colorBlendMode = BlendMode.srcIn,
351+
}) : pictureProvider = FilePicture(
350352
allowDrawingOutsideViewBox == true
351353
? svgByteDecoderOutsideViewBox
352354
: svgByteDecoder,
@@ -376,18 +378,19 @@ class SvgPicture extends StatefulWidget {
376378
///
377379
/// The `color` and `colorBlendMode` arguments, if specified, will be used to set a
378380
/// [ColorFilter] on any [Paint]s created for this drawing.
379-
SvgPicture.memory(Uint8List bytes,
380-
{Key key,
381-
this.width,
382-
this.height,
383-
this.fit = BoxFit.contain,
384-
this.alignment = Alignment.center,
385-
this.matchTextDirection = false,
386-
this.allowDrawingOutsideViewBox = false,
387-
this.placeholderBuilder,
388-
Color color,
389-
BlendMode colorBlendMode = BlendMode.srcIn})
390-
: pictureProvider = MemoryPicture(
381+
SvgPicture.memory(
382+
Uint8List bytes, {
383+
Key key,
384+
this.width,
385+
this.height,
386+
this.fit = BoxFit.contain,
387+
this.alignment = Alignment.center,
388+
this.matchTextDirection = false,
389+
this.allowDrawingOutsideViewBox = false,
390+
this.placeholderBuilder,
391+
Color color,
392+
BlendMode colorBlendMode = BlendMode.srcIn,
393+
}) : pictureProvider = MemoryPicture(
391394
allowDrawingOutsideViewBox == true
392395
? svgByteDecoderOutsideViewBox
393396
: svgByteDecoder,
@@ -417,18 +420,19 @@ class SvgPicture extends StatefulWidget {
417420
///
418421
/// The `color` and `colorBlendMode` arguments, if specified, will be used to set a
419422
/// [ColorFilter] on any [Paint]s created for this drawing.
420-
SvgPicture.string(String bytes,
421-
{Key key,
422-
this.width,
423-
this.height,
424-
this.fit = BoxFit.contain,
425-
this.alignment = Alignment.center,
426-
this.matchTextDirection = false,
427-
this.allowDrawingOutsideViewBox = false,
428-
this.placeholderBuilder,
429-
Color color,
430-
BlendMode colorBlendMode = BlendMode.srcIn})
431-
: pictureProvider = StringPicture(
423+
SvgPicture.string(
424+
String bytes, {
425+
Key key,
426+
this.width,
427+
this.height,
428+
this.fit = BoxFit.contain,
429+
this.alignment = Alignment.center,
430+
this.matchTextDirection = false,
431+
this.allowDrawingOutsideViewBox = false,
432+
this.placeholderBuilder,
433+
Color color,
434+
BlendMode colorBlendMode = BlendMode.srcIn,
435+
}) : pictureProvider = StringPicture(
432436
allowDrawingOutsideViewBox == true
433437
? svgStringDecoderOutsideViewBox
434438
: svgStringDecoder,

test/xml_svg_test.dart

+16-3
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,23 @@ import 'package:flutter_svg/src/svg/xml_parsers.dart';
77
import 'package:flutter_svg/src/utilities/xml.dart';
88

99
void main() {
10-
test('Attribute and style tests', () {
11-
final XmlElement el = parse(
12-
'<test stroke="#fff" fill="#eee" stroke-dashpattern="1 2" style="stroke-opacity:1;fill-opacity:.23" />')
10+
test('Xlink href tests', () {
11+
final XmlElement el = parse('<test href="http://localhost" />').rootElement;
12+
13+
final XmlElement elXlink = parse('<test xmlns:xlink="$kXlinkNamespace" '
14+
'xlink:href="http://localhost" />')
1315
.rootElement;
1416

17+
expect(getHrefAttribute(el), 'http://localhost');
18+
expect(getHrefAttribute(elXlink), 'http://localhost');
19+
});
20+
21+
test('Attribute and style tests', () {
22+
final XmlElement el =
23+
parse('<test stroke="#fff" fill="#eee" stroke-dashpattern="1 2" '
24+
'style="stroke-opacity:1;fill-opacity:.23" />')
25+
.rootElement;
26+
1527
expect(getAttribute(el, 'stroke'), '#fff');
1628
expect(getAttribute(el, 'fill'), '#eee');
1729
expect(getAttribute(el, 'stroke-dashpattern'), '1 2');
@@ -22,6 +34,7 @@ void main() {
2234
expect(getAttribute(el, 'fill-opacity', checkStyle: false), '');
2335
expect(getAttribute(el, 'fill', checkStyle: false), '#eee');
2436
});
37+
2538
// if the parsing logic changes, we can simplify some methods. for now assert that whitespace in attributes is preserved
2639
test('Attribute WhiteSpace test', () {
2740
final XmlDocument xd =

0 commit comments

Comments
 (0)