@@ -23,8 +23,12 @@ This file is part of the iText (R) project.
23
23
package com .itextpdf .svg .renderers ;
24
24
25
25
import com .itextpdf .kernel .geom .Rectangle ;
26
+ import com .itextpdf .kernel .pdf .PdfArray ;
27
+ import com .itextpdf .kernel .pdf .PdfNumber ;
26
28
import com .itextpdf .layout .layout .LayoutContext ;
27
29
import com .itextpdf .layout .layout .LayoutResult ;
30
+ import com .itextpdf .layout .properties .Property ;
31
+ import com .itextpdf .layout .properties .UnitValue ;
28
32
import com .itextpdf .layout .renderer .DrawContext ;
29
33
import com .itextpdf .layout .renderer .ImageRenderer ;
30
34
import com .itextpdf .svg .SvgConstants ;
@@ -50,10 +54,35 @@ public SvgImageRenderer(SvgImage image) {
50
54
@ Override
51
55
public LayoutResult layout (LayoutContext layoutContext ) {
52
56
SvgImage svgImage = (SvgImage ) modelElement ;
57
+ Rectangle layoutBox = layoutContext .getArea ().getBBox ();
53
58
if (svgImage .getSvgImageXObject ().isRelativeSized ()) {
54
- calculateRelativeSizedSvgSize (svgImage , layoutContext .getArea ().getBBox ());
59
+ calculateRelativeSizedSvgSize (svgImage , layoutBox );
60
+ } else if (svgImage .getSvgImageXObject ().isCreatedByObject ()
61
+ && !Boolean .TRUE .equals (getPropertyAsBoolean (Property .FORCED_PLACEMENT ))) {
62
+
63
+ NullableArea retrievedArea = new NullableArea (retrieveWidth (layoutBox .getWidth ()), retrieveHeight ());
64
+ PdfArray bbox = svgImage .getSvgImageXObject ().getBBox ();
65
+ if (retrievedArea .width != null && retrievedArea .height != null ) {
66
+ bbox .set (2 , new PdfNumber ((double ) retrievedArea .width ));
67
+ bbox .set (3 , new PdfNumber ((double ) retrievedArea .height ));
68
+ imageWidth = (float ) retrievedArea .width ;
69
+ imageHeight = (float ) retrievedArea .height ;
70
+ } else if (retrievedArea .width != null ) {
71
+ Area bboxArea = new Area (((PdfNumber )bbox .get (2 )).floatValue (), ((PdfNumber )bbox .get (3 )).floatValue ());
72
+ double verticalScaling = (double ) retrievedArea .width / bboxArea .width ;
73
+ bbox .set (2 , new PdfNumber ((double ) retrievedArea .width ));
74
+ bbox .set (3 , new PdfNumber (bboxArea .height * verticalScaling ));
75
+ imageWidth = (float ) retrievedArea .width ;
76
+ imageHeight = imageHeight * (float ) verticalScaling ;
77
+ } else if (retrievedArea .height != null ) {
78
+ Area bboxArea = new Area (((PdfNumber )bbox .get (2 )).floatValue (), ((PdfNumber )bbox .get (3 )).floatValue ());
79
+ double horizontalScaling = (double ) retrievedArea .height / bboxArea .height ;
80
+ bbox .set (2 , new PdfNumber (bboxArea .width * horizontalScaling ));
81
+ bbox .set (3 , new PdfNumber ((double ) retrievedArea .height ));
82
+ imageWidth = imageWidth * (float ) horizontalScaling ;
83
+ imageHeight = (float ) retrievedArea .height ;
84
+ }
55
85
}
56
-
57
86
return super .layout (layoutContext );
58
87
}
59
88
@@ -78,35 +107,114 @@ private void calculateRelativeSizedSvgSize(SvgImage svgImage, Rectangle layoutBo
78
107
aspectRatio = viewBoxValues [2 ] / viewBoxValues [3 ];
79
108
}
80
109
81
- Float retrievedAreaWidth = retrieveWidth (layoutBox .getWidth ());
82
- Float retrievedAreaHeight = retrieveHeight ();
110
+ NullableArea retrievedArea = new NullableArea (retrieveWidth (layoutBox .getWidth ()), retrieveHeight ());
111
+ boolean preserveAspectRatioNone
112
+ = Values .NONE .equals (svgRootRenderer .getAttribute (Attributes .PRESERVE_ASPECT_RATIO ));
113
+ Area area = new Area ();
83
114
84
- float areaWidth = retrievedAreaWidth == null ?
115
+ area . width = retrievedArea . width == null ?
85
116
(aspectRatio == null ? Values .DEFAULT_VIEWPORT_WIDTH : layoutBox .getWidth ())
86
- : (float ) retrievedAreaWidth ;
87
- float areaHeight = retrievedAreaHeight == null ? Values . DEFAULT_VIEWPORT_HEIGHT : ( float ) retrievedAreaHeight ;
88
-
89
- float finalWidth ;
90
- float finalHeight ;
91
-
92
- if ( aspectRatio != null && ( retrievedAreaHeight == null || retrievedAreaWidth == null )) {
93
- if ( retrievedAreaWidth == null && retrievedAreaHeight != null ) {
94
- finalHeight = areaHeight ;
95
- finalWidth = ( float ) ( finalHeight * aspectRatio );
96
- } else {
97
- finalWidth = areaWidth ;
98
- finalHeight = (float ) ( finalWidth / aspectRatio ) ;
99
- }
117
+ : (float ) retrievedArea . width ;
118
+ area . height = retrievedArea . height == null ?
119
+ ( aspectRatio == null ? Values . DEFAULT_VIEWPORT_HEIGHT : layoutBox . getHeight ())
120
+ : ( float ) retrievedArea . height ;
121
+
122
+ UnitValue elementWidth = svgImageXObject . getElementWidth ();
123
+ UnitValue elementHeight = svgImageXObject . getElementHeight ();
124
+
125
+ //For aspect ratio none we're using the default viewport instead of layoutBox to behave like a browser
126
+ //But this only for <img>, for all other cases using layoutBox as a fallback
127
+ Area finalArea = new Area ();
128
+ if ( preserveAspectRatioNone && svgImageXObject . isCreatedByImg ()) {
129
+ finalArea . width = retrievedArea . width == null ? Values . DEFAULT_VIEWPORT_WIDTH : (float ) retrievedArea . width ;
130
+ finalArea . height = retrievedArea . height == null ? Values . DEFAULT_VIEWPORT_HEIGHT : ( float ) retrievedArea . height ;
100
131
} else {
101
- finalWidth = areaWidth ;
102
- finalHeight = areaHeight ;
132
+ finalArea = initMissingMetricsAndApplyAspectRatio ( aspectRatio , retrievedArea ,
133
+ area , elementWidth , elementHeight ) ;
103
134
}
104
135
105
- svgRootRenderer .setAttribute (Attributes .WIDTH , null );
106
- svgRootRenderer .setAttribute (Attributes .HEIGHT , null );
136
+ if (svgImageXObject .isCreatedByImg () && viewBoxValues == null ) {
137
+ if (this .<UnitValue >getProperty (Property .WIDTH ) == null ) {
138
+ this .setProperty (Property .WIDTH , UnitValue .createPointValue (finalArea .width ));
139
+ }
140
+ if (retrieveHeight () == null ) {
141
+ this .setProperty (Property .HEIGHT , UnitValue .createPointValue (finalArea .height ));
142
+ }
143
+ svgImageXObject .updateBBox (finalArea .width , finalArea .height );
144
+ } else {
145
+ svgRootRenderer .setAttribute (Attributes .WIDTH , null );
146
+ svgRootRenderer .setAttribute (Attributes .HEIGHT , null );
147
+ svgImageXObject .updateBBox (finalArea .width , finalArea .height );
148
+ }
107
149
108
- svgImageXObject .updateBBox (finalWidth , finalHeight );
109
150
imageWidth = svgImage .getImageWidth ();
110
151
imageHeight = svgImage .getImageHeight ();
111
152
}
153
+
154
+ private Area initMissingMetricsAndApplyAspectRatio (Float aspectRatio , NullableArea retrievedArea ,
155
+ Area area , UnitValue xObjectWidth , UnitValue xObjectHeight ) {
156
+ Area finalArea = new Area ();
157
+ if (!tryToApplyAspectRatio (retrievedArea , area , finalArea , aspectRatio )) {
158
+ if (xObjectWidth != null && xObjectWidth .isPointValue () && retrievedArea .width == null ) {
159
+ area .width = xObjectWidth .getValue ();
160
+ retrievedArea .width = area .width ;
161
+ this .setProperty (Property .WIDTH , UnitValue .createPointValue (area .width ));
162
+ }
163
+ if (xObjectHeight != null && xObjectHeight .isPointValue () && retrievedArea .height == null ) {
164
+ area .height = xObjectHeight .getValue ();
165
+ retrievedArea .height = area .height ;
166
+ this .setProperty (Property .HEIGHT , UnitValue .createPointValue (area .height ));
167
+ }
168
+ boolean isAspectRatioApplied = tryToApplyAspectRatio (retrievedArea , area , area , aspectRatio );
169
+ if (!isAspectRatioApplied && aspectRatio != null && retrievedArea .height == null ) {
170
+ // retrievedArea.width is also null here
171
+ area .height = (float ) (area .width / aspectRatio );
172
+ }
173
+ finalArea .width = area .width ;
174
+ finalArea .height = area .height ;
175
+ }
176
+ return finalArea ;
177
+ }
178
+
179
+ private static boolean tryToApplyAspectRatio (NullableArea retrievedArea , Area inputArea , Area resultArea ,
180
+ Float aspectRatio ) {
181
+
182
+ if (aspectRatio == null ) {
183
+ return false ;
184
+ }
185
+ if (retrievedArea .width == null && retrievedArea .height != null ) {
186
+ resultArea .height = inputArea .height ;
187
+ resultArea .width = (float ) (inputArea .height * (float ) aspectRatio );
188
+ return true ;
189
+ } else if (retrievedArea .width != null && retrievedArea .height == null ) {
190
+ resultArea .width = inputArea .width ;
191
+ resultArea .height = (float ) (inputArea .width / (float ) aspectRatio );
192
+ return true ;
193
+ }
194
+ return false ;
195
+ }
196
+
197
+ private static class NullableArea {
198
+ public Float width ;
199
+ public Float height ;
200
+
201
+ public NullableArea (Float width , Float height ) {
202
+ this .width = width ;
203
+ this .height = height ;
204
+ }
205
+ }
206
+
207
+ private static class Area {
208
+ public float width ;
209
+ public float height ;
210
+
211
+ public Area () {
212
+ width = 0.0f ;
213
+ height = 0.0f ;
214
+ }
215
+ public Area (float width , float height ) {
216
+ this .width = width ;
217
+ this .height = height ;
218
+ }
219
+ }
112
220
}
0 commit comments