@@ -42,7 +42,6 @@ This file is part of the iText (R) project.
42
42
*/
43
43
package com .itextpdf .svg .renderers .impl ;
44
44
45
- import com .itextpdf .io .util .MessageFormatUtil ;
46
45
import com .itextpdf .kernel .geom .Point ;
47
46
import com .itextpdf .kernel .pdf .canvas .PdfCanvas ;
48
47
import com .itextpdf .svg .SvgConstants ;
@@ -54,13 +53,12 @@ This file is part of the iText (R) project.
54
53
import com .itextpdf .svg .renderers .path .IPathShape ;
55
54
import com .itextpdf .svg .renderers .path .SvgPathShapeFactory ;
56
55
import com .itextpdf .svg .renderers .path .impl .ClosePath ;
57
- import com .itextpdf .svg .renderers .path .impl .CurveTo ;
56
+ import com .itextpdf .svg .renderers .path .impl .IControlPointCurve ;
58
57
import com .itextpdf .svg .renderers .path .impl .MoveTo ;
58
+ import com .itextpdf .svg .renderers .path .impl .QuadraticSmoothCurveTo ;
59
59
import com .itextpdf .svg .renderers .path .impl .SmoothSCurveTo ;
60
60
import com .itextpdf .svg .utils .SvgCssUtils ;
61
61
import com .itextpdf .svg .utils .SvgRegexUtils ;
62
- import org .slf4j .Logger ;
63
- import org .slf4j .LoggerFactory ;
64
62
65
63
import java .util .ArrayList ;
66
64
import java .util .Arrays ;
@@ -75,9 +73,6 @@ public class PathSvgNodeRenderer extends AbstractSvgNodeRenderer {
75
73
76
74
private static final String SPACE_CHAR = " " ;
77
75
78
- private static final Logger LOGGER = LoggerFactory .getLogger (PathSvgNodeRenderer .class );
79
- private static final int MOVETOARGUMENTNR = 2 ;
80
-
81
76
/**
82
77
* The regular expression to find invalid operators in the <a href="https://www.w3.org/TR/SVG/paths.html#PathData">PathData attribute of the <path> element</a>
83
78
* <p>
@@ -103,7 +98,7 @@ public class PathSvgNodeRenderer extends AbstractSvgNodeRenderer {
103
98
104
99
/**
105
100
* The {@link ClosePath} shape keeping track of the initial point set by a {@link MoveTo} operation.
106
- * The original value is {@code null}, and must be set via a {@link MoveTo} operation before it may drawn.
101
+ * The original value is {@code null}, and must be set via a {@link MoveTo} operation before it may be drawn.
107
102
*/
108
103
private ClosePath zOperator = null ;
109
104
@@ -137,14 +132,13 @@ private String[] getShapeCoordinates(IPathShape shape, IPathShape previousShape,
137
132
return null ;
138
133
}
139
134
String [] shapeCoordinates = null ;
140
- String [] operatorArgs = Arrays .copyOfRange (pathProperties , 1 , pathProperties .length );
141
- if (shape instanceof SmoothSCurveTo ) {
135
+ if (shape instanceof SmoothSCurveTo || shape instanceof QuadraticSmoothCurveTo ) {
142
136
String [] startingControlPoint = new String [2 ];
143
137
if (previousShape != null ) {
144
138
Point previousEndPoint = previousShape .getEndingPoint ();
145
- //if the previous command was a C or S use its last control point
146
- if ((( previousShape instanceof CurveTo )) ) {
147
- Point lastControlPoint = ((CurveTo ) previousShape ).getLastControlPoint ();
139
+ //if the previous command was a Bézier curve, use its last control point
140
+ if (previousShape instanceof IControlPointCurve ) {
141
+ Point lastControlPoint = ((IControlPointCurve ) previousShape ).getLastControlPoint ();
148
142
float reflectedX = (float ) (2 * previousEndPoint .getX () - lastControlPoint .getX ());
149
143
float reflectedY = (float ) (2 * previousEndPoint .getY () - lastControlPoint .getY ());
150
144
@@ -156,13 +150,13 @@ private String[] getShapeCoordinates(IPathShape shape, IPathShape previousShape,
156
150
}
157
151
} else {
158
152
// TODO RND-951
159
- startingControlPoint [0 ] = pathProperties [1 ];
160
- startingControlPoint [1 ] = pathProperties [2 ];
153
+ startingControlPoint [0 ] = pathProperties [0 ];
154
+ startingControlPoint [1 ] = pathProperties [1 ];
161
155
}
162
- shapeCoordinates = concatenate (startingControlPoint , operatorArgs );
156
+ shapeCoordinates = concatenate (startingControlPoint , pathProperties );
163
157
}
164
158
if (shapeCoordinates == null ) {
165
- shapeCoordinates = operatorArgs ;
159
+ shapeCoordinates = pathProperties ;
166
160
}
167
161
return shapeCoordinates ;
168
162
}
@@ -178,34 +172,63 @@ private String[] getShapeCoordinates(IPathShape shape, IPathShape previousShape,
178
172
*/
179
173
private List <IPathShape > processPathOperator (String [] pathProperties , IPathShape previousShape ) {
180
174
List <IPathShape > shapes = new ArrayList <>();
181
- if (pathProperties .length == 0 || pathProperties [0 ].isEmpty ()) {
175
+ if (pathProperties .length == 0 || pathProperties [0 ].isEmpty () || SvgPathShapeFactory . getArgumentCount ( pathProperties [ 0 ]) < 0 ) {
182
176
return shapes ;
183
177
}
184
- //Implements (absolute) command value only
185
- //TODO implement relative values e. C(absolute), c(relative)
186
- IPathShape pathShape = SvgPathShapeFactory .createPathShape (pathProperties [0 ]);
187
- String [] shapeCoordinates = getShapeCoordinates (pathShape , previousShape , pathProperties );
188
- if (pathShape instanceof ClosePath ) {
189
- if (previousShape != null ) {
190
- pathShape = zOperator ;
191
- } else {
178
+
179
+ int argumentCount = SvgPathShapeFactory .getArgumentCount (pathProperties [0 ]);
180
+ if (argumentCount == 0 ) { // closePath operator
181
+ if (previousShape == null ) {
192
182
throw new SvgProcessingException (SvgLogMessageConstant .INVALID_CLOSEPATH_OPERATOR_USE );
193
183
}
194
- } else if (pathShape instanceof MoveTo ) {
195
- zOperator = new ClosePath (pathShape .isRelative ());
196
- if (shapeCoordinates != null && shapeCoordinates .length != MOVETOARGUMENTNR ) {
197
- LOGGER .warn (MessageFormatUtil .format (SvgLogMessageConstant .PATH_WRONG_NUMBER_OF_ARGUMENTS , pathProperties [0 ], shapeCoordinates .length , MOVETOARGUMENTNR , MOVETOARGUMENTNR ));
184
+ shapes .add (zOperator );
185
+ currentPoint = zOperator .getEndingPoint ();
186
+ return shapes ;
187
+ }
188
+ for (int index = 1 ; index < pathProperties .length ; index += argumentCount ) {
189
+ if (index + argumentCount > pathProperties .length ) {
190
+ break ;
191
+ }
192
+ IPathShape pathShape = SvgPathShapeFactory .createPathShape (pathProperties [0 ]);
193
+ if (pathShape instanceof MoveTo ) {
194
+ shapes .addAll (addMoveToShapes (pathShape , pathProperties ));
195
+ return shapes ;
198
196
}
199
- zOperator .setCoordinates (shapeCoordinates , currentPoint );
197
+
198
+ String [] shapeCoordinates = getShapeCoordinates (pathShape , previousShape , Arrays .copyOfRange (pathProperties , index , index +argumentCount ));
199
+ if (pathShape != null ) {
200
+ if (shapeCoordinates != null ) {
201
+ pathShape .setCoordinates (shapeCoordinates , currentPoint );
202
+ }
203
+ currentPoint = pathShape .getEndingPoint (); // unsupported operators are ignored.
204
+ shapes .add (pathShape );
205
+ }
206
+ previousShape = pathShape ;
200
207
}
208
+ return shapes ;
209
+ }
201
210
202
- if (pathShape != null ) {
203
- if (shapeCoordinates != null ) {
204
- // Cast will be removed when the method is introduced in the interface
205
- pathShape .setCoordinates (shapeCoordinates , currentPoint );
211
+ private List <IPathShape > addMoveToShapes (IPathShape pathShape , String [] pathProperties ) {
212
+ List <IPathShape > shapes = new ArrayList <>();
213
+ int argumentCount = 2 ;
214
+ String [] shapeCoordinates = getShapeCoordinates (pathShape , null , Arrays .copyOfRange (pathProperties , 1 , 3 ));
215
+ zOperator = new ClosePath (pathShape .isRelative ());
216
+ zOperator .setCoordinates (shapeCoordinates , currentPoint );
217
+ pathShape .setCoordinates (shapeCoordinates , currentPoint );
218
+ currentPoint = pathShape .getEndingPoint ();
219
+ shapes .add (pathShape );
220
+ IPathShape previousShape = pathShape ;
221
+ if (pathProperties .length > 3 ) {
222
+ for (int index = 3 ; index < pathProperties .length ; index += argumentCount ) {
223
+ if (index + 2 > pathProperties .length ) {
224
+ break ;
225
+ }
226
+ pathShape = pathShape .isRelative () ? SvgPathShapeFactory .createPathShape ("l" ) : SvgPathShapeFactory .createPathShape ("L" );
227
+ shapeCoordinates = getShapeCoordinates (pathShape , previousShape , Arrays .copyOfRange (pathProperties , index , index + 2 ));
228
+ pathShape .setCoordinates (shapeCoordinates , previousShape .getEndingPoint ());
229
+ shapes .add (pathShape );
230
+ previousShape = pathShape ;
206
231
}
207
- currentPoint = pathShape .getEndingPoint (); // unsupported operators are ignored.
208
- shapes .add (pathShape );
209
232
}
210
233
return shapes ;
211
234
}
0 commit comments