Showing
10 changed files
with
219 additions
and
61 deletions
@@ -13,6 +13,7 @@ | @@ -13,6 +13,7 @@ | ||
13 | - Opt-out from dart library | 13 | - Opt-out from dart library |
14 | - Improve graphic operations | 14 | - Improve graphic operations |
15 | - Automatically calculate Shape() bounding box | 15 | - Automatically calculate Shape() bounding box |
16 | +- Improve gradient functions | ||
16 | 17 | ||
17 | ## 1.12.0 | 18 | ## 1.12.0 |
18 | 19 |
@@ -32,6 +32,7 @@ export 'src/info.dart'; | @@ -32,6 +32,7 @@ export 'src/info.dart'; | ||
32 | export 'src/outline.dart'; | 32 | export 'src/outline.dart'; |
33 | export 'src/page.dart'; | 33 | export 'src/page.dart'; |
34 | export 'src/page_format.dart'; | 34 | export 'src/page_format.dart'; |
35 | +export 'src/pattern.dart'; | ||
35 | export 'src/point.dart'; | 36 | export 'src/point.dart'; |
36 | export 'src/rect.dart'; | 37 | export 'src/rect.dart'; |
37 | export 'src/shading.dart'; | 38 | export 'src/shading.dart'; |
@@ -258,7 +258,7 @@ class PdfSecString extends PdfString { | @@ -258,7 +258,7 @@ class PdfSecString extends PdfString { | ||
258 | } | 258 | } |
259 | 259 | ||
260 | class PdfName extends PdfDataType { | 260 | class PdfName extends PdfDataType { |
261 | - const PdfName(this.value); | 261 | + const PdfName(this.value) : assert(value != null); |
262 | 262 | ||
263 | final String value; | 263 | final String value; |
264 | 264 |
@@ -24,35 +24,103 @@ import 'object_stream.dart'; | @@ -24,35 +24,103 @@ import 'object_stream.dart'; | ||
24 | 24 | ||
25 | abstract class PdfBaseFunction extends PdfObject { | 25 | abstract class PdfBaseFunction extends PdfObject { |
26 | PdfBaseFunction(PdfDocument pdfDocument) : super(pdfDocument); | 26 | PdfBaseFunction(PdfDocument pdfDocument) : super(pdfDocument); |
27 | + | ||
28 | + factory PdfBaseFunction.colorsAndStops( | ||
29 | + PdfDocument pdfDocument, | ||
30 | + List<PdfColor> colors, [ | ||
31 | + List<double> stops, | ||
32 | + ]) { | ||
33 | + if (stops == null || stops.isEmpty) { | ||
34 | + return PdfFunction.fromColors(pdfDocument, colors); | ||
35 | + } | ||
36 | + | ||
37 | + final _colors = List<PdfColor>.from(colors); | ||
38 | + final _stops = List<double>.from(stops); | ||
39 | + | ||
40 | + final fn = <PdfFunction>[]; | ||
41 | + var lc = _colors.first; | ||
42 | + | ||
43 | + if (_stops[0] > 0) { | ||
44 | + _colors.insert(0, lc); | ||
45 | + _stops.insert(0, 0); | ||
46 | + } | ||
47 | + | ||
48 | + if (_stops.last < 1) { | ||
49 | + _colors.add(_colors.last); | ||
50 | + _stops.add(1); | ||
51 | + } | ||
52 | + | ||
53 | + if (_stops.length != _colors.length) { | ||
54 | + throw Exception( | ||
55 | + 'The number of colors in a gradient must match the number of stops'); | ||
56 | + } | ||
57 | + | ||
58 | + for (final c in _colors.sublist(1)) { | ||
59 | + fn.add(PdfFunction.fromColors(pdfDocument, <PdfColor>[lc, c])); | ||
60 | + lc = c; | ||
61 | + } | ||
62 | + | ||
63 | + return PdfStitchingFunction( | ||
64 | + pdfDocument, | ||
65 | + functions: fn, | ||
66 | + bounds: _stops.sublist(1, _stops.length - 1), | ||
67 | + domainStart: 0, | ||
68 | + domainEnd: 1, | ||
69 | + ); | ||
70 | + } | ||
27 | } | 71 | } |
28 | 72 | ||
29 | class PdfFunction extends PdfObjectStream implements PdfBaseFunction { | 73 | class PdfFunction extends PdfObjectStream implements PdfBaseFunction { |
30 | PdfFunction( | 74 | PdfFunction( |
31 | PdfDocument pdfDocument, { | 75 | PdfDocument pdfDocument, { |
32 | - this.colors, | 76 | + this.data, |
77 | + this.bitsPerSample = 8, | ||
78 | + this.order = 1, | ||
79 | + this.domain = const <num>[0, 1], | ||
80 | + this.range = const <num>[0, 1], | ||
33 | }) : super(pdfDocument); | 81 | }) : super(pdfDocument); |
34 | 82 | ||
35 | - final List<PdfColor> colors; | ||
36 | - | ||
37 | - @override | ||
38 | - void prepare() { | 83 | + factory PdfFunction.fromColors( |
84 | + PdfDocument pdfDocument, List<PdfColor> colors) { | ||
85 | + final data = <int>[]; | ||
39 | for (final color in colors) { | 86 | for (final color in colors) { |
40 | - buf.putBytes(<int>[ | ||
41 | - (color.red * 255.0).round() & 0xff, | ||
42 | - (color.green * 255.0).round() & 0xff, | ||
43 | - (color.blue * 255.0).round() & 0xff, | ||
44 | - ]); | 87 | + data.add((color.red * 255.0).round() & 0xff); |
88 | + data.add((color.green * 255.0).round() & 0xff); | ||
89 | + data.add((color.blue * 255.0).round() & 0xff); | ||
45 | } | 90 | } |
91 | + return PdfFunction( | ||
92 | + pdfDocument, | ||
93 | + order: 3, | ||
94 | + data: data, | ||
95 | + range: const <num>[0, 1, 0, 1, 0, 1], | ||
96 | + ); | ||
97 | + } | ||
98 | + | ||
99 | + final List<int> data; | ||
100 | + | ||
101 | + final int bitsPerSample; | ||
102 | + | ||
103 | + final int order; | ||
104 | + | ||
105 | + final List<num> domain; | ||
106 | + | ||
107 | + final List<num> range; | ||
46 | 108 | ||
109 | + @override | ||
110 | + void prepare() { | ||
111 | + buf.putBytes(data); | ||
47 | super.prepare(); | 112 | super.prepare(); |
48 | 113 | ||
49 | params['/FunctionType'] = const PdfNum(0); | 114 | params['/FunctionType'] = const PdfNum(0); |
50 | - params['/BitsPerSample'] = const PdfNum(8); | ||
51 | - params['/Order'] = const PdfNum(3); | ||
52 | - params['/Domain'] = PdfArray.fromNum(const <num>[0, 1]); | ||
53 | - params['/Range'] = PdfArray.fromNum(const <num>[0, 1, 0, 1, 0, 1]); | ||
54 | - params['/Size'] = PdfArray.fromNum(<int>[colors.length]); | 115 | + params['/BitsPerSample'] = PdfNum(bitsPerSample); |
116 | + params['/Order'] = PdfNum(order); | ||
117 | + params['/Domain'] = PdfArray.fromNum(domain); | ||
118 | + params['/Range'] = PdfArray.fromNum(range); | ||
119 | + params['/Size'] = PdfArray.fromNum(<int>[data.length ~/ order]); | ||
55 | } | 120 | } |
121 | + | ||
122 | + @override | ||
123 | + String toString() => '$runtimeType $bitsPerSample $order $data'; | ||
56 | } | 124 | } |
57 | 125 | ||
58 | class PdfStitchingFunction extends PdfBaseFunction { | 126 | class PdfStitchingFunction extends PdfBaseFunction { |
@@ -86,4 +154,8 @@ class PdfStitchingFunction extends PdfBaseFunction { | @@ -86,4 +154,8 @@ class PdfStitchingFunction extends PdfBaseFunction { | ||
86 | params['/Encode'] = PdfArray.fromNum( | 154 | params['/Encode'] = PdfArray.fromNum( |
87 | List<int>.generate(functions.length * 2, (int i) => i % 2)); | 155 | List<int>.generate(functions.length * 2, (int i) => i % 2)); |
88 | } | 156 | } |
157 | + | ||
158 | + @override | ||
159 | + String toString() => | ||
160 | + '$runtimeType $domainStart $bounds $domainEnd $functions'; | ||
89 | } | 161 | } |
@@ -31,8 +31,7 @@ class PdfGraphicState { | @@ -31,8 +31,7 @@ class PdfGraphicState { | ||
31 | /// The opacity to apply to this graphic state | 31 | /// The opacity to apply to this graphic state |
32 | final double opacity; | 32 | final double opacity; |
33 | 33 | ||
34 | - @protected | ||
35 | - PdfDict _output() { | 34 | + PdfDict output() { |
36 | final params = PdfDict(); | 35 | final params = PdfDict(); |
37 | 36 | ||
38 | if (opacity != null) { | 37 | if (opacity != null) { |
@@ -79,7 +78,7 @@ class PdfGraphicStates extends PdfObject { | @@ -79,7 +78,7 @@ class PdfGraphicStates extends PdfObject { | ||
79 | super.prepare(); | 78 | super.prepare(); |
80 | 79 | ||
81 | for (var index = 0; index < _states.length; index++) { | 80 | for (var index = 0; index < _states.length; index++) { |
82 | - params['$_prefix$index'] = _states[index]._output(); | 81 | + params['$_prefix$index'] = _states[index].output(); |
83 | } | 82 | } |
84 | } | 83 | } |
85 | } | 84 | } |
@@ -19,6 +19,7 @@ import 'document.dart'; | @@ -19,6 +19,7 @@ import 'document.dart'; | ||
19 | import 'font.dart'; | 19 | import 'font.dart'; |
20 | import 'graphic_state.dart'; | 20 | import 'graphic_state.dart'; |
21 | import 'object.dart'; | 21 | import 'object.dart'; |
22 | +import 'pattern.dart'; | ||
22 | import 'shading.dart'; | 23 | import 'shading.dart'; |
23 | import 'xobject.dart'; | 24 | import 'xobject.dart'; |
24 | 25 | ||
@@ -37,13 +38,16 @@ mixin PdfGraphicStream on PdfObject { | @@ -37,13 +38,16 @@ mixin PdfGraphicStream on PdfObject { | ||
37 | bool knockoutTransparency = false; | 38 | bool knockoutTransparency = false; |
38 | 39 | ||
39 | /// The fonts associated with this page | 40 | /// The fonts associated with this page |
40 | - final Map<String, PdfFont> fonts = <String, PdfFont>{}; | 41 | + final fonts = <String, PdfFont>{}; |
41 | 42 | ||
42 | - /// The fonts associated with this page | ||
43 | - final Map<String, PdfShading> shading = <String, PdfShading>{}; | 43 | + /// The shaders associated with this page |
44 | + final shading = <String, PdfShading>{}; | ||
45 | + | ||
46 | + /// The shaders associated with this page | ||
47 | + final patterns = <String, PdfPattern>{}; | ||
44 | 48 | ||
45 | /// The xobjects or other images in the pdf | 49 | /// The xobjects or other images in the pdf |
46 | - final Map<String, PdfXObject> xObjects = <String, PdfXObject>{}; | 50 | + final xObjects = <String, PdfXObject>{}; |
47 | 51 | ||
48 | /// Add a font to this graphic object | 52 | /// Add a font to this graphic object |
49 | void addFont(PdfFont font) { | 53 | void addFont(PdfFont font) { |
@@ -59,6 +63,13 @@ mixin PdfGraphicStream on PdfObject { | @@ -59,6 +63,13 @@ mixin PdfGraphicStream on PdfObject { | ||
59 | } | 63 | } |
60 | } | 64 | } |
61 | 65 | ||
66 | + /// Add a pattern to this graphic object | ||
67 | + void addPattern(PdfPattern pattern) { | ||
68 | + if (!patterns.containsKey(pattern.name)) { | ||
69 | + patterns[pattern.name] = pattern; | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
62 | /// Add an XObject to this graphic object | 73 | /// Add an XObject to this graphic object |
63 | void addXObject(PdfXObject object) { | 74 | void addXObject(PdfXObject object) { |
64 | if (!xObjects.containsKey(object.name)) { | 75 | if (!xObjects.containsKey(object.name)) { |
@@ -99,11 +110,16 @@ mixin PdfGraphicStream on PdfObject { | @@ -99,11 +110,16 @@ mixin PdfGraphicStream on PdfObject { | ||
99 | resources['/Font'] = PdfDict.fromObjectMap(fonts); | 110 | resources['/Font'] = PdfDict.fromObjectMap(fonts); |
100 | } | 111 | } |
101 | 112 | ||
102 | - // shading | 113 | + // shaders |
103 | if (shading.isNotEmpty) { | 114 | if (shading.isNotEmpty) { |
104 | resources['/Shading'] = PdfDict.fromObjectMap(shading); | 115 | resources['/Shading'] = PdfDict.fromObjectMap(shading); |
105 | } | 116 | } |
106 | 117 | ||
118 | + // patterns | ||
119 | + if (patterns.isNotEmpty) { | ||
120 | + resources['/Pattern'] = PdfDict.fromObjectMap(patterns); | ||
121 | + } | ||
122 | + | ||
107 | // Now the XObjects | 123 | // Now the XObjects |
108 | if (xObjects.isNotEmpty) { | 124 | if (xObjects.isNotEmpty) { |
109 | resources['/XObject'] = PdfDict.fromObjectMap(xObjects); | 125 | resources['/XObject'] = PdfDict.fromObjectMap(xObjects); |
@@ -28,6 +28,7 @@ import 'graphic_state.dart'; | @@ -28,6 +28,7 @@ import 'graphic_state.dart'; | ||
28 | import 'graphic_stream.dart'; | 28 | import 'graphic_stream.dart'; |
29 | import 'image.dart'; | 29 | import 'image.dart'; |
30 | import 'page.dart'; | 30 | import 'page.dart'; |
31 | +import 'pattern.dart'; | ||
31 | import 'rect.dart'; | 32 | import 'rect.dart'; |
32 | import 'shading.dart'; | 33 | import 'shading.dart'; |
33 | import 'stream.dart'; | 34 | import 'stream.dart'; |
@@ -86,10 +87,14 @@ enum PdfTextRenderingMode { | @@ -86,10 +87,14 @@ enum PdfTextRenderingMode { | ||
86 | 87 | ||
87 | @immutable | 88 | @immutable |
88 | class _PdfGraphicsContext { | 89 | class _PdfGraphicsContext { |
89 | - const _PdfGraphicsContext({@required this.ctm}) : assert(ctm != null); | 90 | + const _PdfGraphicsContext({ |
91 | + @required this.ctm, | ||
92 | + }) : assert(ctm != null); | ||
90 | final Matrix4 ctm; | 93 | final Matrix4 ctm; |
91 | 94 | ||
92 | - _PdfGraphicsContext copy() => _PdfGraphicsContext(ctm: ctm.clone()); | 95 | + _PdfGraphicsContext copy() => _PdfGraphicsContext( |
96 | + ctm: ctm.clone(), | ||
97 | + ); | ||
93 | } | 98 | } |
94 | 99 | ||
95 | /// Pdf drawing operations | 100 | /// Pdf drawing operations |
@@ -346,6 +351,20 @@ class PdfGraphics { | @@ -346,6 +351,20 @@ class PdfGraphics { | ||
346 | } | 351 | } |
347 | } | 352 | } |
348 | 353 | ||
354 | + /// Sets the fill pattern for drawing | ||
355 | + void setFillPattern(PdfPattern pattern) { | ||
356 | + // The shader needs to be registered in the page resources | ||
357 | + _page.addPattern(pattern); | ||
358 | + buf.putString('/Pattern cs${pattern.name} scn\n'); | ||
359 | + } | ||
360 | + | ||
361 | + /// Sets the stroke pattern for drawing | ||
362 | + void setStrokePattern(PdfPattern pattern) { | ||
363 | + // The shader needs to be registered in the page resources | ||
364 | + _page.addPattern(pattern); | ||
365 | + buf.putString('/Pattern CS${pattern.name} SCN\n'); | ||
366 | + } | ||
367 | + | ||
349 | /// Set the graphic state for drawing | 368 | /// Set the graphic state for drawing |
350 | void setGraphicState(PdfGraphicState state) { | 369 | void setGraphicState(PdfGraphicState state) { |
351 | final name = _page.stateName(state); | 370 | final name = _page.stateName(state); |
pdf/lib/src/pattern.dart
0 → 100644
1 | +/* | ||
2 | + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +import 'package:meta/meta.dart'; | ||
18 | +import 'package:pdf/src/data_types.dart'; | ||
19 | +import 'package:vector_math/vector_math_64.dart'; | ||
20 | + | ||
21 | +import 'document.dart'; | ||
22 | +import 'graphic_state.dart'; | ||
23 | +import 'object.dart'; | ||
24 | +import 'shading.dart'; | ||
25 | + | ||
26 | +abstract class PdfPattern extends PdfObject { | ||
27 | + PdfPattern(PdfDocument pdfDocument, this.patternType, this.matrix) | ||
28 | + : super(pdfDocument); | ||
29 | + | ||
30 | + /// Name of the Pattern object | ||
31 | + String get name => '/P$objser'; | ||
32 | + | ||
33 | + final int patternType; | ||
34 | + | ||
35 | + final Matrix4 matrix; | ||
36 | + | ||
37 | + @override | ||
38 | + void prepare() { | ||
39 | + super.prepare(); | ||
40 | + | ||
41 | + params['/PatternType'] = PdfNum(patternType); | ||
42 | + | ||
43 | + if (matrix != null) { | ||
44 | + final s = matrix.storage; | ||
45 | + params['/Matrix'] = | ||
46 | + PdfArray.fromNum(<double>[s[0], s[1], s[4], s[5], s[12], s[13]]); | ||
47 | + } | ||
48 | + } | ||
49 | +} | ||
50 | + | ||
51 | +class PdfShadingPattern extends PdfPattern { | ||
52 | + PdfShadingPattern( | ||
53 | + PdfDocument pdfDocument, { | ||
54 | + @required this.shading, | ||
55 | + Matrix4 matrix, | ||
56 | + this.graphicState, | ||
57 | + }) : assert(shading != null), | ||
58 | + super(pdfDocument, 2, matrix); | ||
59 | + | ||
60 | + final PdfShading shading; | ||
61 | + | ||
62 | + final PdfGraphicState graphicState; | ||
63 | + | ||
64 | + @override | ||
65 | + void prepare() { | ||
66 | + super.prepare(); | ||
67 | + | ||
68 | + params['/Shading'] = shading.ref(); | ||
69 | + | ||
70 | + if (graphicState != null) { | ||
71 | + params['/ExtGState'] = graphicState.output(); | ||
72 | + } | ||
73 | + } | ||
74 | +} |
@@ -231,38 +231,6 @@ abstract class Gradient { | @@ -231,38 +231,6 @@ abstract class Gradient { | ||
231 | /// A list of values from 0.0 to 1.0 that denote fractions along the gradient. | 231 | /// A list of values from 0.0 to 1.0 that denote fractions along the gradient. |
232 | final List<double> stops; | 232 | final List<double> stops; |
233 | 233 | ||
234 | - PdfBaseFunction _buildFunction( | ||
235 | - Context context, | ||
236 | - List<PdfColor> colors, | ||
237 | - List<double> stops, | ||
238 | - ) { | ||
239 | - if (stops == null) { | ||
240 | - return PdfFunction( | ||
241 | - context.document, | ||
242 | - colors: colors, | ||
243 | - ); | ||
244 | - } | ||
245 | - | ||
246 | - final fn = <PdfFunction>[]; | ||
247 | - | ||
248 | - var lc = colors.first; | ||
249 | - for (final c in colors.sublist(1)) { | ||
250 | - fn.add(PdfFunction( | ||
251 | - context.document, | ||
252 | - colors: <PdfColor>[lc, c], | ||
253 | - )); | ||
254 | - lc = c; | ||
255 | - } | ||
256 | - | ||
257 | - return PdfStitchingFunction( | ||
258 | - context.document, | ||
259 | - functions: fn, | ||
260 | - bounds: stops.sublist(1, stops.length - 1), | ||
261 | - domainStart: stops.first, | ||
262 | - domainEnd: stops.last, | ||
263 | - ); | ||
264 | - } | ||
265 | - | ||
266 | void paint(Context context, PdfRect box); | 234 | void paint(Context context, PdfRect box); |
267 | } | 235 | } |
268 | 236 | ||
@@ -311,7 +279,11 @@ class LinearGradient extends Gradient { | @@ -311,7 +279,11 @@ class LinearGradient extends Gradient { | ||
311 | context.document, | 279 | context.document, |
312 | shadingType: PdfShadingType.axial, | 280 | shadingType: PdfShadingType.axial, |
313 | boundingBox: box, | 281 | boundingBox: box, |
314 | - function: _buildFunction(context, colors, stops), | 282 | + function: PdfBaseFunction.colorsAndStops( |
283 | + context.document, | ||
284 | + colors, | ||
285 | + stops, | ||
286 | + ), | ||
315 | start: begin.withinRect(box), | 287 | start: begin.withinRect(box), |
316 | end: end.withinRect(box), | 288 | end: end.withinRect(box), |
317 | extendStart: true, | 289 | extendStart: true, |
@@ -384,7 +356,11 @@ class RadialGradient extends Gradient { | @@ -384,7 +356,11 @@ class RadialGradient extends Gradient { | ||
384 | context.document, | 356 | context.document, |
385 | shadingType: PdfShadingType.radial, | 357 | shadingType: PdfShadingType.radial, |
386 | boundingBox: box, | 358 | boundingBox: box, |
387 | - function: _buildFunction(context, colors, stops), | 359 | + function: PdfBaseFunction.colorsAndStops( |
360 | + context.document, | ||
361 | + colors, | ||
362 | + stops, | ||
363 | + ), | ||
388 | start: _focal.withinRect(box), | 364 | start: _focal.withinRect(box), |
389 | end: center.withinRect(box), | 365 | end: center.withinRect(box), |
390 | radius0: focalRadius * _radius, | 366 | radius0: focalRadius * _radius, |
@@ -60,7 +60,7 @@ void printTextTtf( | @@ -60,7 +60,7 @@ void printTextTtf( | ||
60 | 60 | ||
61 | void main() { | 61 | void main() { |
62 | test('Pdf TrueType', () { | 62 | test('Pdf TrueType', () { |
63 | - final pdf = PdfDocument(compress: false); | 63 | + final pdf = PdfDocument(); |
64 | final page = PdfPage(pdf, pageFormat: const PdfPageFormat(500, 300)); | 64 | final page = PdfPage(pdf, pageFormat: const PdfPageFormat(500, 300)); |
65 | 65 | ||
66 | final g = page.getGraphics(); | 66 | final g = page.getGraphics(); |
-
Please register or login to post a comment