Skip to content

Commit f5314ad

Browse files
BezrukovMiText-CI
authored andcommitted
Linear gradient logic
Implementation of linear gradient builders for low level build and strategy based build. Add background image support for linear gradient for layout module. Add css parsing of linear gradient. Base svg implementation of linear gradient parsing and using for most types of svg objects. DEVSIX-2086
1 parent c37c4bb commit f5314ad

File tree

220 files changed

+5055
-233
lines changed

Some content is hidden

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

220 files changed

+5055
-233
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ public final class LogMessageConstant {
188188
public static final String TOUNICODE_CMAP_MORE_THAN_2_BYTES_NOT_SUPPORTED = "ToUnicode CMap more than 2 bytes not supported.";
189189
public static final String TYPE3_FONT_CANNOT_BE_ADDED = "Type 3 font cannot be added to FontSet. Custom FontProvider class may be created for this purpose.";
190190
public static final String TYPE3_FONT_ISSUE_TAGGED_PDF = "Type 3 font issue. Font Descriptor is required for tagged PDF. FontName shall be specified.";
191+
public static final String UNABLE_TO_INVERT_GRADIENT_TRANSFORMATION = "Unable to invert gradient transformation, ignoring it";
191192
public static final String UNABLE_TO_APPLY_PAGE_DEPENDENT_PROP_UNKNOWN_PAGE_ON_WHICH_ELEMENT_IS_DRAWN = "Unable to apply page dependent property, because the page on which element is drawn is unknown. Usually this means that element was added to the Canvas instance that was created not with constructor taking PdfPage as argument. Not processed property: {0}";
192193
public static final String UNABLE_TO_REGISTER_EVENT_DATA_HANDLER_SHUTDOWN_HOOK = "Unable to register event data handler shutdown hook because of security reasons.";
193194
public static final String UNABLE_TO_SEARCH_FOR_EVENT_CONTEXT = "It is impossible to retrieve event context because of the security reasons. Event counting may behave in unexpected way";

kernel/src/main/java/com/itextpdf/kernel/colors/gradients/AbstractLinearGradientBuilder.java

Lines changed: 604 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
package com.itextpdf.kernel.colors.gradients;
2+
3+
import java.util.Arrays;
4+
import java.util.Objects;
5+
6+
/**
7+
* The gradient stop color structure representing the stop color configuration.
8+
* The stop color consists of:
9+
* - {@code float[]} rgb color array. Values should be in [0, 1] range. All values outside of
10+
* this range would be adjusted to the nearest corner of the range.
11+
* - {@code double} offset and {@link OffsetType} offset type specifies the coordinate of
12+
* the stop color on the targeting gradient coordinates vector
13+
* - {@code double} hint offset and {@link HintOffsetType} hint offset type specifies the color
14+
* transition mid point offset between the current color and the next color
15+
*/
16+
public class GradientColorStop {
17+
private final float[] rgb;
18+
private final float opacity;
19+
20+
private OffsetType offsetType;
21+
private double offset;
22+
private double hintOffset = 0d;
23+
private HintOffsetType hintOffsetType = HintOffsetType.NONE;
24+
25+
/**
26+
* Constructor of stop color with with specified rgb color and default ({@link OffsetType#AUTO})
27+
* offset
28+
*
29+
* @param rgb the color value
30+
*/
31+
public GradientColorStop(float[] rgb) {
32+
this(rgb, 1f, 0d, OffsetType.AUTO);
33+
}
34+
35+
/**
36+
* Constructor of stop color with with specified rgb color and offset
37+
*
38+
* @param rgb the color value
39+
* @param offset the offset value. Makes sense only if the {@code offsetType} is not {@link OffsetType#AUTO}
40+
* @param offsetType the offset's type
41+
*/
42+
public GradientColorStop(float[] rgb, double offset, OffsetType offsetType) {
43+
this(rgb, 1f, offset, offsetType);
44+
}
45+
46+
/**
47+
* Constructor that creates the stop with the same color as the another stop and new offset
48+
*
49+
* @param gradientColorStop the gradient stop color from which the color value would be copied
50+
* @param offset the new offset. Makes sense only if the {@code offsetType} is not {@link OffsetType#AUTO}
51+
* @param offsetType the new offset's type
52+
*/
53+
public GradientColorStop(GradientColorStop gradientColorStop, double offset, OffsetType offsetType) {
54+
this(gradientColorStop.getRgbArray(), gradientColorStop.getOpacity(), offset, offsetType);
55+
}
56+
57+
private GradientColorStop(float[] rgb, float opacity, double offset, OffsetType offsetType) {
58+
this.rgb = copyRgbArray(rgb);
59+
60+
this.opacity = normalize(opacity);
61+
62+
setOffset(offset, offsetType);
63+
}
64+
65+
/**
66+
* Get the stop color rgb value
67+
*
68+
* @return the copy of stop's rgb value
69+
*/
70+
public float[] getRgbArray() {
71+
return copyRgbArray(this.rgb);
72+
}
73+
74+
// TODO: DEVSIX-4136 make public with opacity logic implementation
75+
/**
76+
* Get the stop color opacity value
77+
*
78+
* @return the stop color opacity value
79+
*/
80+
private float getOpacity() {
81+
return this.opacity;
82+
}
83+
84+
/**
85+
* Get the offset type
86+
*
87+
* @return the offset type
88+
*/
89+
public OffsetType getOffsetType() {
90+
return offsetType;
91+
}
92+
93+
/**
94+
* Get the offset value
95+
*
96+
* @return the offset value
97+
*/
98+
public double getOffset() {
99+
return this.offset;
100+
}
101+
102+
/**
103+
* Get the hint offset value
104+
*
105+
* @return the hint offset value
106+
*/
107+
public double getHintOffset() {
108+
return hintOffset;
109+
}
110+
111+
/**
112+
* Get the hint offset type
113+
*
114+
* @return the hint offset type
115+
*/
116+
public HintOffsetType getHintOffsetType() {
117+
return hintOffsetType;
118+
}
119+
120+
/**
121+
* Set the offset specified by its value and type
122+
*
123+
* @param offset the offset's value to be set. Makes sense only if the {@code offsetType}
124+
* is not {@link OffsetType#AUTO}
125+
* @param offsetType the offset's type to be set
126+
* @return the current {@link GradientColorStop} instance
127+
*/
128+
public GradientColorStop setOffset(double offset, OffsetType offsetType) {
129+
this.offsetType = offsetType != null ? offsetType : OffsetType.AUTO;
130+
this.offset = this.offsetType != OffsetType.AUTO ? offset : 0d;
131+
return this;
132+
}
133+
134+
/**
135+
* Set the color hint specified by its value and type ({@link GradientColorStop more details}).
136+
*
137+
* @param hintOffset the hint offset's value to be set. Makes sense only
138+
* if the {@code hintOffsetType} is not {@link HintOffsetType#NONE}
139+
* @param hintOffsetType the hint offset's type to be set
140+
* @return the current {@link GradientColorStop} instance
141+
*/
142+
public GradientColorStop setHint(double hintOffset, HintOffsetType hintOffsetType) {
143+
this.hintOffsetType = hintOffsetType != null ? hintOffsetType : HintOffsetType.NONE;
144+
this.hintOffset = this.hintOffsetType != HintOffsetType.NONE ? hintOffset : 0d;
145+
return this;
146+
}
147+
148+
@Override
149+
public boolean equals(Object o) {
150+
if (this == o) {
151+
return true;
152+
}
153+
if (o == null || getClass() != o.getClass()) {
154+
return false;
155+
}
156+
GradientColorStop that = (GradientColorStop) o;
157+
return Float.compare(that.opacity, opacity) == 0 &&
158+
Double.compare(that.offset, offset) == 0 &&
159+
Double.compare(that.hintOffset, hintOffset) == 0 &&
160+
Arrays.equals(rgb, that.rgb) &&
161+
offsetType == that.offsetType &&
162+
hintOffsetType == that.hintOffsetType;
163+
}
164+
165+
@Override
166+
public int hashCode() {
167+
int result = Objects.hash(opacity, offset, hintOffset);
168+
result = 31 * result + offsetType.hashCode();
169+
result = 31 * result + hintOffsetType.hashCode();
170+
result = 31 * result + Arrays.hashCode(rgb);
171+
return result;
172+
}
173+
174+
private static float normalize(float toNormalize) {
175+
return toNormalize > 1f ? 1f : toNormalize > 0f ? toNormalize : 0f;
176+
}
177+
178+
private static float[] copyRgbArray(float[] toCopy) {
179+
if (toCopy == null || toCopy.length < 3) {
180+
return new float[] {0f, 0f, 0f};
181+
}
182+
return new float[] {normalize(toCopy[0]), normalize(toCopy[1]), normalize(toCopy[2])};
183+
}
184+
185+
/**
186+
* Represents the possible offset type
187+
*/
188+
public enum OffsetType {
189+
/**
190+
* The absolute offset value from the target coordinates vector's start
191+
*/
192+
ABSOLUTE,
193+
/**
194+
* The automatic offset evaluation. The offset value should be evaluated automatically
195+
* based on the whole stop colors list specified for the gradient. The general auto offset
196+
* logic should be the next:
197+
* - find the previous and the next specified offset or hint offset values
198+
* - the sublist of sequent auto offsets should spread evenly between the found values
199+
*/
200+
AUTO,
201+
/**
202+
* The relative offset value to the target coordinates vector. The {@code 0} value means
203+
* the target vector start, the {@code 1} value means the target vector end.
204+
*/
205+
RELATIVE
206+
}
207+
208+
/**
209+
* Represents the possible hint offset type
210+
*/
211+
public enum HintOffsetType {
212+
/**
213+
* The absolute hint offset value on the target gradient value
214+
*/
215+
ABSOLUTE_ON_GRADIENT,
216+
/**
217+
* The relative hint offset value to the target coordinates vector. The {@code 0} value
218+
* means the target vector start, the {@code 1} value means the target vector end.
219+
*/
220+
RELATIVE_ON_GRADIENT,
221+
/**
222+
* The relative hint offset value to the interval between the current gradient stop color
223+
* and the next one.
224+
*/
225+
RELATIVE_BETWEEN_COLORS,
226+
/**
227+
* None hint offset specified
228+
*/
229+
NONE
230+
}
231+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.itextpdf.kernel.colors.gradients;
2+
3+
/**
4+
* Represents possible spreading methods for gradient color outside of the coordinates vector
5+
*/
6+
public enum GradientSpreadMethod {
7+
/**
8+
* Pad the corner colors to fill the necessary area
9+
*/
10+
PAD,
11+
/**
12+
* Reflect the coloring to fill the necessary area
13+
*/
14+
REFLECT,
15+
/**
16+
* Repeat the coloring to fill the necessary area
17+
*/
18+
REPEAT,
19+
/**
20+
* No coloring outside of the coordinates vector
21+
*/
22+
NONE
23+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.itextpdf.kernel.colors.gradients;
2+
3+
import com.itextpdf.kernel.geom.AffineTransform;
4+
import com.itextpdf.kernel.geom.Point;
5+
import com.itextpdf.kernel.geom.Rectangle;
6+
7+
/**
8+
* The linear gradient builder implementation with direct target gradient vector
9+
* and shading transformation ({@link AbstractLinearGradientBuilder more info})
10+
*/
11+
public class LinearGradientBuilder extends AbstractLinearGradientBuilder {
12+
13+
private final Point[] coordinates = new Point[] {new Point(), new Point()};
14+
private AffineTransform transformation = null;
15+
16+
/**
17+
* Constructs the builder instance
18+
*/
19+
public LinearGradientBuilder() {
20+
}
21+
22+
/**
23+
* Set coordinates for gradient vector ({@link AbstractLinearGradientBuilder more info})
24+
*
25+
* @param x0 the x coordinate of the vector start
26+
* @param y0 the y coordinate of the vector start
27+
* @param x1 the x coordinate of the vector end
28+
* @param y1 the y coordinate of the vector end
29+
* @return the current builder instance
30+
*/
31+
public LinearGradientBuilder setGradientVector(double x0, double y0, double x1, double y1) {
32+
this.coordinates[0].setLocation(x0, y0);
33+
this.coordinates[1].setLocation(x1, y1);
34+
return this;
35+
}
36+
37+
/**
38+
* Set the linear gradient space transformation which specifies the transformation from
39+
* the current coordinates space to gradient vector space
40+
* <p>
41+
* The current space is the one on which linear gradient will be drawn (as a fill or stroke
42+
* color for shapes on PDF canvas). This transformation mainly used for color lines skewing.
43+
*
44+
* @param transformation the {@link AffineTransform} representing the transformation to set
45+
* @return the current builder instance
46+
*/
47+
public LinearGradientBuilder setCurrentSpaceToGradientVectorSpaceTransformation(
48+
AffineTransform transformation) {
49+
this.transformation = transformation;
50+
return this;
51+
}
52+
53+
@Override
54+
public Point[] getGradientVector(Rectangle targetBoundingBox, AffineTransform contextTransform) {
55+
return this.coordinates;
56+
}
57+
58+
@Override
59+
public AffineTransform getCurrentSpaceToGradientVectorSpaceTransformation(
60+
Rectangle targetBoundingBox, AffineTransform contextTransform) {
61+
return this.transformation;
62+
}
63+
}

0 commit comments

Comments
 (0)