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