Showing
6 changed files
with
270 additions
and
110 deletions
@@ -14,6 +14,7 @@ | @@ -14,6 +14,7 @@ | ||
14 | - Improve TextOverflow support | 14 | - Improve TextOverflow support |
15 | - Fix Table horizontalInside borders | 15 | - Fix Table horizontalInside borders |
16 | - Improve PieChart default colors | 16 | - Improve PieChart default colors |
17 | +- Implement donnut chart | ||
17 | 18 | ||
18 | ## 3.2.0 | 19 | ## 3.2.0 |
19 | 20 |
@@ -100,7 +100,7 @@ class _PdfGraphicsContext { | @@ -100,7 +100,7 @@ class _PdfGraphicsContext { | ||
100 | /// Pdf drawing operations | 100 | /// Pdf drawing operations |
101 | class PdfGraphics { | 101 | class PdfGraphics { |
102 | /// Create a new graphic canvas | 102 | /// Create a new graphic canvas |
103 | - PdfGraphics(this._page, this.buf) { | 103 | + PdfGraphics(this._page, this._buf) { |
104 | _context = _PdfGraphicsContext(ctm: Matrix4.identity()); | 104 | _context = _PdfGraphicsContext(ctm: Matrix4.identity()); |
105 | } | 105 | } |
106 | 106 | ||
@@ -114,7 +114,7 @@ class PdfGraphics { | @@ -114,7 +114,7 @@ class PdfGraphics { | ||
114 | final PdfGraphicStream _page; | 114 | final PdfGraphicStream _page; |
115 | 115 | ||
116 | /// Buffer where to write the graphic operations | 116 | /// Buffer where to write the graphic operations |
117 | - final PdfStream buf; | 117 | + final PdfStream _buf; |
118 | 118 | ||
119 | /// Default font if none selected | 119 | /// Default font if none selected |
120 | PdfFont? get defaultFont => _page.getDefaultFont(); | 120 | PdfFont? get defaultFont => _page.getDefaultFont(); |
@@ -122,36 +122,36 @@ class PdfGraphics { | @@ -122,36 +122,36 @@ class PdfGraphics { | ||
122 | /// Draw a surface on the previously defined shape | 122 | /// Draw a surface on the previously defined shape |
123 | /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill | 123 | /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill |
124 | void fillPath({bool evenOdd = false}) { | 124 | void fillPath({bool evenOdd = false}) { |
125 | - buf.putString('f${evenOdd ? '*' : ''}\n'); | 125 | + _buf.putString('f${evenOdd ? '*' : ''}\n'); |
126 | } | 126 | } |
127 | 127 | ||
128 | /// Draw the contour of the previously defined shape | 128 | /// Draw the contour of the previously defined shape |
129 | void strokePath({bool close = false}) { | 129 | void strokePath({bool close = false}) { |
130 | - buf.putString('${close ? 's' : 'S'}\n'); | 130 | + _buf.putString('${close ? 's' : 'S'}\n'); |
131 | } | 131 | } |
132 | 132 | ||
133 | /// Close the path with a line | 133 | /// Close the path with a line |
134 | void closePath() { | 134 | void closePath() { |
135 | - buf.putString('h\n'); | 135 | + _buf.putString('h\n'); |
136 | } | 136 | } |
137 | 137 | ||
138 | /// Create a clipping surface from the previously defined shape, | 138 | /// Create a clipping surface from the previously defined shape, |
139 | /// to prevent any further drawing outside | 139 | /// to prevent any further drawing outside |
140 | void clipPath({bool evenOdd = false, bool end = true}) { | 140 | void clipPath({bool evenOdd = false, bool end = true}) { |
141 | - buf.putString('W${evenOdd ? '*' : ''}${end ? ' n' : ''}\n'); | 141 | + _buf.putString('W${evenOdd ? '*' : ''}${end ? ' n' : ''}\n'); |
142 | } | 142 | } |
143 | 143 | ||
144 | /// Draw a surface on the previously defined shape and then draw the contour | 144 | /// Draw a surface on the previously defined shape and then draw the contour |
145 | /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill | 145 | /// set evenOdd to false to use the nonzero winding number rule to determine the region to fill and to true to use the even-odd rule to determine the region to fill |
146 | void fillAndStrokePath({bool evenOdd = false, bool close = false}) { | 146 | void fillAndStrokePath({bool evenOdd = false, bool close = false}) { |
147 | - buf.putString('${close ? 'b' : 'B'}${evenOdd ? '*' : ''}\n'); | 147 | + _buf.putString('${close ? 'b' : 'B'}${evenOdd ? '*' : ''}\n'); |
148 | } | 148 | } |
149 | 149 | ||
150 | /// Apply a shader | 150 | /// Apply a shader |
151 | void applyShader(PdfShading shader) { | 151 | void applyShader(PdfShading shader) { |
152 | // The shader needs to be registered in the page resources | 152 | // The shader needs to be registered in the page resources |
153 | _page.addShader(shader); | 153 | _page.addShader(shader); |
154 | - buf.putString('${shader.name} sh\n'); | 154 | + _buf.putString('${shader.name} sh\n'); |
155 | } | 155 | } |
156 | 156 | ||
157 | /// This releases any resources used by this Graphics object. You must use | 157 | /// This releases any resources used by this Graphics object. You must use |
@@ -162,14 +162,14 @@ class PdfGraphics { | @@ -162,14 +162,14 @@ class PdfGraphics { | ||
162 | void restoreContext() { | 162 | void restoreContext() { |
163 | if (_contextQueue.isNotEmpty) { | 163 | if (_contextQueue.isNotEmpty) { |
164 | // restore graphics context | 164 | // restore graphics context |
165 | - buf.putString('Q\n'); | 165 | + _buf.putString('Q\n'); |
166 | _context = _contextQueue.removeLast(); | 166 | _context = _contextQueue.removeLast(); |
167 | } | 167 | } |
168 | } | 168 | } |
169 | 169 | ||
170 | /// Save the graphc context | 170 | /// Save the graphc context |
171 | void saveContext() { | 171 | void saveContext() { |
172 | - buf.putString('q\n'); | 172 | + _buf.putString('q\n'); |
173 | _contextQueue.addLast(_context.copy()); | 173 | _contextQueue.addLast(_context.copy()); |
174 | } | 174 | } |
175 | 175 | ||
@@ -182,35 +182,35 @@ class PdfGraphics { | @@ -182,35 +182,35 @@ class PdfGraphics { | ||
182 | _page.addXObject(img); | 182 | _page.addXObject(img); |
183 | 183 | ||
184 | // q w 0 0 h x y cm % the coordinate matrix | 184 | // q w 0 0 h x y cm % the coordinate matrix |
185 | - buf.putString('q '); | 185 | + _buf.putString('q '); |
186 | switch (img.orientation) { | 186 | switch (img.orientation) { |
187 | case PdfImageOrientation.topLeft: | 187 | case PdfImageOrientation.topLeft: |
188 | - PdfNumList(<double>[w, 0, 0, h, x, y]).output(buf); | 188 | + PdfNumList(<double>[w, 0, 0, h, x, y]).output(_buf); |
189 | break; | 189 | break; |
190 | case PdfImageOrientation.topRight: | 190 | case PdfImageOrientation.topRight: |
191 | - PdfNumList(<double>[-w, 0, 0, h, w + x, y]).output(buf); | 191 | + PdfNumList(<double>[-w, 0, 0, h, w + x, y]).output(_buf); |
192 | break; | 192 | break; |
193 | case PdfImageOrientation.bottomRight: | 193 | case PdfImageOrientation.bottomRight: |
194 | - PdfNumList(<double>[-w, 0, 0, -h, w + x, h + y]).output(buf); | 194 | + PdfNumList(<double>[-w, 0, 0, -h, w + x, h + y]).output(_buf); |
195 | break; | 195 | break; |
196 | case PdfImageOrientation.bottomLeft: | 196 | case PdfImageOrientation.bottomLeft: |
197 | - PdfNumList(<double>[w, 0, 0, -h, x, h + y]).output(buf); | 197 | + PdfNumList(<double>[w, 0, 0, -h, x, h + y]).output(_buf); |
198 | break; | 198 | break; |
199 | case PdfImageOrientation.leftTop: | 199 | case PdfImageOrientation.leftTop: |
200 | - PdfNumList(<double>[0, -h, -w, 0, w + x, h + y]).output(buf); | 200 | + PdfNumList(<double>[0, -h, -w, 0, w + x, h + y]).output(_buf); |
201 | break; | 201 | break; |
202 | case PdfImageOrientation.rightTop: | 202 | case PdfImageOrientation.rightTop: |
203 | - PdfNumList(<double>[0, -h, w, 0, x, h + y]).output(buf); | 203 | + PdfNumList(<double>[0, -h, w, 0, x, h + y]).output(_buf); |
204 | break; | 204 | break; |
205 | case PdfImageOrientation.rightBottom: | 205 | case PdfImageOrientation.rightBottom: |
206 | - PdfNumList(<double>[0, h, w, 0, x, y]).output(buf); | 206 | + PdfNumList(<double>[0, h, w, 0, x, y]).output(_buf); |
207 | break; | 207 | break; |
208 | case PdfImageOrientation.leftBottom: | 208 | case PdfImageOrientation.leftBottom: |
209 | - PdfNumList(<double>[0, h, -w, 0, w + x, y]).output(buf); | 209 | + PdfNumList(<double>[0, h, -w, 0, w + x, y]).output(_buf); |
210 | break; | 210 | break; |
211 | } | 211 | } |
212 | 212 | ||
213 | - buf.putString(' cm ${img.name} Do Q\n'); | 213 | + _buf.putString(' cm ${img.name} Do Q\n'); |
214 | } | 214 | } |
215 | 215 | ||
216 | /// Draws a line between two coordinates. | 216 | /// Draws a line between two coordinates. |
@@ -220,12 +220,22 @@ class PdfGraphics { | @@ -220,12 +220,22 @@ class PdfGraphics { | ||
220 | } | 220 | } |
221 | 221 | ||
222 | /// Draws an ellipse | 222 | /// Draws an ellipse |
223 | - void drawEllipse(double x, double y, double r1, double r2) { | 223 | + /// |
224 | + /// Use clockwise=false to draw the inside of a donnnut | ||
225 | + void drawEllipse(double x, double y, double r1, double r2, | ||
226 | + {bool clockwise = true}) { | ||
224 | moveTo(x, y - r2); | 227 | moveTo(x, y - r2); |
228 | + if (clockwise) { | ||
225 | curveTo(x + _m4 * r1, y - r2, x + r1, y - _m4 * r2, x + r1, y); | 229 | curveTo(x + _m4 * r1, y - r2, x + r1, y - _m4 * r2, x + r1, y); |
226 | curveTo(x + r1, y + _m4 * r2, x + _m4 * r1, y + r2, x, y + r2); | 230 | curveTo(x + r1, y + _m4 * r2, x + _m4 * r1, y + r2, x, y + r2); |
227 | curveTo(x - _m4 * r1, y + r2, x - r1, y + _m4 * r2, x - r1, y); | 231 | curveTo(x - _m4 * r1, y + r2, x - r1, y + _m4 * r2, x - r1, y); |
228 | curveTo(x - r1, y - _m4 * r2, x - _m4 * r1, y - r2, x, y - r2); | 232 | curveTo(x - r1, y - _m4 * r2, x - _m4 * r1, y - r2, x, y - r2); |
233 | + } else { | ||
234 | + curveTo(x - _m4 * r1, y - r2, x - r1, y - _m4 * r2, x - r1, y); | ||
235 | + curveTo(x - r1, y + _m4 * r2, x - _m4 * r1, y + r2, x, y + r2); | ||
236 | + curveTo(x + _m4 * r1, y + r2, x + r1, y + _m4 * r2, x + r1, y); | ||
237 | + curveTo(x + r1, y - _m4 * r2, x + _m4 * r1, y - r2, x, y - r2); | ||
238 | + } | ||
229 | } | 239 | } |
230 | 240 | ||
231 | /// Draws a Rectangle | 241 | /// Draws a Rectangle |
@@ -235,8 +245,8 @@ class PdfGraphics { | @@ -235,8 +245,8 @@ class PdfGraphics { | ||
235 | double w, | 245 | double w, |
236 | double h, | 246 | double h, |
237 | ) { | 247 | ) { |
238 | - PdfNumList([x, y, w, h]).output(buf); | ||
239 | - buf.putString(' re\n'); | 248 | + PdfNumList([x, y, w, h]).output(_buf); |
249 | + _buf.putString(' re\n'); | ||
240 | } | 250 | } |
241 | 251 | ||
242 | /// Draws a Rectangle | 252 | /// Draws a Rectangle |
@@ -268,27 +278,27 @@ class PdfGraphics { | @@ -268,27 +278,27 @@ class PdfGraphics { | ||
268 | PdfTextRenderingMode? mode = PdfTextRenderingMode.fill, | 278 | PdfTextRenderingMode? mode = PdfTextRenderingMode.fill, |
269 | double? rise, | 279 | double? rise, |
270 | }) { | 280 | }) { |
271 | - buf.putString('${font.name} '); | ||
272 | - PdfNum(size).output(buf); | ||
273 | - buf.putString(' Tf\n'); | 281 | + _buf.putString('${font.name} '); |
282 | + PdfNum(size).output(_buf); | ||
283 | + _buf.putString(' Tf\n'); | ||
274 | if (charSpace != null) { | 284 | if (charSpace != null) { |
275 | - PdfNum(charSpace).output(buf); | ||
276 | - buf.putString(' Tc\n'); | 285 | + PdfNum(charSpace).output(_buf); |
286 | + _buf.putString(' Tc\n'); | ||
277 | } | 287 | } |
278 | if (wordSpace != null) { | 288 | if (wordSpace != null) { |
279 | - PdfNum(wordSpace).output(buf); | ||
280 | - buf.putString(' Tw\n'); | 289 | + PdfNum(wordSpace).output(_buf); |
290 | + _buf.putString(' Tw\n'); | ||
281 | } | 291 | } |
282 | if (scale != null) { | 292 | if (scale != null) { |
283 | - PdfNum(scale * 100).output(buf); | ||
284 | - buf.putString(' Tz\n'); | 293 | + PdfNum(scale * 100).output(_buf); |
294 | + _buf.putString(' Tz\n'); | ||
285 | } | 295 | } |
286 | if (rise != null) { | 296 | if (rise != null) { |
287 | - PdfNum(rise).output(buf); | ||
288 | - buf.putString(' Ts\n'); | 297 | + PdfNum(rise).output(_buf); |
298 | + _buf.putString(' Ts\n'); | ||
289 | } | 299 | } |
290 | if (mode != PdfTextRenderingMode.fill) { | 300 | if (mode != PdfTextRenderingMode.fill) { |
291 | - buf.putString('${mode!.index} Tr\n'); | 301 | + _buf.putString('${mode!.index} Tr\n'); |
292 | } | 302 | } |
293 | } | 303 | } |
294 | 304 | ||
@@ -307,22 +317,22 @@ class PdfGraphics { | @@ -307,22 +317,22 @@ class PdfGraphics { | ||
307 | }) { | 317 | }) { |
308 | _page.addFont(font); | 318 | _page.addFont(font); |
309 | 319 | ||
310 | - buf.putString('BT '); | ||
311 | - PdfNumList([x, y]).output(buf); | ||
312 | - buf.putString(' Td '); | 320 | + _buf.putString('BT '); |
321 | + PdfNumList([x, y]).output(_buf); | ||
322 | + _buf.putString(' Td '); | ||
313 | setFont(font, size, | 323 | setFont(font, size, |
314 | charSpace: charSpace, | 324 | charSpace: charSpace, |
315 | mode: mode, | 325 | mode: mode, |
316 | rise: rise, | 326 | rise: rise, |
317 | scale: scale, | 327 | scale: scale, |
318 | wordSpace: wordSpace); | 328 | wordSpace: wordSpace); |
319 | - buf.putString('['); | ||
320 | - font.putText(buf, s); | ||
321 | - buf.putString(']TJ ET\n'); | 329 | + _buf.putString('['); |
330 | + font.putText(_buf, s); | ||
331 | + _buf.putString(']TJ ET\n'); | ||
322 | } | 332 | } |
323 | 333 | ||
324 | void reset() { | 334 | void reset() { |
325 | - buf.putString('0 Tr\n'); | 335 | + _buf.putString('0 Tr\n'); |
326 | } | 336 | } |
327 | 337 | ||
328 | /// Sets the color for drawing | 338 | /// Sets the color for drawing |
@@ -335,11 +345,11 @@ class PdfGraphics { | @@ -335,11 +345,11 @@ class PdfGraphics { | ||
335 | void setFillColor(PdfColor? color) { | 345 | void setFillColor(PdfColor? color) { |
336 | if (color is PdfColorCmyk) { | 346 | if (color is PdfColorCmyk) { |
337 | PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black]) | 347 | PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black]) |
338 | - .output(buf); | ||
339 | - buf.putString(' k\n'); | 348 | + .output(_buf); |
349 | + _buf.putString(' k\n'); | ||
340 | } else { | 350 | } else { |
341 | - PdfNumList(<double>[color!.red, color.green, color.blue]).output(buf); | ||
342 | - buf.putString(' rg\n'); | 351 | + PdfNumList(<double>[color!.red, color.green, color.blue]).output(_buf); |
352 | + _buf.putString(' rg\n'); | ||
343 | } | 353 | } |
344 | } | 354 | } |
345 | 355 | ||
@@ -347,11 +357,11 @@ class PdfGraphics { | @@ -347,11 +357,11 @@ class PdfGraphics { | ||
347 | void setStrokeColor(PdfColor? color) { | 357 | void setStrokeColor(PdfColor? color) { |
348 | if (color is PdfColorCmyk) { | 358 | if (color is PdfColorCmyk) { |
349 | PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black]) | 359 | PdfNumList(<double>[color.cyan, color.magenta, color.yellow, color.black]) |
350 | - .output(buf); | ||
351 | - buf.putString(' K\n'); | 360 | + .output(_buf); |
361 | + _buf.putString(' K\n'); | ||
352 | } else { | 362 | } else { |
353 | - PdfNumList(<double>[color!.red, color.green, color.blue]).output(buf); | ||
354 | - buf.putString(' RG\n'); | 363 | + PdfNumList(<double>[color!.red, color.green, color.blue]).output(_buf); |
364 | + _buf.putString(' RG\n'); | ||
355 | } | 365 | } |
356 | } | 366 | } |
357 | 367 | ||
@@ -359,27 +369,27 @@ class PdfGraphics { | @@ -359,27 +369,27 @@ class PdfGraphics { | ||
359 | void setFillPattern(PdfPattern pattern) { | 369 | void setFillPattern(PdfPattern pattern) { |
360 | // The shader needs to be registered in the page resources | 370 | // The shader needs to be registered in the page resources |
361 | _page.addPattern(pattern); | 371 | _page.addPattern(pattern); |
362 | - buf.putString('/Pattern cs${pattern.name} scn\n'); | 372 | + _buf.putString('/Pattern cs${pattern.name} scn\n'); |
363 | } | 373 | } |
364 | 374 | ||
365 | /// Sets the stroke pattern for drawing | 375 | /// Sets the stroke pattern for drawing |
366 | void setStrokePattern(PdfPattern pattern) { | 376 | void setStrokePattern(PdfPattern pattern) { |
367 | // The shader needs to be registered in the page resources | 377 | // The shader needs to be registered in the page resources |
368 | _page.addPattern(pattern); | 378 | _page.addPattern(pattern); |
369 | - buf.putString('/Pattern CS${pattern.name} SCN\n'); | 379 | + _buf.putString('/Pattern CS${pattern.name} SCN\n'); |
370 | } | 380 | } |
371 | 381 | ||
372 | /// Set the graphic state for drawing | 382 | /// Set the graphic state for drawing |
373 | void setGraphicState(PdfGraphicState state) { | 383 | void setGraphicState(PdfGraphicState state) { |
374 | final name = _page.stateName(state); | 384 | final name = _page.stateName(state); |
375 | - buf.putString('$name gs\n'); | 385 | + _buf.putString('$name gs\n'); |
376 | } | 386 | } |
377 | 387 | ||
378 | /// Set the transformation Matrix | 388 | /// Set the transformation Matrix |
379 | void setTransform(Matrix4 t) { | 389 | void setTransform(Matrix4 t) { |
380 | final s = t.storage; | 390 | final s = t.storage; |
381 | - PdfNumList(<double>[s[0], s[1], s[4], s[5], s[12], s[13]]).output(buf); | ||
382 | - buf.putString(' cm\n'); | 391 | + PdfNumList(<double>[s[0], s[1], s[4], s[5], s[12], s[13]]).output(_buf); |
392 | + _buf.putString(' cm\n'); | ||
383 | _context.ctm.multiply(t); | 393 | _context.ctm.multiply(t); |
384 | } | 394 | } |
385 | 395 | ||
@@ -390,14 +400,14 @@ class PdfGraphics { | @@ -390,14 +400,14 @@ class PdfGraphics { | ||
390 | 400 | ||
391 | /// This adds a line segment to the current path | 401 | /// This adds a line segment to the current path |
392 | void lineTo(double x, double y) { | 402 | void lineTo(double x, double y) { |
393 | - PdfNumList([x, y]).output(buf); | ||
394 | - buf.putString(' l\n'); | 403 | + PdfNumList([x, y]).output(_buf); |
404 | + _buf.putString(' l\n'); | ||
395 | } | 405 | } |
396 | 406 | ||
397 | /// This moves the current drawing point. | 407 | /// This moves the current drawing point. |
398 | void moveTo(double x, double y) { | 408 | void moveTo(double x, double y) { |
399 | - PdfNumList([x, y]).output(buf); | ||
400 | - buf.putString(' m\n'); | 409 | + PdfNumList([x, y]).output(_buf); |
410 | + _buf.putString(' m\n'); | ||
401 | } | 411 | } |
402 | 412 | ||
403 | /// Draw a cubic bézier curve from the current point to (x3,y3) | 413 | /// Draw a cubic bézier curve from the current point to (x3,y3) |
@@ -405,8 +415,8 @@ class PdfGraphics { | @@ -405,8 +415,8 @@ class PdfGraphics { | ||
405 | /// and (x2,y2) as the control point at the end of the curve. | 415 | /// and (x2,y2) as the control point at the end of the curve. |
406 | void curveTo( | 416 | void curveTo( |
407 | double x1, double y1, double x2, double y2, double x3, double y3) { | 417 | double x1, double y1, double x2, double y2, double x3, double y3) { |
408 | - PdfNumList([x1, y1, x2, y2, x3, y3]).output(buf); | ||
409 | - buf.putString(' c\n'); | 418 | + PdfNumList([x1, y1, x2, y2, x3, y3]).output(_buf); |
419 | + _buf.putString(' c\n'); | ||
410 | } | 420 | } |
411 | 421 | ||
412 | double _vectorAngle(double ux, double uy, double vx, double vy) { | 422 | double _vectorAngle(double ux, double uy, double vx, double vy) { |
@@ -566,25 +576,25 @@ class PdfGraphics { | @@ -566,25 +576,25 @@ class PdfGraphics { | ||
566 | 576 | ||
567 | /// Set line starting and ending cap type | 577 | /// Set line starting and ending cap type |
568 | void setLineCap(PdfLineCap cap) { | 578 | void setLineCap(PdfLineCap cap) { |
569 | - buf.putString('${cap.index} J\n'); | 579 | + _buf.putString('${cap.index} J\n'); |
570 | } | 580 | } |
571 | 581 | ||
572 | /// Set line join type | 582 | /// Set line join type |
573 | void setLineJoin(PdfLineJoin join) { | 583 | void setLineJoin(PdfLineJoin join) { |
574 | - buf.putString('${join.index} j\n'); | 584 | + _buf.putString('${join.index} j\n'); |
575 | } | 585 | } |
576 | 586 | ||
577 | /// Set line width | 587 | /// Set line width |
578 | void setLineWidth(double width) { | 588 | void setLineWidth(double width) { |
579 | - PdfNum(width).output(buf); | ||
580 | - buf.putString(' w\n'); | 589 | + PdfNum(width).output(_buf); |
590 | + _buf.putString(' w\n'); | ||
581 | } | 591 | } |
582 | 592 | ||
583 | /// Set line joint miter limit, applies if the | 593 | /// Set line joint miter limit, applies if the |
584 | void setMiterLimit(double limit) { | 594 | void setMiterLimit(double limit) { |
585 | assert(limit >= 1.0); | 595 | assert(limit >= 1.0); |
586 | - PdfNum(limit).output(buf); | ||
587 | - buf.putString(' M\n'); | 596 | + PdfNum(limit).output(_buf); |
597 | + _buf.putString(' M\n'); | ||
588 | } | 598 | } |
589 | 599 | ||
590 | /// The dash array shall be cycled through, adding up the lengths of dashes and gaps. | 600 | /// The dash array shall be cycled through, adding up the lengths of dashes and gaps. |
@@ -592,8 +602,17 @@ class PdfGraphics { | @@ -592,8 +602,17 @@ class PdfGraphics { | ||
592 | /// | 602 | /// |
593 | /// Example: [2 1] will create a dash pattern with 2 on, 1 off, 2 on, 1 off, ... | 603 | /// Example: [2 1] will create a dash pattern with 2 on, 1 off, 2 on, 1 off, ... |
594 | void setLineDashPattern([List<num> array = const <num>[], int phase = 0]) { | 604 | void setLineDashPattern([List<num> array = const <num>[], int phase = 0]) { |
595 | - PdfArray.fromNum(array).output(buf); | ||
596 | - buf.putString(' $phase d\n'); | 605 | + PdfArray.fromNum(array).output(_buf); |
606 | + _buf.putString(' $phase d\n'); | ||
607 | + } | ||
608 | + | ||
609 | + void markContentBegin(PdfName tag) { | ||
610 | + tag.output(_buf); | ||
611 | + _buf.putString(' BMC\n'); | ||
612 | + } | ||
613 | + | ||
614 | + void markContentEnd() { | ||
615 | + _buf.putString('EMC\n'); | ||
597 | } | 616 | } |
598 | } | 617 | } |
599 | 618 |
@@ -2,20 +2,21 @@ | @@ -2,20 +2,21 @@ | ||
2 | 2 | ||
3 | import 'dart:math'; | 3 | import 'dart:math'; |
4 | 4 | ||
5 | +import 'package:meta/meta.dart'; | ||
5 | import 'package:pdf/pdf.dart'; | 6 | import 'package:pdf/pdf.dart'; |
6 | import 'package:pdf/widgets.dart'; | 7 | import 'package:pdf/widgets.dart'; |
7 | import 'package:vector_math/vector_math_64.dart'; | 8 | import 'package:vector_math/vector_math_64.dart'; |
8 | 9 | ||
9 | class PieGrid extends ChartGrid { | 10 | class PieGrid extends ChartGrid { |
10 | - PieGrid(); | 11 | + PieGrid({this.startAngle = 0}); |
11 | 12 | ||
12 | - late PdfRect gridBox; | 13 | + /// Start angle for the first [PieDataSet] |
14 | + final double startAngle; | ||
13 | 15 | ||
14 | - late double total; | 16 | + late double _radius; |
15 | 17 | ||
16 | - late double unit; | ||
17 | - | ||
18 | - late double pieSize; | 18 | + /// Nominal radius of a pie slice |
19 | + double get radius => _radius; | ||
19 | 20 | ||
20 | @override | 21 | @override |
21 | void layout(Context context, BoxConstraints constraints, | 22 | void layout(Context context, BoxConstraints constraints, |
@@ -25,19 +26,19 @@ class PieGrid extends ChartGrid { | @@ -25,19 +26,19 @@ class PieGrid extends ChartGrid { | ||
25 | final datasets = Chart.of(context).datasets; | 26 | final datasets = Chart.of(context).datasets; |
26 | final size = constraints.biggest; | 27 | final size = constraints.biggest; |
27 | 28 | ||
28 | - gridBox = PdfRect(0, 0, size.x, size.y); | 29 | + final _gridBox = PdfRect(0, 0, size.x, size.y); |
29 | 30 | ||
30 | - total = 0.0; | 31 | + var _total = 0.0; |
31 | 32 | ||
32 | for (final dataset in datasets) { | 33 | for (final dataset in datasets) { |
33 | - assert(dataset is PieDataSet, 'Use only PieDataSet with a PieGrid'); | 34 | + assert(dataset is PieDataSet, 'Use only PieDataset with a PieGrid'); |
34 | if (dataset is PieDataSet) { | 35 | if (dataset is PieDataSet) { |
35 | - total += dataset.value; | 36 | + _total += dataset.value; |
36 | } | 37 | } |
37 | } | 38 | } |
38 | 39 | ||
39 | - unit = pi / total * 2; | ||
40 | - var angle = 0.0; | 40 | + final unit = pi / _total * 2; |
41 | + var angle = startAngle; | ||
41 | 42 | ||
42 | for (final dataset in datasets) { | 43 | for (final dataset in datasets) { |
43 | if (dataset is PieDataSet) { | 44 | if (dataset is PieDataSet) { |
@@ -47,19 +48,19 @@ class PieGrid extends ChartGrid { | @@ -47,19 +48,19 @@ class PieGrid extends ChartGrid { | ||
47 | } | 48 | } |
48 | } | 49 | } |
49 | 50 | ||
50 | - pieSize = min(gridBox.width / 2, gridBox.height / 2); | 51 | + _radius = min(_gridBox.width / 2, _gridBox.height / 2); |
51 | var reduce = false; | 52 | var reduce = false; |
52 | 53 | ||
53 | do { | 54 | do { |
54 | reduce = false; | 55 | reduce = false; |
55 | for (final dataset in datasets) { | 56 | for (final dataset in datasets) { |
56 | if (dataset is PieDataSet) { | 57 | if (dataset is PieDataSet) { |
57 | - dataset.layout(context, BoxConstraints.tight(gridBox.size)); | 58 | + dataset.layout(context, BoxConstraints.tight(_gridBox.size)); |
58 | assert(dataset.box != null); | 59 | assert(dataset.box != null); |
59 | - if (pieSize > 20 && | ||
60 | - (dataset.box!.width > gridBox.width || | ||
61 | - dataset.box!.height > gridBox.height)) { | ||
62 | - pieSize -= 10; | 60 | + if (_radius > 20 && |
61 | + (dataset.box!.width > _gridBox.width || | ||
62 | + dataset.box!.height > _gridBox.height)) { | ||
63 | + _radius -= 10; | ||
63 | reduce = true; | 64 | reduce = true; |
64 | break; | 65 | break; |
65 | } | 66 | } |
@@ -129,7 +130,10 @@ class PieDataSet extends Dataset { | @@ -129,7 +130,10 @@ class PieDataSet extends Dataset { | ||
129 | PdfColor? legendLineColor, | 130 | PdfColor? legendLineColor, |
130 | Widget? legendWidget, | 131 | Widget? legendWidget, |
131 | this.legendOffset = 20, | 132 | this.legendOffset = 20, |
132 | - }) : drawBorder = drawBorder ?? borderColor != null && color != borderColor, | 133 | + this.innerRadius = 0, |
134 | + }) : assert(innerRadius >= 0), | ||
135 | + assert(offset >= 0), | ||
136 | + drawBorder = drawBorder ?? borderColor != null && color != borderColor, | ||
133 | assert((drawBorder ?? borderColor != null && color != borderColor) || | 137 | assert((drawBorder ?? borderColor != null && color != borderColor) || |
134 | drawSurface), | 138 | drawSurface), |
135 | _legendWidget = legendWidget, | 139 | _legendWidget = legendWidget, |
@@ -168,6 +172,8 @@ class PieDataSet extends Dataset { | @@ -168,6 +172,8 @@ class PieDataSet extends Dataset { | ||
168 | 172 | ||
169 | final PdfColor legendLineColor; | 173 | final PdfColor legendLineColor; |
170 | 174 | ||
175 | + final double innerRadius; | ||
176 | + | ||
171 | PdfPoint? _legendAnchor; | 177 | PdfPoint? _legendAnchor; |
172 | PdfPoint? _legendPivot; | 178 | PdfPoint? _legendPivot; |
173 | PdfPoint? _legendStart; | 179 | PdfPoint? _legendStart; |
@@ -180,7 +186,7 @@ class PieDataSet extends Dataset { | @@ -180,7 +186,7 @@ class PieDataSet extends Dataset { | ||
180 | final _offset = _isFullCircle ? 0 : offset; | 186 | final _offset = _isFullCircle ? 0 : offset; |
181 | 187 | ||
182 | final grid = Chart.of(context).grid as PieGrid; | 188 | final grid = Chart.of(context).grid as PieGrid; |
183 | - final len = grid.pieSize + _offset; | 189 | + final len = grid.radius + _offset; |
184 | var x = -len; | 190 | var x = -len; |
185 | var y = -len; | 191 | var y = -len; |
186 | var w = len * 2; | 192 | var w = len * 2; |
@@ -217,7 +223,7 @@ class PieDataSet extends Dataset { | @@ -217,7 +223,7 @@ class PieDataSet extends Dataset { | ||
217 | 223 | ||
218 | if (_legendWidget != null) { | 224 | if (_legendWidget != null) { |
219 | _legendWidget!.layout(context, | 225 | _legendWidget!.layout(context, |
220 | - BoxConstraints(maxWidth: grid.pieSize, maxHeight: grid.pieSize)); | 226 | + BoxConstraints(maxWidth: grid.radius, maxHeight: grid.radius)); |
221 | assert(_legendWidget!.box != null); | 227 | assert(_legendWidget!.box != null); |
222 | 228 | ||
223 | final ls = _legendWidget!.box!.size; | 229 | final ls = _legendWidget!.box!.size; |
@@ -226,13 +232,13 @@ class PieDataSet extends Dataset { | @@ -226,13 +232,13 @@ class PieDataSet extends Dataset { | ||
226 | 232 | ||
227 | switch (lp) { | 233 | switch (lp) { |
228 | case PieLegendPosition.outside: | 234 | case PieLegendPosition.outside: |
229 | - final o = grid.pieSize + legendOffset; | 235 | + final o = grid.radius + legendOffset; |
230 | final cx = sin(bisect) * (_offset + o); | 236 | final cx = sin(bisect) * (_offset + o); |
231 | final cy = cos(bisect) * (_offset + o); | 237 | final cy = cos(bisect) * (_offset + o); |
232 | 238 | ||
233 | _legendStart = PdfPoint( | 239 | _legendStart = PdfPoint( |
234 | - sin(bisect) * (_offset + grid.pieSize + legendOffset * 0.1), | ||
235 | - cos(bisect) * (_offset + grid.pieSize + legendOffset * 0.1), | 240 | + sin(bisect) * (_offset + grid.radius + legendOffset * 0.1), |
241 | + cos(bisect) * (_offset + grid.radius + legendOffset * 0.1), | ||
236 | ); | 242 | ); |
237 | 243 | ||
238 | _legendPivot = PdfPoint(cx, cy); | 244 | _legendPivot = PdfPoint(cx, cy); |
@@ -269,9 +275,23 @@ class PieDataSet extends Dataset { | @@ -269,9 +275,23 @@ class PieDataSet extends Dataset { | ||
269 | } | 275 | } |
270 | break; | 276 | break; |
271 | case PieLegendPosition.inside: | 277 | case PieLegendPosition.inside: |
272 | - final o = _isFullCircle ? 0 : grid.pieSize * 2 / 3; | ||
273 | - final cx = sin(bisect) * (_offset + o); | ||
274 | - final cy = cos(bisect) * (_offset + o); | 278 | + final double o; |
279 | + final double cx; | ||
280 | + final double cy; | ||
281 | + if (innerRadius == 0) { | ||
282 | + o = _isFullCircle ? 0 : grid.radius * 2 / 3; | ||
283 | + cx = sin(bisect) * (_offset + o); | ||
284 | + cy = cos(bisect) * (_offset + o); | ||
285 | + } else { | ||
286 | + o = (grid.radius + innerRadius) / 2; | ||
287 | + if (_isFullCircle) { | ||
288 | + cx = 0; | ||
289 | + cy = o; | ||
290 | + } else { | ||
291 | + cx = sin(bisect) * (_offset + o); | ||
292 | + cy = cos(bisect) * (_offset + o); | ||
293 | + } | ||
294 | + } | ||
275 | _legendWidget!.box = PdfRect.fromPoints( | 295 | _legendWidget!.box = PdfRect.fromPoints( |
276 | PdfPoint( | 296 | PdfPoint( |
277 | cx - ls.x / 2, | 297 | cx - ls.x / 2, |
@@ -287,7 +307,7 @@ class PieDataSet extends Dataset { | @@ -287,7 +307,7 @@ class PieDataSet extends Dataset { | ||
287 | box = PdfRect(x, y, w, h); | 307 | box = PdfRect(x, y, w, h); |
288 | } | 308 | } |
289 | 309 | ||
290 | - void _shape(Context context) { | 310 | + void _paintSliceShape(Context context) { |
291 | final grid = Chart.of(context).grid as PieGrid; | 311 | final grid = Chart.of(context).grid as PieGrid; |
292 | 312 | ||
293 | final bisect = (angleStart + angleEnd) / 2; | 313 | final bisect = (angleStart + angleEnd) / 2; |
@@ -295,28 +315,69 @@ class PieDataSet extends Dataset { | @@ -295,28 +315,69 @@ class PieDataSet extends Dataset { | ||
295 | final cx = sin(bisect) * offset; | 315 | final cx = sin(bisect) * offset; |
296 | final cy = cos(bisect) * offset; | 316 | final cy = cos(bisect) * offset; |
297 | 317 | ||
298 | - final sx = cx + sin(angleStart) * grid.pieSize; | ||
299 | - final sy = cy + cos(angleStart) * grid.pieSize; | ||
300 | - final ex = cx + sin(angleEnd) * grid.pieSize; | ||
301 | - final ey = cy + cos(angleEnd) * grid.pieSize; | 318 | + final sx = cx + sin(angleStart) * grid.radius; |
319 | + final sy = cy + cos(angleStart) * grid.radius; | ||
320 | + final ex = cx + sin(angleEnd) * grid.radius; | ||
321 | + final ey = cy + cos(angleEnd) * grid.radius; | ||
302 | 322 | ||
303 | if (_isFullCircle) { | 323 | if (_isFullCircle) { |
304 | - context.canvas.drawEllipse(0, 0, grid.pieSize, grid.pieSize); | 324 | + context.canvas.drawEllipse(0, 0, grid.radius, grid.radius); |
305 | } else { | 325 | } else { |
306 | context.canvas | 326 | context.canvas |
307 | ..moveTo(cx, cy) | 327 | ..moveTo(cx, cy) |
308 | ..lineTo(sx, sy) | 328 | ..lineTo(sx, sy) |
309 | - ..bezierArc(sx, sy, grid.pieSize, grid.pieSize, ex, ey, | 329 | + ..bezierArc(sx, sy, grid.radius, grid.radius, ex, ey, |
310 | large: angleEnd - angleStart > pi); | 330 | large: angleEnd - angleStart > pi); |
311 | } | 331 | } |
312 | } | 332 | } |
313 | 333 | ||
334 | + void _paintDonnutShape(Context context) { | ||
335 | + final grid = Chart.of(context).grid as PieGrid; | ||
336 | + | ||
337 | + final bisect = (angleStart + angleEnd) / 2; | ||
338 | + | ||
339 | + final cx = sin(bisect) * offset; | ||
340 | + final cy = cos(bisect) * offset; | ||
341 | + | ||
342 | + final stx = cx + sin(angleStart) * grid.radius; | ||
343 | + final sty = cy + cos(angleStart) * grid.radius; | ||
344 | + final etx = cx + sin(angleEnd) * grid.radius; | ||
345 | + final ety = cy + cos(angleEnd) * grid.radius; | ||
346 | + final sbx = cx + sin(angleStart) * innerRadius; | ||
347 | + final sby = cy + cos(angleStart) * innerRadius; | ||
348 | + final ebx = cx + sin(angleEnd) * innerRadius; | ||
349 | + final eby = cy + cos(angleEnd) * innerRadius; | ||
350 | + | ||
351 | + if (_isFullCircle) { | ||
352 | + context.canvas.drawEllipse(0, 0, grid.radius, grid.radius); | ||
353 | + context.canvas | ||
354 | + .drawEllipse(0, 0, innerRadius, innerRadius, clockwise: false); | ||
355 | + } else { | ||
356 | + context.canvas | ||
357 | + ..moveTo(stx, sty) | ||
358 | + ..bezierArc(stx, sty, grid.radius, grid.radius, etx, ety, | ||
359 | + large: angleEnd - angleStart > pi) | ||
360 | + ..lineTo(ebx, eby) | ||
361 | + ..bezierArc(ebx, eby, innerRadius, innerRadius, sbx, sby, | ||
362 | + large: angleEnd - angleStart > pi, sweep: true) | ||
363 | + ..lineTo(stx, sty); | ||
364 | + } | ||
365 | + } | ||
366 | + | ||
367 | + void _paintShape(Context context) { | ||
368 | + if (innerRadius == 0) { | ||
369 | + _paintSliceShape(context); | ||
370 | + } else { | ||
371 | + _paintDonnutShape(context); | ||
372 | + } | ||
373 | + } | ||
374 | + | ||
314 | @override | 375 | @override |
315 | void paintBackground(Context context) { | 376 | void paintBackground(Context context) { |
316 | super.paint(context); | 377 | super.paint(context); |
317 | 378 | ||
318 | if (drawSurface) { | 379 | if (drawSurface) { |
319 | - _shape(context); | 380 | + _paintShape(context); |
320 | if (surfaceOpacity != 1) { | 381 | if (surfaceOpacity != 1) { |
321 | context.canvas | 382 | context.canvas |
322 | ..saveContext() | 383 | ..saveContext() |
@@ -340,7 +401,7 @@ class PieDataSet extends Dataset { | @@ -340,7 +401,7 @@ class PieDataSet extends Dataset { | ||
340 | super.paint(context); | 401 | super.paint(context); |
341 | 402 | ||
342 | if (drawBorder) { | 403 | if (drawBorder) { |
343 | - _shape(context); | 404 | + _paintShape(context); |
344 | context.canvas | 405 | context.canvas |
345 | ..setLineWidth(borderWidth) | 406 | ..setLineWidth(borderWidth) |
346 | ..setLineJoin(PdfLineJoin.round) | 407 | ..setLineJoin(PdfLineJoin.round) |
@@ -349,6 +410,7 @@ class PieDataSet extends Dataset { | @@ -349,6 +410,7 @@ class PieDataSet extends Dataset { | ||
349 | } | 410 | } |
350 | } | 411 | } |
351 | 412 | ||
413 | + @protected | ||
352 | void paintLegend(Context context) { | 414 | void paintLegend(Context context) { |
353 | if (legendPosition != PieLegendPosition.none && _legendWidget != null) { | 415 | if (legendPosition != PieLegendPosition.none && _legendWidget != null) { |
354 | if (_legendAnchor != null && | 416 | if (_legendAnchor != null && |
@@ -379,8 +441,8 @@ class PieDataSet extends Dataset { | @@ -379,8 +441,8 @@ class PieDataSet extends Dataset { | ||
379 | 441 | ||
380 | final bisect = (angleStart + angleEnd) / 2; | 442 | final bisect = (angleStart + angleEnd) / 2; |
381 | 443 | ||
382 | - final cx = sin(bisect) * (offset + grid.pieSize + legendOffset); | ||
383 | - final cy = cos(bisect) * (offset + grid.pieSize + legendOffset); | 444 | + final cx = sin(bisect) * (offset + grid.radius + legendOffset); |
445 | + final cy = cos(bisect) * (offset + grid.radius + legendOffset); | ||
384 | 446 | ||
385 | if (_legendWidget != null) { | 447 | if (_legendWidget != null) { |
386 | context.canvas | 448 | context.canvas |
@@ -17,6 +17,7 @@ | @@ -17,6 +17,7 @@ | ||
17 | import 'dart:typed_data'; | 17 | import 'dart:typed_data'; |
18 | 18 | ||
19 | import 'package:pdf/pdf.dart'; | 19 | import 'package:pdf/pdf.dart'; |
20 | +import 'package:pdf/src/pdf/data_types.dart'; | ||
20 | import 'package:vector_math/vector_math_64.dart'; | 21 | import 'package:vector_math/vector_math_64.dart'; |
21 | 22 | ||
22 | import 'basic.dart'; | 23 | import 'basic.dart'; |
@@ -294,7 +295,7 @@ class TextField extends StatelessWidget { | @@ -294,7 +295,7 @@ class TextField extends StatelessWidget { | ||
294 | if (value != null) { | 295 | if (value != null) { |
295 | final canvas = tf.appearance(context.document, PdfAnnotAppearance.normal, | 296 | final canvas = tf.appearance(context.document, PdfAnnotAppearance.normal, |
296 | matrix: mat, boundingBox: box); | 297 | matrix: mat, boundingBox: box); |
297 | - canvas.buf.putString('/Tx BMC\n'); | 298 | + canvas.markContentBegin(const PdfName('/Tx')); |
298 | Widget.draw( | 299 | Widget.draw( |
299 | Text(value!, style: _textStyle), | 300 | Text(value!, style: _textStyle), |
300 | offset: PdfPoint.zero, | 301 | offset: PdfPoint.zero, |
@@ -303,7 +304,7 @@ class TextField extends StatelessWidget { | @@ -303,7 +304,7 @@ class TextField extends StatelessWidget { | ||
303 | constraints: | 304 | constraints: |
304 | BoxConstraints.tightFor(width: box!.width, height: box!.height), | 305 | BoxConstraints.tightFor(width: box!.width, height: box!.height), |
305 | ); | 306 | ); |
306 | - canvas.buf.putString('EMC\n'); | 307 | + canvas.markContentEnd(); |
307 | } | 308 | } |
308 | 309 | ||
309 | PdfAnnot(context.page, tf); | 310 | PdfAnnot(context.page, tf); |
@@ -16,9 +16,9 @@ | @@ -16,9 +16,9 @@ | ||
16 | 16 | ||
17 | import 'dart:io'; | 17 | import 'dart:io'; |
18 | 18 | ||
19 | -import 'package:test/test.dart'; | ||
20 | import 'package:pdf/pdf.dart'; | 19 | import 'package:pdf/pdf.dart'; |
21 | import 'package:pdf/widgets.dart'; | 20 | import 'package:pdf/widgets.dart'; |
21 | +import 'package:test/test.dart'; | ||
22 | 22 | ||
23 | late Document pdf; | 23 | late Document pdf; |
24 | 24 | ||
@@ -213,6 +213,83 @@ void main() { | @@ -213,6 +213,83 @@ void main() { | ||
213 | }); | 213 | }); |
214 | }); | 214 | }); |
215 | 215 | ||
216 | + test('Standard PieChart', () { | ||
217 | + const data = <String, double>{ | ||
218 | + 'Wind': 8.4, | ||
219 | + 'Hydro': 7.4, | ||
220 | + 'Solar': 2.4, | ||
221 | + 'Biomass': 1.4, | ||
222 | + 'Geothermal': 0.4, | ||
223 | + 'Nuclear': 20, | ||
224 | + 'Coal': 19, | ||
225 | + 'Petroleum': 1, | ||
226 | + 'Natural gas': 40, | ||
227 | + }; | ||
228 | + var color = 0; | ||
229 | + | ||
230 | + pdf.addPage( | ||
231 | + Page( | ||
232 | + pageFormat: PdfPageFormat.standard.landscape, | ||
233 | + build: (Context context) => Chart( | ||
234 | + title: Text('Sources of U.S. electricity generation, 2020'), | ||
235 | + grid: PieGrid(), | ||
236 | + datasets: [ | ||
237 | + for (final item in data.entries) | ||
238 | + PieDataSet( | ||
239 | + legend: item.key, | ||
240 | + value: item.value, | ||
241 | + color: PdfColors | ||
242 | + .primaries[(color++) * 4 % PdfColors.primaries.length], | ||
243 | + offset: color == 6 ? 30 : 0, | ||
244 | + ), | ||
245 | + ], | ||
246 | + ), | ||
247 | + ), | ||
248 | + ); | ||
249 | + }); | ||
250 | + | ||
251 | + test('Donnuts PieChart', () { | ||
252 | + const internalRadius = 150.0; | ||
253 | + const data = <String, int>{ | ||
254 | + 'Dogs': 5528, | ||
255 | + 'Birds': 2211, | ||
256 | + 'Rabbits': 3216, | ||
257 | + 'Ermine': 740, | ||
258 | + 'Cats': 8241, | ||
259 | + }; | ||
260 | + var color = 0; | ||
261 | + final total = data.values.fold<int>(0, (v, e) => v + e); | ||
262 | + | ||
263 | + pdf.addPage( | ||
264 | + Page( | ||
265 | + pageFormat: PdfPageFormat.standard.landscape, | ||
266 | + build: (Context context) => Stack( | ||
267 | + alignment: Alignment.center, | ||
268 | + children: [ | ||
269 | + Text( | ||
270 | + 'Pets', | ||
271 | + textAlign: TextAlign.center, | ||
272 | + style: const TextStyle(fontSize: 30), | ||
273 | + ), | ||
274 | + Chart( | ||
275 | + grid: PieGrid(startAngle: 1), | ||
276 | + datasets: [ | ||
277 | + for (final item in data.entries) | ||
278 | + PieDataSet( | ||
279 | + legend: '${item.key} ${item.value * 100 ~/ total}%', | ||
280 | + value: item.value, | ||
281 | + color: PdfColors | ||
282 | + .primaries[(color++) * 2 % PdfColors.primaries.length], | ||
283 | + innerRadius: internalRadius, | ||
284 | + ), | ||
285 | + ], | ||
286 | + ), | ||
287 | + ], | ||
288 | + ), | ||
289 | + ), | ||
290 | + ); | ||
291 | + }); | ||
292 | + | ||
216 | tearDownAll(() async { | 293 | tearDownAll(() async { |
217 | final file = File('widgets-chart.pdf'); | 294 | final file = File('widgets-chart.pdf'); |
218 | await file.writeAsBytes(await pdf.save()); | 295 | await file.writeAsBytes(await pdf.save()); |
No preview for this file type
-
Please register or login to post a comment