Skip to content

Commit f7c3c11

Browse files
ar3emiText-CI
authored andcommitted
Support 'viewbox' and `preserveAspectRatio' attribute for SVG pattern element
DEVSIX-4782
1 parent f810573 commit f7c3c11

File tree

94 files changed

+1079
-86
lines changed

Some content is hidden

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

94 files changed

+1079
-86
lines changed

svg/src/main/java/com/itextpdf/svg/exceptions/SvgExceptionMessageConstant.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,15 @@ public class SvgExceptionMessageConstant {
5050
public static final String CURVE_TO_EXPECTS_FOLLOWING_PARAMETERS_GOT_0 = "(x1 y1 x2 y2 x y)+ parameters are expected for curves. Got: {0}";
5151
public static final String INVALID_SMOOTH_CURVE_USE = "The smooth curve operations (S, s, T, t) may not be used as a first operator in path.";
5252
public static final String QUADRATIC_CURVE_TO_EXPECTS_FOLLOWING_PARAMETERS_GOT_0 = "(x1 y1 x y)+ parameters are expected for quadratic curves. Got: {0}";
53+
public static final String MEET_OR_SLICE_ARGUMENT_IS_INCORRECT =
54+
"The meetOrSlice argument is incorrect. It must be `meet`, `slice` or null.";
5355
public static final String MOVE_TO_EXPECTS_FOLLOWING_PARAMETERS_GOT_0 = "(x y)+ parameters are expected for moveTo operator. Got: {0}";
5456
public static final String LINE_TO_EXPECTS_FOLLOWING_PARAMETERS_GOT_0 = "(x y)+ parameters are expected for lineTo operator. Got: {0}";
5557

5658
public static final String COULD_NOT_DETERMINE_MIDDLE_POINT_OF_ELLIPTICAL_ARC = "Could not determine the middle point of the ellipse traced by this elliptical arc";
59+
60+
public static final String VIEWBOX_APPLYING_COULD_NOT_BE_PROCESSED =
61+
"The viewBox or the current viewport is null. The viewBox applying could not be processed.";
62+
63+
5764
}

svg/src/main/java/com/itextpdf/svg/exceptions/SvgLogMessageConstant.java

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ public final class SvgLogMessageConstant {
6060
@Deprecated
6161
public static final String FLOAT_PARSING_NAN = "The passed value is not a number.";
6262
public static final String FONT_NOT_FOUND = "The font wasn't found.";
63-
/** Message in case the font provider doesn't know about any fonts. */
63+
/**
64+
* Message in case the font provider doesn't know about any fonts.
65+
*/
6466
@Deprecated
6567
public static final String FONT_PROVIDER_CONTAINS_ZERO_FONTS = "Font Provider contains zero fonts. At least one font shall be present";
6668
public static final String GRADIENT_INVALID_GRADIENT_UNITS_LOG = "Could not recognize gradient units value {0}";
@@ -69,19 +71,24 @@ public final class SvgLogMessageConstant {
6971
public static final String INVALID_CLOSEPATH_OPERATOR_USE = "The close path operator (Z) may not be used before a move to operation (M)";
7072
public static final String INVALID_PATH_D_ATTRIBUTE_OPERATORS = "Invalid operators found in path data attribute: {0}";
7173
public static final String INVALID_TRANSFORM_DECLARATION = "Transformation declaration is not formed correctly.";
72-
public static final String LOOP ="Loop detected";
74+
public static final String LOOP = "Loop detected";
7375
public static final String MARKER_HEIGHT_IS_NEGATIVE_VALUE = "markerHeight has negative value. Marker will not be rendered.";
7476
public static final String MARKER_HEIGHT_IS_ZERO_VALUE = "markerHeight has zero value. Marker will not be rendered.";
7577
public static final String MARKER_WIDTH_IS_NEGATIVE_VALUE = "markerWidth has negative value. Marker will not be rendered.";
7678
public static final String MARKER_WIDTH_IS_ZERO_VALUE = "markerWidth has zero value. Marker will not be rendered.";
77-
public static final String MISSING_WIDTH="Top Svg tag has no defined width attribute and viewbox width is not present, so browser default of 300px is used";
78-
public static final String MISSING_HEIGHT="Top Svg tag has no defined height attribute and viewbox height is not present, so browser default of 150px is used";
79+
public static final String MISSING_WIDTH = "Top Svg tag has no defined width attribute and viewbox width is not present, so browser default of 300px is used";
80+
public static final String MISSING_HEIGHT = "Top Svg tag has no defined height attribute and viewbox height is not present, so browser default of 150px is used";
7981
public static final String NAMED_OBJECT_NAME_NULL_OR_EMPTY = "The name of the named object can't be null or empty.";
8082
public static final String NAMED_OBJECT_NULL = "A named object can't be null.";
8183
public static final String NONINVERTIBLE_TRANSFORMATION_MATRIX_USED_IN_CLIP_PATH = "Non-invertible transformation matrix was used in a clipping path context. Clipped elements may show undefined behavior.";
8284
public static final String NOROOT = "No root found";
8385
public static final String PATTERN_INVALID_PATTERN_UNITS_LOG = "Could not recognize patternUnits value {0}";
84-
public static final String PATTERN_INVALID_PATTERN_CONTENT_UNITS_LOG = "Could not recognize patternContentUnits value {0}";
86+
public static final String PATTERN_INVALID_PATTERN_CONTENT_UNITS_LOG =
87+
"Could not recognize patternContentUnits value {0}";
88+
public static final String PATTERN_WIDTH_OR_HEIGHT_IS_ZERO =
89+
"Pattern width or height is zero. This pattern will not be rendered.";
90+
public static final String PATTERN_WIDTH_OR_HEIGHT_IS_NEGATIVE =
91+
"Pattern width or height is negative value. This pattern will not be rendered.";
8592
public static final String PATH_WRONG_NUMBER_OF_ARGUMENTS = "Path operator {0} has received {1} arguments, but expects between {2} and {3} arguments. \n Resulting SVG will be incorrect.";
8693
public static final String PARAMETER_CANNOT_BE_NULL = "Parameters for this method cannot be null.";
8794
public static final String POINTS_ATTRIBUTE_INVALID_LIST = "Points attribute {0} on polyline tag does not contain a valid set of points";
@@ -97,6 +104,13 @@ public final class SvgLogMessageConstant {
97104
public static final String UNABLE_TO_RETRIEVE_FONT = "Unable to retrieve font:\n {0}";
98105
public static final String UNMAPPEDTAG = "Could not find implementation for tag {0}";
99106
public static final String UNKNOWN_TRANSFORMATION_TYPE = "Unsupported type of transformation.";
107+
public static final String VIEWBOX_VALUE_MUST_BE_FOUR_NUMBERS =
108+
"The viewBox value must be 4 numbers. This viewBox=\"{0}\" will not be processed.";
109+
public static final String VIEWBOX_WIDTH_AND_HEIGHT_CANNOT_BE_NEGATIVE =
110+
"The viewBox width and height cannot be negative. This viewBox=\"{0}\" will not be processed.";
111+
public static final String VIEWBOX_WIDTH_OR_HEIGHT_IS_ZERO =
112+
"The viewBox width or height is zero. The element with this viewBox will not be rendered.";
100113

101-
private SvgLogMessageConstant(){}
114+
private SvgLogMessageConstant() {
115+
}
102116
}

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

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This file is part of the iText (R) project.
4242
*/
4343
package com.itextpdf.svg.renderers.impl;
4444

45+
import com.itextpdf.io.util.MessageFormatUtil;
4546
import com.itextpdf.kernel.geom.AffineTransform;
4647
import com.itextpdf.kernel.geom.Matrix;
4748
import com.itextpdf.kernel.geom.NoninvertibleTransformException;
@@ -67,7 +68,6 @@ This file is part of the iText (R) project.
6768
import java.util.Arrays;
6869
import java.util.Collections;
6970
import java.util.List;
70-
7171
import org.slf4j.Logger;
7272
import org.slf4j.LoggerFactory;
7373

@@ -77,8 +77,15 @@ This file is part of the iText (R) project.
7777
*/
7878
public abstract class AbstractBranchSvgNodeRenderer extends AbstractSvgNodeRenderer implements IBranchSvgNodeRenderer {
7979

80+
/**
81+
* The number of viewBox values.
82+
*/
83+
protected final static int VIEWBOX_VALUES_NUMBER = 4;
84+
8085
private final List<ISvgNodeRenderer> children = new ArrayList<>();
8186

87+
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractBranchSvgNodeRenderer.class);
88+
8289
/**
8390
* Method that will set properties to be inherited by this branch renderer's
8491
* children and will iterate over all children in order to draw them.
@@ -142,14 +149,14 @@ protected void doDraw(SvgDrawContext context) {
142149
* @param context current svg draw context
143150
*/
144151
void applyViewBox(SvgDrawContext context) {
145-
if (this.attributesAndStyles != null && this.attributesAndStyles.containsKey(SvgConstants.Attributes.VIEWBOX)) {
146-
float[] values = getViewBoxValues();
152+
float[] viewBoxValues = getViewBoxValues();
153+
if (viewBoxValues.length < VIEWBOX_VALUES_NUMBER) {
154+
float[] values = {0, 0, context.getCurrentViewPort().getWidth(), context.getCurrentViewPort().getHeight()};
147155
Rectangle currentViewPort = context.getCurrentViewPort();
148156
calculateAndApplyViewBox(context, values, currentViewPort);
149157
} else {
150-
float[] values = {0, 0, context.getCurrentViewPort().getWidth(), context.getCurrentViewPort().getHeight()};
151158
Rectangle currentViewPort = context.getCurrentViewPort();
152-
calculateAndApplyViewBox(context, values, currentViewPort);
159+
calculateAndApplyViewBox(context, viewBoxValues, currentViewPort);
153160
}
154161
}
155162

@@ -349,6 +356,7 @@ void setPartOfClipPath(boolean isPart) {
349356
}
350357

351358
void calculateAndApplyViewBox(SvgDrawContext context, float[] values, Rectangle currentViewPort) {
359+
// TODO DEVSIX-4861 change this method with using of SvgCoordinateUtils#applyViewBox
352360
String[] alignAndMeet = retrieveAlignAndMeet();
353361
String align = alignAndMeet[0];
354362
String meetOrSlice = alignAndMeet[1];
@@ -395,12 +403,35 @@ void calculateAndApplyViewBox(SvgDrawContext context, float[] values, Rectangle
395403
}
396404

397405
float[] getViewBoxValues() {
406+
if (this.attributesAndStyles == null) {
407+
return new float[]{};
408+
}
398409
String viewBoxValues = attributesAndStyles.get(SvgConstants.Attributes.VIEWBOX);
410+
if (viewBoxValues == null) {
411+
return new float[]{};
412+
}
399413
List<String> valueStrings = SvgCssUtils.splitValueList(viewBoxValues);
400414
float[] values = new float[valueStrings.size()];
401415
for (int i = 0; i < values.length; i++) {
402416
values[i] = CssDimensionParsingUtils.parseAbsoluteLength(valueStrings.get(i));
403417
}
418+
// the value for viewBox should be 4 numbers according to the viewBox documentation
419+
if (values.length != VIEWBOX_VALUES_NUMBER) {
420+
if (LOGGER.isWarnEnabled()) {
421+
LOGGER.warn(MessageFormatUtil.format(
422+
SvgLogMessageConstant.VIEWBOX_VALUE_MUST_BE_FOUR_NUMBERS, viewBoxValues));
423+
}
424+
return new float[]{};
425+
}
426+
// case when viewBox width or height is negative value is an error and
427+
// invalidates the ‘viewBox’ attribute (according to the viewBox documentation)
428+
if (values[2] < 0 || values[3] < 0) {
429+
if (LOGGER.isWarnEnabled()) {
430+
LOGGER.warn(MessageFormatUtil.format(
431+
SvgLogMessageConstant.VIEWBOX_WIDTH_AND_HEIGHT_CANNOT_BE_NEGATIVE, viewBoxValues));
432+
}
433+
return new float[]{};
434+
}
404435
return values;
405436
}
406437

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

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,8 @@ private void applyUserSpaceScaling(SvgDrawContext context) {
206206
private void applyCoordinatesTranslation(SvgDrawContext context) {
207207
float xScale = 1;
208208
float yScale = 1;
209-
if (this.attributesAndStyles.containsKey(SvgConstants.Attributes.VIEWBOX)) {
210-
//Parse viewbox parameters stuff
211-
String viewBoxValues = attributesAndStyles.get(SvgConstants.Attributes.VIEWBOX);
212-
List<String> valueStrings = SvgCssUtils.splitValueList(viewBoxValues);
213-
float[] viewBox = getViewBoxValues();
209+
float[] viewBox = getViewBoxValues();
210+
if (viewBox.length == VIEWBOX_VALUES_NUMBER) {
214211
xScale = context.getCurrentViewPort().getWidth() / viewBox[2];
215212
yScale = context.getCurrentViewPort().getHeight() / viewBox[3];
216213
}
@@ -234,19 +231,14 @@ private void applyCoordinatesTranslation(SvgDrawContext context) {
234231
}
235232

236233
private float[] getViewBoxValues(float defaultWidth, float defaultHeight) {
237-
float[] values;
238-
if (this.attributesAndStyles.containsKey(SvgConstants.Attributes.VIEWBOX)) {
239-
//Parse viewbox parameters stuff
240-
values = super.getViewBoxValues();
234+
float[] values = super.getViewBoxValues();
235+
if (values.length < VIEWBOX_VALUES_NUMBER) {
236+
//If viewBox is not specified or incorrect, it's width and height are the same as passed defaults
237+
return new float[] {0, 0, defaultWidth, defaultHeight};
238+
241239
} else {
242-
//If viewbox is not specified, it's width and height are the same as passed defaults
243-
values = new float[4];
244-
values[0] = 0;
245-
values[1] = 0;
246-
values[2] = defaultWidth;
247-
values[3] = defaultHeight;
240+
return values;
248241
}
249-
return values;
250242
}
251243
}
252244

0 commit comments

Comments
 (0)