David PHAM-VAN

Improve gradient functions

@@ -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);
  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();