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