Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.itextpdf.svg.css;

import com.itextpdf.styledxmlparser.css.util.CssDimensionParsingUtils;
import com.itextpdf.styledxmlparser.css.util.CssTypesValidationUtils;
import com.itextpdf.svg.SvgConstants;
import com.itextpdf.svg.logs.SvgLogMessageConstant;
import com.itextpdf.svg.utils.SvgCssUtils;

import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This class converts stroke-related SVG parameters and attributes into those from PDF specification
*/
public class SvgStrokeParameterConverter {

private SvgStrokeParameterConverter() {
}

private static Logger LOGGER = LoggerFactory.getLogger(SvgStrokeParameterConverter.class);

public static PdfLineDashParameters convertStrokeDashArray(String strokeDashArray) {
if (!SvgConstants.Values.NONE.equalsIgnoreCase(strokeDashArray)) {
List<String> dashArray = SvgCssUtils.splitValueList(strokeDashArray);

for (String dashArrayItem : dashArray) {
if (CssTypesValidationUtils.isPercentageValue(dashArrayItem)) {
LOGGER.error(SvgLogMessageConstant.PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_ARE_NOT_SUPPORTED);
return null;
}
}

if (dashArray.size() > 0) {
if (dashArray.size() % 2 == 1) {
// If an odd number of values is provided, then the list of values is repeated to yield an even
// number of values. Thus, 5,3,2 is equivalent to 5,3,2,5,3,2.
dashArray.addAll(dashArray);
}
float[] dashArrayFloat = new float[dashArray.size()];
for (int i = 0; i < dashArray.size(); i++) {
dashArrayFloat[i] = CssDimensionParsingUtils.parseAbsoluteLength(dashArray.get(i));
}
return new PdfLineDashParameters(dashArrayFloat, 0);
}
}
return null;
}

public static class PdfLineDashParameters {
private float[] lengths;
private float phase;

public PdfLineDashParameters(float[] lengths, float phase) {
this.lengths = lengths;
this.phase = phase;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

PdfLineDashParameters that = (PdfLineDashParameters) o;

if (Float.compare(that.phase, phase) != 0) {
return false;
}
return Arrays.equals(lengths, that.lengths);
}

@Override
public int hashCode() {
int result = Arrays.hashCode(lengths);
result = 31 * result + (phase != +0.0f ? Float.floatToIntBits(phase) : 0);
return result;
}


public float[] getLengths() {
return lengths;
}

public float getPhase() {
return phase;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public final class SvgLogMessageConstant {
public static final String PATTERN_WIDTH_OR_HEIGHT_IS_NEGATIVE =
"Pattern width or height is negative value. This pattern will not be rendered.";

public static final String PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_ARE_NOT_SUPPORTED =
"Percentage values in 'stroke-dasharray' attribute are not supported. Attribute will be ignored completely";

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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ This file is part of the iText (R) project.
import com.itextpdf.kernel.colors.Color;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.colors.WebColors;
import com.itextpdf.kernel.geom.AffineTransform;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
Expand All @@ -62,6 +61,8 @@ This file is part of the iText (R) project.
import com.itextpdf.styledxmlparser.css.validate.CssDeclarationValidationMaster;
import com.itextpdf.svg.MarkerVertexType;
import com.itextpdf.svg.SvgConstants;
import com.itextpdf.svg.SvgConstants.Attributes;
import com.itextpdf.svg.css.SvgStrokeParameterConverter;
import com.itextpdf.svg.css.impl.SvgNodeRendererInheritanceResolver;
import com.itextpdf.svg.renderers.IMarkerCapable;
import com.itextpdf.svg.renderers.ISvgNodeRenderer;
Expand Down Expand Up @@ -320,9 +321,9 @@ void preDraw(SvgDrawContext context) {

PdfExtGState opacityGraphicsState = new PdfExtGState();
if (!partOfClipPath) {
float generalOpacity = getOpacity();
// fill
{
float generalOpacity = getOpacity();
String fillRawValue = getAttributeOrDefault(SvgConstants.Attributes.FILL, "black");
this.doFill = !SvgConstants.Values.NONE.equalsIgnoreCase(fillRawValue);

Expand All @@ -349,47 +350,9 @@ void preDraw(SvgDrawContext context) {
currentCanvas.setFillColor(fillColor);
}
}
// stroke
{
String strokeRawValue = getAttributeOrDefault(SvgConstants.Attributes.STROKE,
SvgConstants.Values.NONE);

if (!SvgConstants.Values.NONE.equalsIgnoreCase(strokeRawValue)) {
String strokeWidthRawValue = getAttribute(SvgConstants.Attributes.STROKE_WIDTH);

// 1 px = 0,75 pt
float strokeWidth = 0.75f;

if (strokeWidthRawValue != null) {
strokeWidth = CssDimensionParsingUtils.parseAbsoluteLength(strokeWidthRawValue);
}

float strokeOpacity = getOpacityByAttributeName(SvgConstants.Attributes.STROKE_OPACITY,
generalOpacity);

Color strokeColor = null;
TransparentColor transparentColor = getColorFromAttributeValue(
context, strokeRawValue, (float) ((double) strokeWidth / 2.0), strokeOpacity);
if (transparentColor != null) {
strokeColor = transparentColor.getColor();
strokeOpacity = transparentColor.getOpacity();
}
applyStrokeProperties(context, currentCanvas, opacityGraphicsState);

if (!CssUtils.compareFloats(strokeOpacity, 1f)) {
opacityGraphicsState.setStrokeOpacity(strokeOpacity);
}

// as default value for stroke is 'none' we should not set
// it in case when value obtaining fails
if (strokeColor != null) {
currentCanvas.setStrokeColor(strokeColor);
}

currentCanvas.setLineWidth(strokeWidth);

doStroke = true;
}
}
// opacity
{
if (!opacityGraphicsState.getPdfObject().isEmpty()) {
Expand Down Expand Up @@ -507,4 +470,52 @@ private float getOpacity() {

return result;
}

private void applyStrokeProperties(SvgDrawContext context, PdfCanvas currentCanvas, PdfExtGState opacityGraphicsState) {
String strokeRawValue = getAttributeOrDefault(SvgConstants.Attributes.STROKE,
SvgConstants.Values.NONE);
if (!SvgConstants.Values.NONE.equalsIgnoreCase(strokeRawValue)) {
String strokeWidthRawValue = getAttribute(SvgConstants.Attributes.STROKE_WIDTH);

// 1 px = 0,75 pt
float strokeWidth = 0.75f;

if (strokeWidthRawValue != null) {
strokeWidth = CssDimensionParsingUtils.parseAbsoluteLength(strokeWidthRawValue);
}

float generalOpacity = getOpacity();
float strokeOpacity = getOpacityByAttributeName(SvgConstants.Attributes.STROKE_OPACITY,
generalOpacity);

Color strokeColor = null;
TransparentColor transparentColor = getColorFromAttributeValue(
context, strokeRawValue, (float) ((double) strokeWidth / 2.0), strokeOpacity);
if (transparentColor != null) {
strokeColor = transparentColor.getColor();
strokeOpacity = transparentColor.getOpacity();
}

if (!CssUtils.compareFloats(strokeOpacity, 1f)) {
opacityGraphicsState.setStrokeOpacity(strokeOpacity);
}

String strokeDashArrayRawValue = getAttribute(Attributes.STROKE_DASHARRAY);
SvgStrokeParameterConverter.PdfLineDashParameters lineDashParameters =
SvgStrokeParameterConverter.convertStrokeDashArray(strokeDashArrayRawValue);
if (lineDashParameters != null) {
currentCanvas.setLineDash(lineDashParameters.getLengths(), lineDashParameters.getPhase());
}

// as default value for stroke is 'none' we should not set
// it in case when value obtaining fails
if (strokeColor != null) {
currentCanvas.setStrokeColor(strokeColor);
}

currentCanvas.setLineWidth(strokeWidth);

doStroke = true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.itextpdf.svg.css;

import com.itextpdf.svg.css.SvgStrokeParameterConverter.PdfLineDashParameters;
import com.itextpdf.svg.logs.SvgLogMessageConstant;
import com.itextpdf.test.ExtendedITextTest;
import com.itextpdf.test.annotations.LogMessage;
import com.itextpdf.test.annotations.LogMessages;

import org.junit.Assert;
import org.junit.Test;

public class SvgStrokeParameterConverterUnitTest extends ExtendedITextTest {

@Test
@LogMessages(messages = {
@LogMessage(messageTemplate =
SvgLogMessageConstant.PERCENTAGE_VALUES_IN_STROKE_DASHARRAY_ARE_NOT_SUPPORTED)})
public void testStrokeDashArrayPercentsAreNotSupported() {
Assert.assertNull(SvgStrokeParameterConverter.convertStrokeDashArray("5,3%"));
}

@Test
public void testStrokeDashArrayOddNumberOfValues() {
PdfLineDashParameters result = SvgStrokeParameterConverter.convertStrokeDashArray("5pt");
Assert.assertNotNull(result);
Assert.assertEquals(0, result.getPhase(), 0);
Assert.assertArrayEquals(new float[] {5, 5}, result.getLengths(), 1e-5f);
}

@Test
public void testEmptyStrokeDashArray() {
PdfLineDashParameters result = SvgStrokeParameterConverter.convertStrokeDashArray("");
Assert.assertNull(result);
}

}
5 changes: 5 additions & 0 deletions svg/src/test/java/com/itextpdf/svg/renderers/StrokeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public void noLineStrokeWidthTest() throws IOException, InterruptedException {
convertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "noLineStrokeWidth");
}

@Test
public void strokeWithDashesTest() throws IOException, InterruptedException {
convertAndCompare(SOURCE_FOLDER, DESTINATION_FOLDER, "strokeWithDashes");
}

@Test
//TODO: update cmp-file after DEVSIX-2258
public void advancedStrokeTest() throws IOException, InterruptedException {
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.