Skip to content

Commit 7ffb2b9

Browse files
authored
Text improvements (#95)
* text improvements -need goldens * add golden, clean up analysis
1 parent 719bd81 commit 7ffb2b9

File tree

9 files changed

+126
-55
lines changed

9 files changed

+126
-55
lines changed

CHANGELOG.md

+13-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# CHANGES
22

3+
## 0.9.0+1
4+
5+
- Fix inheritance issues with `text-anchor`.
6+
- Fix a few inconsistencies in text anchor processing/positioning.
7+
38
## 0.9.0
49

510
- **BREAKING** Improvements to text positioning. Thanks to @krispypen!
@@ -55,7 +60,7 @@
5560
## 0.6.3
5661

5762
- Consume updated version of path_drawing.
58-
- Fix bug with fill-rule inheritence + example to test.
63+
- Fix bug with fill-rule inheritance + example to test.
5964

6065
## 0.6.2
6166

@@ -64,7 +69,7 @@
6469

6570
## 0.6.1
6671

67-
- Fixed an issue with stroke and fill inheritence (and added test)
72+
- Fixed an issue with stroke and fill inheritance (and added test)
6873
- General formatting/analyzer cleanup
6974

7075
## 0.6.0
@@ -82,8 +87,8 @@
8287
- Create a new class to encapsulate `Paint` and assist with inheriting all
8388
painting properties.
8489
- Fixes regression introduced in v0.5.2 where some previously working
85-
inheritence stopped working.
86-
- Support more complex stroke/fill property inheritence.
90+
inheritance stopped working.
91+
- Support more complex stroke/fill property inheritance.
8792

8893
## 0.5.4
8994

@@ -170,7 +175,7 @@
170175

171176
## 0.2.0
172177

173-
- Fix bug(s) in inheritence (better rendering of Ghostscript_Tiger.svg)
178+
- Fix bug(s) in inheritance (better rendering of Ghostscript_Tiger.svg)
174179
- Support for `<clipPath>`s
175180
- Refactoring of how gradients are handled to enable clipPaths
176181
- Refactor of SVG shape -> path logic
@@ -188,20 +193,20 @@
188193
- Add more unit tests and rendering tests (!).
189194
- Add top level flutter_svg.dart.
190195
- Fix bugs found in transform matrix logic for skewX and skewY.
191-
- Minor improvements in handling inheritence for PathFillType.
196+
- Minor improvements in handling inheritance for PathFillType.
192197
- Support gradient spread types (TileModes in Flutter).
193198

194199
## 0.1.2
195200

196201
- Bump to path_drawing 0.2.3 (fix arc defect).
197202
- Handle 'none' in dasharray without throwing exception.
198-
- Better handling of inheritence and 'none' in fill/stroke/dasharray
203+
- Better handling of inheritance and 'none' in fill/stroke/dasharray
199204

200205
## 0.1.1
201206

202207
- Handle opacity on groups and inherited/blended opacity.
203208
- Fixes elements that have both opacity and stroke-opacity or fill-opacity.
204-
- Improvements for inheritence.
209+
- Improvements for inheritance.
205210
- Fixes related to unspecified fills on shapes.
206211

207212
## 0.1.0

analysis_options.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ linter:
110110
# - parameter_assignments # we do this commonly
111111
- prefer_adjacent_string_concatenation
112112
- prefer_asserts_in_initializer_lists
113-
- prefer_bool_in_asserts
114113
- prefer_collection_literals
115114
- prefer_conditional_assignment
116115
- prefer_const_constructors

example/assets/simple/text_3.svg

+11
Loading

example/pubspec.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ packages:
1919
path: ".."
2020
relative: true
2121
source: path
22-
version: "0.9.0"
22+
version: "0.9.0+1"
2323
meta:
2424
dependency: transitive
2525
description:

golden/simple/text_3.png

441 Bytes
Loading

lib/src/svg_parser.dart

+62-34
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,9 @@ void _appendParagraphs(ParagraphBuilder fill, ParagraphBuilder stroke,
230230

231231
stroke
232232
..pushStyle(style.textStyle.toFlutterTextStyle(
233-
foregroundOverride:
234-
DrawablePaint.isEmpty(style.stroke) ? _transparentStroke : style.stroke))
233+
foregroundOverride: DrawablePaint.isEmpty(style.stroke)
234+
? _transparentStroke
235+
: style.stroke))
235236
..addText(text);
236237
}
237238

@@ -245,20 +246,20 @@ Paragraph _finishParagraph(ParagraphBuilder paragraphBuilder) {
245246
}
246247

247248
Drawable _paragraphParser(
248-
ParagraphBuilder fill,
249-
ParagraphBuilder stroke,
250-
Offset parentOffset,
251-
DrawableTextAnchorPosition textAnchor,
252-
DrawableDefinitionServer definitions,
253-
Rect bounds,
254-
XmlNode parent,
255-
DrawableStyle style) {
249+
ParagraphBuilder fill,
250+
ParagraphBuilder stroke,
251+
Offset parentOffset,
252+
DrawableDefinitionServer definitions,
253+
Rect bounds,
254+
XmlNode parent,
255+
DrawableStyle style,
256+
) {
256257
final List<Drawable> children = <Drawable>[];
257258
Offset currentOffset = Offset(parentOffset.dx, parentOffset.dy);
258259
for (XmlNode child in parent.children) {
259260
switch (child.nodeType) {
260261
case XmlNodeType.CDATA:
261-
_appendParagraphs(fill, stroke, child.text, style);
262+
_appendParagraphs(fill, stroke, child.text, style);
262263
break;
263264
case XmlNodeType.TEXT:
264265
if (child.text.trim().isNotEmpty) {
@@ -272,20 +273,35 @@ Drawable _paragraphParser(
272273
final ParagraphBuilder stroke = ParagraphBuilder(ParagraphStyle());
273274
final String x = getAttribute(child, 'x', def: null);
274275
final String y = getAttribute(child, 'y', def: null);
275-
final Offset staticOffset = Offset(x!=null ? double.parse(x) : null,
276-
y!=null ? double.parse(y) : null);
277-
final Offset relativeOffset = Offset(double.parse(getAttribute(child, 'dx', def: '0')),
278-
double.parse(getAttribute(child, 'dy', def: '0')));
279-
final Offset offset = Offset(staticOffset.dx ?? (currentOffset.dx + relativeOffset.dx),
280-
staticOffset.dy ?? (currentOffset.dy + relativeOffset.dy));
281-
final Drawable drawable = _paragraphParser(fill, stroke, offset, textAnchor, definitions,
282-
bounds, child, childStyle);
276+
final Offset staticOffset = Offset(
277+
x != null ? double.parse(x) : null,
278+
y != null ? double.parse(y) : null,
279+
);
280+
final Offset relativeOffset = Offset(
281+
double.parse(getAttribute(child, 'dx', def: '0')),
282+
double.parse(getAttribute(child, 'dy', def: '0')),
283+
);
284+
final Offset offset = Offset(
285+
staticOffset.dx ?? (currentOffset.dx + relativeOffset.dx),
286+
staticOffset.dy ?? (currentOffset.dy + relativeOffset.dy),
287+
);
288+
final Drawable drawable = _paragraphParser(
289+
fill,
290+
stroke,
291+
offset,
292+
definitions,
293+
bounds,
294+
child,
295+
childStyle,
296+
);
283297
fill.pop();
284298
stroke.pop();
285299
children.add(drawable);
286-
if (drawable is DrawableText){
300+
if (drawable is DrawableText) {
287301
drawable.fill.layout(ParagraphConstraints(width: double.infinity));
288-
currentOffset = Offset(currentOffset.dx + drawable.fill.maxIntrinsicWidth, currentOffset.dy);
302+
currentOffset = Offset(
303+
currentOffset.dx + drawable.fill.maxIntrinsicWidth,
304+
currentOffset.dy);
289305
}
290306
break;
291307
default:
@@ -296,9 +312,10 @@ Drawable _paragraphParser(
296312
_finishParagraph(fill),
297313
_finishParagraph(stroke),
298314
parentOffset,
299-
textAnchor,
315+
style.textStyle.anchor ?? DrawableTextAnchorPosition.start,
316+
transform: style.transform,
300317
));
301-
if (children.length==1){
318+
if (children.length == 1) {
302319
return children.elementAt(0);
303320
}
304321
return DrawableGroup(children, style);
@@ -309,17 +326,26 @@ Drawable parseSvgText(XmlElement el, DrawableDefinitionServer definitions,
309326
final Offset offset = Offset(double.parse(getAttribute(el, 'x', def: '0')),
310327
double.parse(getAttribute(el, 'y', def: '0')));
311328

312-
final DrawableStyle style = parseStyle(el, definitions, bounds, parentStyle);
329+
final DrawableStyle style = parseStyle(
330+
el,
331+
definitions,
332+
bounds,
333+
parentStyle,
334+
needsTransform: true,
335+
);
313336

314337
final ParagraphBuilder fill = ParagraphBuilder(ParagraphStyle());
315338
final ParagraphBuilder stroke = ParagraphBuilder(ParagraphStyle());
316339

317-
final DrawableTextAnchorPosition textAnchor =
318-
parseTextAnchor(getAttribute(el, 'text-anchor', def: 'start'));
319-
320-
return _paragraphParser(fill, stroke, offset, textAnchor, definitions, bounds, el, style);
321-
322-
340+
return _paragraphParser(
341+
fill,
342+
stroke,
343+
offset,
344+
definitions,
345+
bounds,
346+
el,
347+
style,
348+
);
323349
}
324350

325351
/// Parses an SVG <g> element.
@@ -374,18 +400,20 @@ DrawableStyle parseStyle(
374400
clipPath: parseClipPath(el, definitions),
375401
textStyle: DrawableTextStyle(
376402
fontFamily: getAttribute(el, 'font-family', def: null),
377-
fontWeight: getFontWeightByName(getAttribute(el, 'font-weight', def: null)),
403+
fontWeight:
404+
getFontWeightByName(getAttribute(el, 'font-weight', def: null)),
378405
fontSize: parseFontSize(getAttribute(el, 'font-size'),
379406
parentValue: parentStyle?.textStyle?.fontSize),
407+
anchor: parseTextAnchor(getAttribute(el, 'text-anchor', def: 'inherit')),
380408
),
381409
);
382410
}
383411

384-
FontWeight getFontWeightByName(String fontWeight){
385-
if (fontWeight==null) {
412+
FontWeight getFontWeightByName(String fontWeight) {
413+
if (fontWeight == null) {
386414
return null;
387415
}
388-
switch(fontWeight){
416+
switch (fontWeight) {
389417
case '100':
390418
return FontWeight.w100;
391419
case '200':

lib/src/utilities/http.dart

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:async';
12
import 'dart:io';
23
import 'dart:typed_data';
34

lib/src/vector_drawable.dart

+37-10
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class DrawableStyle {
5252
/// Used where 'dasharray' is 'none'
5353
///
5454
/// This will not result in a drawing operation, but will clear out
55-
/// inheritence.
55+
/// inheritance.
5656
static final CircularIntervalList<double> emptyDashArray =
5757
CircularIntervalList<double>(const <double>[]);
5858

@@ -311,6 +311,7 @@ class DrawableTextStyle {
311311
this.height,
312312
this.locale,
313313
this.textBaseline,
314+
this.anchor,
314315
});
315316

316317
factory DrawableTextStyle.merge(DrawableTextStyle a, DrawableTextStyle b) {
@@ -335,6 +336,8 @@ class DrawableTextStyle {
335336
locale: a.locale ?? b.locale,
336337
background: a.background ?? b.background,
337338
foreground: a.foreground ?? b.foreground,
339+
// anchor: a.anchor != DrawableTextAnchorPosition.start ? a.anchor ?? b.anchor : b.anchor,
340+
anchor: a.anchor ?? b.anchor,
338341
);
339342
}
340343

@@ -352,6 +355,7 @@ class DrawableTextStyle {
352355
final Locale locale;
353356
final DrawablePaint background;
354357
final DrawablePaint foreground;
358+
final DrawableTextAnchorPosition anchor;
355359

356360
TextStyle toFlutterTextStyle({DrawablePaint foregroundOverride}) {
357361
return TextStyle(
@@ -374,22 +378,29 @@ class DrawableTextStyle {
374378
}
375379

376380
@override
377-
String toString() {
378-
return 'DrawableTextStyle{$decoration,$decorationColor,$decorationStyle,$fontWeight,$fontFamily,$fontSize,$fontStyle,$foreground,$background,$letterSpacing,$wordSpacing,$height,$locale,$textBaseline}';
379-
}
381+
String toString() =>
382+
'DrawableTextStyle{$decoration,$decorationColor,$decorationStyle,$fontWeight,'
383+
'$fontFamily,$fontSize,$fontStyle,$foreground,$background,$letterSpacing,$wordSpacing,$height,'
384+
'$locale,$textBaseline,$anchor}';
380385
}
381386

382387
enum DrawableTextAnchorPosition { start, middle, end }
383388

384389
// WIP. This only handles very, very minimal use cases right now.
385390
class DrawableText implements Drawable {
386-
DrawableText(this.fill, this.stroke, this.offset, this.anchor)
387-
: assert(fill != null || stroke != null);
391+
DrawableText(
392+
this.fill,
393+
this.stroke,
394+
this.offset,
395+
this.anchor, {
396+
this.transform,
397+
}) : assert(fill != null || stroke != null);
388398

389399
final Offset offset;
390400
final DrawableTextAnchorPosition anchor;
391401
final Paragraph fill;
392402
final Paragraph stroke;
403+
final Float64List transform;
393404

394405
@override
395406
bool get hasDrawableContent =>
@@ -400,12 +411,19 @@ class DrawableText implements Drawable {
400411
if (!hasDrawableContent) {
401412
return;
402413
}
414+
if (transform != null) {
415+
canvas.save();
416+
canvas.transform(transform);
417+
}
403418
if (fill != null) {
404419
canvas.drawParagraph(fill, resolveOffset(fill, anchor, offset));
405420
}
406421
if (stroke != null) {
407422
canvas.drawParagraph(stroke, resolveOffset(stroke, anchor, offset));
408423
}
424+
if (transform != null) {
425+
canvas.restore();
426+
}
409427
}
410428

411429
static Offset resolveOffset(
@@ -418,13 +436,22 @@ class DrawableText implements Drawable {
418436
assert(offset != null);
419437
switch (anchor) {
420438
case DrawableTextAnchorPosition.middle:
421-
return Offset(offset.dx - paragraph.minIntrinsicWidth / 2, offset.dy-paragraph.height);
439+
return Offset(
440+
offset.dx - paragraph.minIntrinsicWidth / 2,
441+
offset.dy - paragraph.alphabeticBaseline,
442+
);
422443
break;
423444
case DrawableTextAnchorPosition.end:
424-
return Offset(offset.dx - paragraph.minIntrinsicWidth, offset.dy-paragraph.height);
445+
return Offset(
446+
offset.dx - paragraph.minIntrinsicWidth,
447+
offset.dy - paragraph.alphabeticBaseline,
448+
);
425449
break;
426450
case DrawableTextAnchorPosition.start:
427-
return Offset(offset.dx, offset.dy-paragraph.alphabeticBaseline);
451+
return Offset(
452+
offset.dx,
453+
offset.dy - paragraph.alphabeticBaseline,
454+
);
428455
break;
429456
default:
430457
return offset;
@@ -552,7 +579,7 @@ class DrawableRoot implements Drawable {
552579
/// Contains reusable definitions such as gradients and clipPaths.
553580
final DrawableDefinitionServer definitions;
554581

555-
/// The [DrawableStyle] for inheritence.
582+
/// The [DrawableStyle] for inheritance.
556583
final DrawableStyle style;
557584

558585
/// Scales the `canvas` so that the drawing units in this [Drawable]

0 commit comments

Comments
 (0)