David PHAM-VAN

Improve gradient functions

... ... @@ -13,6 +13,7 @@
- Opt-out from dart library
- Improve graphic operations
- Automatically calculate Shape() bounding box
- Improve gradient functions
## 1.12.0
... ...
... ... @@ -32,6 +32,7 @@ export 'src/info.dart';
export 'src/outline.dart';
export 'src/page.dart';
export 'src/page_format.dart';
export 'src/pattern.dart';
export 'src/point.dart';
export 'src/rect.dart';
export 'src/shading.dart';
... ...
... ... @@ -258,7 +258,7 @@ class PdfSecString extends PdfString {
}
class PdfName extends PdfDataType {
const PdfName(this.value);
const PdfName(this.value) : assert(value != null);
final String value;
... ...
... ... @@ -24,35 +24,103 @@ import 'object_stream.dart';
abstract class PdfBaseFunction extends PdfObject {
PdfBaseFunction(PdfDocument pdfDocument) : super(pdfDocument);
factory PdfBaseFunction.colorsAndStops(
PdfDocument pdfDocument,
List<PdfColor> colors, [
List<double> stops,
]) {
if (stops == null || stops.isEmpty) {
return PdfFunction.fromColors(pdfDocument, colors);
}
final _colors = List<PdfColor>.from(colors);
final _stops = List<double>.from(stops);
final fn = <PdfFunction>[];
var lc = _colors.first;
if (_stops[0] > 0) {
_colors.insert(0, lc);
_stops.insert(0, 0);
}
if (_stops.last < 1) {
_colors.add(_colors.last);
_stops.add(1);
}
if (_stops.length != _colors.length) {
throw Exception(
'The number of colors in a gradient must match the number of stops');
}
for (final c in _colors.sublist(1)) {
fn.add(PdfFunction.fromColors(pdfDocument, <PdfColor>[lc, c]));
lc = c;
}
return PdfStitchingFunction(
pdfDocument,
functions: fn,
bounds: _stops.sublist(1, _stops.length - 1),
domainStart: 0,
domainEnd: 1,
);
}
}
class PdfFunction extends PdfObjectStream implements PdfBaseFunction {
PdfFunction(
PdfDocument pdfDocument, {
this.colors,
this.data,
this.bitsPerSample = 8,
this.order = 1,
this.domain = const <num>[0, 1],
this.range = const <num>[0, 1],
}) : super(pdfDocument);
final List<PdfColor> colors;
@override
void prepare() {
factory PdfFunction.fromColors(
PdfDocument pdfDocument, List<PdfColor> colors) {
final data = <int>[];
for (final color in colors) {
buf.putBytes(<int>[
(color.red * 255.0).round() & 0xff,
(color.green * 255.0).round() & 0xff,
(color.blue * 255.0).round() & 0xff,
]);
data.add((color.red * 255.0).round() & 0xff);
data.add((color.green * 255.0).round() & 0xff);
data.add((color.blue * 255.0).round() & 0xff);
}
return PdfFunction(
pdfDocument,
order: 3,
data: data,
range: const <num>[0, 1, 0, 1, 0, 1],
);
}
final List<int> data;
final int bitsPerSample;
final int order;
final List<num> domain;
final List<num> range;
@override
void prepare() {
buf.putBytes(data);
super.prepare();
params['/FunctionType'] = const PdfNum(0);
params['/BitsPerSample'] = const PdfNum(8);
params['/Order'] = const PdfNum(3);
params['/Domain'] = PdfArray.fromNum(const <num>[0, 1]);
params['/Range'] = PdfArray.fromNum(const <num>[0, 1, 0, 1, 0, 1]);
params['/Size'] = PdfArray.fromNum(<int>[colors.length]);
params['/BitsPerSample'] = PdfNum(bitsPerSample);
params['/Order'] = PdfNum(order);
params['/Domain'] = PdfArray.fromNum(domain);
params['/Range'] = PdfArray.fromNum(range);
params['/Size'] = PdfArray.fromNum(<int>[data.length ~/ order]);
}
@override
String toString() => '$runtimeType $bitsPerSample $order $data';
}
class PdfStitchingFunction extends PdfBaseFunction {
... ... @@ -86,4 +154,8 @@ class PdfStitchingFunction extends PdfBaseFunction {
params['/Encode'] = PdfArray.fromNum(
List<int>.generate(functions.length * 2, (int i) => i % 2));
}
@override
String toString() =>
'$runtimeType $domainStart $bounds $domainEnd $functions';
}
... ...
... ... @@ -31,8 +31,7 @@ class PdfGraphicState {
/// The opacity to apply to this graphic state
final double opacity;
@protected
PdfDict _output() {
PdfDict output() {
final params = PdfDict();
if (opacity != null) {
... ... @@ -79,7 +78,7 @@ class PdfGraphicStates extends PdfObject {
super.prepare();
for (var index = 0; index < _states.length; index++) {
params['$_prefix$index'] = _states[index]._output();
params['$_prefix$index'] = _states[index].output();
}
}
}
... ...
... ... @@ -19,6 +19,7 @@ import 'document.dart';
import 'font.dart';
import 'graphic_state.dart';
import 'object.dart';
import 'pattern.dart';
import 'shading.dart';
import 'xobject.dart';
... ... @@ -37,13 +38,16 @@ mixin PdfGraphicStream on PdfObject {
bool knockoutTransparency = false;
/// The fonts associated with this page
final Map<String, PdfFont> fonts = <String, PdfFont>{};
final fonts = <String, PdfFont>{};
/// The fonts associated with this page
final Map<String, PdfShading> shading = <String, PdfShading>{};
/// The shaders associated with this page
final shading = <String, PdfShading>{};
/// The shaders associated with this page
final patterns = <String, PdfPattern>{};
/// The xobjects or other images in the pdf
final Map<String, PdfXObject> xObjects = <String, PdfXObject>{};
final xObjects = <String, PdfXObject>{};
/// Add a font to this graphic object
void addFont(PdfFont font) {
... ... @@ -59,6 +63,13 @@ mixin PdfGraphicStream on PdfObject {
}
}
/// Add a pattern to this graphic object
void addPattern(PdfPattern pattern) {
if (!patterns.containsKey(pattern.name)) {
patterns[pattern.name] = pattern;
}
}
/// Add an XObject to this graphic object
void addXObject(PdfXObject object) {
if (!xObjects.containsKey(object.name)) {
... ... @@ -99,11 +110,16 @@ mixin PdfGraphicStream on PdfObject {
resources['/Font'] = PdfDict.fromObjectMap(fonts);
}
// shading
// shaders
if (shading.isNotEmpty) {
resources['/Shading'] = PdfDict.fromObjectMap(shading);
}
// patterns
if (patterns.isNotEmpty) {
resources['/Pattern'] = PdfDict.fromObjectMap(patterns);
}
// Now the XObjects
if (xObjects.isNotEmpty) {
resources['/XObject'] = PdfDict.fromObjectMap(xObjects);
... ...
... ... @@ -28,6 +28,7 @@ import 'graphic_state.dart';
import 'graphic_stream.dart';
import 'image.dart';
import 'page.dart';
import 'pattern.dart';
import 'rect.dart';
import 'shading.dart';
import 'stream.dart';
... ... @@ -86,10 +87,14 @@ enum PdfTextRenderingMode {
@immutable
class _PdfGraphicsContext {
const _PdfGraphicsContext({@required this.ctm}) : assert(ctm != null);
const _PdfGraphicsContext({
@required this.ctm,
}) : assert(ctm != null);
final Matrix4 ctm;
_PdfGraphicsContext copy() => _PdfGraphicsContext(ctm: ctm.clone());
_PdfGraphicsContext copy() => _PdfGraphicsContext(
ctm: ctm.clone(),
);
}
/// Pdf drawing operations
... ... @@ -346,6 +351,20 @@ class PdfGraphics {
}
}
/// Sets the fill pattern for drawing
void setFillPattern(PdfPattern pattern) {
// The shader needs to be registered in the page resources
_page.addPattern(pattern);
buf.putString('/Pattern cs${pattern.name} scn\n');
}
/// Sets the stroke pattern for drawing
void setStrokePattern(PdfPattern pattern) {
// The shader needs to be registered in the page resources
_page.addPattern(pattern);
buf.putString('/Pattern CS${pattern.name} SCN\n');
}
/// Set the graphic state for drawing
void setGraphicState(PdfGraphicState state) {
final name = _page.stateName(state);
... ...
/*
* Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:meta/meta.dart';
import 'package:pdf/src/data_types.dart';
import 'package:vector_math/vector_math_64.dart';
import 'document.dart';
import 'graphic_state.dart';
import 'object.dart';
import 'shading.dart';
abstract class PdfPattern extends PdfObject {
PdfPattern(PdfDocument pdfDocument, this.patternType, this.matrix)
: super(pdfDocument);
/// Name of the Pattern object
String get name => '/P$objser';
final int patternType;
final Matrix4 matrix;
@override
void prepare() {
super.prepare();
params['/PatternType'] = PdfNum(patternType);
if (matrix != null) {
final s = matrix.storage;
params['/Matrix'] =
PdfArray.fromNum(<double>[s[0], s[1], s[4], s[5], s[12], s[13]]);
}
}
}
class PdfShadingPattern extends PdfPattern {
PdfShadingPattern(
PdfDocument pdfDocument, {
@required this.shading,
Matrix4 matrix,
this.graphicState,
}) : assert(shading != null),
super(pdfDocument, 2, matrix);
final PdfShading shading;
final PdfGraphicState graphicState;
@override
void prepare() {
super.prepare();
params['/Shading'] = shading.ref();
if (graphicState != null) {
params['/ExtGState'] = graphicState.output();
}
}
}
... ...
... ... @@ -231,38 +231,6 @@ abstract class Gradient {
/// A list of values from 0.0 to 1.0 that denote fractions along the gradient.
final List<double> stops;
PdfBaseFunction _buildFunction(
Context context,
List<PdfColor> colors,
List<double> stops,
) {
if (stops == null) {
return PdfFunction(
context.document,
colors: colors,
);
}
final fn = <PdfFunction>[];
var lc = colors.first;
for (final c in colors.sublist(1)) {
fn.add(PdfFunction(
context.document,
colors: <PdfColor>[lc, c],
));
lc = c;
}
return PdfStitchingFunction(
context.document,
functions: fn,
bounds: stops.sublist(1, stops.length - 1),
domainStart: stops.first,
domainEnd: stops.last,
);
}
void paint(Context context, PdfRect box);
}
... ... @@ -311,7 +279,11 @@ class LinearGradient extends Gradient {
context.document,
shadingType: PdfShadingType.axial,
boundingBox: box,
function: _buildFunction(context, colors, stops),
function: PdfBaseFunction.colorsAndStops(
context.document,
colors,
stops,
),
start: begin.withinRect(box),
end: end.withinRect(box),
extendStart: true,
... ... @@ -384,7 +356,11 @@ class RadialGradient extends Gradient {
context.document,
shadingType: PdfShadingType.radial,
boundingBox: box,
function: _buildFunction(context, colors, stops),
function: PdfBaseFunction.colorsAndStops(
context.document,
colors,
stops,
),
start: _focal.withinRect(box),
end: center.withinRect(box),
radius0: focalRadius * _radius,
... ...
... ... @@ -60,7 +60,7 @@ void printTextTtf(
void main() {
test('Pdf TrueType', () {
final pdf = PdfDocument(compress: false);
final pdf = PdfDocument();
final page = PdfPage(pdf, pageFormat: const PdfPageFormat(500, 300));
final g = page.getGraphics();
... ...