David PHAM-VAN

Automatically calculate Shape() bounding box

... ... @@ -12,6 +12,7 @@
- Add support for Icon Fonts (MaterialIcons)
- Opt-out from dart library
- Improve graphic operations
- Automatically calculate Shape() bounding box
## 1.12.0
... ...
... ... @@ -28,6 +28,7 @@ import 'graphic_state.dart';
import 'graphic_stream.dart';
import 'image.dart';
import 'page.dart';
import 'rect.dart';
import 'shading.dart';
import 'stream.dart';
... ... @@ -528,6 +529,13 @@ class PdfGraphics {
writeSvgPathDataToPath(d, proxy);
}
/// Calculates the bounding box of an SVG path
static PdfRect shapeBoundingBox(String d) {
final proxy = _PathBBProxy();
writeSvgPathDataToPath(d, proxy);
return proxy.box;
}
/// Set line starting and ending cap type
void setLineCap(PdfLineCap cap) {
buf.putString('${cap.index} J\n');
... ... @@ -586,3 +594,112 @@ class _PathProxy extends PathProxy {
canvas.moveTo(x, y);
}
}
class _PathBBProxy extends PathProxy {
_PathBBProxy();
var _xMin = double.infinity;
var _yMin = double.infinity;
var _xMax = double.negativeInfinity;
var _yMax = double.negativeInfinity;
var _pX = 0.0;
var _pY = 0.0;
PdfRect get box {
if (_xMin > _xMax || _yMin > _yMax) {
return PdfRect.zero;
}
return PdfRect.fromLTRB(_xMin, _yMin, _xMax, _yMax);
}
@override
void close() {}
@override
void cubicTo(
double x1, double y1, double x2, double y2, double x3, double y3) {
final tvalues = <double>[];
double a, b, c, t, t1, t2, b2ac, sqrtb2ac;
for (var i = 0; i < 2; ++i) {
if (i == 0) {
b = 6 * _pX - 12 * x1 + 6 * x2;
a = -3 * _pX + 9 * x1 - 9 * x2 + 3 * x3;
c = 3 * x1 - 3 * _pX;
} else {
b = 6 * _pY - 12 * y1 + 6 * y2;
a = -3 * _pY + 9 * y1 - 9 * y2 + 3 * y3;
c = 3 * y1 - 3 * _pY;
}
if (a.abs() < 1e-12) {
if (b.abs() < 1e-12) {
continue;
}
t = -c / b;
if (0 < t && t < 1) {
tvalues.add(t);
}
continue;
}
b2ac = b * b - 4 * c * a;
if (b2ac < 0) {
if (b2ac.abs() < 1e-12) {
t = -b / (2 * a);
if (0 < t && t < 1) {
tvalues.add(t);
}
}
continue;
}
sqrtb2ac = math.sqrt(b2ac);
t1 = (-b + sqrtb2ac) / (2 * a);
if (0 < t1 && t1 < 1) {
tvalues.add(t1);
}
t2 = (-b - sqrtb2ac) / (2 * a);
if (0 < t2 && t2 < 1) {
tvalues.add(t2);
}
}
for (final t in tvalues) {
final mt = 1 - t;
_updateMinMax(
(mt * mt * mt * _pX) +
(3 * mt * mt * t * x1) +
(3 * mt * t * t * x2) +
(t * t * t * x3),
(mt * mt * mt * _pY) +
(3 * mt * mt * t * y1) +
(3 * mt * t * t * y2) +
(t * t * t * y3));
}
_updateMinMax(_pX, _pY);
_updateMinMax(x3, y3);
_pX = x3;
_pY = y3;
}
@override
void lineTo(double x, double y) {
_pX = x;
_pY = y;
_updateMinMax(x, y);
}
@override
void moveTo(double x, double y) {
_pX = x;
_pY = y;
_updateMinMax(x, y);
}
void _updateMinMax(double x, double y) {
_xMin = math.min(_xMin, x);
_yMin = math.min(_yMin, y);
_xMax = math.max(_xMax, x);
_yMax = math.max(_yMax, y);
}
}
... ...
... ... @@ -154,12 +154,11 @@ class Shape extends Widget {
this.shape, {
this.strokeColor,
this.fillColor,
this.width = 1.0,
this.height = 1.0,
this.width,
this.height,
this.fit = BoxFit.contain,
}) : assert(width != null && width > 0.0),
assert(height != null && height > 0.0),
aspectRatio = height / width;
}) : assert(width == null || width > 0.0),
assert(height == null || height > 0.0);
final String shape;
... ... @@ -171,34 +170,49 @@ class Shape extends Widget {
final double height;
final double aspectRatio;
final BoxFit fit;
PdfRect _boundingBox;
@override
void layout(Context context, BoxConstraints constraints,
{bool parentUsesSize = false}) {
if (width == null || height == null) {
// Compute the bounding box
_boundingBox = PdfGraphics.shapeBoundingBox(shape);
} else {
_boundingBox = PdfRect(0, 0, width, height);
}
final w = constraints.hasBoundedWidth
? constraints.maxWidth
: constraints.constrainWidth(width);
: constraints.constrainWidth(_boundingBox.width);
final h = constraints.hasBoundedHeight
? constraints.maxHeight
: constraints.constrainHeight(height);
: constraints.constrainHeight(_boundingBox.height);
final sizes = applyBoxFit(fit, PdfPoint(width, height), PdfPoint(w, h));
box = PdfRect.fromPoints(PdfPoint.zero, sizes.destination);
final sizes = applyBoxFit(fit, _boundingBox.size, PdfPoint(w, h));
box = PdfRect.fromPoints(
PdfPoint.zero,
sizes.destination,
);
}
@override
void paint(Context context) {
super.paint(context);
final mat = Matrix4.identity();
mat.translate(box.x, box.y + box.height);
mat.scale(box.width / width, -box.height / height);
context.canvas
..saveContext()
..setTransform(mat);
..setTransform(
Matrix4.identity()
..translate(box.x, box.y + box.height)
..scale(
box.width / _boundingBox.width,
-box.height / _boundingBox.height,
)
..translate(-_boundingBox.x, -_boundingBox.y),
);
if (fillColor != null) {
context.canvas
... ...