Showing
6 changed files
with
274 additions
and
114 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); |
| 225 | - 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); | ||
| 227 | - 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); | 228 | + if (clockwise) { |
| 229 | + curveTo(x + _m4 * r1, y - r2, x + r1, y - _m4 * r2, x + r1, y); | ||
| 230 | + curveTo(x + r1, y + _m4 * r2, x + _m4 * r1, y + r2, x, y + r2); | ||
| 231 | + curveTo(x - _m4 * r1, y + r2, x - r1, y + _m4 * r2, x - r1, y); | ||
| 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