Showing
9 changed files
with
388 additions
and
13 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,9 +45,16 @@ class PdfObjectStream extends PdfObject { | @@ -45,9 +45,16 @@ 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()); | 48 | + final Uint8List original = buf.output(); |
49 | + final Uint8List newData = pdfDocument.deflate(original); | ||
50 | + if (newData.lengthInBytes < original.lengthInBytes) { | ||
49 | params['/Filter'] = const PdfName('/FlateDecode'); | 51 | params['/Filter'] = const PdfName('/FlateDecode'); |
50 | - } else if (isBinary) { | 52 | + _data = newData; |
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + if (_data == null) { | ||
57 | + if (isBinary) { | ||
51 | // This is a Ascii85 stream | 58 | // This is a Ascii85 stream |
52 | final Ascii85Encoder e = Ascii85Encoder(); | 59 | final Ascii85Encoder e = Ascii85Encoder(); |
53 | _data = e.convert(buf.output()); | 60 | _data = e.convert(buf.output()); |
@@ -56,6 +63,7 @@ class PdfObjectStream extends PdfObject { | @@ -56,6 +63,7 @@ class PdfObjectStream extends PdfObject { | ||
56 | // This is a non-deflated stream | 63 | // This is a non-deflated stream |
57 | _data = buf.output(); | 64 | _data = buf.output(); |
58 | } | 65 | } |
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); |
61 | } | 69 | } |
@@ -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'); |
79 | + | ||
80 | + if (shadingType == PdfShadingType.axial) { | ||
54 | params['/Coords'] = | 81 | params['/Coords'] = |
55 | PdfArray.fromNum(<double>[start.x, start.y, end.x, end.y]); | 82 | 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)]); | 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