Showing
3 changed files
with
147 additions
and
15 deletions
| @@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
| 12 | - Add support for Icon Fonts (MaterialIcons) | 12 | - Add support for Icon Fonts (MaterialIcons) |
| 13 | - Opt-out from dart library | 13 | - Opt-out from dart library |
| 14 | - Improve graphic operations | 14 | - Improve graphic operations |
| 15 | +- Automatically calculate Shape() bounding box | ||
| 15 | 16 | ||
| 16 | ## 1.12.0 | 17 | ## 1.12.0 |
| 17 | 18 |
| @@ -28,6 +28,7 @@ import 'graphic_state.dart'; | @@ -28,6 +28,7 @@ import 'graphic_state.dart'; | ||
| 28 | import 'graphic_stream.dart'; | 28 | import 'graphic_stream.dart'; |
| 29 | import 'image.dart'; | 29 | import 'image.dart'; |
| 30 | import 'page.dart'; | 30 | import 'page.dart'; |
| 31 | +import 'rect.dart'; | ||
| 31 | import 'shading.dart'; | 32 | import 'shading.dart'; |
| 32 | import 'stream.dart'; | 33 | import 'stream.dart'; |
| 33 | 34 | ||
| @@ -528,6 +529,13 @@ class PdfGraphics { | @@ -528,6 +529,13 @@ class PdfGraphics { | ||
| 528 | writeSvgPathDataToPath(d, proxy); | 529 | writeSvgPathDataToPath(d, proxy); |
| 529 | } | 530 | } |
| 530 | 531 | ||
| 532 | + /// Calculates the bounding box of an SVG path | ||
| 533 | + static PdfRect shapeBoundingBox(String d) { | ||
| 534 | + final proxy = _PathBBProxy(); | ||
| 535 | + writeSvgPathDataToPath(d, proxy); | ||
| 536 | + return proxy.box; | ||
| 537 | + } | ||
| 538 | + | ||
| 531 | /// Set line starting and ending cap type | 539 | /// Set line starting and ending cap type |
| 532 | void setLineCap(PdfLineCap cap) { | 540 | void setLineCap(PdfLineCap cap) { |
| 533 | buf.putString('${cap.index} J\n'); | 541 | buf.putString('${cap.index} J\n'); |
| @@ -586,3 +594,112 @@ class _PathProxy extends PathProxy { | @@ -586,3 +594,112 @@ class _PathProxy extends PathProxy { | ||
| 586 | canvas.moveTo(x, y); | 594 | canvas.moveTo(x, y); |
| 587 | } | 595 | } |
| 588 | } | 596 | } |
| 597 | + | ||
| 598 | +class _PathBBProxy extends PathProxy { | ||
| 599 | + _PathBBProxy(); | ||
| 600 | + | ||
| 601 | + var _xMin = double.infinity; | ||
| 602 | + var _yMin = double.infinity; | ||
| 603 | + var _xMax = double.negativeInfinity; | ||
| 604 | + var _yMax = double.negativeInfinity; | ||
| 605 | + | ||
| 606 | + var _pX = 0.0; | ||
| 607 | + var _pY = 0.0; | ||
| 608 | + | ||
| 609 | + PdfRect get box { | ||
| 610 | + if (_xMin > _xMax || _yMin > _yMax) { | ||
| 611 | + return PdfRect.zero; | ||
| 612 | + } | ||
| 613 | + return PdfRect.fromLTRB(_xMin, _yMin, _xMax, _yMax); | ||
| 614 | + } | ||
| 615 | + | ||
| 616 | + @override | ||
| 617 | + void close() {} | ||
| 618 | + | ||
| 619 | + @override | ||
| 620 | + void cubicTo( | ||
| 621 | + double x1, double y1, double x2, double y2, double x3, double y3) { | ||
| 622 | + final tvalues = <double>[]; | ||
| 623 | + double a, b, c, t, t1, t2, b2ac, sqrtb2ac; | ||
| 624 | + | ||
| 625 | + for (var i = 0; i < 2; ++i) { | ||
| 626 | + if (i == 0) { | ||
| 627 | + b = 6 * _pX - 12 * x1 + 6 * x2; | ||
| 628 | + a = -3 * _pX + 9 * x1 - 9 * x2 + 3 * x3; | ||
| 629 | + c = 3 * x1 - 3 * _pX; | ||
| 630 | + } else { | ||
| 631 | + b = 6 * _pY - 12 * y1 + 6 * y2; | ||
| 632 | + a = -3 * _pY + 9 * y1 - 9 * y2 + 3 * y3; | ||
| 633 | + c = 3 * y1 - 3 * _pY; | ||
| 634 | + } | ||
| 635 | + if (a.abs() < 1e-12) { | ||
| 636 | + if (b.abs() < 1e-12) { | ||
| 637 | + continue; | ||
| 638 | + } | ||
| 639 | + t = -c / b; | ||
| 640 | + if (0 < t && t < 1) { | ||
| 641 | + tvalues.add(t); | ||
| 642 | + } | ||
| 643 | + continue; | ||
| 644 | + } | ||
| 645 | + b2ac = b * b - 4 * c * a; | ||
| 646 | + if (b2ac < 0) { | ||
| 647 | + if (b2ac.abs() < 1e-12) { | ||
| 648 | + t = -b / (2 * a); | ||
| 649 | + if (0 < t && t < 1) { | ||
| 650 | + tvalues.add(t); | ||
| 651 | + } | ||
| 652 | + } | ||
| 653 | + continue; | ||
| 654 | + } | ||
| 655 | + sqrtb2ac = math.sqrt(b2ac); | ||
| 656 | + t1 = (-b + sqrtb2ac) / (2 * a); | ||
| 657 | + if (0 < t1 && t1 < 1) { | ||
| 658 | + tvalues.add(t1); | ||
| 659 | + } | ||
| 660 | + t2 = (-b - sqrtb2ac) / (2 * a); | ||
| 661 | + if (0 < t2 && t2 < 1) { | ||
| 662 | + tvalues.add(t2); | ||
| 663 | + } | ||
| 664 | + } | ||
| 665 | + | ||
| 666 | + for (final t in tvalues) { | ||
| 667 | + final mt = 1 - t; | ||
| 668 | + _updateMinMax( | ||
| 669 | + (mt * mt * mt * _pX) + | ||
| 670 | + (3 * mt * mt * t * x1) + | ||
| 671 | + (3 * mt * t * t * x2) + | ||
| 672 | + (t * t * t * x3), | ||
| 673 | + (mt * mt * mt * _pY) + | ||
| 674 | + (3 * mt * mt * t * y1) + | ||
| 675 | + (3 * mt * t * t * y2) + | ||
| 676 | + (t * t * t * y3)); | ||
| 677 | + } | ||
| 678 | + _updateMinMax(_pX, _pY); | ||
| 679 | + _updateMinMax(x3, y3); | ||
| 680 | + | ||
| 681 | + _pX = x3; | ||
| 682 | + _pY = y3; | ||
| 683 | + } | ||
| 684 | + | ||
| 685 | + @override | ||
| 686 | + void lineTo(double x, double y) { | ||
| 687 | + _pX = x; | ||
| 688 | + _pY = y; | ||
| 689 | + _updateMinMax(x, y); | ||
| 690 | + } | ||
| 691 | + | ||
| 692 | + @override | ||
| 693 | + void moveTo(double x, double y) { | ||
| 694 | + _pX = x; | ||
| 695 | + _pY = y; | ||
| 696 | + _updateMinMax(x, y); | ||
| 697 | + } | ||
| 698 | + | ||
| 699 | + void _updateMinMax(double x, double y) { | ||
| 700 | + _xMin = math.min(_xMin, x); | ||
| 701 | + _yMin = math.min(_yMin, y); | ||
| 702 | + _xMax = math.max(_xMax, x); | ||
| 703 | + _yMax = math.max(_yMax, y); | ||
| 704 | + } | ||
| 705 | +} |
| @@ -154,12 +154,11 @@ class Shape extends Widget { | @@ -154,12 +154,11 @@ class Shape extends Widget { | ||
| 154 | this.shape, { | 154 | this.shape, { |
| 155 | this.strokeColor, | 155 | this.strokeColor, |
| 156 | this.fillColor, | 156 | this.fillColor, |
| 157 | - this.width = 1.0, | ||
| 158 | - this.height = 1.0, | 157 | + this.width, |
| 158 | + this.height, | ||
| 159 | this.fit = BoxFit.contain, | 159 | this.fit = BoxFit.contain, |
| 160 | - }) : assert(width != null && width > 0.0), | ||
| 161 | - assert(height != null && height > 0.0), | ||
| 162 | - aspectRatio = height / width; | 160 | + }) : assert(width == null || width > 0.0), |
| 161 | + assert(height == null || height > 0.0); | ||
| 163 | 162 | ||
| 164 | final String shape; | 163 | final String shape; |
| 165 | 164 | ||
| @@ -171,34 +170,49 @@ class Shape extends Widget { | @@ -171,34 +170,49 @@ class Shape extends Widget { | ||
| 171 | 170 | ||
| 172 | final double height; | 171 | final double height; |
| 173 | 172 | ||
| 174 | - final double aspectRatio; | ||
| 175 | - | ||
| 176 | final BoxFit fit; | 173 | final BoxFit fit; |
| 177 | 174 | ||
| 175 | + PdfRect _boundingBox; | ||
| 176 | + | ||
| 178 | @override | 177 | @override |
| 179 | void layout(Context context, BoxConstraints constraints, | 178 | void layout(Context context, BoxConstraints constraints, |
| 180 | {bool parentUsesSize = false}) { | 179 | {bool parentUsesSize = false}) { |
| 180 | + if (width == null || height == null) { | ||
| 181 | + // Compute the bounding box | ||
| 182 | + _boundingBox = PdfGraphics.shapeBoundingBox(shape); | ||
| 183 | + } else { | ||
| 184 | + _boundingBox = PdfRect(0, 0, width, height); | ||
| 185 | + } | ||
| 186 | + | ||
| 181 | final w = constraints.hasBoundedWidth | 187 | final w = constraints.hasBoundedWidth |
| 182 | ? constraints.maxWidth | 188 | ? constraints.maxWidth |
| 183 | - : constraints.constrainWidth(width); | 189 | + : constraints.constrainWidth(_boundingBox.width); |
| 184 | final h = constraints.hasBoundedHeight | 190 | final h = constraints.hasBoundedHeight |
| 185 | ? constraints.maxHeight | 191 | ? constraints.maxHeight |
| 186 | - : constraints.constrainHeight(height); | 192 | + : constraints.constrainHeight(_boundingBox.height); |
| 187 | 193 | ||
| 188 | - final sizes = applyBoxFit(fit, PdfPoint(width, height), PdfPoint(w, h)); | ||
| 189 | - box = PdfRect.fromPoints(PdfPoint.zero, sizes.destination); | 194 | + final sizes = applyBoxFit(fit, _boundingBox.size, PdfPoint(w, h)); |
| 195 | + box = PdfRect.fromPoints( | ||
| 196 | + PdfPoint.zero, | ||
| 197 | + sizes.destination, | ||
| 198 | + ); | ||
| 190 | } | 199 | } |
| 191 | 200 | ||
| 192 | @override | 201 | @override |
| 193 | void paint(Context context) { | 202 | void paint(Context context) { |
| 194 | super.paint(context); | 203 | super.paint(context); |
| 195 | 204 | ||
| 196 | - final mat = Matrix4.identity(); | ||
| 197 | - mat.translate(box.x, box.y + box.height); | ||
| 198 | - mat.scale(box.width / width, -box.height / height); | ||
| 199 | context.canvas | 205 | context.canvas |
| 200 | ..saveContext() | 206 | ..saveContext() |
| 201 | - ..setTransform(mat); | 207 | + ..setTransform( |
| 208 | + Matrix4.identity() | ||
| 209 | + ..translate(box.x, box.y + box.height) | ||
| 210 | + ..scale( | ||
| 211 | + box.width / _boundingBox.width, | ||
| 212 | + -box.height / _boundingBox.height, | ||
| 213 | + ) | ||
| 214 | + ..translate(-_boundingBox.x, -_boundingBox.y), | ||
| 215 | + ); | ||
| 202 | 216 | ||
| 203 | if (fillColor != null) { | 217 | if (fillColor != null) { |
| 204 | context.canvas | 218 | context.canvas |
-
Please register or login to post a comment