Skip to content

Commit be68535

Browse files
ar3emiText-CI
authored andcommitted
Use double type for better precision in local calculations in SVG and SXP
ITXT-CR-1425
1 parent 96f2918 commit be68535

File tree

126 files changed

+117
-36
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+117
-36
lines changed

styled-xml-parser/src/main/java/com/itextpdf/styledxmlparser/css/util/CssUtils.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -187,41 +187,42 @@ public static float parseAbsoluteLength(String length, String defaultMetric) {
187187
throw new StyledXMLParserException(MessageFormatUtil.format(LogMessageConstant.NAN, length));
188188
}
189189

190-
float f = Float.parseFloat(length.substring(0, pos));
190+
// Use double type locally to have better precision of the result after applying arithmetic operations
191+
double f = Double.parseDouble(length.substring(0, pos));
191192
String unit = length.substring(pos);
192193

193194
//points
194195
if (unit.startsWith(CommonCssConstants.PT) || unit.equals("") && defaultMetric.equals(CommonCssConstants.PT)) {
195-
return f;
196+
return (float) f;
196197
}
197198
// inches
198199
if (unit.startsWith(CommonCssConstants.IN) || (unit.equals("") && defaultMetric.equals(CommonCssConstants.IN))) {
199-
return f * 72f;
200+
return (float) (f * 72);
200201
}
201202
// centimeters
202203
else if (unit.startsWith(CommonCssConstants.CM) || (unit.equals("") && defaultMetric.equals(CommonCssConstants.CM))) {
203-
return (f / 2.54f) * 72f;
204+
return (float) ((f / 2.54) * 72);
204205
}
205206
// quarter of a millimeter (1/40th of a centimeter).
206207
else if (unit.startsWith(CommonCssConstants.Q) || (unit.equals("") && defaultMetric.equals(CommonCssConstants.Q))) {
207-
return (f / 2.54f) * 72f / 40;
208+
return (float) ((f / 2.54) * 72 / 40);
208209
}
209210
// millimeters
210211
else if (unit.startsWith(CommonCssConstants.MM) || (unit.equals("") && defaultMetric.equals(CommonCssConstants.MM))) {
211-
return (f / 25.4f) * 72f;
212+
return (float) ((f / 25.4) * 72);
212213
}
213214
// picas
214215
else if (unit.startsWith(CommonCssConstants.PC) || (unit.equals("") && defaultMetric.equals(CommonCssConstants.PC))) {
215-
return f * 12f;
216+
return (float) (f * 12);
216217
}
217218
// pixels (1px = 0.75pt).
218219
else if (unit.startsWith(CommonCssConstants.PX) || (unit.equals("") && defaultMetric.equals(CommonCssConstants.PX))) {
219-
return f * 0.75f;
220+
return (float) (f * 0.75);
220221
}
221222

222223
Logger logger = LoggerFactory.getLogger(CssUtils.class);
223224
logger.error(MessageFormatUtil.format(LogMessageConstant.UNKNOWN_ABSOLUTE_METRIC_LENGTH_PARSED, unit.equals("") ? defaultMetric : unit));
224-
return f;
225+
return (float) f;
225226
}
226227

227228
/**
@@ -246,6 +247,7 @@ public static float parseRelativeValue(final String relativeValue, final float b
246247
int pos = determinePositionBetweenValueAndUnit(relativeValue);
247248
if (pos == 0)
248249
return 0f;
250+
// Use double type locally to have better precision of the result after applying arithmetic operations
249251
double f = Double.parseDouble(relativeValue.substring(0, pos));
250252
String unit = relativeValue.substring(pos);
251253
if (unit.startsWith(CommonCssConstants.PERCENTAGE)) {
@@ -367,14 +369,14 @@ public static float parseResolution(String resolutionStr) {
367369
int pos = determinePositionBetweenValueAndUnit(resolutionStr);
368370
if (pos == 0)
369371
return 0f;
370-
float f = Float.parseFloat(resolutionStr.substring(0, pos));
372+
double f = Double.parseDouble(resolutionStr.substring(0, pos));
371373
String unit = resolutionStr.substring(pos);
372374
if (unit.startsWith(CommonCssConstants.DPCM)) {
373-
f *= 2.54f;
375+
f *= 2.54;
374376
} else if (unit.startsWith(CommonCssConstants.DPPX)) {
375377
f *= 96;
376378
}
377-
return f;
379+
return (float) f;
378380
}
379381

380382
/**

styled-xml-parser/src/test/java/com/itextpdf/styledxmlparser/css/util/CssUtilTest.java renamed to styled-xml-parser/src/test/java/com/itextpdf/styledxmlparser/css/util/CssUtilsTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ This file is part of the iText (R) project.
6060
import static org.junit.Assert.assertNull;
6161

6262
@Category(UnitTest.class)
63-
public class CssUtilTest extends ExtendedITextTest {
63+
public class CssUtilsTest extends ExtendedITextTest {
6464

6565
public static float EPS = 0.0001f;
6666

@@ -249,4 +249,21 @@ public void parseInvalidFloat() {
249249
Assert.fail();
250250
}
251251
}
252+
253+
@Test
254+
public void parseAbsoluteLength12cmTest() {
255+
// Calculations in CssUtils#parseAbsoluteLength were changed to work
256+
// with double values instead of float to improve precision and eliminate
257+
// the difference between java and .net. So the test verifies this fix.
258+
assertEquals(340.15747f, CssUtils.parseAbsoluteLength("12cm"), 0f);
259+
}
260+
261+
262+
@Test
263+
public void parseAbsoluteLength12qTest() {
264+
// Calculations in CssUtils#parseAbsoluteLength were changed to work
265+
// with double values instead of float to improve precision and eliminate
266+
// the difference between java and .net. So the test verifies this fix
267+
assertEquals(8.503937f, CssUtils.parseAbsoluteLength("12q"), 0f);
268+
}
252269
}

svg/src/main/java/com/itextpdf/svg/renderers/impl/EllipseSvgNodeRenderer.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,37 +60,43 @@ public class EllipseSvgNodeRenderer extends AbstractSvgNodeRenderer {
6060
protected void doDraw(SvgDrawContext context) {
6161
PdfCanvas cv = context.getCurrentCanvas();
6262
cv.writeLiteral("% ellipse\n");
63-
if(setParameters()) {
64-
cv.moveTo(cx + rx, cy);
65-
DrawUtils.arc(cx - rx, cy - ry, cx + rx, cy + ry, 0, 360, cv);
63+
if (setParameters()) {
64+
// Use double type locally to have better precision of the result after applying arithmetic operations
65+
cv.moveTo((double) cx + (double) rx, cy);
66+
DrawUtils.arc((double) cx - (double) rx, (double) cy - (double) ry, (double) cx + (double) rx,
67+
(double) cy + (double) ry, 0, 360, cv);
6668
}
6769
}
6870

6971
/**
7072
* Fetches a map of String values by calling getAttribute(Strng s) method
7173
* and maps it's values to arc parmateter cx, cy , rx, ry respectively
74+
*
7275
* @return boolean values to indicate whether all values exit or not
7376
*/
74-
protected boolean setParameters(){
75-
cx=0; cy=0;
76-
if(getAttribute(SvgConstants.Attributes.CX) != null){
77+
protected boolean setParameters() {
78+
cx = 0;
79+
cy = 0;
80+
if (getAttribute(SvgConstants.Attributes.CX) != null) {
7781
cx = CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CX));
7882
}
79-
if(getAttribute(SvgConstants.Attributes.CY) != null){
83+
if (getAttribute(SvgConstants.Attributes.CY) != null) {
8084
cy = CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.CY));
8185
}
8286

83-
if(getAttribute(SvgConstants.Attributes.RX) != null
84-
&& CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.RX)) >0){
87+
if (getAttribute(SvgConstants.Attributes.RX) != null
88+
&& CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.RX)) > 0) {
8589
rx = CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.RX));
86-
}else{
87-
return false; //No drawing if rx is absent
90+
} else {
91+
//No drawing if rx is absent
92+
return false;
8893
}
89-
if(getAttribute(SvgConstants.Attributes.RY) != null
90-
&&CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.RY)) >0){
94+
if (getAttribute(SvgConstants.Attributes.RY) != null
95+
&& CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.RY)) > 0) {
9196
ry = CssUtils.parseAbsoluteLength(getAttribute(SvgConstants.Attributes.RY));
92-
}else{
93-
return false; //No drawing if ry is absent
97+
} else {
98+
//No drawing if ry is absent
99+
return false;
94100
}
95101
return true;
96102
}
@@ -102,6 +108,4 @@ public ISvgNodeRenderer createDeepCopy() {
102108
return copy;
103109
}
104110

105-
106-
107111
}

svg/src/main/java/com/itextpdf/svg/utils/DrawUtils.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ private DrawUtils() {}
5757
* Draw an arc on the passed canvas,
5858
* enclosed by the rectangle for which two opposite corners are specified.
5959
* The arc starts at the passed starting angle and extends to the starting angle + extent
60+
* @deprecated In {@link PdfCanvas} most of the path drawing methods accept {@code double}.
61+
* So it is preferable to use {@link DrawUtils#arc(double, double, double, double, double, double, PdfCanvas)}.
62+
* This method will be removed in iText 7.2
6063
* @param x1 corner-coordinate of the enclosing rectangle, first corner
6164
* @param y1 corner-coordinate of the enclosing rectangle, first corner
6265
* @param x2 corner-coordinate of the enclosing rectangle, second corner
@@ -65,12 +68,27 @@ private DrawUtils() {}
6568
* @param extent extent of the arc
6669
* @param cv canvas to paint on
6770
*/
71+
@Deprecated
6872
public static void arc(final float x1, final float y1, final float x2, final float y2, final float startAng, final float extent, PdfCanvas cv) {
73+
arc((double) x1, (double) y1, (double) x2, (double) y2, (double) startAng, (double) extent, cv);
74+
}
75+
76+
/**
77+
* Draw an arc on the passed canvas,
78+
* enclosed by the rectangle for which two opposite corners are specified.
79+
* The arc starts at the passed starting angle and extends to the starting angle + extent
80+
* @param x1 corner-coordinate of the enclosing rectangle, first corner
81+
* @param y1 corner-coordinate of the enclosing rectangle, first corner
82+
* @param x2 corner-coordinate of the enclosing rectangle, second corner
83+
* @param y2 corner-coordinate of the enclosing rectangle, second corner
84+
* @param startAng starting angle in degrees
85+
* @param extent extent of the arc
86+
* @param cv canvas to paint on
87+
*/
88+
public static void arc(final double x1, final double y1, final double x2, final double y2, final double startAng, final double extent, PdfCanvas cv) {
6989
List<double[]> ar = PdfCanvas.bezierArc(x1, y1, x2, y2, startAng, extent);
7090
if (!ar.isEmpty()) {
71-
double pt[];
72-
for (int k = 0; k < ar.size(); ++k) {
73-
pt = ar.get(k);
91+
for (double[] pt : ar) {
7492
cv.curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
7593
}
7694
}

svg/src/test/java/com/itextpdf/svg/renderers/impl/EllipseNodeRendererIntegrationTest.java renamed to svg/src/test/java/com/itextpdf/svg/renderers/impl/EllipseSvgNodeRendererIntegrationTest.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,26 @@ This file is part of the iText (R) project.
4343
package com.itextpdf.svg.renderers.impl;
4444

4545
import com.itextpdf.io.IOException;
46+
import com.itextpdf.kernel.pdf.PdfDocument;
47+
import com.itextpdf.kernel.pdf.PdfWriter;
48+
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
49+
import com.itextpdf.svg.SvgConstants;
50+
import com.itextpdf.svg.renderers.SvgDrawContext;
4651
import com.itextpdf.svg.renderers.SvgIntegrationTest;
4752
import com.itextpdf.test.ITextTest;
4853
import com.itextpdf.test.annotations.type.IntegrationTest;
4954

55+
import java.nio.charset.StandardCharsets;
56+
import org.junit.Assert;
5057
import org.junit.BeforeClass;
5158
import org.junit.Test;
5259
import org.junit.experimental.categories.Category;
5360

5461
@Category(IntegrationTest.class)
55-
public class EllipseNodeRendererIntegrationTest extends SvgIntegrationTest {
62+
public class EllipseSvgNodeRendererIntegrationTest extends SvgIntegrationTest {
5663

57-
public static final String sourceFolder = "./src/test/resources/com/itextpdf/svg/renderers/impl/EllipseSvgNodeRendererTest/";
58-
public static final String destinationFolder = "./target/test/com/itextpdf/svg/renderers/impl/EllipseSvgNodeRendererTest/";
64+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/svg/renderers/impl/EllipseSvgNodeRendererIntegrationTest/";
65+
public static final String destinationFolder = "./target/test/com/itextpdf/svg/renderers/impl/EllipseSvgNodeRendererIntegrationTest/";
5966

6067
@BeforeClass
6168
public static void beforeClass() {
@@ -147,4 +154,37 @@ public void ellipseSkewXTest() throws IOException, InterruptedException, java.io
147154
public void ellipseSkewYTest() throws IOException, InterruptedException, java.io.IOException {
148155
convertAndCompare(sourceFolder, destinationFolder, "ellipseSkewY");
149156
}
157+
158+
@Test
159+
public void parseParametersAndCalculateCoordinatesWithBetterPrecisionEllipseTest() throws java.io.IOException, InterruptedException {
160+
String filename = "parseParametersAndCalculateCoordinatesWithBetterPrecisionEllipseTest.pdf";
161+
PdfDocument doc = new PdfDocument(new PdfWriter(destinationFolder + filename));
162+
doc.addNewPage();
163+
164+
EllipseSvgNodeRenderer ellipseRenderer = new EllipseSvgNodeRenderer();
165+
ellipseRenderer.setAttribute(SvgConstants.Attributes.CX, "170.3");
166+
ellipseRenderer.setAttribute(SvgConstants.Attributes.CY, "339.5");
167+
ellipseRenderer.setAttribute(SvgConstants.Attributes.RX, "6");
168+
ellipseRenderer.setAttribute(SvgConstants.Attributes.RY, "6");
169+
170+
// Parse parameters with better precision (in double type) in the method CssUtils#parseAbsoluteLength
171+
ellipseRenderer.setParameters();
172+
173+
SvgDrawContext context = new SvgDrawContext(null, null);
174+
PdfCanvas cv = new PdfCanvas(doc, 1);
175+
context.pushCanvas(cv);
176+
177+
// Calculate coordinates with better precision (in double type) in the method EllipseSvgNodeRenderer#doDraw
178+
ellipseRenderer.draw(context);
179+
180+
String pageContentBytes = new String(doc.getPage(1).getContentBytes(), StandardCharsets.UTF_8);
181+
doc.close();
182+
183+
String expectedResult = "132.22 254.63 m\n"
184+
+ "132.22 257.11 130.21 259.13 127.72 259.13 c\n"
185+
+ "125.24 259.13 123.22 257.11 123.22 254.63 c\n"
186+
+ "123.22 252.14 125.24 250.13 127.72 250.13 c\n"
187+
+ "130.21 250.13 132.22 252.14 132.22 254.63 c";
188+
Assert.assertTrue(pageContentBytes.contains(expectedResult));
189+
}
150190
}

0 commit comments

Comments
 (0)