ClipperBridgeTest.java
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2025 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.kernel.pdf.canvas.parser.clipper;
import com.itextpdf.kernel.geom.IShape;
import com.itextpdf.kernel.geom.Line;
import com.itextpdf.kernel.geom.Path;
import com.itextpdf.kernel.geom.Point;
import com.itextpdf.kernel.geom.Subpath;
import com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants.LineCapStyle;
import com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants.LineJoinStyle;
import com.itextpdf.kernel.pdf.canvas.parser.clipper.IClipper.ClipType;
import com.itextpdf.kernel.pdf.canvas.parser.clipper.IClipper.EndType;
import com.itextpdf.kernel.pdf.canvas.parser.clipper.IClipper.JoinType;
import com.itextpdf.kernel.pdf.canvas.parser.clipper.IClipper.PolyType;
import com.itextpdf.test.ExtendedITextTest;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;
@Tag("UnitTest")
public class ClipperBridgeTest extends ExtendedITextTest {
@Test
public void squareClippingTest() {
Subpath squareSubpath = new Subpath(new com.itextpdf.kernel.geom.Point(10, 10));
squareSubpath.addSegment(new Line(10, 10, 10, 30));
squareSubpath.addSegment(new Line(10, 30, 30, 30));
squareSubpath.addSegment(new Line(30, 30, 30, 10));
squareSubpath.addSegment(new Line(30, 10, 10, 10));
squareSubpath.setClosed(true);
Path squarePath = new Path();
squarePath.addSubpath(squareSubpath);
Subpath rectangleSubpath = new Subpath(new com.itextpdf.kernel.geom.Point(20, 20));
rectangleSubpath.addSegment(new Line(20, 20, 20, 40));
rectangleSubpath.addSegment(new Line(20, 40, 30, 40));
rectangleSubpath.addSegment(new Line(30, 40, 30, 20));
rectangleSubpath.addSegment(new Line(30, 20, 20, 20));
rectangleSubpath.setClosed(true);
Path rectanglePath = new Path();
rectanglePath.addSubpath(rectangleSubpath);
DefaultClipper clipper = new DefaultClipper();
ClipperBridge clipperBridge = new ClipperBridge(squarePath, rectanglePath);
clipperBridge.addPath(clipper, squarePath, PolyType.SUBJECT);
clipperBridge.addPath(clipper, rectanglePath, PolyType.CLIP);
PolyTree polyTree = new PolyTree();
clipper.execute(ClipType.UNION, polyTree);
Path result = clipperBridge.convertToPath(polyTree);
Assertions.assertEquals(new com.itextpdf.kernel.geom.Point(20, 40), result.getCurrentPoint());
Assertions.assertEquals(2, result.getSubpaths().size());
Subpath closedPath = result.getSubpaths().get(0);
Assertions.assertEquals(new com.itextpdf.kernel.geom.Point(20, 40), closedPath.getStartPoint());
List<IShape> closedPartSegments = closedPath.getSegments();
Assertions.assertEquals(5, closedPartSegments.size());
Assertions.assertTrue(areShapesEqual(new Line(20, 40, 20, 30), closedPartSegments.get(0)));
Assertions.assertTrue(areShapesEqual(new Line(20, 30, 10, 30), closedPartSegments.get(1)));
Assertions.assertTrue(areShapesEqual(new Line(10, 30, 10, 10), closedPartSegments.get(2)));
Assertions.assertTrue(areShapesEqual(new Line(10, 10, 30, 10), closedPartSegments.get(3)));
Assertions.assertTrue(areShapesEqual(new Line(30, 10, 30, 40), closedPartSegments.get(4)));
Assertions.assertTrue(closedPath.isClosed());
Subpath openPart = result.getSubpaths().get(1);
Assertions.assertEquals(new com.itextpdf.kernel.geom.Point(20, 40), openPart.getStartPoint());
Assertions.assertEquals(0, openPart.getSegments().size());
Assertions.assertFalse(openPart.isClosed());
}
@Test
public void getJoinTypeTest() {
Assertions.assertEquals(JoinType.BEVEL, ClipperBridge.getJoinType(LineJoinStyle.BEVEL));
Assertions.assertEquals(JoinType.MITER, ClipperBridge.getJoinType(LineJoinStyle.MITER));
Assertions.assertEquals(JoinType.ROUND, ClipperBridge.getJoinType(LineJoinStyle.ROUND));
}
@Test
public void getEndTypeTest() {
Assertions.assertEquals(EndType.OPEN_BUTT, ClipperBridge.getEndType(LineCapStyle.BUTT));
Assertions.assertEquals(EndType.OPEN_SQUARE, ClipperBridge.getEndType(LineCapStyle.PROJECTING_SQUARE));
Assertions.assertEquals(EndType.OPEN_ROUND, ClipperBridge.getEndType(LineCapStyle.ROUND));
}
@Test
public void longRectWidthTest() {
LongRect longRect = new LongRect(14900000000000000L, 21275000000000000L, 71065802001953128L, 71075000000000000L);
Assertions.assertEquals(561.658, new ClipperBridge().longRectCalculateWidth(longRect), 0.001f);
}
@Test
public void longRectHeightTest() {
LongRect longRect = new LongRect(14900000000000000L, 21275000000000000L, 71065802001953128L, 71075000000000000L);
Assertions.assertEquals(498, new ClipperBridge().longRectCalculateHeight(longRect), 0.001f);
}
@Test
public void dynamicFloatMultiplierCalculationsSmallValuesTest() {
Point[] points = new Point[]{
new Point(1e-10, 0),
new Point(0, 1e-13)
};
Assertions.assertEquals(1.8014398509481984e26, new ClipperBridge(points).getFloatMultiplier(), 0e+10);
}
@Test
public void dynamicFloatMultiplierCalculationsBigValuesTest() {
Point[] points = new Point[]{
new Point(1e+11, 10),
new Point(10, 1e+10)
};
Assertions.assertEquals(180143, new ClipperBridge(points).getFloatMultiplier(), 0.001f);
}
@Test
public void smallFloatMultiplierCoefficientTest() {
Point[] points = new Point[]{new Point(1e-10, 1e+10)};
Assertions.assertEquals(
new com.itextpdf.kernel.pdf.canvas.parser.clipper.Point.LongPoint(0, 18014390000000000L),
new ClipperBridge(points).convertToLongPoints(Arrays.asList(points)).get(0));
}
private boolean areShapesEqual(IShape expected, IShape actual) {
if (expected == actual) {
return true;
}
if (actual == null || expected.getClass() != actual.getClass()) {
return false;
}
return expected.getBasePoints().equals(actual.getBasePoints());
}
}