@@ -66,7 +66,7 @@ This file is part of the iText (R) project.
66
66
*/
67
67
public class PatternSvgNodeRenderer extends AbstractBranchSvgNodeRenderer implements ISvgPaintServer {
68
68
69
- private static final double ZERO = 1E-10 ;
69
+ private static final double CONVERT_COEFF = 0.75 ;
70
70
71
71
@ Override
72
72
public ISvgNodeRenderer createDeepCopy () {
@@ -84,44 +84,66 @@ public Color createColor(SvgDrawContext context, Rectangle objectBoundingBox, fl
84
84
return null ;
85
85
}
86
86
try {
87
- PdfPattern .Tiling tilingPattern = createTilingPattern (context );
87
+ PdfPattern .Tiling tilingPattern = createTilingPattern (context , objectBoundingBox );
88
88
drawPatternContent (context , tilingPattern );
89
89
return (tilingPattern == null ) ? null : new PatternColor (tilingPattern );
90
90
} finally {
91
91
context .popPatternId ();
92
92
}
93
93
}
94
94
95
- private PdfPattern .Tiling createTilingPattern (SvgDrawContext context ) {
95
+ private PdfPattern .Tiling createTilingPattern (SvgDrawContext context ,
96
+ Rectangle objectBoundingBox ) {
96
97
final boolean isObjectBoundingBoxInPatternUnits = isObjectBoundingBoxInPatternUnits ();
97
98
final boolean isObjectBoundingBoxInPatternContentUnits = isObjectBoundingBoxInPatternContentUnits ();
98
99
final boolean isViewBoxExist = getAttribute (Attributes .VIEWBOX ) != null ;
100
+
101
+ // evaluate pattern rectangle on target pattern units
102
+ Rectangle originalPatternRectangle = calculateOriginalPatternRectangle (
103
+ context , isObjectBoundingBoxInPatternUnits );
104
+
105
+ // get xStep and yStep on target pattern units
106
+ double xStep = originalPatternRectangle .getWidth ();
107
+ double yStep = originalPatternRectangle .getHeight ();
108
+
109
+ if (xStep == 0 || yStep == 0 ) {
110
+ return null ;
111
+ }
112
+
113
+ // transform user space to target pattern rectangle origin and scale
99
114
final AffineTransform patternAffineTransform = new AffineTransform ();
100
- double xOffset , yOffset , xStep , yStep ;
101
- Rectangle bbox ;
102
- if (isObjectBoundingBoxInPatternUnits || isViewBoxExist || isObjectBoundingBoxInPatternContentUnits ) {
115
+ if (isObjectBoundingBoxInPatternUnits ) {
116
+ patternAffineTransform .concatenate (getTransformToUserSpaceOnUse (objectBoundingBox ));
117
+ }
118
+ patternAffineTransform .translate (originalPatternRectangle .getX (), originalPatternRectangle .getY ());
119
+
120
+ double bboxWidth , bboxHeight ;
121
+ if (isViewBoxExist ) {
122
+ // TODO: DEVSIX-4782 support 'viewbox' and `preserveAspectRatio' attribute for SVG pattern element
103
123
return null ;
104
124
} else {
105
- final Rectangle currentViewPort = context .getCurrentViewPort ();
106
- final double viewPortX = currentViewPort .getX ();
107
- final double viewPortY = currentViewPort .getY ();
108
- final double viewPortWidth = currentViewPort .getWidth ();
109
- final double viewPortHeight = currentViewPort .getHeight ();
110
- final float em = getCurrentFontSize ();
111
- final float rem = context .getCssContext ().getRootFontSize ();
112
- xOffset = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
113
- getAttribute (Attributes .X ), viewPortX , viewPortX , viewPortWidth , em , rem );
114
- yOffset = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
115
- getAttribute (Attributes .Y ), viewPortY , viewPortY , viewPortHeight , em , rem );
116
- xStep = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
117
- getAttribute (Attributes .WIDTH ), viewPortX , viewPortX , viewPortWidth , em , rem );
118
- yStep = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
119
- getAttribute (Attributes .HEIGHT ), viewPortX , viewPortX , viewPortHeight , em , rem );
120
- bbox = new Rectangle (0F , 0F , (float ) xStep , (float ) yStep );
121
- if (!isZero (xOffset , ZERO ) || !isZero (yOffset , ZERO )) {
122
- patternAffineTransform .translate (xOffset , yOffset );
125
+ if (isObjectBoundingBoxInPatternUnits != isObjectBoundingBoxInPatternContentUnits ) {
126
+ // If pattern units are not the same as pattern content units, then we need to scale
127
+ // the resulted space into a space to draw pattern content. The pattern rectangle origin
128
+ // is already in place, but measures should be adjusted.
129
+ double scaleX , scaleY ;
130
+ if (isObjectBoundingBoxInPatternContentUnits ) {
131
+ scaleX = objectBoundingBox .getWidth () / CONVERT_COEFF ;
132
+ scaleY = objectBoundingBox .getHeight () / CONVERT_COEFF ;
133
+ } else {
134
+ scaleX = CONVERT_COEFF / objectBoundingBox .getWidth ();
135
+ scaleY = CONVERT_COEFF / objectBoundingBox .getHeight ();
136
+ }
137
+ patternAffineTransform .concatenate (AffineTransform .getScaleInstance (scaleX , scaleY ));
138
+ xStep /= scaleX ;
139
+ yStep /= scaleY ;
123
140
}
141
+ bboxWidth = xStep ;
142
+ bboxHeight = yStep ;
124
143
}
144
+
145
+ Rectangle bbox = new Rectangle (0F , 0F , (float ) bboxWidth , (float ) bboxHeight );
146
+
125
147
return createColoredTilingPatternInstance (patternAffineTransform , bbox , xStep , yStep );
126
148
}
127
149
@@ -157,6 +179,39 @@ private void setPatternMatrix(PdfPattern.Tiling pattern, AffineTransform affineT
157
179
}
158
180
}
159
181
182
+ private Rectangle calculateOriginalPatternRectangle (SvgDrawContext context ,
183
+ boolean isObjectBoundingBoxInPatternUnits ) {
184
+ double xOffset , yOffset , xStep , yStep ;
185
+ if (isObjectBoundingBoxInPatternUnits ) {
186
+ xOffset = SvgCoordinateUtils .getCoordinateForObjectBoundingBox (
187
+ getAttribute (Attributes .X ), 0 ) * CONVERT_COEFF ;
188
+ yOffset = SvgCoordinateUtils .getCoordinateForObjectBoundingBox (
189
+ getAttribute (Attributes .Y ), 0 ) * CONVERT_COEFF ;
190
+ xStep = SvgCoordinateUtils .getCoordinateForObjectBoundingBox (
191
+ getAttribute (Attributes .WIDTH ), 0 ) * CONVERT_COEFF ;
192
+ yStep = SvgCoordinateUtils .getCoordinateForObjectBoundingBox (
193
+ getAttribute (Attributes .HEIGHT ), 0 ) * CONVERT_COEFF ;
194
+ } else {
195
+ final Rectangle currentViewPort = context .getCurrentViewPort ();
196
+ final double viewPortX = currentViewPort .getX ();
197
+ final double viewPortY = currentViewPort .getY ();
198
+ final double viewPortWidth = currentViewPort .getWidth ();
199
+ final double viewPortHeight = currentViewPort .getHeight ();
200
+ final float em = getCurrentFontSize ();
201
+ final float rem = context .getCssContext ().getRootFontSize ();
202
+ // get pattern coordinates in userSpaceOnUse coordinate system
203
+ xOffset = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
204
+ getAttribute (Attributes .X ), viewPortX , viewPortX , viewPortWidth , em , rem );
205
+ yOffset = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
206
+ getAttribute (Attributes .Y ), viewPortY , viewPortY , viewPortHeight , em , rem );
207
+ xStep = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
208
+ getAttribute (Attributes .WIDTH ), viewPortX , viewPortX , viewPortWidth , em , rem );
209
+ yStep = SvgCoordinateUtils .getCoordinateForUserSpaceOnUse (
210
+ getAttribute (Attributes .HEIGHT ), viewPortY , viewPortY , viewPortHeight , em , rem );
211
+ }
212
+ return new Rectangle ((float ) xOffset , (float ) yOffset , (float ) xStep , (float ) yStep );
213
+ }
214
+
160
215
private boolean isObjectBoundingBoxInPatternUnits () {
161
216
final String patternUnits = getAttribute (Attributes .PATTERN_UNITS );
162
217
if (Values .USER_SPACE_ON_USE .equals (patternUnits )) {
@@ -180,7 +235,11 @@ private boolean isObjectBoundingBoxInPatternContentUnits() {
180
235
return false ;
181
236
}
182
237
183
- private static boolean isZero (double val , double delta ) {
184
- return -delta < val && val < delta ;
238
+ private AffineTransform getTransformToUserSpaceOnUse (Rectangle objectBoundingBox ) {
239
+ AffineTransform transform = new AffineTransform ();
240
+ transform .translate (objectBoundingBox .getX (), objectBoundingBox .getY ());
241
+ transform .scale (objectBoundingBox .getWidth () / CONVERT_COEFF ,
242
+ objectBoundingBox .getHeight () / CONVERT_COEFF );
243
+ return transform ;
185
244
}
186
245
}
0 commit comments