@@ -30,8 +30,9 @@ This file is part of the iText (R) project.
30
30
import java .util .List ;
31
31
32
32
/**
33
- * This class contains variety of methods allowing to convert iText
34
- * abstractions into the abstractions of the Clipper library and vise versa.
33
+ * This class contains a variety of methods allowing the conversion of iText
34
+ * abstractions into abstractions of the Clipper library, and vice versa.
35
+ *
35
36
* <p>
36
37
* For example:
37
38
* <ul>
@@ -41,17 +42,78 @@ This file is part of the iText (R) project.
41
42
* </ul>
42
43
*/
43
44
public final class ClipperBridge {
45
+ private static final long MAX_ALLOWED_VALUE = 0x3FFFFFFFFFFFFFL ;
44
46
45
47
/**
46
48
* Since the clipper library uses integer coordinates, we should convert
47
49
* our floating point numbers into fixed point numbers by multiplying by
48
50
* this coefficient. Vary it to adjust the preciseness of the calculations.
51
+ *
52
+ * <p>
53
+ * Note that if this value is specified, it will be used for all ClipperBridge instances and
54
+ * dynamic float multiplier calculation will be disabled.
55
+ *
49
56
*/
50
- //TODO DEVSIX-5770 make this constant a single non-static configuration
51
- public static double floatMultiplier = Math .pow (10 , 14 );
57
+ public static Double floatMultiplier ;
52
58
53
- private ClipperBridge () {
54
- //empty constructor
59
+ private double approximatedFloatMultiplier = Math .pow (10 , 14 );
60
+
61
+ /**
62
+ * Creates new {@link ClipperBridge} instance with default float multiplier value which is 10^14.
63
+ *
64
+ * <p>
65
+ * Since the clipper library uses integer coordinates, we should convert our floating point numbers into fixed
66
+ * point numbers by multiplying by float multiplier coefficient. It is possible to vary it to adjust the preciseness
67
+ * of the calculations: if static {@link #floatMultiplier} is specified, it will be used for all ClipperBridge
68
+ * instances and default value will be ignored.
69
+ */
70
+ public ClipperBridge () {
71
+ // Empty constructor.
72
+ }
73
+
74
+ /**
75
+ * Creates new {@link ClipperBridge} instance with adjusted float multiplier value. This instance will work
76
+ * correctly with the provided paths only.
77
+ *
78
+ * <p>
79
+ * Since the clipper library uses integer coordinates, we should convert our floating point numbers into fixed
80
+ * point numbers by multiplying by float multiplier coefficient. It is calculated automatically, however
81
+ * it is possible to vary it to adjust the preciseness of the calculations: if static {@link #floatMultiplier} is
82
+ * specified, it will be used for all ClipperBridge instances and automatic calculation won't work.
83
+ *
84
+ * @param paths paths to calculate multiplier coefficient to convert floating point numbers into fixed point numbers
85
+ */
86
+ public ClipperBridge (com .itextpdf .kernel .geom .Path ... paths ) {
87
+ if (floatMultiplier == null ) {
88
+ List <com .itextpdf .kernel .geom .Point > pointsList = new ArrayList <>();
89
+ for (com .itextpdf .kernel .geom .Path path : paths ) {
90
+ for (Subpath subpath : path .getSubpaths ()) {
91
+ if (!subpath .isSinglePointClosed () && !subpath .isSinglePointOpen ()) {
92
+ pointsList .addAll (subpath .getPiecewiseLinearApproximation ());
93
+ }
94
+ }
95
+ }
96
+ calculateFloatMultiplier (pointsList .toArray (new com .itextpdf .kernel .geom .Point [0 ]));
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Creates new {@link ClipperBridge} instance with adjusted float multiplier value. This instance will work
102
+ * correctly with the provided point only.
103
+ *
104
+ * <p>
105
+ * Since the clipper library uses integer coordinates, we should convert our floating point numbers into fixed
106
+ * point numbers by multiplying by float multiplier coefficient. It is calculated automatically, however
107
+ * it is possible to vary it to adjust the preciseness of the calculations: if static {@link #floatMultiplier} is
108
+ * specified, it will be used for all ClipperBridge instances and automatic calculation won't work.
109
+ *
110
+ * @param points points to calculate multiplier coefficient to convert floating point numbers
111
+ * into fixed point numbers
112
+ */
113
+ public ClipperBridge (com .itextpdf .kernel .geom .Point []... points ) {
114
+ if (floatMultiplier == null ) {
115
+ calculateFloatMultiplier (points );
116
+ }
55
117
}
56
118
57
119
/**
@@ -61,7 +123,7 @@ private ClipperBridge() {
61
123
* @param result {@link PolyTree} object to convert
62
124
* @return resultant {@link com.itextpdf.kernel.geom.Path} object
63
125
*/
64
- public static com .itextpdf .kernel .geom .Path convertToPath (PolyTree result ) {
126
+ public com .itextpdf .kernel .geom .Path convertToPath (PolyTree result ) {
65
127
com .itextpdf .kernel .geom .Path path = new com .itextpdf .kernel .geom .Path ();
66
128
PolyNode node = result .getFirst ();
67
129
@@ -79,7 +141,7 @@ public static com.itextpdf.kernel.geom.Path convertToPath(PolyTree result) {
79
141
* @param path The {@link com.itextpdf.kernel.geom.Path} object to be added to the {@link IClipper}.
80
142
* @param polyType See {@link IClipper.PolyType}.
81
143
*/
82
- public static void addPath (IClipper clipper , com .itextpdf .kernel .geom .Path path , IClipper .PolyType polyType ) {
144
+ public void addPath (IClipper clipper , com .itextpdf .kernel .geom .Path path , IClipper .PolyType polyType ) {
83
145
for (Subpath subpath : path .getSubpaths ()) {
84
146
if (!subpath .isSinglePointClosed () && !subpath .isSinglePointOpen ()) {
85
147
List <com .itextpdf .kernel .geom .Point > linearApproxPoints = subpath .getPiecewiseLinearApproximation ();
@@ -101,7 +163,8 @@ public static void addPath(IClipper clipper, com.itextpdf.kernel.geom.Path path,
101
163
* {@link IClipper.EndType#OPEN_ROUND}
102
164
* @return {@link java.util.List} consisting of all degenerate iText {@link Subpath}s of the path.
103
165
*/
104
- public static List <Subpath > addPath (ClipperOffset offset , com .itextpdf .kernel .geom .Path path , IClipper .JoinType joinType , IClipper .EndType endType ) {
166
+ public List <Subpath > addPath (ClipperOffset offset , com .itextpdf .kernel .geom .Path path , IClipper .JoinType joinType ,
167
+ IClipper .EndType endType ) {
105
168
List <Subpath > degenerateSubpaths = new ArrayList <>();
106
169
107
170
for (Subpath subpath : path .getSubpaths ()) {
@@ -135,13 +198,13 @@ public static List<Subpath> addPath(ClipperOffset offset, com.itextpdf.kernel.ge
135
198
* @param points the list of {@link Point.LongPoint} objects to convert
136
199
* @return the resultant list of {@link com.itextpdf.kernel.geom.Point} objects.
137
200
*/
138
- public static List <com .itextpdf .kernel .geom .Point > convertToFloatPoints (List <Point .LongPoint > points ) {
201
+ public List <com .itextpdf .kernel .geom .Point > convertToFloatPoints (List <Point .LongPoint > points ) {
139
202
List <com .itextpdf .kernel .geom .Point > convertedPoints = new ArrayList <>(points .size ());
140
203
141
204
for (Point .LongPoint point : points ) {
142
205
convertedPoints .add (new com .itextpdf .kernel .geom .Point (
143
- point .getX () / floatMultiplier ,
144
- point .getY () / floatMultiplier
206
+ point .getX () / getFloatMultiplier () ,
207
+ point .getY () / getFloatMultiplier ()
145
208
));
146
209
}
147
210
@@ -155,13 +218,13 @@ public static List<com.itextpdf.kernel.geom.Point> convertToFloatPoints(List<Poi
155
218
* @param points the list of {@link com.itextpdf.kernel.geom.Point} objects to convert
156
219
* @return the resultant list of {@link Point.LongPoint} objects.
157
220
*/
158
- public static List <Point .LongPoint > convertToLongPoints (List <com .itextpdf .kernel .geom .Point > points ) {
221
+ public List <Point .LongPoint > convertToLongPoints (List <com .itextpdf .kernel .geom .Point > points ) {
159
222
List <Point .LongPoint > convertedPoints = new ArrayList <>(points .size ());
160
223
161
224
for (com .itextpdf .kernel .geom .Point point : points ) {
162
225
convertedPoints .add (new Point .LongPoint (
163
- floatMultiplier * point .getX (),
164
- floatMultiplier * point .getY ()
226
+ getFloatMultiplier () * point .getX (),
227
+ getFloatMultiplier () * point .getY ()
165
228
));
166
229
}
167
230
@@ -238,7 +301,8 @@ public static IClipper.PolyFillType getFillType(int fillingRule) {
238
301
* path is a subject of clipping or a part of the clipping polygon.
239
302
* @return true if polygon path was successfully added, false otherwise.
240
303
*/
241
- public static boolean addPolygonToClipper (IClipper clipper , com .itextpdf .kernel .geom .Point [] polyVertices , IClipper .PolyType polyType ) {
304
+ public boolean addPolygonToClipper (IClipper clipper , com .itextpdf .kernel .geom .Point [] polyVertices ,
305
+ IClipper .PolyType polyType ) {
242
306
return clipper .addPath (new Path (convertToLongPoints (new ArrayList <>(Arrays .asList (polyVertices )))), polyType , true );
243
307
}
244
308
@@ -257,7 +321,7 @@ public static boolean addPolygonToClipper(IClipper clipper, com.itextpdf.kernel.
257
321
* to clipper path and added to the clipper instance.
258
322
* @return true if polyline path was successfully added, false otherwise.
259
323
*/
260
- public static boolean addPolylineSubjectToClipper (IClipper clipper , com .itextpdf .kernel .geom .Point [] lineVertices ) {
324
+ public boolean addPolylineSubjectToClipper (IClipper clipper , com .itextpdf .kernel .geom .Point [] lineVertices ) {
261
325
return clipper .addPath (new Path (convertToLongPoints (new ArrayList <>(Arrays .asList (lineVertices )))), IClipper .PolyType .SUBJECT , false );
262
326
}
263
327
@@ -267,8 +331,8 @@ public static boolean addPolylineSubjectToClipper(IClipper clipper, com.itextpdf
267
331
*
268
332
* @return the width of the rectangle.
269
333
*/
270
- public static float longRectCalculateWidth (LongRect rect ) {
271
- return (float ) (Math .abs (rect .left - rect .right ) / ClipperBridge . floatMultiplier );
334
+ public float longRectCalculateWidth (LongRect rect ) {
335
+ return (float ) (Math .abs (rect .left - rect .right ) / getFloatMultiplier () );
272
336
}
273
337
274
338
/**
@@ -277,11 +341,23 @@ public static float longRectCalculateWidth(LongRect rect) {
277
341
*
278
342
* @return the height of the rectangle.
279
343
*/
280
- public static float longRectCalculateHeight (LongRect rect ) {
281
- return (float ) (Math .abs (rect .top - rect .bottom ) / ClipperBridge .floatMultiplier );
344
+ public float longRectCalculateHeight (LongRect rect ) {
345
+ return (float ) (Math .abs (rect .top - rect .bottom ) / getFloatMultiplier ());
346
+ }
347
+
348
+ /**
349
+ * Gets multiplier coefficient for converting our floating point numbers into fixed point numbers.
350
+ *
351
+ * @return multiplier coefficient for converting our floating point numbers into fixed point numbers
352
+ */
353
+ public double getFloatMultiplier () {
354
+ if (floatMultiplier == null ) {
355
+ return approximatedFloatMultiplier ;
356
+ }
357
+ return (double ) floatMultiplier ;
282
358
}
283
359
284
- static void addContour (com .itextpdf .kernel .geom .Path path , List <Point .LongPoint > contour , boolean close ) {
360
+ void addContour (com .itextpdf .kernel .geom .Path path , List <Point .LongPoint > contour , boolean close ) {
285
361
List <com .itextpdf .kernel .geom .Point > floatContour = convertToFloatPoints (contour );
286
362
com .itextpdf .kernel .geom .Point point = floatContour .get (0 );
287
363
path .moveTo ((float ) point .getX (), (float ) point .getY ());
@@ -295,4 +371,19 @@ static void addContour(com.itextpdf.kernel.geom.Path path, List<Point.LongPoint>
295
371
path .closeSubpath ();
296
372
}
297
373
}
374
+
375
+ private void calculateFloatMultiplier (com .itextpdf .kernel .geom .Point []... points ) {
376
+ double maxPoint = 0 ;
377
+ for (com .itextpdf .kernel .geom .Point [] pointsArray : points ) {
378
+ for (com .itextpdf .kernel .geom .Point point : pointsArray ) {
379
+ maxPoint = Math .max (maxPoint , Math .abs (point .getX ()));
380
+ maxPoint = Math .max (maxPoint , Math .abs (point .getY ()));
381
+ }
382
+ }
383
+ // The significand of the double type is approximately 15 to 17 decimal digits for most platforms.
384
+ double epsilon = 1E-16 ;
385
+ if (maxPoint > epsilon ) {
386
+ this .approximatedFloatMultiplier = Math .floor (MAX_ALLOWED_VALUE / maxPoint );
387
+ }
388
+ }
298
389
}
0 commit comments