Showing
4 changed files
with
232 additions
and
2 deletions
| @@ -40,6 +40,65 @@ class PdfColor { | @@ -40,6 +40,65 @@ class PdfColor { | ||
| 40 | (int.parse(color.substring(6, 7), radix: 16) >> 24 & 0xff) / 255.0); | 40 | (int.parse(color.substring(6, 7), radix: 16) >> 24 & 0xff) / 255.0); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | + factory PdfColor.fromRYB(double red, double yellow, double blue, | ||
| 44 | + [double alpha = 1.0]) { | ||
| 45 | + assert(red >= 0 && red <= 1); | ||
| 46 | + assert(yellow >= 0 && yellow <= 1); | ||
| 47 | + assert(blue >= 0 && blue <= 1); | ||
| 48 | + assert(alpha >= 0 && alpha <= 1); | ||
| 49 | + | ||
| 50 | + const List<List<double>> magic = <List<double>>[ | ||
| 51 | + <double>[1, 1, 1], | ||
| 52 | + <double>[1, 1, 0], | ||
| 53 | + <double>[1, 0, 0], | ||
| 54 | + <double>[1, 0.5, 0], | ||
| 55 | + <double>[0.163, 0.373, 0.6], | ||
| 56 | + <double>[0.0, 0.66, 0.2], | ||
| 57 | + <double>[0.5, 0.0, 0.5], | ||
| 58 | + <double>[0.2, 0.094, 0.0] | ||
| 59 | + ]; | ||
| 60 | + | ||
| 61 | + double cubicInt(double t, double A, double B) { | ||
| 62 | + final double weight = t * t * (3 - 2 * t); | ||
| 63 | + return A + weight * (B - A); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + double getRed(double iR, double iY, double iB) { | ||
| 67 | + final double x0 = cubicInt(iB, magic[0][0], magic[4][0]); | ||
| 68 | + final double x1 = cubicInt(iB, magic[1][0], magic[5][0]); | ||
| 69 | + final double x2 = cubicInt(iB, magic[2][0], magic[6][0]); | ||
| 70 | + final double x3 = cubicInt(iB, magic[3][0], magic[7][0]); | ||
| 71 | + final double y0 = cubicInt(iY, x0, x1); | ||
| 72 | + final double y1 = cubicInt(iY, x2, x3); | ||
| 73 | + return cubicInt(iR, y0, y1); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + double getGreen(double iR, double iY, double iB) { | ||
| 77 | + final double x0 = cubicInt(iB, magic[0][1], magic[4][1]); | ||
| 78 | + final double x1 = cubicInt(iB, magic[1][1], magic[5][1]); | ||
| 79 | + final double x2 = cubicInt(iB, magic[2][1], magic[6][1]); | ||
| 80 | + final double x3 = cubicInt(iB, magic[3][1], magic[7][1]); | ||
| 81 | + final double y0 = cubicInt(iY, x0, x1); | ||
| 82 | + final double y1 = cubicInt(iY, x2, x3); | ||
| 83 | + return cubicInt(iR, y0, y1); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + double getBlue(double iR, double iY, double iB) { | ||
| 87 | + final double x0 = cubicInt(iB, magic[0][2], magic[4][2]); | ||
| 88 | + final double x1 = cubicInt(iB, magic[1][2], magic[5][2]); | ||
| 89 | + final double x2 = cubicInt(iB, magic[2][2], magic[6][2]); | ||
| 90 | + final double x3 = cubicInt(iB, magic[3][2], magic[7][2]); | ||
| 91 | + final double y0 = cubicInt(iY, x0, x1); | ||
| 92 | + final double y1 = cubicInt(iY, x2, x3); | ||
| 93 | + return cubicInt(iR, y0, y1); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + final double redValue = getRed(red, yellow, blue); | ||
| 97 | + final double greenValue = getGreen(red, yellow, blue); | ||
| 98 | + final double blueValue = getBlue(red, yellow, blue); | ||
| 99 | + return PdfColor(redValue, greenValue, blueValue, alpha); | ||
| 100 | + } | ||
| 101 | + | ||
| 43 | final double alpha; | 102 | final double alpha; |
| 44 | final double red; | 103 | final double red; |
| 45 | final double green; | 104 | final double green; |
| @@ -392,4 +392,13 @@ class PdfColors { | @@ -392,4 +392,13 @@ class PdfColors { | ||
| 392 | orangeAccent, | 392 | orangeAccent, |
| 393 | deepOrangeAccent, | 393 | deepOrangeAccent, |
| 394 | ]; | 394 | ]; |
| 395 | + | ||
| 396 | + static PdfColor getColor(int index) { | ||
| 397 | + final double hue = index * 137.508; | ||
| 398 | + final PdfColor color = PdfColorHsv(hue % 360, 1, 1); | ||
| 399 | + if ((index / 3) % 2 == 0) { | ||
| 400 | + return PdfColor.fromRYB(color.red, color.green, color.blue); | ||
| 401 | + } | ||
| 402 | + return color; | ||
| 403 | + } | ||
| 395 | } | 404 | } |
| @@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | import 'dart:io'; | 17 | import 'dart:io'; |
| 18 | +import 'dart:math' as math; | ||
| 18 | 19 | ||
| 19 | import 'package:pdf/pdf.dart'; | 20 | import 'package:pdf/pdf.dart'; |
| 20 | import 'package:pdf/widgets.dart'; | 21 | import 'package:pdf/widgets.dart'; |
| @@ -57,10 +58,125 @@ class Color extends StatelessWidget { | @@ -57,10 +58,125 @@ class Color extends StatelessWidget { | ||
| 57 | } | 58 | } |
| 58 | } | 59 | } |
| 59 | 60 | ||
| 61 | +enum ColorSpace { rgb, ryb, cmy } | ||
| 62 | + | ||
| 63 | +class ColorWheel extends Widget { | ||
| 64 | + ColorWheel({ | ||
| 65 | + this.colorSpace = ColorSpace.rgb, | ||
| 66 | + this.divisions = 12, | ||
| 67 | + this.rings = 5, | ||
| 68 | + this.brightness = .5, | ||
| 69 | + }) : assert(brightness >= 0 && brightness <= 1); | ||
| 70 | + | ||
| 71 | + final int divisions; | ||
| 72 | + final int rings; | ||
| 73 | + final double brightness; | ||
| 74 | + final ColorSpace colorSpace; | ||
| 75 | + | ||
| 76 | + @override | ||
| 77 | + void layout(Context context, BoxConstraints constraints, | ||
| 78 | + {bool parentUsesSize = false}) { | ||
| 79 | + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + void drawFilledArc(Context context, double centerX, double centerY, | ||
| 83 | + double angleStart, double angleEnd, double radius1, double radius2) { | ||
| 84 | + assert(radius1 > radius2); | ||
| 85 | + | ||
| 86 | + final PdfPoint startTop = PdfPoint( | ||
| 87 | + box.x + centerX + math.cos(angleStart) * radius1, | ||
| 88 | + box.y + centerY + math.sin(angleStart) * radius1, | ||
| 89 | + ); | ||
| 90 | + final PdfPoint endTop = PdfPoint( | ||
| 91 | + box.x + centerX + math.cos(angleEnd) * radius1, | ||
| 92 | + box.y + centerY + math.sin(angleEnd) * radius1, | ||
| 93 | + ); | ||
| 94 | + final PdfPoint startBottom = PdfPoint( | ||
| 95 | + box.x + centerX + math.cos(angleStart) * radius2, | ||
| 96 | + box.y + centerY + math.sin(angleStart) * radius2, | ||
| 97 | + ); | ||
| 98 | + final PdfPoint endBottom = PdfPoint( | ||
| 99 | + box.x + centerX + math.cos(angleEnd) * radius2, | ||
| 100 | + box.y + centerY + math.sin(angleEnd) * radius2, | ||
| 101 | + ); | ||
| 102 | + | ||
| 103 | + context.canvas | ||
| 104 | + ..moveTo(startTop.x, startTop.y) | ||
| 105 | + ..bezierArc(startTop.x, startTop.y, radius1, radius1, endTop.x, endTop.y, | ||
| 106 | + large: false, sweep: true) | ||
| 107 | + ..lineTo(endBottom.x, endBottom.y) | ||
| 108 | + ..bezierArc(endBottom.x, endBottom.y, radius2, radius2, startBottom.x, | ||
| 109 | + startBottom.y, large: false) | ||
| 110 | + ..lineTo(startTop.x, startTop.y) | ||
| 111 | + ..fillPath() | ||
| 112 | + ..moveTo(startTop.x, startTop.y) | ||
| 113 | + ..bezierArc(startTop.x, startTop.y, radius1, radius1, endTop.x, endTop.y, | ||
| 114 | + large: false, sweep: true) | ||
| 115 | + ..lineTo(endBottom.x, endBottom.y) | ||
| 116 | + ..bezierArc(endBottom.x, endBottom.y, radius2, radius2, startBottom.x, | ||
| 117 | + startBottom.y, | ||
| 118 | + large: false) | ||
| 119 | + ..lineTo(startTop.x, startTop.y) | ||
| 120 | + ..strokePath(); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + @override | ||
| 124 | + void paint(Context context) { | ||
| 125 | + super.paint(context); | ||
| 126 | + | ||
| 127 | + final double centerX = box.width / 2; | ||
| 128 | + final double centerY = box.height / 2; | ||
| 129 | + final double step = math.pi * 2 / divisions; | ||
| 130 | + final double angleStart = math.pi / 2 - step; | ||
| 131 | + | ||
| 132 | + final double ringStep = math.min(centerX, centerY) / rings; | ||
| 133 | + | ||
| 134 | + context.canvas.setStrokeColor(PdfColors.black); | ||
| 135 | + | ||
| 136 | + for (int ring = 0; ring <= rings; ring++) { | ||
| 137 | + final double radius1 = ringStep * ring; | ||
| 138 | + final double radius2 = ringStep * (ring - 1); | ||
| 139 | + for (double angle = 0; angle < math.pi * 2; angle += step) { | ||
| 140 | + final PdfColor ic = | ||
| 141 | + PdfColorHsl(angle / math.pi * 180, ring / rings, brightness); | ||
| 142 | + | ||
| 143 | + switch (colorSpace) { | ||
| 144 | + case ColorSpace.rgb: | ||
| 145 | + context.canvas.setFillColor(ic); | ||
| 146 | + break; | ||
| 147 | + case ColorSpace.ryb: | ||
| 148 | + context.canvas | ||
| 149 | + .setFillColor(PdfColor.fromRYB(ic.red, ic.green, ic.blue)); | ||
| 150 | + break; | ||
| 151 | + case ColorSpace.cmy: | ||
| 152 | + context.canvas | ||
| 153 | + .setFillColor(PdfColorCmyk(ic.red, ic.green, ic.blue, 0)); | ||
| 154 | + break; | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + drawFilledArc( | ||
| 158 | + context, | ||
| 159 | + centerX, | ||
| 160 | + centerY, | ||
| 161 | + angleStart + angle, | ||
| 162 | + angleStart + angle + step, | ||
| 163 | + radius1, | ||
| 164 | + radius2, | ||
| 165 | + ); | ||
| 166 | + } | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +Document pdf; | ||
| 172 | + | ||
| 60 | void main() { | 173 | void main() { |
| 61 | - test('Pdf Colors', () { | ||
| 62 | - final Document pdf = Document(title: 'Material Colors'); | 174 | + setUpAll(() { |
| 175 | + Document.debug = true; | ||
| 176 | + pdf = Document(); | ||
| 177 | + }); | ||
| 63 | 178 | ||
| 179 | + test('Pdf Colors', () { | ||
| 64 | pdf.addPage(MultiPage( | 180 | pdf.addPage(MultiPage( |
| 65 | pageFormat: PdfPageFormat.standard, | 181 | pageFormat: PdfPageFormat.standard, |
| 66 | build: (Context context) => <Widget>[ | 182 | build: (Context context) => <Widget>[ |
| @@ -546,7 +662,52 @@ void main() { | @@ -546,7 +662,52 @@ void main() { | ||
| 546 | Color(PdfColors.black, 'Black'), | 662 | Color(PdfColors.black, 'Black'), |
| 547 | ]), | 663 | ]), |
| 548 | ])); | 664 | ])); |
| 665 | + }); | ||
| 666 | + | ||
| 667 | + test('Pdf Colors Wheel', () { | ||
| 668 | + const Map<ColorSpace, String> wheels = <ColorSpace, String>{ | ||
| 669 | + ColorSpace.rgb: 'Red Green Blue', | ||
| 670 | + ColorSpace.ryb: 'Red Yellow Blue', | ||
| 671 | + ColorSpace.cmy: 'Cyan Magenta Yellow', | ||
| 672 | + }; | ||
| 673 | + | ||
| 674 | + wheels.forEach((ColorSpace colorSpace, String name) { | ||
| 675 | + pdf.addPage(Page( | ||
| 676 | + build: (Context context) => Column( | ||
| 677 | + children: <Widget>[ | ||
| 678 | + Header(text: name), | ||
| 679 | + SizedBox( | ||
| 680 | + height: context.page.pageFormat.availableWidth, | ||
| 681 | + child: ColorWheel( | ||
| 682 | + colorSpace: colorSpace, | ||
| 683 | + ), | ||
| 684 | + ), | ||
| 685 | + ], | ||
| 686 | + ))); | ||
| 687 | + }); | ||
| 688 | + }); | ||
| 689 | + | ||
| 690 | + test('Pdf Colors Generator', () { | ||
| 691 | + const double widthCount = 26; | ||
| 692 | + const PdfPageFormat format = PdfPageFormat(400, 400); | ||
| 693 | + final double w = (format.width - 1) / widthCount; | ||
| 694 | + final int count = widthCount * (format.height - 1) ~/ w; | ||
| 695 | + | ||
| 696 | + pdf.addPage(MultiPage( | ||
| 697 | + pageFormat: format, | ||
| 698 | + build: (Context context) => <Widget>[ | ||
| 699 | + Wrap( | ||
| 700 | + children: List<Widget>.generate(count, (int i) { | ||
| 701 | + return Container( | ||
| 702 | + width: w, | ||
| 703 | + height: w, | ||
| 704 | + color: PdfColors.getColor(i), | ||
| 705 | + ); | ||
| 706 | + })), | ||
| 707 | + ])); | ||
| 708 | + }); | ||
| 549 | 709 | ||
| 710 | + tearDownAll(() { | ||
| 550 | final File file = File('colors.pdf'); | 711 | final File file = File('colors.pdf'); |
| 551 | file.writeAsBytesSync(pdf.save()); | 712 | file.writeAsBytesSync(pdf.save()); |
| 552 | }); | 713 | }); |
-
Please register or login to post a comment