David PHAM-VAN

Implement different border radius on all corners

@@ -158,7 +158,8 @@ class Invoice { @@ -158,7 +158,8 @@ class Invoice {
158 ), 158 ),
159 pw.Container( 159 pw.Container(
160 decoration: pw.BoxDecoration( 160 decoration: pw.BoxDecoration(
161 - borderRadius: 2, 161 + borderRadiusEx:
  162 + pw.BorderRadius.all(pw.Radius.circular(2)),
162 color: accentColor, 163 color: accentColor,
163 ), 164 ),
164 padding: const pw.EdgeInsets.only( 165 padding: const pw.EdgeInsets.only(
@@ -497,7 +498,7 @@ class Invoice { @@ -497,7 +498,7 @@ class Invoice {
497 border: null, 498 border: null,
498 cellAlignment: pw.Alignment.centerLeft, 499 cellAlignment: pw.Alignment.centerLeft,
499 headerDecoration: pw.BoxDecoration( 500 headerDecoration: pw.BoxDecoration(
500 - borderRadius: 2, 501 + borderRadiusEx: pw.BorderRadius.all(pw.Radius.circular(2)),
501 color: baseColor, 502 color: baseColor,
502 ), 503 ),
503 headerHeight: 25, 504 headerHeight: 25,
@@ -262,7 +262,10 @@ class _Category extends pw.StatelessWidget { @@ -262,7 +262,10 @@ class _Category extends pw.StatelessWidget {
262 @override 262 @override
263 pw.Widget build(pw.Context context) { 263 pw.Widget build(pw.Context context) {
264 return pw.Container( 264 return pw.Container(
265 - decoration: const pw.BoxDecoration(color: lightGreen, borderRadius: 6), 265 + decoration: const pw.BoxDecoration(
  266 + color: lightGreen,
  267 + borderRadiusEx: pw.BorderRadius.all(pw.Radius.circular(6)),
  268 + ),
266 margin: const pw.EdgeInsets.only(bottom: 10, top: 20), 269 margin: const pw.EdgeInsets.only(bottom: 10, top: 20),
267 padding: const pw.EdgeInsets.fromLTRB(10, 7, 10, 4), 270 padding: const pw.EdgeInsets.fromLTRB(10, 7, 10, 4),
268 child: pw.Text(title, textScaleFactor: 1.5)); 271 child: pw.Text(title, textScaleFactor: 1.5));
1 # Changelog 1 # Changelog
2 2
  3 +## 1.13.0
  4 +
  5 +- Implement different border radius on all corners
3 6
4 ## 1.12.0 7 ## 1.12.0
5 8
@@ -130,7 +130,7 @@ class BoxBorder { @@ -130,7 +130,7 @@ class BoxBorder {
130 ..strokePath(); 130 ..strokePath();
131 } 131 }
132 132
133 - void paintRRect(Context context, PdfRect box, double borderRadius) { 133 + void paintRRect(Context context, PdfRect box, BorderRadius borderRadius) {
134 assert(box.x != null); 134 assert(box.x != null);
135 assert(box.y != null); 135 assert(box.y != null);
136 assert(box.width != null); 136 assert(box.width != null);
@@ -138,10 +138,9 @@ class BoxBorder { @@ -138,10 +138,9 @@ class BoxBorder {
138 138
139 context.canvas 139 context.canvas
140 ..setStrokeColor(color) 140 ..setStrokeColor(color)
141 - ..setLineWidth(width)  
142 - ..drawRRect(  
143 - box.x, box.y, box.width, box.height, borderRadius, borderRadius)  
144 - ..strokePath(); 141 + ..setLineWidth(width);
  142 + borderRadius.paint(context, box);
  143 + context.canvas.strokePath();
145 } 144 }
146 } 145 }
147 146
@@ -408,26 +407,6 @@ class BoxShadow { @@ -408,26 +407,6 @@ class BoxShadow {
408 return shadow; 407 return shadow;
409 } 408 }
410 409
411 - im.Image _rRect(double width, double height, double rv, double rh) {  
412 - final im.Image shadow = im.Image(  
413 - (width + spreadRadius * 2).round(),  
414 - (height + spreadRadius * 2).round(),  
415 - );  
416 -  
417 - im.fillRect(  
418 - shadow,  
419 - spreadRadius.round(),  
420 - spreadRadius.round(),  
421 - (spreadRadius + width).round(),  
422 - (spreadRadius + height).round(),  
423 - color.toInt(),  
424 - );  
425 -  
426 - im.gaussianBlur(shadow, blurRadius.round());  
427 -  
428 - return shadow;  
429 - }  
430 -  
431 im.Image _ellipse(double width, double height) { 410 im.Image _ellipse(double width, double height) {
432 final im.Image shadow = im.Image( 411 final im.Image shadow = im.Image(
433 (width + spreadRadius * 2).round(), 412 (width + spreadRadius * 2).round(),
@@ -452,27 +431,171 @@ enum BoxShape { circle, rectangle } @@ -452,27 +431,171 @@ enum BoxShape { circle, rectangle }
452 431
453 enum PaintPhase { all, background, foreground } 432 enum PaintPhase { all, background, foreground }
454 433
  434 +/// A radius for either circular or elliptical shapes.
  435 +class Radius {
  436 + /// Constructs a circular radius. [x] and [y] will have the same radius value.
  437 + const Radius.circular(double radius) : this.elliptical(radius, radius);
  438 +
  439 + /// Constructs an elliptical radius with the given radii.
  440 + const Radius.elliptical(this.x, this.y);
  441 +
  442 + /// The radius value on the horizontal axis.
  443 + final double x;
  444 +
  445 + /// The radius value on the vertical axis.
  446 + final double y;
  447 +
  448 + /// A radius with [x] and [y] values set to zero.
  449 + static const Radius zero = Radius.circular(0.0);
  450 +}
  451 +
  452 +/// An immutable set of radii for each corner of a rectangle.
  453 +class BorderRadius {
  454 + /// Creates a border radius where all radii are [radius].
  455 + const BorderRadius.all(Radius radius)
  456 + : this.only(
  457 + topLeft: radius,
  458 + topRight: radius,
  459 + bottomLeft: radius,
  460 + bottomRight: radius,
  461 + );
  462 +
  463 + /// Creates a border radius where all radii are [Radius.circular(radius)].
  464 + BorderRadius.circular(double radius)
  465 + : this.all(
  466 + Radius.circular(radius),
  467 + );
  468 +
  469 + /// Creates a vertically symmetric border radius where the top and bottom
  470 + /// sides of the rectangle have the same radii.
  471 + const BorderRadius.vertical({
  472 + Radius top = Radius.zero,
  473 + Radius bottom = Radius.zero,
  474 + }) : this.only(
  475 + topLeft: top,
  476 + topRight: top,
  477 + bottomLeft: bottom,
  478 + bottomRight: bottom,
  479 + );
  480 +
  481 + /// Creates a horizontally symmetrical border radius where the left and right
  482 + /// sides of the rectangle have the same radii.
  483 + const BorderRadius.horizontal({
  484 + Radius left = Radius.zero,
  485 + Radius right = Radius.zero,
  486 + }) : this.only(
  487 + topLeft: left,
  488 + topRight: right,
  489 + bottomLeft: left,
  490 + bottomRight: right,
  491 + );
  492 +
  493 + /// Creates a border radius with only the given non-zero values. The other
  494 + /// corners will be right angles.
  495 + const BorderRadius.only({
  496 + this.topLeft = Radius.zero,
  497 + this.topRight = Radius.zero,
  498 + this.bottomLeft = Radius.zero,
  499 + this.bottomRight = Radius.zero,
  500 + });
  501 +
  502 + /// A border radius with all zero radii.
  503 + static const BorderRadius zero = BorderRadius.all(Radius.zero);
  504 +
  505 + /// The top-left [Radius].
  506 + final Radius topLeft;
  507 +
  508 + /// The top-right [Radius].
  509 + final Radius topRight;
  510 +
  511 + /// The bottom-left [Radius].
  512 + final Radius bottomLeft;
  513 +
  514 + /// The bottom-right [Radius].
  515 + final Radius bottomRight;
  516 +
  517 + void paint(Context context, PdfRect box) {
  518 + // Ellipse 4-spline magic number
  519 + const double _m4 = 0.551784;
  520 +
  521 + context.canvas
  522 + // Start
  523 + ..moveTo(box.x, box.y + bottomLeft.y)
  524 + // bottomLeft
  525 + ..curveTo(
  526 + box.x,
  527 + box.y - _m4 * bottomLeft.y + bottomLeft.y,
  528 + box.x - _m4 * bottomLeft.x + bottomLeft.x,
  529 + box.y,
  530 + box.x + bottomLeft.x,
  531 + box.y)
  532 + // bottom
  533 + ..lineTo(box.x + box.width - bottomRight.x, box.y)
  534 + // bottomRight
  535 + ..curveTo(
  536 + box.x + _m4 * bottomRight.x + box.width - bottomRight.x,
  537 + box.y,
  538 + box.x + box.width,
  539 + box.y - _m4 * bottomRight.y + bottomRight.y,
  540 + box.x + box.width,
  541 + box.y + bottomRight.y)
  542 + // right
  543 + ..lineTo(box.x + box.width, box.y + box.height - topRight.y)
  544 + // topRight
  545 + ..curveTo(
  546 + box.x + box.width,
  547 + box.y + _m4 * topRight.y + box.height - topRight.y,
  548 + box.x + _m4 * topRight.x + box.width - topRight.x,
  549 + box.y + box.height,
  550 + box.x + box.width - topRight.x,
  551 + box.y + box.height)
  552 + // top
  553 + ..lineTo(box.x + topLeft.x, box.y + box.height)
  554 + // topLeft
  555 + ..curveTo(
  556 + box.x - _m4 * topLeft.x + topLeft.x,
  557 + box.y + box.height,
  558 + box.x,
  559 + box.y + _m4 * topLeft.y + box.height - topLeft.y,
  560 + box.x,
  561 + box.y + box.height - topLeft.y)
  562 + // left
  563 + ..lineTo(box.x, box.y + bottomLeft.y);
  564 + }
  565 +}
  566 +
455 @immutable 567 @immutable
456 class BoxDecoration { 568 class BoxDecoration {
457 const BoxDecoration({ 569 const BoxDecoration({
458 this.color, 570 this.color,
459 this.border, 571 this.border,
460 - this.borderRadius, 572 + @Deprecated('Use borderRadiusEx with `BorderRadius.all(Radius.circular(20))`')
  573 + double borderRadius,
  574 + BorderRadius borderRadiusEx,
461 this.boxShadow, 575 this.boxShadow,
462 this.gradient, 576 this.gradient,
463 this.image, 577 this.image,
464 this.shape = BoxShape.rectangle, 578 this.shape = BoxShape.rectangle,
465 - }) : assert(shape != null); 579 + }) : assert(shape != null),
  580 + assert(!(borderRadius != null && borderRadiusEx != null),
  581 + 'Don\'t set both borderRadius and borderRadiusEx'),
  582 + _borderRadius = borderRadiusEx,
  583 + _radius = borderRadius;
466 584
467 /// The color to fill in the background of the box. 585 /// The color to fill in the background of the box.
468 final PdfColor color; 586 final PdfColor color;
469 final BoxBorder border; 587 final BoxBorder border;
470 - final double borderRadius; 588 + final BorderRadius _borderRadius;
  589 + final double _radius;
471 final BoxShape shape; 590 final BoxShape shape;
472 final DecorationImage image; 591 final DecorationImage image;
473 final Gradient gradient; 592 final Gradient gradient;
474 final List<BoxShadow> boxShadow; 593 final List<BoxShadow> boxShadow;
475 594
  595 + BorderRadius get borderRadius =>
  596 + _borderRadius ??
  597 + (_radius == null ? null : BorderRadius.all(Radius.circular(_radius)));
  598 +
476 void paint( 599 void paint(
477 Context context, 600 Context context,
478 PdfRect box, [ 601 PdfRect box, [
@@ -504,8 +627,7 @@ class BoxDecoration { @@ -504,8 +627,7 @@ class BoxDecoration {
504 } else { 627 } else {
505 if (boxShadow != null) { 628 if (boxShadow != null) {
506 for (final BoxShadow s in boxShadow) { 629 for (final BoxShadow s in boxShadow) {
507 - final im.Image i = s._rRect(  
508 - box.width, box.height, borderRadius, borderRadius); 630 + final im.Image i = s._rect(box.width, box.height);
509 final PdfImage m = 631 final PdfImage m =
510 PdfImage.fromImage(context.document, image: i); 632 PdfImage.fromImage(context.document, image: i);
511 context.canvas.drawImage( 633 context.canvas.drawImage(
@@ -515,8 +637,7 @@ class BoxDecoration { @@ -515,8 +637,7 @@ class BoxDecoration {
515 ); 637 );
516 } 638 }
517 } 639 }
518 - context.canvas.drawRRect(box.x, box.y, box.width, box.height,  
519 - borderRadius, borderRadius); 640 + borderRadius.paint(context, box);
520 } 641 }
521 break; 642 break;
522 case BoxShape.circle: 643 case BoxShape.circle:
@@ -547,8 +668,7 @@ class BoxDecoration { @@ -547,8 +668,7 @@ class BoxDecoration {
547 if (borderRadius == null) { 668 if (borderRadius == null) {
548 context.canvas.drawRect(box.x, box.y, box.width, box.height); 669 context.canvas.drawRect(box.x, box.y, box.width, box.height);
549 } else { 670 } else {
550 - context.canvas.drawRRect(box.x, box.y, box.width, box.height,  
551 - borderRadius, borderRadius); 671 + borderRadius.paint(context, box);
552 } 672 }
553 break; 673 break;
554 case BoxShape.circle: 674 case BoxShape.circle:
@@ -572,10 +692,8 @@ class BoxDecoration { @@ -572,10 +692,8 @@ class BoxDecoration {
572 break; 692 break;
573 case BoxShape.rectangle: 693 case BoxShape.rectangle:
574 if (borderRadius != null) { 694 if (borderRadius != null) {
575 - context.canvas  
576 - ..drawRRect(box.x, box.y, box.width, box.height, borderRadius,  
577 - borderRadius)  
578 - ..clipPath(); 695 + borderRadius.paint(context, box);
  696 + context.canvas.clipPath();
579 } 697 }
580 break; 698 break;
581 } 699 }
@@ -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.12.0 7 +version: 1.13.0
8 8
9 environment: 9 environment:
10 sdk: ">=2.3.0 <3.0.0" 10 sdk: ">=2.3.0 <3.0.0"
@@ -40,7 +40,7 @@ void main() { @@ -40,7 +40,7 @@ void main() {
40 padding: const EdgeInsets.all(20), 40 padding: const EdgeInsets.all(20),
41 decoration: const BoxDecoration( 41 decoration: const BoxDecoration(
42 color: PdfColors.blue, 42 color: PdfColors.blue,
43 - borderRadius: 20, 43 + borderRadiusEx: BorderRadius.all(Radius.circular(20)),
44 border: BoxBorder( 44 border: BoxBorder(
45 color: PdfColors.blue800, 45 color: PdfColors.blue800,
46 top: true, 46 top: true,
@@ -67,7 +67,7 @@ void main() { @@ -67,7 +67,7 @@ void main() {
67 alignment: Alignment.center, 67 alignment: Alignment.center,
68 decoration: BoxDecoration( 68 decoration: BoxDecoration(
69 shape: shape, 69 shape: shape,
70 - borderRadius: 10, 70 + borderRadiusEx: const BorderRadius.all(Radius.circular(10)),
71 image: DecorationImage(image: image, fit: fit), 71 image: DecorationImage(image: image, fit: fit),
72 ), 72 ),
73 width: 100, 73 width: 100,
@@ -121,7 +121,7 @@ void main() { @@ -121,7 +121,7 @@ void main() {
121 width: 200.0, 121 width: 200.0,
122 decoration: const BoxDecoration( 122 decoration: const BoxDecoration(
123 shape: BoxShape.rectangle, 123 shape: BoxShape.rectangle,
124 - borderRadius: 40, 124 + borderRadiusEx: BorderRadius.all(Radius.circular(40)),
125 border: BoxBorder( 125 border: BoxBorder(
126 bottom: true, 126 bottom: true,
127 top: true, 127 top: true,
@@ -144,7 +144,7 @@ void main() { @@ -144,7 +144,7 @@ void main() {
144 margin: const EdgeInsets.all(30), 144 margin: const EdgeInsets.all(30),
145 padding: const EdgeInsets.all(20), 145 padding: const EdgeInsets.all(20),
146 decoration: const BoxDecoration( 146 decoration: const BoxDecoration(
147 - borderRadius: 20, 147 + borderRadiusEx: BorderRadius.all(Radius.circular(20)),
148 gradient: LinearGradient( 148 gradient: LinearGradient(
149 colors: <PdfColor>[ 149 colors: <PdfColor>[
150 PdfColors.blue, 150 PdfColors.blue,
@@ -177,7 +177,7 @@ void main() { @@ -177,7 +177,7 @@ void main() {
177 margin: const EdgeInsets.all(30), 177 margin: const EdgeInsets.all(30),
178 padding: const EdgeInsets.all(20), 178 padding: const EdgeInsets.all(20),
179 decoration: const BoxDecoration( 179 decoration: const BoxDecoration(
180 - borderRadius: 20, 180 + borderRadiusEx: BorderRadius.all(Radius.circular(20)),
181 gradient: RadialGradient( 181 gradient: RadialGradient(
182 colors: <PdfColor>[ 182 colors: <PdfColor>[
183 PdfColors.blue, 183 PdfColors.blue,
@@ -51,7 +51,10 @@ List<TableRow> buildTable( @@ -51,7 +51,10 @@ List<TableRow> buildTable(
51 child: Text('${h.toInt()}°', style: Theme.of(context).tableCell)), 51 child: Text('${h.toInt()}°', style: Theme.of(context).tableCell)),
52 Container( 52 Container(
53 margin: const EdgeInsets.all(5), 53 margin: const EdgeInsets.all(5),
54 - decoration: BoxDecoration(color: color, borderRadius: 5), 54 + decoration: BoxDecoration(
  55 + color: color,
  56 + borderRadiusEx: const BorderRadius.all(Radius.circular(5)),
  57 + ),
55 height: Theme.of(context).tableCell.fontSize), 58 height: Theme.of(context).tableCell.fontSize),
56 Container( 59 Container(
57 margin: const EdgeInsets.all(5), 60 margin: const EdgeInsets.all(5),
@@ -208,7 +211,7 @@ void main() { @@ -208,7 +211,7 @@ void main() {
208 border: null, 211 border: null,
209 cellAlignment: Alignment.center, 212 cellAlignment: Alignment.center,
210 headerDecoration: const BoxDecoration( 213 headerDecoration: const BoxDecoration(
211 - borderRadius: 2, 214 + borderRadiusEx: BorderRadius.all(Radius.circular(2)),
212 color: PdfColors.indigo, 215 color: PdfColors.indigo,
213 ), 216 ),
214 headerHeight: 25, 217 headerHeight: 25,