Showing
4 changed files
with
302 additions
and
93 deletions
| @@ -18,25 +18,85 @@ part of widget; | @@ -18,25 +18,85 @@ part of widget; | ||
| 18 | 18 | ||
| 19 | enum TextAlign { left, right, center, justify } | 19 | enum TextAlign { left, right, center, justify } |
| 20 | 20 | ||
| 21 | -class _Word { | ||
| 22 | - _Word(this.text, this.style, this.metrics, this.annotation); | ||
| 23 | - | ||
| 24 | - final String text; | 21 | +abstract class _Span { |
| 22 | + _Span(this.style, this.annotation); | ||
| 25 | 23 | ||
| 26 | final TextStyle style; | 24 | final TextStyle style; |
| 27 | 25 | ||
| 28 | - final PdfFontMetrics metrics; | 26 | + final AnnotationBuilder annotation; |
| 29 | 27 | ||
| 30 | PdfPoint offset = PdfPoint.zero; | 28 | PdfPoint offset = PdfPoint.zero; |
| 31 | 29 | ||
| 32 | - final AnnotationBuilder annotation; | 30 | + double left; |
| 31 | + double top; | ||
| 32 | + double width; | ||
| 33 | + double height; | ||
| 34 | + | ||
| 35 | + @override | ||
| 36 | + String toString() { | ||
| 37 | + return 'Span "offset:$offset'; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + void debugPaint( | ||
| 41 | + Context context, | ||
| 42 | + double textScaleFactor, | ||
| 43 | + PdfRect globalBox, | ||
| 44 | + ) {} | ||
| 45 | + | ||
| 46 | + void paint( | ||
| 47 | + Context context, | ||
| 48 | + TextStyle style, | ||
| 49 | + double textScaleFactor, | ||
| 50 | + PdfPoint point, | ||
| 51 | + ); | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +class _Word extends _Span { | ||
| 55 | + _Word(this.text, TextStyle style, this.metrics, AnnotationBuilder annotation) | ||
| 56 | + : super(style, annotation); | ||
| 57 | + | ||
| 58 | + final String text; | ||
| 59 | + | ||
| 60 | + final PdfFontMetrics metrics; | ||
| 61 | + | ||
| 62 | + @override | ||
| 63 | + double get left => metrics.left; | ||
| 64 | + | ||
| 65 | + @override | ||
| 66 | + double get top => metrics.top; | ||
| 67 | + | ||
| 68 | + @override | ||
| 69 | + double get width => metrics.width; | ||
| 70 | + | ||
| 71 | + @override | ||
| 72 | + double get height => metrics.height; | ||
| 33 | 73 | ||
| 34 | @override | 74 | @override |
| 35 | String toString() { | 75 | String toString() { |
| 36 | return 'Word "$text" offset:$offset metrics:$metrics style:$style'; | 76 | return 'Word "$text" offset:$offset metrics:$metrics style:$style'; |
| 37 | } | 77 | } |
| 38 | 78 | ||
| 39 | - void debugPaint(Context context, double textScaleFactor, PdfRect globalBox) { | 79 | + @override |
| 80 | + void paint( | ||
| 81 | + Context context, | ||
| 82 | + TextStyle style, | ||
| 83 | + double textScaleFactor, | ||
| 84 | + PdfPoint point, | ||
| 85 | + ) { | ||
| 86 | + context.canvas.drawString( | ||
| 87 | + style.font.getFont(context), | ||
| 88 | + style.fontSize * textScaleFactor, | ||
| 89 | + text, | ||
| 90 | + point.x + offset.x, | ||
| 91 | + point.y + offset.y); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + @override | ||
| 95 | + void debugPaint( | ||
| 96 | + Context context, | ||
| 97 | + double textScaleFactor, | ||
| 98 | + PdfRect globalBox, | ||
| 99 | + ) { | ||
| 40 | const double deb = 5; | 100 | const double deb = 5; |
| 41 | 101 | ||
| 42 | context.canvas | 102 | context.canvas |
| @@ -54,27 +114,122 @@ class _Word { | @@ -54,27 +114,122 @@ class _Word { | ||
| 54 | } | 114 | } |
| 55 | } | 115 | } |
| 56 | 116 | ||
| 57 | -class TextSpan { | ||
| 58 | - const TextSpan({this.style, this.text, this.children, this.annotation}); | 117 | +class _WidgetSpan extends _Span { |
| 118 | + _WidgetSpan(this.widget, TextStyle style, AnnotationBuilder annotation) | ||
| 119 | + : assert(widget != null), | ||
| 120 | + assert(style != null), | ||
| 121 | + super(style, annotation); | ||
| 59 | 122 | ||
| 60 | - final TextStyle style; | 123 | + final Widget widget; |
| 61 | 124 | ||
| 62 | - final String text; | 125 | + @override |
| 126 | + double get left => 0; | ||
| 127 | + | ||
| 128 | + @override | ||
| 129 | + double get top => 0; | ||
| 130 | + | ||
| 131 | + @override | ||
| 132 | + double get width => widget.box.width; | ||
| 133 | + | ||
| 134 | + @override | ||
| 135 | + double get height => widget.box.height; | ||
| 136 | + | ||
| 137 | + @override | ||
| 138 | + PdfPoint get offset => widget.box.offset; | ||
| 139 | + | ||
| 140 | + @override | ||
| 141 | + set offset(PdfPoint value) { | ||
| 142 | + widget.box = PdfRect.fromPoints(value, widget.box.size); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + @override | ||
| 146 | + String toString() { | ||
| 147 | + return 'Widget "$widget" offset:$offset'; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + @override | ||
| 151 | + void paint( | ||
| 152 | + Context context, | ||
| 153 | + TextStyle style, | ||
| 154 | + double textScaleFactor, | ||
| 155 | + PdfPoint point, | ||
| 156 | + ) { | ||
| 157 | + widget.box = PdfRect.fromPoints( | ||
| 158 | + PdfPoint(point.x + widget.box.offset.x, point.y + widget.box.offset.y), | ||
| 159 | + widget.box.size); | ||
| 160 | + widget.paint(context); | ||
| 161 | + } | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +@immutable | ||
| 165 | +abstract class InlineSpan { | ||
| 166 | + const InlineSpan({this.style, this.baseline, this.annotation}); | ||
| 167 | + | ||
| 168 | + final TextStyle style; | ||
| 63 | 169 | ||
| 64 | - final List<TextSpan> children; | 170 | + final double baseline; |
| 65 | 171 | ||
| 66 | final AnnotationBuilder annotation; | 172 | final AnnotationBuilder annotation; |
| 67 | 173 | ||
| 68 | String toPlainText() { | 174 | String toPlainText() { |
| 69 | final StringBuffer buffer = StringBuffer(); | 175 | final StringBuffer buffer = StringBuffer(); |
| 70 | - visitTextSpan((TextSpan span, TextStyle style) { | ||
| 71 | - buffer.write(span.text); | 176 | + visitChildren((InlineSpan span, TextStyle style) { |
| 177 | + if (span is TextSpan) { | ||
| 178 | + buffer.write(span.text); | ||
| 179 | + } | ||
| 72 | return true; | 180 | return true; |
| 73 | }, null); | 181 | }, null); |
| 74 | return buffer.toString(); | 182 | return buffer.toString(); |
| 75 | } | 183 | } |
| 76 | 184 | ||
| 77 | - bool visitTextSpan(bool visitor(TextSpan span, TextStyle parentStyle), | 185 | + bool visitChildren(bool visitor(InlineSpan span, TextStyle parentStyle), |
| 186 | + TextStyle parentStyle); | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +class WidgetSpan extends InlineSpan { | ||
| 190 | + /// Creates a [WidgetSpan] with the given values. | ||
| 191 | + const WidgetSpan({ | ||
| 192 | + @required this.child, | ||
| 193 | + double baseline = 0, | ||
| 194 | + TextStyle style, | ||
| 195 | + AnnotationBuilder annotation, | ||
| 196 | + }) : assert(child != null), | ||
| 197 | + super(style: style, baseline: baseline, annotation: annotation); | ||
| 198 | + | ||
| 199 | + /// The widget to embed inline within text. | ||
| 200 | + final Widget child; | ||
| 201 | + | ||
| 202 | + /// Calls `visitor` on this [WidgetSpan]. There are no children spans to walk. | ||
| 203 | + @override | ||
| 204 | + bool visitChildren(bool visitor(InlineSpan span, TextStyle parentStyle), | ||
| 205 | + TextStyle parentStyle) { | ||
| 206 | + final TextStyle _style = parentStyle?.merge(style); | ||
| 207 | + | ||
| 208 | + if (child != null) { | ||
| 209 | + if (!visitor(this, _style)) { | ||
| 210 | + return false; | ||
| 211 | + } | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + return true; | ||
| 215 | + } | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | +class TextSpan extends InlineSpan { | ||
| 219 | + const TextSpan({ | ||
| 220 | + TextStyle style, | ||
| 221 | + this.text, | ||
| 222 | + double baseline = 0, | ||
| 223 | + this.children, | ||
| 224 | + AnnotationBuilder annotation, | ||
| 225 | + }) : super(style: style, baseline: baseline, annotation: annotation); | ||
| 226 | + | ||
| 227 | + final String text; | ||
| 228 | + | ||
| 229 | + final List<InlineSpan> children; | ||
| 230 | + | ||
| 231 | + @override | ||
| 232 | + bool visitChildren(bool visitor(InlineSpan span, TextStyle parentStyle), | ||
| 78 | TextStyle parentStyle) { | 233 | TextStyle parentStyle) { |
| 79 | final TextStyle _style = parentStyle?.merge(style); | 234 | final TextStyle _style = parentStyle?.merge(style); |
| 80 | 235 | ||
| @@ -84,8 +239,8 @@ class TextSpan { | @@ -84,8 +239,8 @@ class TextSpan { | ||
| 84 | } | 239 | } |
| 85 | } | 240 | } |
| 86 | if (children != null) { | 241 | if (children != null) { |
| 87 | - for (TextSpan child in children) { | ||
| 88 | - if (!child.visitTextSpan(visitor, _style)) { | 242 | + for (InlineSpan child in children) { |
| 243 | + if (!child.visitChildren(visitor, _style)) { | ||
| 89 | return false; | 244 | return false; |
| 90 | } | 245 | } |
| 91 | } | 246 | } |
| @@ -105,7 +260,7 @@ class RichText extends Widget { | @@ -105,7 +260,7 @@ class RichText extends Widget { | ||
| 105 | 260 | ||
| 106 | static bool debug = false; | 261 | static bool debug = false; |
| 107 | 262 | ||
| 108 | - final TextSpan text; | 263 | + final InlineSpan text; |
| 109 | 264 | ||
| 110 | final TextAlign textAlign; | 265 | final TextAlign textAlign; |
| 111 | 266 | ||
| @@ -115,9 +270,9 @@ class RichText extends Widget { | @@ -115,9 +270,9 @@ class RichText extends Widget { | ||
| 115 | 270 | ||
| 116 | final int maxLines; | 271 | final int maxLines; |
| 117 | 272 | ||
| 118 | - final List<_Word> _words = <_Word>[]; | 273 | + final List<_Span> _spans = <_Span>[]; |
| 119 | 274 | ||
| 120 | - double _realignLine(List<_Word> words, double totalWidth, double wordsWidth, | 275 | + double _realignLine(List<_Span> spans, double totalWidth, double wordsWidth, |
| 121 | bool last, double baseline) { | 276 | bool last, double baseline) { |
| 122 | double delta = 0; | 277 | double delta = 0; |
| 123 | switch (textAlign) { | 278 | switch (textAlign) { |
| @@ -135,17 +290,17 @@ class RichText extends Widget { | @@ -135,17 +290,17 @@ class RichText extends Widget { | ||
| 135 | totalWidth = wordsWidth; | 290 | totalWidth = wordsWidth; |
| 136 | break; | 291 | break; |
| 137 | } | 292 | } |
| 138 | - delta = (totalWidth - wordsWidth) / (words.length - 1); | 293 | + delta = (totalWidth - wordsWidth) / (spans.length - 1); |
| 139 | double x = 0; | 294 | double x = 0; |
| 140 | - for (_Word word in words) { | ||
| 141 | - word.offset = word.offset.translate(x, -baseline); | 295 | + for (_Span span in spans) { |
| 296 | + span.offset = span.offset.translate(x, -baseline); | ||
| 142 | x += delta; | 297 | x += delta; |
| 143 | } | 298 | } |
| 144 | return totalWidth; | 299 | return totalWidth; |
| 145 | } | 300 | } |
| 146 | 301 | ||
| 147 | - for (_Word word in words) { | ||
| 148 | - word.offset = word.offset.translate(delta, -baseline); | 302 | + for (_Span span in spans) { |
| 303 | + span.offset = span.offset.translate(delta, -baseline); | ||
| 149 | } | 304 | } |
| 150 | return totalWidth; | 305 | return totalWidth; |
| 151 | } | 306 | } |
| @@ -153,7 +308,7 @@ class RichText extends Widget { | @@ -153,7 +308,7 @@ class RichText extends Widget { | ||
| 153 | @override | 308 | @override |
| 154 | void layout(Context context, BoxConstraints constraints, | 309 | void layout(Context context, BoxConstraints constraints, |
| 155 | {bool parentUsesSize = false}) { | 310 | {bool parentUsesSize = false}) { |
| 156 | - _words.clear(); | 311 | + _spans.clear(); |
| 157 | 312 | ||
| 158 | final TextStyle defaultstyle = Theme.of(context).defaultTextStyle; | 313 | final TextStyle defaultstyle = Theme.of(context).defaultTextStyle; |
| 159 | 314 | ||
| @@ -174,32 +329,75 @@ class RichText extends Widget { | @@ -174,32 +329,75 @@ class RichText extends Widget { | ||
| 174 | int wCount = 0; | 329 | int wCount = 0; |
| 175 | int lineStart = 0; | 330 | int lineStart = 0; |
| 176 | 331 | ||
| 177 | - text.visitTextSpan((TextSpan span, TextStyle style) { | ||
| 178 | - if (span.text == null) { | ||
| 179 | - return true; | ||
| 180 | - } | 332 | + text.visitChildren((InlineSpan span, TextStyle style) { |
| 333 | + if (span is TextSpan) { | ||
| 334 | + if (span.text == null) { | ||
| 335 | + return true; | ||
| 336 | + } | ||
| 181 | 337 | ||
| 182 | - final PdfFont font = style.font.getFont(context); | 338 | + final PdfFont font = style.font.getFont(context); |
| 183 | 339 | ||
| 184 | - final PdfFontMetrics space = | ||
| 185 | - font.stringMetrics(' ') * (style.fontSize * textScaleFactor); | 340 | + final PdfFontMetrics space = |
| 341 | + font.stringMetrics(' ') * (style.fontSize * textScaleFactor); | ||
| 186 | 342 | ||
| 187 | - final List<String> spanLines = span.text.split('\n'); | ||
| 188 | - for (int line = 0; line < spanLines.length; line++) { | ||
| 189 | - for (String word in spanLines[line].split(RegExp(r'\s'))) { | ||
| 190 | - if (word.isEmpty) { | ||
| 191 | - offsetX += space.advanceWidth * style.wordSpacing; | ||
| 192 | - continue; | ||
| 193 | - } | 343 | + final List<String> spanLines = span.text.split('\n'); |
| 344 | + for (int line = 0; line < spanLines.length; line++) { | ||
| 345 | + for (String word in spanLines[line].split(RegExp(r'\s'))) { | ||
| 346 | + if (word.isEmpty) { | ||
| 347 | + offsetX += space.advanceWidth * style.wordSpacing; | ||
| 348 | + continue; | ||
| 349 | + } | ||
| 194 | 350 | ||
| 195 | - final PdfFontMetrics metrics = | ||
| 196 | - font.stringMetrics(word) * (style.fontSize * textScaleFactor); | 351 | + final PdfFontMetrics metrics = |
| 352 | + font.stringMetrics(word) * (style.fontSize * textScaleFactor); | ||
| 353 | + | ||
| 354 | + if (offsetX + metrics.width > constraintWidth && wCount > 0) { | ||
| 355 | + width = math.max( | ||
| 356 | + width, | ||
| 357 | + _realignLine( | ||
| 358 | + _spans.sublist(lineStart), | ||
| 359 | + constraintWidth, | ||
| 360 | + offsetX - space.advanceWidth * style.wordSpacing, | ||
| 361 | + false, | ||
| 362 | + bottom)); | ||
| 363 | + | ||
| 364 | + lineStart += wCount; | ||
| 365 | + | ||
| 366 | + if (maxLines != null && ++lines > maxLines) { | ||
| 367 | + break; | ||
| 368 | + } | ||
| 369 | + | ||
| 370 | + offsetX = 0.0; | ||
| 371 | + offsetY += bottom - top + style.lineSpacing; | ||
| 372 | + top = null; | ||
| 373 | + bottom = null; | ||
| 374 | + | ||
| 375 | + if (offsetY > constraintHeight) { | ||
| 376 | + return false; | ||
| 377 | + } | ||
| 378 | + wCount = 0; | ||
| 379 | + } | ||
| 197 | 380 | ||
| 198 | - if (offsetX + metrics.width > constraintWidth && wCount > 0) { | 381 | + final double baseline = span.baseline * textScaleFactor; |
| 382 | + top = | ||
| 383 | + math.min(top ?? metrics.top + baseline, metrics.top + baseline); | ||
| 384 | + bottom = math.max( | ||
| 385 | + bottom ?? metrics.bottom + baseline, metrics.bottom + baseline); | ||
| 386 | + | ||
| 387 | + final _Word wd = _Word(word, style, metrics, span.annotation); | ||
| 388 | + wd.offset = PdfPoint(offsetX, -offsetY + baseline); | ||
| 389 | + | ||
| 390 | + _spans.add(wd); | ||
| 391 | + wCount++; | ||
| 392 | + offsetX += | ||
| 393 | + metrics.advanceWidth + space.advanceWidth * style.wordSpacing; | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + if (softWrap && line < spanLines.length - 1) { | ||
| 199 | width = math.max( | 397 | width = math.max( |
| 200 | width, | 398 | width, |
| 201 | _realignLine( | 399 | _realignLine( |
| 202 | - _words.sublist(lineStart), | 400 | + _spans.sublist(lineStart), |
| 203 | constraintWidth, | 401 | constraintWidth, |
| 204 | offsetX - space.advanceWidth * style.wordSpacing, | 402 | offsetX - space.advanceWidth * style.wordSpacing, |
| 205 | false, | 403 | false, |
| @@ -212,7 +410,11 @@ class RichText extends Widget { | @@ -212,7 +410,11 @@ class RichText extends Widget { | ||
| 212 | } | 410 | } |
| 213 | 411 | ||
| 214 | offsetX = 0.0; | 412 | offsetX = 0.0; |
| 215 | - offsetY += bottom - top + style.lineSpacing; | 413 | + if (wCount > 0) { |
| 414 | + offsetY += bottom - top + style.lineSpacing; | ||
| 415 | + } else { | ||
| 416 | + offsetY += space.ascent + space.descent + style.lineSpacing; | ||
| 417 | + } | ||
| 216 | top = null; | 418 | top = null; |
| 217 | bottom = null; | 419 | bottom = null; |
| 218 | 420 | ||
| @@ -221,41 +423,32 @@ class RichText extends Widget { | @@ -221,41 +423,32 @@ class RichText extends Widget { | ||
| 221 | } | 423 | } |
| 222 | wCount = 0; | 424 | wCount = 0; |
| 223 | } | 425 | } |
| 224 | - | ||
| 225 | - top = math.min(top ?? metrics.top, metrics.top); | ||
| 226 | - bottom = math.max(bottom ?? metrics.bottom, metrics.bottom); | ||
| 227 | - | ||
| 228 | - final _Word wd = _Word(word, style, metrics, span.annotation); | ||
| 229 | - wd.offset = PdfPoint(offsetX, -offsetY); | ||
| 230 | - | ||
| 231 | - _words.add(wd); | ||
| 232 | - wCount++; | ||
| 233 | - offsetX += | ||
| 234 | - metrics.advanceWidth + space.advanceWidth * style.wordSpacing; | ||
| 235 | } | 426 | } |
| 236 | 427 | ||
| 237 | - if (softWrap && line < spanLines.length - 1) { | 428 | + offsetX -= space.advanceWidth * style.wordSpacing; |
| 429 | + } else if (span is WidgetSpan) { | ||
| 430 | + span.child.layout( | ||
| 431 | + context, | ||
| 432 | + BoxConstraints.tight(PdfPoint( | ||
| 433 | + double.infinity, | ||
| 434 | + style.fontSize * textScaleFactor, | ||
| 435 | + ))); | ||
| 436 | + final _WidgetSpan ws = _WidgetSpan(span.child, style, span.annotation); | ||
| 437 | + | ||
| 438 | + if (offsetX + ws.width > constraintWidth && wCount > 0) { | ||
| 238 | width = math.max( | 439 | width = math.max( |
| 239 | width, | 440 | width, |
| 240 | - _realignLine( | ||
| 241 | - _words.sublist(lineStart), | ||
| 242 | - constraintWidth, | ||
| 243 | - offsetX - space.advanceWidth * style.wordSpacing, | ||
| 244 | - false, | ||
| 245 | - bottom)); | 441 | + _realignLine(_spans.sublist(lineStart), constraintWidth, offsetX, |
| 442 | + false, bottom)); | ||
| 246 | 443 | ||
| 247 | lineStart += wCount; | 444 | lineStart += wCount; |
| 248 | 445 | ||
| 249 | if (maxLines != null && ++lines > maxLines) { | 446 | if (maxLines != null && ++lines > maxLines) { |
| 250 | - break; | 447 | + return false; |
| 251 | } | 448 | } |
| 252 | 449 | ||
| 253 | offsetX = 0.0; | 450 | offsetX = 0.0; |
| 254 | - if (wCount > 0) { | ||
| 255 | - offsetY += bottom - top + style.lineSpacing; | ||
| 256 | - } else { | ||
| 257 | - offsetY += space.ascent + space.descent + style.lineSpacing; | ||
| 258 | - } | 451 | + offsetY += bottom - top + style.lineSpacing; |
| 259 | top = null; | 452 | top = null; |
| 260 | bottom = null; | 453 | bottom = null; |
| 261 | 454 | ||
| @@ -264,16 +457,24 @@ class RichText extends Widget { | @@ -264,16 +457,24 @@ class RichText extends Widget { | ||
| 264 | } | 457 | } |
| 265 | wCount = 0; | 458 | wCount = 0; |
| 266 | } | 459 | } |
| 460 | + | ||
| 461 | + final double baseline = span.baseline * textScaleFactor; | ||
| 462 | + top = math.min(top ?? baseline, baseline); | ||
| 463 | + bottom = math.max(bottom ?? ws.height + baseline, ws.height + baseline); | ||
| 464 | + | ||
| 465 | + ws.offset = PdfPoint(offsetX, -offsetY + baseline); | ||
| 466 | + _spans.add(ws); | ||
| 467 | + wCount++; | ||
| 468 | + offsetX += ws.left + ws.width; | ||
| 267 | } | 469 | } |
| 268 | 470 | ||
| 269 | - offsetX -= space.advanceWidth * style.wordSpacing; | ||
| 270 | return true; | 471 | return true; |
| 271 | }, defaultstyle); | 472 | }, defaultstyle); |
| 272 | 473 | ||
| 273 | width = math.max( | 474 | width = math.max( |
| 274 | width, | 475 | width, |
| 275 | _realignLine( | 476 | _realignLine( |
| 276 | - _words.sublist(lineStart), constraintWidth, offsetX, true, bottom)); | 477 | + _spans.sublist(lineStart), constraintWidth, offsetX, true, bottom)); |
| 277 | 478 | ||
| 278 | bottom ??= 0.0; | 479 | bottom ??= 0.0; |
| 279 | top ??= 0.0; | 480 | top ??= 0.0; |
| @@ -296,37 +497,34 @@ class RichText extends Widget { | @@ -296,37 +497,34 @@ class RichText extends Widget { | ||
| 296 | TextStyle currentStyle; | 497 | TextStyle currentStyle; |
| 297 | PdfColor currentColor; | 498 | PdfColor currentColor; |
| 298 | 499 | ||
| 299 | - for (_Word word in _words) { | 500 | + for (_Span span in _spans) { |
| 300 | assert(() { | 501 | assert(() { |
| 301 | if (Document.debug && RichText.debug) { | 502 | if (Document.debug && RichText.debug) { |
| 302 | - word.debugPaint(context, textScaleFactor, box); | 503 | + span.debugPaint(context, textScaleFactor, box); |
| 303 | } | 504 | } |
| 304 | return true; | 505 | return true; |
| 305 | }()); | 506 | }()); |
| 306 | 507 | ||
| 307 | - if (word.style != currentStyle) { | ||
| 308 | - currentStyle = word.style; | 508 | + if (span.style != currentStyle) { |
| 509 | + currentStyle = span.style; | ||
| 309 | if (currentStyle.color != currentColor) { | 510 | if (currentStyle.color != currentColor) { |
| 310 | currentColor = currentStyle.color; | 511 | currentColor = currentStyle.color; |
| 311 | context.canvas.setFillColor(currentColor); | 512 | context.canvas.setFillColor(currentColor); |
| 312 | } | 513 | } |
| 313 | } | 514 | } |
| 314 | 515 | ||
| 315 | - if (word.annotation != null) { | ||
| 316 | - final PdfRect wordBox = PdfRect( | ||
| 317 | - box.x + word.offset.x + word.metrics.left, | ||
| 318 | - box.top + word.offset.y + word.metrics.top, | ||
| 319 | - word.metrics.width, | ||
| 320 | - word.metrics.height); | ||
| 321 | - word.annotation.build(context, wordBox); | 516 | + if (span.annotation != null) { |
| 517 | + final PdfRect spanBox = PdfRect(box.x + span.offset.x + span.left, | ||
| 518 | + box.top + span.offset.y + span.top, span.width, span.height); | ||
| 519 | + span.annotation.build(context, spanBox); | ||
| 322 | } | 520 | } |
| 323 | 521 | ||
| 324 | - context.canvas.drawString( | ||
| 325 | - currentStyle.font.getFont(context), | ||
| 326 | - currentStyle.fontSize * textScaleFactor, | ||
| 327 | - word.text, | ||
| 328 | - box.x + word.offset.x, | ||
| 329 | - box.top + word.offset.y); | 522 | + span.paint( |
| 523 | + context, | ||
| 524 | + currentStyle, | ||
| 525 | + textScaleFactor, | ||
| 526 | + PdfPoint(box.left, box.top), | ||
| 527 | + ); | ||
| 330 | } | 528 | } |
| 331 | } | 529 | } |
| 332 | } | 530 | } |
| @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | ||
| 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf | 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf |
| 5 | repository: https://github.com/DavBfr/dart_pdf | 5 | repository: https://github.com/DavBfr/dart_pdf |
| 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues | 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues |
| 7 | -version: 1.3.17 | 7 | +version: 1.3.18 |
| 8 | 8 | ||
| 9 | environment: | 9 | environment: |
| 10 | sdk: ">=2.1.0 <3.0.0" | 10 | sdk: ">=2.1.0 <3.0.0" |
| @@ -169,17 +169,24 @@ void main() { | @@ -169,17 +169,24 @@ void main() { | ||
| 169 | font: ttf, | 169 | font: ttf, |
| 170 | fontSize: 20, | 170 | fontSize: 20, |
| 171 | ), | 171 | ), |
| 172 | - children: <TextSpan>[ | 172 | + children: <InlineSpan>[ |
| 173 | TextSpan( | 173 | TextSpan( |
| 174 | text: 'bold', | 174 | text: 'bold', |
| 175 | style: TextStyle( | 175 | style: TextStyle( |
| 176 | - font: ttfBold, | ||
| 177 | - fontSize: 40, | ||
| 178 | - color: PdfColors.blue)), | 176 | + font: ttfBold, fontSize: 40, color: PdfColors.blue), |
| 177 | + children: <InlineSpan>[ | ||
| 178 | + const TextSpan(text: '*', baseline: 20), | ||
| 179 | + WidgetSpan(child: PdfLogo(), baseline: -10), | ||
| 180 | + ]), | ||
| 179 | TextSpan( | 181 | TextSpan( |
| 180 | text: ' world!\n', | 182 | text: ' world!\n', |
| 181 | children: spans, | 183 | children: spans, |
| 182 | ), | 184 | ), |
| 185 | + WidgetSpan( | ||
| 186 | + child: PdfLogo(), | ||
| 187 | + annotation: AnnotationUrl( | ||
| 188 | + 'https://github.com/DavBfr/dart_pdf', | ||
| 189 | + )), | ||
| 183 | ], | 190 | ], |
| 184 | ), | 191 | ), |
| 185 | ), | 192 | ), |
-
Please register or login to post a comment