Showing
9 changed files
with
398 additions
and
23 deletions
| @@ -18,7 +18,11 @@ | @@ -18,7 +18,11 @@ | ||
| 18 | 18 | ||
| 19 | part of pdf; | 19 | part of pdf; |
| 20 | 20 | ||
| 21 | -class PdfFunction extends PdfObjectStream { | 21 | +abstract class PdfBaseFunction extends PdfObject { |
| 22 | + PdfBaseFunction(PdfDocument pdfDocument) : super(pdfDocument); | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +class PdfFunction extends PdfObjectStream implements PdfBaseFunction { | ||
| 22 | PdfFunction( | 26 | PdfFunction( |
| 23 | PdfDocument pdfDocument, { | 27 | PdfDocument pdfDocument, { |
| 24 | this.colors, | 28 | this.colors, |
| @@ -43,6 +47,39 @@ class PdfFunction extends PdfObjectStream { | @@ -43,6 +47,39 @@ class PdfFunction extends PdfObjectStream { | ||
| 43 | params['/Order'] = const PdfNum(3); | 47 | params['/Order'] = const PdfNum(3); |
| 44 | params['/Domain'] = PdfArray.fromNum(const <num>[0, 1]); | 48 | params['/Domain'] = PdfArray.fromNum(const <num>[0, 1]); |
| 45 | params['/Range'] = PdfArray.fromNum(const <num>[0, 1, 0, 1, 0, 1]); | 49 | params['/Range'] = PdfArray.fromNum(const <num>[0, 1, 0, 1, 0, 1]); |
| 46 | - params['/Size'] = PdfNum(colors.length); | 50 | + params['/Size'] = PdfArray.fromNum(<int>[colors.length]); |
| 51 | + } | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +class PdfStitchingFunction extends PdfBaseFunction { | ||
| 55 | + PdfStitchingFunction( | ||
| 56 | + PdfDocument pdfDocument, { | ||
| 57 | + @required this.functions, | ||
| 58 | + @required this.bounds, | ||
| 59 | + this.domainStart = 0, | ||
| 60 | + this.domainEnd = 1, | ||
| 61 | + }) : assert(functions != null), | ||
| 62 | + assert(bounds != null), | ||
| 63 | + super(pdfDocument); | ||
| 64 | + | ||
| 65 | + final List<PdfFunction> functions; | ||
| 66 | + | ||
| 67 | + final List<double> bounds; | ||
| 68 | + | ||
| 69 | + final double domainStart; | ||
| 70 | + | ||
| 71 | + final double domainEnd; | ||
| 72 | + | ||
| 73 | + @override | ||
| 74 | + void _prepare() { | ||
| 75 | + super._prepare(); | ||
| 76 | + | ||
| 77 | + params['/FunctionType'] = const PdfNum(3); | ||
| 78 | + params['/Functions'] = PdfArray.fromObjects(functions); | ||
| 79 | + params['/Order'] = const PdfNum(3); | ||
| 80 | + params['/Domain'] = PdfArray.fromNum(<num>[domainStart, domainEnd]); | ||
| 81 | + params['/Bounds'] = PdfArray.fromNum(bounds); | ||
| 82 | + params['/Encode'] = PdfArray.fromNum( | ||
| 83 | + List<int>.generate(functions.length * 2, (int i) => i % 2)); | ||
| 47 | } | 84 | } |
| 48 | } | 85 | } |
| @@ -45,16 +45,24 @@ class PdfObjectStream extends PdfObject { | @@ -45,16 +45,24 @@ class PdfObjectStream extends PdfObject { | ||
| 45 | // The data is already in the right format | 45 | // The data is already in the right format |
| 46 | _data = buf.output(); | 46 | _data = buf.output(); |
| 47 | } else if (pdfDocument.deflate != null) { | 47 | } else if (pdfDocument.deflate != null) { |
| 48 | - _data = pdfDocument.deflate(buf.output()); | ||
| 49 | - params['/Filter'] = const PdfName('/FlateDecode'); | ||
| 50 | - } else if (isBinary) { | ||
| 51 | - // This is a Ascii85 stream | ||
| 52 | - final Ascii85Encoder e = Ascii85Encoder(); | ||
| 53 | - _data = e.convert(buf.output()); | ||
| 54 | - params['/Filter'] = const PdfName('/ASCII85Decode'); | ||
| 55 | - } else { | ||
| 56 | - // This is a non-deflated stream | ||
| 57 | - _data = buf.output(); | 48 | + final Uint8List original = buf.output(); |
| 49 | + final Uint8List newData = pdfDocument.deflate(original); | ||
| 50 | + if (newData.lengthInBytes < original.lengthInBytes) { | ||
| 51 | + params['/Filter'] = const PdfName('/FlateDecode'); | ||
| 52 | + _data = newData; | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + if (_data == null) { | ||
| 57 | + if (isBinary) { | ||
| 58 | + // This is a Ascii85 stream | ||
| 59 | + final Ascii85Encoder e = Ascii85Encoder(); | ||
| 60 | + _data = e.convert(buf.output()); | ||
| 61 | + params['/Filter'] = const PdfName('/ASCII85Decode'); | ||
| 62 | + } else { | ||
| 63 | + // This is a non-deflated stream | ||
| 64 | + _data = buf.output(); | ||
| 65 | + } | ||
| 58 | } | 66 | } |
| 59 | if (pdfDocument.encryption != null) { | 67 | if (pdfDocument.encryption != null) { |
| 60 | _data = pdfDocument.encryption.encrypt(_data, this); | 68 | _data = pdfDocument.encryption.encrypt(_data, this); |
| @@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
| 18 | 18 | ||
| 19 | part of pdf; | 19 | part of pdf; |
| 20 | 20 | ||
| 21 | -enum PdfShadingType { function, axial, radial } | 21 | +enum PdfShadingType { axial, radial } |
| 22 | 22 | ||
| 23 | class PdfShading extends PdfObject { | 23 | class PdfShading extends PdfObject { |
| 24 | PdfShading( | 24 | PdfShading( |
| @@ -27,10 +27,17 @@ class PdfShading extends PdfObject { | @@ -27,10 +27,17 @@ class PdfShading extends PdfObject { | ||
| 27 | @required this.function, | 27 | @required this.function, |
| 28 | @required this.start, | 28 | @required this.start, |
| 29 | @required this.end, | 29 | @required this.end, |
| 30 | + this.radius0, | ||
| 31 | + this.radius1, | ||
| 32 | + this.boundingBox, | ||
| 33 | + this.extendStart = false, | ||
| 34 | + this.extendEnd = false, | ||
| 30 | }) : assert(shadingType != null), | 35 | }) : assert(shadingType != null), |
| 31 | assert(function != null), | 36 | assert(function != null), |
| 32 | assert(start != null), | 37 | assert(start != null), |
| 33 | assert(end != null), | 38 | assert(end != null), |
| 39 | + assert(extendStart != null), | ||
| 40 | + assert(extendEnd != null), | ||
| 34 | super(pdfDocument); | 41 | super(pdfDocument); |
| 35 | 42 | ||
| 36 | /// Name of the Shading object | 43 | /// Name of the Shading object |
| @@ -38,23 +45,52 @@ class PdfShading extends PdfObject { | @@ -38,23 +45,52 @@ class PdfShading extends PdfObject { | ||
| 38 | 45 | ||
| 39 | final PdfShadingType shadingType; | 46 | final PdfShadingType shadingType; |
| 40 | 47 | ||
| 41 | - final PdfFunction function; | 48 | + final PdfBaseFunction function; |
| 42 | 49 | ||
| 43 | final PdfPoint start; | 50 | final PdfPoint start; |
| 44 | 51 | ||
| 45 | final PdfPoint end; | 52 | final PdfPoint end; |
| 46 | 53 | ||
| 54 | + final PdfRect boundingBox; | ||
| 55 | + | ||
| 56 | + final bool extendStart; | ||
| 57 | + | ||
| 58 | + final bool extendEnd; | ||
| 59 | + | ||
| 60 | + final double radius0; | ||
| 61 | + | ||
| 62 | + final double radius1; | ||
| 63 | + | ||
| 47 | @override | 64 | @override |
| 48 | void _prepare() { | 65 | void _prepare() { |
| 49 | super._prepare(); | 66 | super._prepare(); |
| 50 | 67 | ||
| 51 | - params['/ShadingType'] = PdfNum(shadingType.index + 1); | 68 | + params['/ShadingType'] = PdfNum(shadingType.index + 2); |
| 69 | + if (boundingBox != null) { | ||
| 70 | + params['/BBox'] = PdfArray.fromNum(<double>[ | ||
| 71 | + boundingBox.left, | ||
| 72 | + boundingBox.bottom, | ||
| 73 | + boundingBox.right, | ||
| 74 | + boundingBox.top, | ||
| 75 | + ]); | ||
| 76 | + } | ||
| 52 | params['/AntiAlias'] = const PdfBool(true); | 77 | params['/AntiAlias'] = const PdfBool(true); |
| 53 | params['/ColorSpace'] = const PdfName('/DeviceRGB'); | 78 | params['/ColorSpace'] = const PdfName('/DeviceRGB'); |
| 54 | - params['/Coords'] = | ||
| 55 | - PdfArray.fromNum(<double>[start.x, start.y, end.x, end.y]); | ||
| 56 | - params['/Domain'] = PdfArray.fromNum(<num>[0, 1]); | ||
| 57 | - params['/Extend'] = PdfArray(const <PdfBool>[PdfBool(true), PdfBool(true)]); | 79 | + |
| 80 | + if (shadingType == PdfShadingType.axial) { | ||
| 81 | + params['/Coords'] = | ||
| 82 | + PdfArray.fromNum(<double>[start.x, start.y, end.x, end.y]); | ||
| 83 | + } else if (shadingType == PdfShadingType.radial) { | ||
| 84 | + assert(radius0 != null); | ||
| 85 | + assert(radius1 != null); | ||
| 86 | + params['/Coords'] = PdfArray.fromNum( | ||
| 87 | + <double>[start.x, start.y, radius0, end.x, end.y, radius1]); | ||
| 88 | + } | ||
| 89 | + // params['/Domain'] = PdfArray.fromNum(<num>[0, 1]); | ||
| 90 | + if (extendStart || extendEnd) { | ||
| 91 | + params['/Extend'] = | ||
| 92 | + PdfArray(<PdfBool>[PdfBool(extendStart), PdfBool(extendEnd)]); | ||
| 93 | + } | ||
| 58 | params['/Function'] = function.ref(); | 94 | params['/Function'] = function.ref(); |
| 59 | } | 95 | } |
| 60 | } | 96 | } |
| @@ -155,6 +155,198 @@ class DecorationImage { | @@ -155,6 +155,198 @@ class DecorationImage { | ||
| 155 | } | 155 | } |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | +/// Defines what happens at the edge of the gradient. | ||
| 159 | +enum TileMode { | ||
| 160 | + /// Edge is clamped to the final color. | ||
| 161 | + clamp, | ||
| 162 | + | ||
| 163 | + /// Edge is repeated from first color to last. | ||
| 164 | + // repeated, | ||
| 165 | + | ||
| 166 | + /// Edge is mirrored from last color to first. | ||
| 167 | + // mirror, | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +/// A 2D gradient. | ||
| 171 | +@immutable | ||
| 172 | +abstract class Gradient { | ||
| 173 | + /// Initialize the gradient's colors and stops. | ||
| 174 | + const Gradient({ | ||
| 175 | + @required this.colors, | ||
| 176 | + this.stops, | ||
| 177 | + }) : assert(colors != null); | ||
| 178 | + | ||
| 179 | + final List<PdfColor> colors; | ||
| 180 | + | ||
| 181 | + /// A list of values from 0.0 to 1.0 that denote fractions along the gradient. | ||
| 182 | + final List<double> stops; | ||
| 183 | + | ||
| 184 | + PdfBaseFunction _buildFunction( | ||
| 185 | + Context context, | ||
| 186 | + List<PdfColor> colors, | ||
| 187 | + List<double> stops, | ||
| 188 | + ) { | ||
| 189 | + if (stops == null) { | ||
| 190 | + return PdfFunction( | ||
| 191 | + context.document, | ||
| 192 | + colors: colors, | ||
| 193 | + ); | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + final List<PdfFunction> fn = <PdfFunction>[]; | ||
| 197 | + | ||
| 198 | + PdfColor lc = colors.first; | ||
| 199 | + for (final PdfColor c in colors.sublist(1)) { | ||
| 200 | + fn.add(PdfFunction( | ||
| 201 | + context.document, | ||
| 202 | + colors: <PdfColor>[lc, c], | ||
| 203 | + )); | ||
| 204 | + lc = c; | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + return PdfStitchingFunction( | ||
| 208 | + context.document, | ||
| 209 | + functions: fn, | ||
| 210 | + bounds: stops.sublist(1, stops.length - 1), | ||
| 211 | + domainStart: stops.first, | ||
| 212 | + domainEnd: stops.last, | ||
| 213 | + ); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + void paint(Context context, PdfRect box); | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +/// A 2D linear gradient. | ||
| 220 | +class LinearGradient extends Gradient { | ||
| 221 | + /// Creates a linear gradient. | ||
| 222 | + const LinearGradient({ | ||
| 223 | + this.begin = Alignment.centerLeft, | ||
| 224 | + this.end = Alignment.centerRight, | ||
| 225 | + @required List<PdfColor> colors, | ||
| 226 | + List<double> stops, | ||
| 227 | + this.tileMode = TileMode.clamp, | ||
| 228 | + }) : assert(begin != null), | ||
| 229 | + assert(end != null), | ||
| 230 | + assert(tileMode != null), | ||
| 231 | + super(colors: colors, stops: stops); | ||
| 232 | + | ||
| 233 | + /// The offset at which stop 0.0 of the gradient is placed. | ||
| 234 | + final Alignment begin; | ||
| 235 | + | ||
| 236 | + /// The offset at which stop 1.0 of the gradient is placed. | ||
| 237 | + final Alignment end; | ||
| 238 | + | ||
| 239 | + /// How this gradient should tile the plane beyond in the region before | ||
| 240 | + final TileMode tileMode; | ||
| 241 | + | ||
| 242 | + @override | ||
| 243 | + void paint(Context context, PdfRect box) { | ||
| 244 | + if (colors.isEmpty) { | ||
| 245 | + return; | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + if (colors.length == 1) { | ||
| 249 | + context.canvas | ||
| 250 | + ..setFillColor(colors.first) | ||
| 251 | + ..fillPath(); | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + assert(stops == null || stops.length == colors.length); | ||
| 255 | + | ||
| 256 | + context.canvas | ||
| 257 | + ..saveContext() | ||
| 258 | + ..clipPath() | ||
| 259 | + ..applyShader( | ||
| 260 | + PdfShading( | ||
| 261 | + context.document, | ||
| 262 | + shadingType: PdfShadingType.axial, | ||
| 263 | + boundingBox: box, | ||
| 264 | + function: _buildFunction(context, colors, stops), | ||
| 265 | + start: begin.withinRect(box), | ||
| 266 | + end: end.withinRect(box), | ||
| 267 | + extendStart: true, | ||
| 268 | + extendEnd: true, | ||
| 269 | + ), | ||
| 270 | + ) | ||
| 271 | + ..restoreContext(); | ||
| 272 | + } | ||
| 273 | +} | ||
| 274 | + | ||
| 275 | +/// A 2D radial gradient. | ||
| 276 | +class RadialGradient extends Gradient { | ||
| 277 | + /// Creates a radial gradient. | ||
| 278 | + /// | ||
| 279 | + /// The [colors] argument must not be null. If [stops] is non-null, it must | ||
| 280 | + /// have the same length as [colors]. | ||
| 281 | + const RadialGradient({ | ||
| 282 | + this.center = Alignment.center, | ||
| 283 | + this.radius = 0.5, | ||
| 284 | + @required List<PdfColor> colors, | ||
| 285 | + List<double> stops, | ||
| 286 | + this.tileMode = TileMode.clamp, | ||
| 287 | + this.focal, | ||
| 288 | + this.focalRadius = 0.0, | ||
| 289 | + }) : assert(center != null), | ||
| 290 | + assert(radius != null), | ||
| 291 | + assert(tileMode != null), | ||
| 292 | + assert(focalRadius != null), | ||
| 293 | + super(colors: colors, stops: stops); | ||
| 294 | + | ||
| 295 | + /// The center of the gradient | ||
| 296 | + final Alignment center; | ||
| 297 | + | ||
| 298 | + /// The radius of the gradient | ||
| 299 | + final double radius; | ||
| 300 | + | ||
| 301 | + /// How this gradient should tile the plane beyond the outer ring at [radius] | ||
| 302 | + /// pixels from the [center]. | ||
| 303 | + final TileMode tileMode; | ||
| 304 | + | ||
| 305 | + /// The focal point of the gradient. | ||
| 306 | + final Alignment focal; | ||
| 307 | + | ||
| 308 | + /// The radius of the focal point of the gradient. | ||
| 309 | + final double focalRadius; | ||
| 310 | + | ||
| 311 | + @override | ||
| 312 | + void paint(Context context, PdfRect box) { | ||
| 313 | + if (colors.isEmpty) { | ||
| 314 | + return; | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + if (colors.length == 1) { | ||
| 318 | + context.canvas | ||
| 319 | + ..setFillColor(colors.first) | ||
| 320 | + ..fillPath(); | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + assert(stops == null || stops.length == colors.length); | ||
| 324 | + | ||
| 325 | + final Alignment _focal = focal ?? center; | ||
| 326 | + | ||
| 327 | + final double _radius = math.min(box.width, box.height); | ||
| 328 | + | ||
| 329 | + context.canvas | ||
| 330 | + ..saveContext() | ||
| 331 | + ..clipPath() | ||
| 332 | + ..applyShader( | ||
| 333 | + PdfShading( | ||
| 334 | + context.document, | ||
| 335 | + shadingType: PdfShadingType.radial, | ||
| 336 | + boundingBox: box, | ||
| 337 | + function: _buildFunction(context, colors, stops), | ||
| 338 | + start: _focal.withinRect(box), | ||
| 339 | + end: center.withinRect(box), | ||
| 340 | + radius0: focalRadius * _radius, | ||
| 341 | + radius1: radius * _radius, | ||
| 342 | + extendStart: true, | ||
| 343 | + extendEnd: true, | ||
| 344 | + ), | ||
| 345 | + ) | ||
| 346 | + ..restoreContext(); | ||
| 347 | + } | ||
| 348 | +} | ||
| 349 | + | ||
| 158 | enum BoxShape { circle, rectangle } | 350 | enum BoxShape { circle, rectangle } |
| 159 | 351 | ||
| 160 | @immutable | 352 | @immutable |
| @@ -163,6 +355,7 @@ class BoxDecoration { | @@ -163,6 +355,7 @@ class BoxDecoration { | ||
| 163 | {this.color, | 355 | {this.color, |
| 164 | this.border, | 356 | this.border, |
| 165 | this.borderRadius, | 357 | this.borderRadius, |
| 358 | + this.gradient, | ||
| 166 | this.image, | 359 | this.image, |
| 167 | this.shape = BoxShape.rectangle}) | 360 | this.shape = BoxShape.rectangle}) |
| 168 | : assert(shape != null); | 361 | : assert(shape != null); |
| @@ -173,6 +366,7 @@ class BoxDecoration { | @@ -173,6 +366,7 @@ class BoxDecoration { | ||
| 173 | final double borderRadius; | 366 | final double borderRadius; |
| 174 | final BoxShape shape; | 367 | final BoxShape shape; |
| 175 | final DecorationImage image; | 368 | final DecorationImage image; |
| 369 | + final Gradient gradient; | ||
| 176 | 370 | ||
| 177 | void paint(Context context, PdfRect box) { | 371 | void paint(Context context, PdfRect box) { |
| 178 | assert(box.x != null); | 372 | assert(box.x != null); |
| @@ -200,6 +394,25 @@ class BoxDecoration { | @@ -200,6 +394,25 @@ class BoxDecoration { | ||
| 200 | ..fillPath(); | 394 | ..fillPath(); |
| 201 | } | 395 | } |
| 202 | 396 | ||
| 397 | + if (gradient != null) { | ||
| 398 | + switch (shape) { | ||
| 399 | + case BoxShape.rectangle: | ||
| 400 | + if (borderRadius == null) { | ||
| 401 | + context.canvas.drawRect(box.x, box.y, box.width, box.height); | ||
| 402 | + } else { | ||
| 403 | + context.canvas.drawRRect(box.x, box.y, box.width, box.height, | ||
| 404 | + borderRadius, borderRadius); | ||
| 405 | + } | ||
| 406 | + break; | ||
| 407 | + case BoxShape.circle: | ||
| 408 | + context.canvas.drawEllipse(box.x + box.width / 2.0, | ||
| 409 | + box.y + box.height / 2.0, box.width / 2.0, box.height / 2.0); | ||
| 410 | + break; | ||
| 411 | + } | ||
| 412 | + | ||
| 413 | + gradient.paint(context, box); | ||
| 414 | + } | ||
| 415 | + | ||
| 203 | if (image != null) { | 416 | if (image != null) { |
| 204 | context.canvas.saveContext(); | 417 | context.canvas.saveContext(); |
| 205 | switch (shape) { | 418 | switch (shape) { |
| @@ -332,7 +332,7 @@ class Alignment { | @@ -332,7 +332,7 @@ class Alignment { | ||
| 332 | final double halfHeight = rect.height / 2.0; | 332 | final double halfHeight = rect.height / 2.0; |
| 333 | return PdfPoint( | 333 | return PdfPoint( |
| 334 | rect.left + halfWidth + x * halfWidth, | 334 | rect.left + halfWidth + x * halfWidth, |
| 335 | - rect.top + halfHeight + y * halfHeight, | 335 | + rect.bottom + halfHeight + y * halfHeight, |
| 336 | ); | 336 | ); |
| 337 | } | 337 | } |
| 338 | 338 | ||
| @@ -353,6 +353,16 @@ class Alignment { | @@ -353,6 +353,16 @@ class Alignment { | ||
| 353 | String toString() => '($x, $y)'; | 353 | String toString() => '($x, $y)'; |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | +/// An offset that's expressed as a fraction of a [PdfPoint]. | ||
| 357 | +@immutable | ||
| 358 | +class FractionalOffset extends Alignment { | ||
| 359 | + /// Creates a fractional offset. | ||
| 360 | + const FractionalOffset(double dx, double dy) | ||
| 361 | + : assert(dx != null), | ||
| 362 | + assert(dy != null), | ||
| 363 | + super(dx * 2 - 1, 1 - dy * 2); | ||
| 364 | +} | ||
| 365 | + | ||
| 356 | /// The pair of sizes returned by [applyBoxFit]. | 366 | /// The pair of sizes returned by [applyBoxFit]. |
| 357 | @immutable | 367 | @immutable |
| 358 | class FittedSizes { | 368 | class FittedSizes { |
| @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | ||
| 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf | 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf |
| 5 | repository: https://github.com/DavBfr/dart_pdf | 5 | repository: https://github.com/DavBfr/dart_pdf |
| 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues | 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues |
| 7 | -version: 1.6.2 | 7 | +version: 1.7.0 |
| 8 | 8 | ||
| 9 | environment: | 9 | environment: |
| 10 | sdk: ">=2.3.0 <3.0.0" | 10 | sdk: ">=2.3.0 <3.0.0" |
| @@ -158,6 +158,73 @@ void main() { | @@ -158,6 +158,73 @@ void main() { | ||
| 158 | )); | 158 | )); |
| 159 | }); | 159 | }); |
| 160 | 160 | ||
| 161 | + test('Container Widgets LinearGradient', () { | ||
| 162 | + pdf.addPage(Page( | ||
| 163 | + build: (Context context) => Container( | ||
| 164 | + alignment: Alignment.center, | ||
| 165 | + margin: const EdgeInsets.all(30), | ||
| 166 | + padding: const EdgeInsets.all(20), | ||
| 167 | + decoration: const BoxDecoration( | ||
| 168 | + borderRadius: 20, | ||
| 169 | + gradient: LinearGradient( | ||
| 170 | + colors: <PdfColor>[ | ||
| 171 | + PdfColors.blue, | ||
| 172 | + PdfColors.red, | ||
| 173 | + PdfColors.yellow, | ||
| 174 | + ], | ||
| 175 | + begin: Alignment.bottomLeft, | ||
| 176 | + end: Alignment.topRight, | ||
| 177 | + stops: <double>[0, .8, 1.0], | ||
| 178 | + tileMode: TileMode.clamp, | ||
| 179 | + ), | ||
| 180 | + border: BoxBorder( | ||
| 181 | + color: PdfColors.blue800, | ||
| 182 | + top: true, | ||
| 183 | + left: true, | ||
| 184 | + right: true, | ||
| 185 | + bottom: true, | ||
| 186 | + width: 2, | ||
| 187 | + )), | ||
| 188 | + width: 200, | ||
| 189 | + height: 400, | ||
| 190 | + ), | ||
| 191 | + )); | ||
| 192 | + }); | ||
| 193 | + | ||
| 194 | + test('Container Widgets RadialGradient', () { | ||
| 195 | + pdf.addPage(Page( | ||
| 196 | + build: (Context context) => Container( | ||
| 197 | + alignment: Alignment.center, | ||
| 198 | + margin: const EdgeInsets.all(30), | ||
| 199 | + padding: const EdgeInsets.all(20), | ||
| 200 | + decoration: const BoxDecoration( | ||
| 201 | + borderRadius: 20, | ||
| 202 | + gradient: RadialGradient( | ||
| 203 | + colors: <PdfColor>[ | ||
| 204 | + PdfColors.blue, | ||
| 205 | + PdfColors.red, | ||
| 206 | + PdfColors.yellow, | ||
| 207 | + ], | ||
| 208 | + stops: <double>[0.0, .2, 1.0], | ||
| 209 | + center: FractionalOffset(.7, .2), | ||
| 210 | + focal: FractionalOffset(.7, .45), | ||
| 211 | + focalRadius: 1, | ||
| 212 | + ), | ||
| 213 | + border: BoxBorder( | ||
| 214 | + color: PdfColors.blue800, | ||
| 215 | + top: true, | ||
| 216 | + left: true, | ||
| 217 | + right: true, | ||
| 218 | + bottom: true, | ||
| 219 | + width: 2, | ||
| 220 | + )), | ||
| 221 | + width: 200, | ||
| 222 | + height: 400, | ||
| 223 | + // child: Placeholder(), | ||
| 224 | + ), | ||
| 225 | + )); | ||
| 226 | + }); | ||
| 227 | + | ||
| 161 | tearDownAll(() { | 228 | tearDownAll(() { |
| 162 | final File file = File('widgets-container.pdf'); | 229 | final File file = File('widgets-container.pdf'); |
| 163 | file.writeAsBytesSync(pdf.save()); | 230 | file.writeAsBytesSync(pdf.save()); |
No preview for this file type
-
Please register or login to post a comment